本篇详细的记录了如何使用STM32CubeMX配置 STM32103RET6 的硬件GPIO外设读取温度传感器DS18B20的数据。
1. 准备工作
硬件准备
- 开发板
首先需要准备一个开发板,这里我准备的是一个工业DTU,主控芯片为STM32103RBT6。
- DS18B20
DTU开发板板载一颗DS18B20温度传感器。
3. 使用STM32CubeMX生成工程
选择芯片型号
打开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文件末尾添加重定向代码:
#if 1
#include <stdio.h>
int fputc(int ch, FILE *stream)
{
while((USART1->SR & 0x40) == 0);
USART1->DR = (uint8_t) ch;
return ch;
}
#endif
详细说明请参考文章: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"
#define DS18B20_GPIO_PORT GPIOA
#define DS18B20_GPIO_PIN GPIO_PIN_1
#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;}
#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)
#define DS18B20_Delay_us(n) CPU_TS_Tmr_Delay_US(n)
4.3. DS18B20操作时序实现
DS18B20主要有几个操作时序,在ds18b20.c
中实现:
① 复位信号:
实现代码如下:
static void DS18B20_Send_Reset_Single(void)
{
DS18B20_OutPut_Mode();
DS18B20_Out(0);
DS18B20_Delay_us(750);
DS18B20_Out(1);
DS18B20_Delay_us(15);
}
static uint8_t DS18B20_Check_Ready_Single(void)
{
uint8_t cnt = 0;
DS18B20_InPut_Mode();
//等待DS18B20 拉低总线 (60~240 us 响应复位信号)
while (DS18B20_In() && cnt < 240) {
DS18B20_Delay_us(1);
cnt++;
}
if (cnt > 240) {
return 1;
}
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;
}
}
static uint8_t DS18B20_Check_Device(void)
{
DS18B20_Send_Reset_Single();
return DS18B20_Check_Ready_Single();
}
void DS18B20_Init(void)
{
//在main函数中已经初始化,不需要再次重复。
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写一个字节时序:
实现代码如下:
static uint8_t DS18B20_Write_Byte(uint8_t cmd)
{
uint8_t i = 0;
DS18B20_OutPut_Mode();
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读取一个字节数据时序:
实现代码如下:
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(读取暂存器中的九字节数据)
实现代码如下:
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中包含驱动头文件:
#include <stdio.h>
#include "ds18b20.h"
在main函数中定义存放温度数据的变量:
uint16_t temp;
int intT, decT;
在main函数中调用DS18B20初始化函数:
printf("DS18B20 Test By Mculover666\r\n");
DS18B20_Init();
接着每隔1s读取一次数据,并打印:
while (1)
{
temp = DS18B20_Read_Temperature();
intT = temp >> 4 ;
decT = temp & 0xF ;
printf("Temp:%d.%d\r\n", intT, decT);
HAL_Delay(1000);
}
编译,下载,在串口助手中查看结果,如下:
更多精彩文章及资源,请关注我的微信公众号:『mculover666』。