STM32F103C8T6 CubeMX HAL库驱动DS18B20从硬件连接到串口打印温度的保姆级教程第一次接触嵌入式开发的新手们面对STM32和CubeMX的组合往往会感到无从下手。本文将带你从零开始一步步完成DS18B20温度传感器的驱动开发。不同于市面上零散的教程我们不仅会覆盖硬件连接和CubeMX配置更会深入解析HAL库下的时序控制难点特别是那些让新手头疼的GPIO模式切换和延时函数问题。1. 硬件准备与连接在开始编码之前正确的硬件连接是项目成功的基础。你需要准备以下材料STM32F103C8T6最小系统板蓝色药丸板DS18B20温度传感器模块建议选择带防水探头版本杜邦线若干建议使用不同颜色区分电源、地、信号线USB转TTL串口模块用于调试信息输出连接示意图如下DS18B20引脚STM32F103C8T6连接点VCC (红线)3.3V电源GND (黑线)GNDDATA (黄线)PB5 (或其他GPIO)注意DS18B20是单总线设备DATA线需要接4.7KΩ上拉电阻。如果使用模块通常已经内置此电阻。实际连接时建议先断电操作按照以下步骤进行用红色杜邦线连接模块VCC到开发板3.3V用黑色杜邦线连接模块GND到开发板GND用黄色杜邦线连接模块DATA到PB5检查所有连接是否牢固2. CubeMX工程配置CubeMX的图形化配置大大简化了STM32的初始化工作但对于新手来说选项众多的界面也容易让人困惑。我们按步骤分解2.1 创建新工程打开CubeMX点击New Project在芯片选择器中输入STM32F103C8选择对应的型号在工程配置界面设置项目名称和存储路径将Toolchain/IDE设置为MDK-ARM如果使用Keil2.2 时钟配置时钟是STM32的心脏配置不当会导致各种奇怪问题在Pinout Configuration选项卡中进入RCC设置将High Speed Clock (HSE)设置为Crystal/Ceramic Resonator返回Clock Configuration标签页按如下参数配置输入时钟频率8MHzHCLK设置为72MHz输入72后按回车CubeMX会自动计算分频系数确保USB时钟为48MHz如果使用USB功能2.3 GPIO配置DS18B20需要精确的时序控制GPIO配置尤为关键在Pinout视图中找到PB5右键选择GPIO_Output在左侧GPIO配置中设置如下参数GPIO output level: HighGPIO mode: Output Push PullGPIO Pull-up/Pull-down: No pull-up and no pull-downMaximum output speed: HighUser Label: DS18B20_DQ2.4 串口配置为方便调试我们配置USART1用于打印温度数据在Pinout视图中找到PA9和PA10USART1_TX和USART1_RX将PA9配置为USART1_TXPA10配置为USART1_RX在Connectivity USART1中设置参数Mode: AsynchronousBaud Rate: 115200Word Length: 8 BitsParity: NoneStop Bits: 12.5 生成代码完成上述配置后点击Project Generate Code在Code Generator标签页中勾选Generate peripheral initialization as a pair of .c/.h files点击Generate Code按钮等待工程生成完成3. 代码实现与移植CubeMX生成的代码框架已经帮我们完成了硬件初始化现在需要添加DS18B20的驱动逻辑。3.1 串口重定向为方便使用printf打印调试信息需要在main.c中添加串口重定向代码/* 在main.c的/* USER CODE BEGIN PV */部分添加 */ #include stdio.h #ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif PUTCHAR_PROTOTYPE { HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, HAL_MAX_DELAY); return ch; }3.2 DS18B20驱动实现创建两个新文件ds18b20.c和ds18b20.h存放驱动代码。ds18b20.h内容#ifndef __DS18B20_H #define __DS18B20_H #include main.h #define DS18B20_DQ_PIN GPIO_PIN_5 #define DS18B20_DQ_PORT GPIOB #define DS18B20_DQ_HIGH() HAL_GPIO_WritePin(DS18B20_DQ_PORT, DS18B20_DQ_PIN, GPIO_PIN_SET) #define DS18B20_DQ_LOW() HAL_GPIO_WritePin(DS18B20_DQ_PORT, DS18B20_DQ_PIN, GPIO_PIN_RESET) #define DS18B20_DQ_READ() HAL_GPIO_ReadPin(DS18B20_DQ_PORT, DS18B20_DQ_PIN) uint8_t DS18B20_Init(void); float DS18B20_GetTemp(void); void DS18B20_Start(void); #endifds18b20.c核心函数实现#include ds18b20.h #include tim.h // 用于精确延时 static void DS18B20_DelayUs(uint16_t us) { __HAL_TIM_SET_COUNTER(htim1, 0); while(__HAL_TIM_GET_COUNTER(htim1) us); } static void DS18B20_SetInput(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin DS18B20_DQ_PIN; GPIO_InitStruct.Mode GPIO_MODE_INPUT; GPIO_InitStruct.Pull GPIO_NOPULL; HAL_GPIO_Init(DS18B20_DQ_PORT, GPIO_InitStruct); } static void DS18B20_SetOutput(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin DS18B20_DQ_PIN; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(DS18B20_DQ_PORT, GPIO_InitStruct); } uint8_t DS18B20_Init(void) { DS18B20_SetOutput(); DS18B20_DQ_HIGH(); HAL_Delay(1); DS18B20_DQ_LOW(); DS18B20_DelayUs(480); DS18B20_DQ_HIGH(); DS18B20_DelayUs(60); DS18B20_SetInput(); uint8_t retry 0; while(DS18B20_DQ_READ() retry 200) { retry; DS18B20_DelayUs(1); } if(retry 200) return 1; retry 0; while(!DS18B20_DQ_READ() retry 240) { retry; DS18B20_DelayUs(1); } if(retry 240) return 2; return 0; }3.3 温度读取逻辑继续在ds18b20.c中添加温度读取函数static void DS18B20_WriteByte(uint8_t dat) { DS18B20_SetOutput(); for(uint8_t i 0; i 8; i) { DS18B20_DQ_LOW(); DS18B20_DelayUs(2); if(dat 0x01) DS18B20_DQ_HIGH(); else DS18B20_DQ_LOW(); DS18B20_DelayUs(60); DS18B20_DQ_HIGH(); dat 1; } } static uint8_t DS18B20_ReadByte(void) { uint8_t value 0; for(uint8_t i 0; i 8; i) { DS18B20_SetOutput(); DS18B20_DQ_LOW(); DS18B20_DelayUs(2); DS18B20_DQ_HIGH(); DS18B20_DelayUs(10); DS18B20_SetInput(); if(DS18B20_DQ_READ()) value | 0x01 i; DS18B20_DelayUs(50); } return value; } void DS18B20_Start(void) { DS18B20_Init(); DS18B20_WriteByte(0xCC); // Skip ROM DS18B20_WriteByte(0x44); // Convert T } float DS18B20_GetTemp(void) { uint8_t tempL, tempH; int16_t temp; DS18B20_Start(); HAL_Delay(750); // 等待转换完成 DS18B20_Init(); DS18B20_WriteByte(0xCC); // Skip ROM DS18B20_WriteByte(0xBE); // Read Scratchpad tempL DS18B20_ReadByte(); tempH DS18B20_ReadByte(); temp (tempH 8) | tempL; return temp * 0.0625; // 12位精度每个LSB为0.0625°C }4. 主程序与调试技巧4.1 主函数实现在main.c中添加温度读取逻辑/* 在main.c的/* USER CODE BEGIN Includes */部分添加 */ #include ds18b20.h /* 在main函数中添加 */ int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); MX_TIM1_Init(); // 初始化用于延时的TIM1 printf(DS18B20 Temperature Test\r\n); if(DS18B20_Init()) { printf(DS18B20 Init Failed!\r\n); while(1); } printf(DS18B20 Init Success!\r\n); while (1) { float temp DS18B20_GetTemp(); printf(Temperature: %.2f°C\r\n, temp); HAL_Delay(1000); } }4.2 常见问题排查在实际项目中你可能会遇到以下问题读取温度始终为85°C这是DS18B20的上电默认值检查初始化时序是否正确确保在读取前有足够的转换时间750ms读取值波动大或不准确检查电源是否稳定建议在VCC和GND之间加0.1uF电容确保上拉电阻正常工作约4.7KΩ检查连接线是否过长建议不超过20米HAL库延时不够精确使用定时器实现微秒级延时如文中使用的TIM1避免在中断中使用HAL_DelayGPIO模式切换问题每次读写操作前都要正确设置GPIO输入/输出模式输出模式下要设置合适的输出速度4.3 性能优化建议当系统需要更高性能时可以考虑以下优化使用DMA传输串口数据减少CPU占用在等待温度转换时让MCU进入低功耗模式实现多路DS18B20的并联读取添加CRC校验确保数据可靠性// 示例带CRC校验的温度读取 uint8_t DS18B20_CheckCRC(uint8_t *data, uint8_t len) { uint8_t crc 0; for(uint8_t i 0; i len; i) { uint8_t inbyte data[i]; for(uint8_t j 0; j 8; j) { uint8_t mix (crc ^ inbyte) 0x01; crc 1; if(mix) crc ^ 0x8C; inbyte 1; } } return crc; }通过本教程你应该已经掌握了在STM32F103上使用HAL库驱动DS18B20的完整流程。实际开发中建议先用示波器观察单总线时序确保信号质量。遇到问题时可以从最简单的读写测试开始逐步排查。