STM32CubeMX | 33-使用GPIO读取温度传感器数据(DS18B20)

本篇详细的记录了如何使用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文件末尾添加重定向代码:

/* USER CODE BEGIN 1 */
#if 1
#include <stdio.h>

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级延时函数,参考文章:

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 <stdio.h>
#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 Mculover666\r\n");
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.%d\r\n", intT, decT);
	  
	HAL_Delay(1000);
}

编译,下载,在串口助手中查看结果,如下:

更多精彩文章及资源,请关注我的微信公众号:『mculover666』。

猜你喜欢

转载自blog.csdn.net/Mculover666/article/details/108302704