STM32半双工USART模式下带中断驱动DS18B20的实现求助
STM32半双工USART中断+状态机驱动DS18B20改造方案
现有基于STM32半双工USART驱动DS18B20的代码采用轮询方式实现,不符合项目要求的中断驱动需求,需要改为状态机+中断的非阻塞模式。以下是原代码及改造方案:
原轮询模式代码
void setBaudrate (uint32_t baud) { huart1.Instance = USART1; huart1.Init.BaudRate = baud; 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; if (HAL_HalfDuplex_Init(&huart1) != HAL_OK) { Error_Handler(); } } int DS18B20_start() { uint8_t data = 0xF0; setBaudrate(9600); HAL_UART_Transmit(&huart1, &data, 1, 100); if (HAL_UART_Receive(&huart1, &data, 1, 1000) != HAL_OK) return -1; setBaudrate(115200); if (data == 0xf0) return -2; return 1; } void DS18B20_write (uint8_t data) { uint8_t buffer[8]; for (int i=0; i<8; i++) { if ((data & (1<<i)) != 0) { buffer[i] = 0xFF; } else { buffer[i] = 0x00; } } HAL_UART_Transmit(&huart1, buffer, 8, 1000); } uint8_t DS18B20_read (void) { uint8_t buffer[8]; uint8_t value = 0; for (int i=0; i<8; i++) { buffer[i] = 0xFF; } HAL_UART_Transmit_DMA(&huart1, buffer, 8); HAL_UART_Receive_DMA(&huart1, RxData, 8); while (isRxed == 0); for (int i=0; i<8; i++) { if (RxData[i] == 0xFF) { value |= 1<<i; } } isRxed = 0; return value; } void measureTemp(){ static uint32_t lastMeasurementTime = 0; uint32_t currentTime = HAL_GetTick(); if((currentTime - lastMeasurementTime >= measurementInterval) || currentTime == 0){ 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 Scratchpad Temp_LSB = DS18B20_read(); Temp_MSB = DS18B20_read(); Temp = (Temp_MSB<<8) | Temp_LSB; Temperature = (float)Temp/16.0; lastMeasurementTime = currentTime;//更新测量时间 } }
改造方案:状态机+中断驱动
1. 定义状态枚举与全局变量
typedef enum { DS18B20_STATE_IDLE, DS18B20_STATE_SEND_RESET, DS18B20_STATE_WAIT_PRESENCE, DS18B20_STATE_SEND_SKIP_ROM, DS18B20_STATE_SEND_CONVERT_CMD, DS18B20_STATE_WAIT_CONVERT_DONE, DS18B20_STATE_SEND_READ_SCRATCHPAD, DS18B20_STATE_READ_LSB, DS18B20_STATE_READ_MSB, DS18B20_STATE_CALC_TEMP } DS18B20_StateTypeDef; DS18B20_StateTypeDef ds18b20_state = DS18B20_STATE_IDLE; uint8_t rx_buffer[8]; uint8_t tx_buffer[8]; uint8_t presence_flag = 0; uint16_t raw_temp = 0; float temperature = 0.0f; uint32_t measure_tick = 0; #define MEASURE_INTERVAL 1000 //1秒测量一次
2. 重写UART中断回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { switch(ds18b20_state) { case DS18B20_STATE_SEND_RESET: //发送复位后切换到等待存在信号状态 ds18b20_state = DS18B20_STATE_WAIT_PRESENCE; HAL_UART_Receive_IT(&huart1, rx_buffer, 1); break; case DS18B20_STATE_SEND_SKIP_ROM: //发送Skip ROM后,根据流程阶段切换状态 if(measure_tick == 0) { ds18b20_state = DS18B20_STATE_SEND_CONVERT_CMD; tx_buffer[0] = 0x44; //转换温度指令 HAL_UART_Transmit_IT(&huart1, tx_buffer, 1); } else { ds18b20_state = DS18B20_STATE_SEND_READ_SCRATCHPAD; tx_buffer[0] = 0xBE; //读取暂存器指令 HAL_UART_Transmit_IT(&huart1, tx_buffer, 1); } break; case DS18B20_STATE_SEND_CONVERT_CMD: //转换指令发送完成,进入等待转换阶段 ds18b20_state = DS18B20_STATE_WAIT_CONVERT_DONE; measure_tick = HAL_GetTick(); break; case DS18B20_STATE_SEND_READ_SCRATCHPAD: //读取指令发送完成,切换到读取LSB状态 ds18b20_state = DS18B20_STATE_READ_LSB; //准备读取时序的高电平信号 for(int i=0; i<8; i++) tx_buffer[i] = 0xFF; HAL_UART_Transmit_IT(&huart1, tx_buffer, 8); break; case DS18B20_STATE_READ_LSB: //LSB发送完成,启动接收并切换到读取MSB状态 ds18b20_state = DS18B20_STATE_READ_MSB; for(int i=0; i<8; i++) tx_buffer[i] = 0xFF; HAL_UART_Transmit_IT(&huart1, tx_buffer, 8); break; case DS18B20_STATE_READ_MSB: //MSB发送完成,进入温度计算阶段 ds18b20_state = DS18B20_STATE_CALC_TEMP; break; default: break; } } } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { switch(ds18b20_state) { case DS18B20_STATE_WAIT_PRESENCE: //检查传感器存在信号 presence_flag = (rx_buffer[0] != 0xF0) ? 1 : 0; if(presence_flag) { //存在信号正常,发送Skip ROM指令 ds18b20_state = DS18B20_STATE_SEND_SKIP_ROM; tx_buffer[0] = 0xCC; HAL_UART_Transmit_IT(&huart1, tx_buffer, 1); } else { //传感器不存在,回到空闲状态 ds18b20_state = DS18B20_STATE_IDLE; } //恢复波特率到115200 setBaudrate(115200); break; case DS18B20_STATE_READ_LSB: //存储LSB数据并启动接收 raw_temp &= 0xFF00; raw_temp |= rx_buffer[0]; HAL_UART_Receive_IT(&huart1, rx_buffer, 8); break; case DS18B20_STATE_READ_MSB: //存储MSB数据 raw_temp &= 0x00FF; raw_temp |= (rx_buffer[0] << 8); HAL_UART_Receive_IT(&huart1, rx_buffer, 8); break; default: break; } } }
3. 改造核心函数
void setBaudrate (uint32_t baud) { huart1.Instance = USART1; huart1.Init.BaudRate = baud; 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; if (HAL_HalfDuplex_Init(&huart1) != HAL_OK) { Error_Handler(); } } //启动一次温度测量流程 void DS18B20_StartMeasure(void) { if(ds18b20_state == DS18B20_STATE_IDLE) { setBaudrate(9600); tx_buffer[0] = 0xF0; ds18b20_state = DS18B20_STATE_SEND_RESET; HAL_UART_Transmit_IT(&huart1, tx_buffer, 1); } } //状态机处理函数,放在主循环中调用 void DS18B20_StateMachineHandler(void) { switch(ds18b20_state) { case DS18B20_STATE_WAIT_CONVERT_DONE: //等待温度转换完成(最长750ms) if(HAL_GetTick() - measure_tick >= 750) { //转换完成,重新发送复位启动读取流程 setBaudrate(9600); tx_buffer[0] = 0xF0; ds18b20_state = DS18B20_STATE_SEND_RESET; measure_tick = 1; //标记进入读取流程 HAL_UART_Transmit_IT(&huart1, tx_buffer, 1); } break; case DS18B20_STATE_CALC_TEMP: //计算最终温度值 temperature = (float)raw_temp / 16.0f; ds18b20_state = DS18B20_STATE_IDLE; measure_tick = 0; break; default: break; } }
4. 主循环中的调用
int main(void) { //系统初始化代码... HAL_UART_Receive_IT(&huart1, rx_buffer, 1); //提前开启UART接收中断 while (1) { //定时触发温度测量 static uint32_t last_measure_time = 0; if(HAL_GetTick() - last_measure_time >= MEASURE_INTERVAL) { DS18B20_StartMeasure(); last_measure_time = HAL_GetTick(); } //处理DS18B20状态机流转 DS18B20_StateMachineHandler(); //其他业务逻辑代码... } }
关键说明
- 所有UART操作均采用中断触发方式,避免阻塞主循环
- 状态机负责管理DS18B20的工作流程,每个阶段完成后通过中断回调自动切换到下一阶段
- 温度转换等待逻辑放在主循环的状态机处理中,不占用额外阻塞时间
- 波特率在复位阶段自动切换为9600,后续指令阶段恢复为115200
内容的提问来源于stack exchange,提问作者RaghRoog
相关产品推荐
相关产品推荐

