历史上的今天
今天是:2025年07月26日(星期六)
2021年07月26日 | STM32CubeMX | 33-使用GPIO读取温度传感器数据(DS18B20)
2021-07-26 来源:eefocus
本篇详细的记录了如何使用STM32CubeMX配置 STM32103RET6 的硬件GPIO外设读取温度传感器DS18B20的数据。
硬件准备
开发板
首先需要准备一个开发板,这里我准备的是一个工业DTU,主控芯片为STM32103RBT6。

DS18B20
DTU开发板板载一颗DS18B20温度传感器。

选择芯片型号
打开STM32CubeMX,打开MCU选择器:
搜索并选中芯片STM32F103RET6:
配置时钟源
如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;
如果使用默认内部时钟(HSI),这一步可以略过;
这里我都使用外部时钟:
配置调试选项
STM32CubMX配置默认没有开启调试选项,需要手动开启:
配置串口
开发板板载了一个CH340换串口,连接到USART1,可用于日志打印:
接下来开始配置USART1:
配置DS18B20通信GPIO
查看开发板原理图,找到与DS18B20通信的GPIO:
配置PA1引脚:
配置时钟树
STM32F103RET6的最高主频到72M,使HCLK = 72Mhz即可:
生成工程设置

代码生成设置
最后设置生成独立的初始化文件:
生成代码
点击GENERATE CODE即可生成MDK-V5工程:

3. 重定向printf到串口1
在usart.c文件末尾添加重定向代码:
/* USER CODE BEGIN 1 */
#if 1
#include int fputc(int ch, FILE *stream) { /* 堵塞判断串口是否发送完成 */ while((USART1->SR & 0X40) == 0); /* 串口发送完成,将该字符发送 */ USART1->DR = (uint8_t) ch; return ch; } #endif /* USER CODE END 1 */ 详细说明请参考文章:STM32CubeMX_09 | 重定向printf函数到串口输出的多种方法。 4. 编写DS18B20驱动代码 4.1. STM32微妙级延时函数 DS18B20是单总线驱动,所以需要精确的us级延时函数,参考文章: 一种Cortex-M内核中的精确延时方法 4.2. DS18B20底层操作函数封装 主要包括: GPIO输出模式配置 GPIO输出高电平 GPIO输出低电平 GPIO输入模式配置 GPIO读取输入 us级延时函数 在ds18b20.h中编写代码,使用宏来封装,便于驱动移植: #include "stm32f1xx.h" #include "core_delay/core_delay.h" /* DS18B20控制引脚 */ #define DS18B20_GPIO_PORT GPIOA #define DS18B20_GPIO_PIN GPIO_PIN_1 /* DS18B20控制IO模式配置 */ #define DS18B20_OutPut_Mode() {DS18B20_GPIO_PORT->CRL &= 0x0FFFFFFF;DS18B20_GPIO_PORT->CRL |= 0x30000000;} #define DS18B20_InPut_Mode() {DS18B20_GPIO_PORT->CRL &= 0x0FFFFFFF;DS18B20_GPIO_PORT->CRL |= 0x80000000;} /* DS18B20控制IO操作函数 */ #define DS18B20_Out(n) (n?HAL_GPIO_WritePin(DS18B20_GPIO_PORT,DS18B20_GPIO_PIN,GPIO_PIN_SET):HAL_GPIO_WritePin(DS18B20_GPIO_PORT,DS18B20_GPIO_PIN,GPIO_PIN_RESET)) #define DS18B20_In() HAL_GPIO_ReadPin(DS18B20_GPIO_PORT,DS18B20_GPIO_PIN) /* DS18B20 us 级延时函数 */ #define DS18B20_Delay_us(n) CPU_TS_Tmr_Delay_US(n) 4.3. DS18B20操作时序实现 DS18B20主要有几个操作时序,在ds18b20.c中实现: ① 复位信号: 实现代码如下: /** * @brief 发送复位信号 * @param none * @retval none */ static void DS18B20_Send_Reset_Single(void) { DS18B20_OutPut_Mode(); /* 拉低总线 480 - 960 us*/ DS18B20_Out(0); DS18B20_Delay_us(750); /* 释放总线 15 - 60 us */ DS18B20_Out(1); DS18B20_Delay_us(15); } /** * @brief 检测DS18B20存在脉冲 * @param none * @retval 0 DS18B20设备正常 * @retval 1 DS18B20设备响应复位信号失败 * @retval 2 DS18B20设备释放总线失败 */ static uint8_t DS18B20_Check_Ready_Single(void) { uint8_t cnt = 0; /* 1.检测存在脉冲 */ DS18B20_InPut_Mode(); //等待DS18B20 拉低总线 (60~240 us 响应复位信号) while (DS18B20_In() && cnt < 240) { DS18B20_Delay_us(1); cnt++; } if (cnt > 240) { return 1; } /* 2.检测DS18B20是否释放总线 */ cnt = 0; DS18B20_InPut_Mode(); //判断DS18B20是否释放总线(60~240 us 响应复位信号之后会释放总线) while ((!DS18B20_In()) && cnt<240) { DS18B20_Delay_us(1); cnt++; } if (cnt > 240) { return 2; } else { return 0; } } /** * @brief 检测DS18B20设备是否正常 * @param none * @retval 0 DS18B20设备正常 * @retval 1 DS18B20设备响应复位信号失败 * @retval 2 DS18B20设备释放总线失败 */ static uint8_t DS18B20_Check_Device(void) { /*1.主机发送复位信号*/ DS18B20_Send_Reset_Single(); /*2.检测存在脉冲*/ return DS18B20_Check_Ready_Single(); } /** * @brief DS18B20初始化 * @param none * @retval none */ void DS18B20_Init(void) { /* 1.DS18B20控制引脚初始化 */ //在main函数中已经初始化,不需要再次重复。 /*2.检测DS18B20设备是否正常*/ switch (DS18B20_Check_Device()) { case 0: printf("DS18B20_Init OK!n"); break; case 1: printf("DS18B20设备响应复位信号失败!n"); break; case 2: printf("DS18B20设备释放总线失败!n"); break; } } ② 向DS18B20写一个字节时序: 实现代码如下: /** * @brief 向DS18B20写一个字节 * @param cmd 要写入的字节 * @retval none */ static uint8_t DS18B20_Write_Byte(uint8_t cmd) { uint8_t i = 0; /* 1. 设置总线为输出模式 */ DS18B20_OutPut_Mode(); /* 2. 发送数据,低位在前 */ for (i = 0; i < 8; i++) { DS18B20_Out(0); DS18B20_Delay_us(2); DS18B20_Out(cmd & 0x01); DS18B20_Delay_us(60); DS18B20_Out(1); cmd >>= 1; DS18B20_Delay_us(2); } return 0; } ③ 从DS18B20读取一个字节数据时序: 实现代码如下: /** * @brief 从DS18B20读一个字节 * @param none * @retval 读取到的一个字节数据 */ uint8_t DS18B20_Read_Byte(void) { uint8_t i = 0; uint8_t data = 0; /* 读取数据 */ for (i =0; i < 8; i++) { DS18B20_OutPut_Mode(); DS18B20_Out(0); DS18B20_Delay_us(2); DS18B20_Out(1); DS18B20_InPut_Mode(); DS18B20_Delay_us(10); data >>= 1 ; if (DS18B20_In()) { data |= 0x80; } DS18B20_Delay_us(60); DS18B20_Out(1); } return data; } 4.4. DS18B20读取温度函数实现 DS18B20读取温度需要发送一些命令, 温度转换指令:0x44(启动Ds18b20启动转换温度) 读暂存器指令:0xBE(读取暂存器中的九字节数据) 实现代码如下: /** * @brief 从DS18B20读取一次数据 * @param none * @retval 读取到的温度数据 * @note 适用于总线上只有一个DS18B20的情况 */ uint16_t DS18B20_Read_Temperature(void) { uint16_t temp = 0; uint8_t temp_H, temp_L; DS18B20_Check_Device(); DS18B20_Write_Byte(0xCC); DS18B20_Write_Byte(0x44); while (DS18B20_Read_Byte() != 0xFF); DS18B20_Check_Device(); //必须,不能省略 DS18B20_Write_Byte(0xCC); DS18B20_Write_Byte(0xBE); temp_L = DS18B20_Read_Byte(); temp_H = DS18B20_Read_Byte(); temp = temp_L | (temp_H << 8); return temp; } 至此,驱动编写完成,将两个供用户调用的函数在头文件中声明: void DS18B20_Init(void); uint16_t DS18B20_Read_Temperature(void); 4.5. 测试DS18B20温度值读取 在main.c中包含驱动头文件: /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include #include "ds18b20.h" /* USER CODE END Includes */ 在main函数中定义存放温度数据的变量: /* USER CODE BEGIN 1 */ uint16_t temp; int intT, decT; /* USER CODE END 1 */ 在main函数中调用DS18B20初始化函数: /* USER CODE BEGIN 2 */ printf("DS18B20 Test By Mculover666rn"); DS18B20_Init(); /* USER CODE END 2 */ 接着每隔1s读取一次数据,并打印: /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ temp = DS18B20_Read_Temperature(); intT = temp >> 4 ; /*合成实际温度整数部分****精度相对上面的更高*/ decT = temp & 0xF ; /*合成实际温度小数部分*/ printf("Temp:%d.%drn", intT, decT); HAL_Delay(1000); } 编译,下载,在串口助手中查看结果,如下:


史海拾趣
|
供S5PC100开发板 ARMcortex A8 core iPhone 3GS 采用Samsung S5PC100主控拥有720p高清视频engineering, 在iPhone中Apple特意降低了处理器运行频率为600MHz,以减少功耗。 显然S5PC100的拥有很强大的处理功能,包括3D engineering S5PC100集多 ...… 查看全部问答> |
|
汉王科技股份有限公司 嵌入式软件开发工程师-OCR软件部 电子邮箱: yinsg@hanwang.com.cn 发布日期: 2007-03-21 工作地 ...… 查看全部问答> |
|
简介:德州仪器电源管理产品经理Pietro Scalia为您简述一款LED路灯板。通过分析PR788参考设计电路图使你更清楚的了解到这个设计是如何实现更节能的目的的 $(\'swf_cT7\').innerHTML=AC_FL_RunContent(\'width\', \'550\', \'height\', \'4 ...… 查看全部问答> |




