DigiKey好物畅享】ST NUCLEO-U385RG-Q开发板 检测环境温湿度

这块板子是ST的,对我而言使用STM32CubeIDE是最好的选择。不过在进行开发之前,我想先下载对应的SDK程序包,然而用之前安装的CubeMX找不到STM32U385的示例程序包,抱着试试看的想法,下载最新的CubeMX6.16.1,才获得成功。于是索性连CubeIDE也使用最新版本的,2.0版的。
按照惯例,先在CubeIDE中导入GPIO_IOToggle工程进行测试,目的是确认该开发板的编译、下载、运行环境是否通顺。示例程序包中刚好有专门适用于NUCLEO-U385RG-Q开发板的

经过验证,下载程序和运行没有任何问题。
接下来,处理显示方面的代码。出于节省GPIO的考量,使用I2C方式驱动0.96寸的OLED作为显示部件。板子上提供了I2C2引出脚,

CN10以及CN5共有的PB13、PB14可以作为SCL、SDA使用。作为GPIO复用,对应于I2C2外设。把CubeMX中生成的设计外设设置、初始化代码拷贝到新的工程中,同时在工程中引入OLED的处理代码。因为测量温湿度的传感器GXHT30也是用I2C总线,且与OLED的I2C地址分配不一样,所以也同时把处理温湿度传感器GXHT30的代码也一并加进来。
主程序代码如下:

///* USER CODE BEGIN Header */
///**
// ******************************************************************************
// * @file    GPIO/GPIO_IOToggle/Src/main.c
// * @author  MCD Application Team
// * @brief   This example describes how to configure and use GPIOs through
// *          the STM32WBAxx HAL API.
// ******************************************************************************
// * @attention
// *
// * Copyright (c) 2024 STMicroelectronics.
// * All rights reserved.
// *
// * This software is licensed under terms that can be found in the LICENSE file
// * in the root directory of this software component.
// * If no LICENSE file comes with this software, it is provided AS-IS.
// *
// ******************************************************************************
// */
#include "main.h"
#include "ssd1306.h"

//========================================OLED + GXHT30===============================================
/* main.c */
#include "main.h"
#include "ssd1306.h"


#define ADDR_GXHT30 		0x88
#define PUTCHAR_PROTOTYPE 	int __io_putchar(int ch)
#define RXBUFFERSIZE_COM   1024
#define DELAY_TIME			2000

I2C_HandleTypeDef hi2c3;
uint8_t i2c_rx_buffer[I2C3_RX_BUFFER_SIZE] = {0};
volatile uint8_t i2c_rx_len = 0;
volatile uint8_t i2c_data_ready = 0;

// 数据结构
typedef struct {
	float temperature;
	float humidity;
} GXHT30_HandleTypeDef;


// 数据结构
typedef struct {
	char TChar;				// 温度标识表示符号
	char TColon;			// 冒号
	char TVal[5];			// 温度数值,固定为5位

	char p1;				// 逗号分隔符

	char HChar;				// 湿度温度标识表示符号
	char HColon;			// 冒号
	char HVal[5];			// 温度数值,固定为5位

	char p2;				// 逗号分隔符

	char LChar;				// 湿度温度标识表示符号
	char LColon;			// 冒号
	char LVal[5];			// 温度数值,固定为5位

	char dumy[1024];		// 垃圾数据,防止拷贝数据时溢出
} K10_HandleTypeDef;

// 串口有收发数据标志
__IO ITStatus UartReady = RESET;

I2C_HandleTypeDef hi2c2;
UART_HandleTypeDef huart1;

// 串口接收数据缓冲区
uint8_t rxBuffer[RXBUFFERSIZE_COM];
uint8_t rxBufferCopy[RXBUFFERSIZE_COM];
// 串口接收数据指针
uint32_t rxpos = 0;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C2_Init(void);
static void MX_USART1_UART_Init(void);


void readGXHT30(void);
uint8_t GXHT30_ReadTemperatureHumidity(GXHT30_HandleTypeDef *hgxht);
uint8_t GXHT30_CheckCRC(uint8_t *data, uint8_t len, uint8_t crc);
void GXHT30_FloatToString(float value, uint8_t *buffer, uint8_t decimal_places);


uint32_t tick1 = 0;

int main(void) {

	HAL_Init();
	SystemClock_Config();
	MX_GPIO_Init();
	MX_I2C2_Init();
	MX_USART1_UART_Init();

	// 初始化OLED
	OLED_Init();  // 使用I2C2

	// 清屏
	OLED_Clear(0);

	//printf("Test...\r\n");

	// 显示测试内容
	OLED_ShowString(0, 0, (uint8_t*) "Hello,EEWorld!", 8, 1);
	OLED_ShowString(0, 8, (uint8_t*) "Hello DigiKey!", 8, 1);

	HAL_Delay(3000);
	OLED_Clear(0);
	OLED_ShowString(0, 0, (uint8_t*) "T : ", 8, 1);
	OLED_ShowString(0, 8, (uint8_t*) "H : ", 8, 1);

	// 初始时刻
	tick1 = HAL_GetTick();
	while (1) {
		while ((HAL_GetTick() - tick1) >= DELAY_TIME) {
			// 发动指令,读取来自行空板的数据
			printf("i\r\n");

			// 读取温湿度传感器的数据
			readGXHT30();

			tick1 = HAL_GetTick();
		}
	}

}

// 读取温湿度传感器的数据
void readGXHT30(void) {
	GXHT30_HandleTypeDef hgxht30;
	uint8_t str[32] = { '\0' };

	OLED_ShowString(0, 16, (uint8_t*) "Read ...", 8, 1);
	//HAL_Delay(100);
	if (GXHT30_ReadTemperatureHumidity(&hgxht30) == 0) {
		OLED_ShowString(0, 16, (uint8_t*) "Read OK ", 8, 1);

		// 温度单位:℃,湿度单位:%
		float temperature = hgxht30.temperature;
		float humidity = hgxht30.humidity;

		GXHT30_FloatToString(temperature, str, 2);
		OLED_ShowString(20, 0, str, 8, 1);
		//printf("T:%s\r\n", str);

		//// 测试显示
		//OLED_ShowString(60, 0, str, 8, 1);



		GXHT30_FloatToString(humidity, str, 2);
		OLED_ShowString(20, 8, str, 8, 1);
		//printf("H:%s\r\n", str);

		//// 测试显示
		//OLED_ShowString(60, 8, str, 8, 1);

	}
}

/**
 * @brief System Clock Configuration
 * @retval None
 */
void SystemClock_Config(void) {
	RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
	RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };

	/** Configure the System Power Supply
	 */
	if (HAL_PWREx_ConfigSupply(PWR_SMPS_SUPPLY) != HAL_OK) {
		Error_Handler();
	}

	/** Configure the main internal regulator output voltage
	 */
	if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE2)
			!= HAL_OK) {
		Error_Handler();
	}

	/** Initializes the CPU, AHB and APB buses clocks
	 */
	RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSIS;
	RCC_OscInitStruct.MSISState = RCC_MSI_ON;
	RCC_OscInitStruct.MSISSource = RCC_MSI_RC0;
	RCC_OscInitStruct.MSISDiv = RCC_MSI_DIV8;
	if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
		Error_Handler();
	}

	/** Initializes the CPU, AHB and APB buses clocks
	 */
	RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
			| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_PCLK3;
	RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSIS;
	RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
	RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
	RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
	RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;

	if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) {
		Error_Handler();
	}
}

/**
 * @brief I2C2 Initialization Function
 * @param None
 * @retval None
 */
static void MX_I2C2_Init(void) {

	/* USER CODE BEGIN I2C2_Init 0 */

	/* USER CODE END I2C2_Init 0 */

	/* USER CODE BEGIN I2C2_Init 1 */

	/* USER CODE END I2C2_Init 1 */
	hi2c2.Instance = I2C2;
	hi2c2.Init.Timing = 0x00200410;
	hi2c2.Init.OwnAddress1 = 0;
	hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
	hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
	hi2c2.Init.OwnAddress2 = 0;
	hi2c2.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
	hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
	hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
	if (HAL_I2C_Init(&hi2c2) != HAL_OK) {
		Error_Handler();
	}

	/** Configure Analogue filter
	 */
	if (HAL_I2CEx_ConfigAnalogFilter(&hi2c2, I2C_ANALOGFILTER_ENABLE)
			!= HAL_OK) {
		Error_Handler();
	}

	/** Configure Digital filter
	 */
	if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0) != HAL_OK) {
		Error_Handler();
	}
	/* USER CODE BEGIN I2C2_Init 2 */

	/* USER CODE END I2C2_Init 2 */

}

/**
 * @brief USART1 Initialization Function
 * @param None
 * @retval None
 */
static void MX_USART1_UART_Init(void) {

	/* USER CODE BEGIN USART1_Init 0 */

	/* USER CODE END USART1_Init 0 */

	/* USER CODE BEGIN USART1_Init 1 */

	/* USER CODE END USART1_Init 1 */
	huart1.Instance = USART1;
	huart1.Init.BaudRate = 115200;
	huart1.Init.WordLength = UART_WORDLENGTH_8B;
	huart1.Init.StopBits = UART_STOPBITS_1;
	huart1.Init.Parity = UART_PARITY_NONE;
	huart1.Init.Mode = UART_MODE_TX_RX;
	huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart1.Init.OverSampling = UART_OVERSAMPLING_16;
	huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
	huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
	huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
	if (HAL_UART_Init(&huart1) != HAL_OK) {
		Error_Handler();
	}
	if (HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8)
			!= HAL_OK) {
		Error_Handler();
	}
	if (HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8)
			!= HAL_OK) {
		Error_Handler();
	}
	if (HAL_UARTEx_DisableFifoMode(&huart1) != HAL_OK) {
		Error_Handler();
	}

}

/**
 * @brief GPIO Initialization Function
 * @param None
 * @retval None
 */
static void MX_GPIO_Init(void) {
	/* USER CODE BEGIN MX_GPIO_Init_1 */

	/* USER CODE END MX_GPIO_Init_1 */

	/* GPIO Ports Clock Enable */
	__HAL_RCC_GPIOB_CLK_ENABLE();
	__HAL_RCC_GPIOA_CLK_ENABLE();

	/* USER CODE BEGIN MX_GPIO_Init_2 */

	/* USER CODE END MX_GPIO_Init_2 */
}

/**
 * @brief  读取温湿度数据
 * @param  hgxht: GXHT30句柄
 * @retval 状态
 */
uint8_t GXHT30_ReadTemperatureHumidity(GXHT30_HandleTypeDef *hgxht) {
	uint8_t cmd[] = { 0x2C, 0x06 };
	uint8_t data[10] = { '\0' };
	uint16_t temp_raw, hum_raw;

//	// 测量命令
//	#define GXHT30_MEAS_HIGHREP_STRETCH  0x2C06  // 高重复性,时钟拉伸
//	#define GXHT30_MEAS_MEDREP_STRETCH   0x2C0D  // 中重复性,时钟拉伸
//	#define GXHT30_MEAS_LOWREP_STRETCH   0x2C10  // 低重复性,时钟拉伸
//	#define GXHT30_MEAS_HIGHREP          0x2400  // 高重复性
//	#define GXHT30_MEAS_MEDREP           0x240B  // 中重复性
//	#define GXHT30_MEAS_LOWREP           0x2416  // 低重复性

	if (HAL_I2C_Master_Transmit(&hi2c2, ADDR_GXHT30, cmd, 2, 1000) != HAL_OK)
		return 1;

	// 等待测量完成
	HAL_Delay(500);

	// 读取6字节数据
	if (HAL_I2C_Master_Receive(&hi2c2, ADDR_GXHT30 + 1, data, 6, 1000) != HAL_OK)
		return 1;

	// 检查CRC
	if (!GXHT30_CheckCRC(&data[0], 2, data[2]) || !GXHT30_CheckCRC(&data[3], 2, data[5]))
		return 2;

	// 转换温度数据
	temp_raw = (data[0] << 8) | data[1];
	hgxht->temperature = -45.0 + 175.0 * ((float) temp_raw / 65535.0);

	// 转换湿度数据
	hum_raw = (data[3] << 8) | data[4];
	hgxht->humidity = 100.0 * ((float) hum_raw / 65535.0);

	return 0;
}

/**
 * @brief  CRC校验
 * @param  data: 数据指针
 * @param  len: 数据长度
 * @param  crc: 接收到的CRC值
 * @retval 校验结果:1-成功,0-失败
 */
uint8_t GXHT30_CheckCRC(uint8_t *data, uint8_t len, uint8_t crc) {
	uint8_t crc_calc = 0xFF;
	uint8_t bit;

	for (uint8_t i = 0; i < len; i++) {
		crc_calc ^= data[i];
		for (bit = 0; bit < 8; bit++) {
			if (crc_calc & 0x80)
				crc_calc = (crc_calc << 1) ^ 0x31;
			else
				crc_calc <<= 1;
		}
	}

	return (crc_calc == crc);
}

/**
 * @brief  浮点数转字符串
 * @param  value: 浮点数值
 * @param  buffer: 输出缓冲区
 * @param  decimal_places: 小数位数
 * @retval 无
 */
void GXHT30_FloatToString(float value, uint8_t *buffer, uint8_t decimal_places) {
	if (buffer == NULL)
		return;

	// 处理负号
	uint8_t is_negative = 0;
	if (value < 0) {
		is_negative = 1;
		value = -value;
	}

	// 转换为整数部分和小数部分
	int32_t integer_part = (int32_t) value;
	float fractional_part = value - integer_part;

	// 计算小数部分(根据指定的小数位数)
	int32_t fractional_int = (int32_t) (fractional_part
			* pow(10, decimal_places));

	// 构建字符串
	if (is_negative) {
		sprintf(buffer, "-%ld.%0*ld", integer_part, decimal_places,fractional_int);
	} else {
		sprintf(buffer, "%ld.%0*ld", integer_part, decimal_places,fractional_int);
	}
}

/* USER CODE BEGIN 4 */
/**
 * @brief  Tx Transfer completed callback
 * @param  UartHandle: UART handle.
 * @note   This example shows a simple way to report end of IT Tx transfer, and
 *         you can add your own implementation.
 * @retval None
 */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle) {
	/* Set transmission flag: transfer complete */
	//UartReady = SET;
	//BSP_LED_Toggle(LD2);

}


/**
 * @brief  UART error callbacks
 * @param  UartHandle: UART handle
 * @note   This example shows a simple way to report transfer error, and you can
 *         add your own implementation.
 * @retval None
 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *UartHandle) {
	Error_Handler();
}

/**
 * @brief  Retargets the C library printf function to the USART.
 * @param  None
 * @retval None
 */
PUTCHAR_PROTOTYPE {
	/* Place your implementation of fputc here */
	/* e.g. write a character to the USART1 and Loop until the end of transmission */
	HAL_UART_Transmit(&huart1, (uint8_t*) &ch, 1, 0xFFFF);

	return ch;
}

/**
 * @brief  This function is executed in case of error occurrence.
 * @retval None
 */
void Error_Handler(void) {
	/* User can add his own implementation to report the HAL error return state */
	//__disable_irq();
	//while (1) {
	//}

}


#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
    ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* Infinite loop */
  while (1)
  {
  }

  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */



编译程序、下载到开发板上,运行正常。每隔固定的周期间隔,将GXHT30传感器得到的温湿度数据,在OLED显示屏上显示出来。