STM32H7平台DS18B20温度传感器读数异常求助
问题
在STM32H7开发板上使用DS18B20温度传感器,需求为:正确读取温度并通过Putty显示,当温度超过45℃时输出"Heat Warning"提示。
当前代码如下:
#include "main.h" #include "string.h" /* Private variables ---------------------------------------------------------*/ ADC_HandleTypeDef hadc1; ADC_HandleTypeDef hadc2; ADC_HandleTypeDef hadc3; FDCAN_HandleTypeDef hfdcan1; FDCAN_HandleTypeDef hfdcan2; I2C_HandleTypeDef hi2c4; LTDC_HandleTypeDef hltdc; QSPI_HandleTypeDef hqspi; RTC_HandleTypeDef hrtc; SAI_HandleTypeDef hsai_BlockA2; SAI_HandleTypeDef hsai_BlockB2; MMC_HandleTypeDef hmmc1; SPI_HandleTypeDef hspi2; UART_HandleTypeDef huart3; PCD_HandleTypeDef hpcd_USB_OTG_FS; SDRAM_HandleTypeDef hsdram1; /* USER CODE BEGIN PV */ #define BUFSIZE 256 #define DS18B20_PORT GPIOA #define DS18B20_PIN GPIO_PIN_1 uint8_t SendBuffer[BUFSIZE]; int Counter; int KeyState=0; int TouchX, TouchY; uint8_t dataBuffer[10]; uint8_t VendorID, DeviceMode, Gesture, Status, Touchxh, Touchxl, Touchyh, Touchyl; //uint8_t temp_lsb, temp_msb; //int16_t temp_raw; //float temperature; uint8_t Temp_byte1, Temp_byte2; uint16_t SUM, TEMP; float Temperature = 0; float Humidity = 0; uint8_t Presence = 0;// Store temperature value HAL_StatusTypeDef retval; uint8_t Answer; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void Display_Temp (float Temp); void Set_Pin_Output (GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); void Set_Pin_Input (GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); uint8_t DS18B20_Start (void); void DS18B20_Write (uint8_t data); uint8_t DS18B20_Read (void); void SystemClock_Config(void); void PeriphCommonClock_Config(void); static void MX_GPIO_Init(void); static void MX_ADC1_Init(void); static void MX_ADC2_Init(void); static void MX_ADC3_Init(void); static void MX_FDCAN1_Init(void); static void MX_FDCAN2_Init(void); static void MX_FMC_Init(void); static void MX_LTDC_Init(void); static void MX_QUADSPI_Init(void); static void MX_RTC_Init(void); static void MX_SAI2_Init(void); static void MX_SDMMC1_MMC_Init(void); static void MX_SPI2_Init(void); static void MX_USART3_UART_Init(void); static void MX_USB_OTG_FS_PCD_Init(void); static void MX_I2C4_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { HAL_Init(); SystemClock_Config(); PeriphCommonClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); MX_ADC2_Init(); MX_ADC3_Init(); MX_FDCAN1_Init(); MX_FDCAN2_Init(); MX_FMC_Init(); MX_LTDC_Init(); MX_QUADSPI_Init(); MX_RTC_Init(); MX_SAI2_Init(); MX_SPI2_Init(); MX_USART3_UART_Init(); MX_USB_OTG_FS_PCD_Init(); MX_I2C4_Init(); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, 1); DS18B20_Init(); while (1) { Presence = DS18B20_Start (); DS18B20_Write (0xCC); // skip ROM DS18B20_Write (0x44); // convert t Presence = DS18B20_Start (); DS18B20_Write (0xCC); // skip ROM DS18B20_Write (0xBE); // Read Scratch-pad Temp_byte1 = DS18B20_Read(); Temp_byte2 = DS18B20_Read(); printf("Raw Temp_byte1: %u, Temp_byte2: %u\n", Temp_byte1, Temp_byte2); TEMP = ((Temp_byte2<<8))|Temp_byte1; Temperature = (float)TEMP/16.0; // resolution is 0.0625 HAL_Delay(3000); Display_Temp(Temperature); } /* USER CODE END 3 */ } uint8_t DS18B20_Start (void) { uint8_t Response = 0; Set_Pin_Output(DS18B20_PORT, DS18B20_PIN); // set the pin as output HAL_GPIO_WritePin (DS18B20_PORT, DS18B20_PIN, 0); // pull the pin low HAL_Delay(480); // delay according to datasheet Set_Pin_Input(DS18B20_PORT, DS18B20_PIN); HAL_Delay(80); // delay according to datasheet if (!(HAL_GPIO_ReadPin (DS18B20_PORT, DS18B20_PIN))) Response = 1; // if the pin is low i.e the presence pulse is detected else Response = -1; HAL_Delay (400); // 480 us delay totally. return Response; } void DS18B20_Write (uint8_t data) { Set_Pin_Output(DS18B20_PORT, DS18B20_PIN); // set as output for (int i=0; i<8; i++) { if ((data & (1<<i))!=0) // if the bit is high { // write 1 Set_Pin_Output(DS18B20_PORT, DS18B20_PIN); // set as output HAL_GPIO_WritePin (DS18B20_PORT, DS18B20_PIN, 0); // pull the pin LOW HAL_Delay (1); // wait for 1 us Set_Pin_Input(DS18B20_PORT, DS18B20_PIN); // set as input HAL_Delay (50); // wait for 60 us } else // if the bit is low { // write 0 Set_Pin_Output(DS18B20_PORT, DS18B20_PIN); HAL_GPIO_WritePin (DS18B20_PORT, DS18B20_PIN, 0); // pull the pin LOW HAL_Delay (50); // wait for 60 us Set_Pin_Input(DS18B20_PORT, DS18B20_PIN); } } } uint8_t DS18B20_Read (void) { uint8_t value=0; Set_Pin_Input (DS18B20_PORT, DS18B20_PIN); for (int i=0;i<8;i++) { Set_Pin_Output(DS18B20_PORT, DS18B20_PIN); // set as output HAL_GPIO_WritePin (GPIOA, GPIO_PIN_1, 0); // pull the data pin LOW HAL_Delay (2); // wait for 2 us Set_Pin_Input(DS18B20_PORT, DS18B20_PIN); // set as input if (HAL_GPIO_ReadPin (GPIOA, GPIO_PIN_1)) // if the pin is HIGH { value |= 1<<i; // read = 1 } HAL_Delay (60); // wait for 60 us } return value; } void Set_Pin_Output (GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOx, &GPIO_InitStruct); } void Set_Pin_Input (GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOx, &GPIO_InitStruct); } void Display_Temp (float Temp) { // char str[20] = {0}; // lcd_put_cur(0, 0); // // sprintf (str, "TEMP:- %.2f ", Temp); // lcd_send_string(str); // lcd_send_data('C'); // Display temperature and check for heat warning if (Temp > 40.0) { snprintf(SendBuffer, BUFSIZE, "Heat Warning! Current Temperature: %.2f°C\n\r", Temp); } else { snprintf(SendBuffer, BUFSIZE, "Current Temperature: %.2f°C\n\r", Temp); } HAL_UART_Transmit(&huart3, SendBuffer, strlen(SendBuffer), 100); } /** * @brief System Clock Configuration * @retval None */
尝试将引脚改为模拟模式(替代输入输出模式)后,出现异常读数:
Heat Warning! Current Temperature: 2272.25°C
Heat Warning! Current Temperature: 1055.44°C
Heat Warning! Current Temperature: 2231.31°C
问题排查与解决方法
1. 核心问题:错误使用模拟模式引脚
DS18B20采用单总线通信协议,需要引脚在输入/输出模式之间切换,实现电平的读写控制。模拟模式下,引脚被配置为ADC采样用,无法进行GPIO的电平输出和输入检测,导致读取的Temp_byte1、Temp_byte2为随机无效值,计算出的温度自然异常。
解决:
立即恢复引脚的输入/输出模式配置,删除所有将该引脚设置为模拟模式的代码,保留原代码中Set_Pin_Input和Set_Pin_Output的逻辑。
2. 修复时序问题:替换毫秒级延时而为微秒级
DS18B20对通信时序要求极高(比如写1需要拉低引脚1us,读操作需要拉低2us后释放),但当前代码使用的HAL_Delay()是毫秒级延时(最小延时1ms),远大于协议要求的微秒级时长,会导致通信时序混乱,读取错误数据。
解决:
实现微秒级延时函数,比如利用定时器实现精确延时:
// 初始化TIM6,配置时钟为1MHz(1us计数一次) void delay_us(uint16_t us) { __HAL_TIM_SET_COUNTER(&htim6, 0); while(__HAL_TIM_GET_COUNTER(&htim6) < us); }
然后将代码中所有HAL_Delay(1)、HAL_Delay(2)、HAL_Delay(50)等替换为对应的微秒延时,比如:
HAL_Delay(480)→delay_us(480)HAL_Delay(1)→delay_us(1)HAL_Delay(2)→delay_us(2)
3. 修正引脚硬编码问题
在DS18B20_Read()函数中,硬编码使用了GPIOA, GPIO_PIN_1,而不是宏定义的DS18B20_PORT和DS18B20_PIN,后续修改引脚时会导致逻辑不一致。
解决:
修改DS18B20_Read()中的对应代码:
HAL_GPIO_WritePin (DS18B20_PORT, DS18B20_PIN, 0); // 替换原硬编码引脚 // ... if (HAL_GPIO_ReadPin (DS18B20_PORT, DS18B20_PIN)) // 替换原硬编码引脚
4. 增加传感器应答检测
当前代码未判断DS18B20_Start()的返回值,如果传感器未正常应答(Presence≠1),仍会继续读取并计算温度,导致无效数据输出。
解决:
在主循环中增加应答判断:
while (1) { Presence = DS18B20_Start (); if (Presence != 1) { snprintf(SendBuffer, BUFSIZE, "DS18B20 No Response!\n\r"); HAL_UART_Transmit(&huart3, SendBuffer, strlen(SendBuffer), 100); HAL_Delay(3000); continue; } DS18B20_Write (0xCC); // skip ROM DS18B20_Write (0x44); // convert t HAL_Delay(750); // 等待温度转换完成(最长750ms,根据分辨率调整) Presence = DS18B20_Start (); if (Presence != 1) { snprintf(SendBuffer, BUFSIZE, "DS18B20 No Response!\n\r"); HAL_UART_Transmit(&huart3, SendBuffer, strlen(SendBuffer), 100); HAL_Delay(3000); continue; } DS18B20_Write (0xCC); // skip ROM DS18B20_Write (0xBE); // Read Scratch-pad Temp_byte1 = DS18B20_Read(); Temp_byte2 = DS18B20_Read(); printf("Raw Temp_byte1: %u, Temp_byte2: %u\n", Temp_byte1, Temp_byte2); TEMP = ((Temp_byte2<<8))|Temp_byte1; Temperature = (float)TEMP/16.0; // resolution is 0.0625 Display_Temp(Temperature); HAL_Delay(3000); }
5. 修正温度阈值
需求是温度超过45℃时提示,但当前Display_Temp()函数中判断的是Temp > 40.0,不符合需求。
解决:
修改阈值判断:
if (Temp > 45.0) { snprintf(SendBuffer, BUFSIZE, "Heat Warning! Current Temperature: %.2f°C\n\r", Temp); } else { snprintf(SendBuffer, BUFSIZE, "Current Temperature: %.2f°C\n\r", Temp); }
内容的提问来源于stack exchange,提问作者Red Smurf

