STM32F407霸天虎HAL库CubeMX学习笔记——DS18B20
一、软件准备
软件准备
二、硬件准备
STM32F407霸天虎
DAP高速下载器
USB转串口线
DS18B20温度传感器
工作原理
DS18B20的读写时序和测温原理与DS1820相同,只是得到的温度值的位数因分辨率不同而不同,且温度转换时的延时时间由2s减为750ms低温度系数晶振的振荡频率受温度影响很小,用于产生固定频率的脉冲信号发送给计数器1。高温度系数晶振随温度变化其振荡频率明显改变,所产生的信号作为计数器2的脉冲输入。计数器1和温度寄存器被预置在-55℃所对应的一个基数值。计数器1对低温度系数晶振产生的脉冲信号进行减法计数,当计数器1的预置值减到0时,温度寄存器的值将加1,计数器1的预置将重新被装入,计数器1重新开始对低温度系数晶振产生的脉冲信号进行计数,如此循环直到计数器2计数到0时,停止温度寄存器值的累加,此时温度寄存器中的数值即为所测温度。斜率累加器用于补偿和修正测温过程中的非线性,其输出用于修正计数器1的预置值。
三、CubeMX配置
STM32F407霸天虎用的是STM32F407ZGT6
选择外部高速时钟
我们这里选择LED_B,又因为霸道开发板上的LED是默认低电平点亮的,所以这里我们GPIO选择默认输出高电平
根据STM32F407霸道开发板原理图可以知道DS18B20的数据引脚是在PE3上
这里配置PE3为输出模式,其他具体配置如下:
USART1选择异步通信,其他配置默认
时钟树选择168MHz,具体配置方法见上篇的时钟树配置
因为DS18B20是单总线通讯,所以我们这里需要使用到us微秒级别的延时,选择普通定时器TIM6,具体配置如下:
PSC填写168-1,这是在STM32芯片定义好了的公式,实际上用到的还是168
而后根据自己的喜好编辑工程的名称,存放位置,IDE选择ARM-MDKV5
最后点击
到此CubeMX的配置就完成了。
四、Keil
由于我们的Debug用的是DAP,如图下选择Debug和下载配置
printf重写
首先勾选Use MicroLIB调用MDK的微库,MicroLIB是对C标准库高度优化之后的库,比标准库代码更少,使用效率更高。
添加至usart.c文件的用户自定义代码块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
tim.c
在该文件的最末尾加上us函数
void delay_us(uint16_t us)
{
uint16_t differ=0xffff-us-5; //设定定时器计数器起始值
HAL_TIM_Base_Start(&htim6); //启动定时器
__HAL_TIM_SetCounter(&htim6,differ);
while(differ < 0xffff-5) //补偿,判断
{
differ = __HAL_TIM_GetCounter(&htim6); //查询计数器的计数值
}
HAL_TIM_Base_Stop(&htim6);
}
DS18B20.h
先在DS18B20.h中宏定义封装以下函数:
- GPIO输出模式配置
- GPIO输入模式配置
- GPIO输出高电平
- GPIO输出低电平
- GPIO读取输入
相关引脚定义已在mian.h文件中
#ifndef __DS18B20_H
#define __DS18B20_H
#include "main.h"
#include "tim.h"
#include "usart.h"
#include <stdio.h>
//IO方向设置
#define DS18B20_IO_IN() {DS18B20_GPIO_Port->MODER&=~(3<<(3*2));DS18B20_GPIO_Port->MODER|=0<<3*2;} //PE3输入模式
#define DS18B20_IO_OUT() {DS18B20_GPIO_Port->MODER&=~(3<<(3*2));DS18B20_GPIO_Port->MODER|=1<<3*2;} //PE3输出模式
/* DS18B20控制IO操作函数 */
#define DS18B20_Out(n) (n? HAL_GPIO_WritePin(DS18B20_GPIO_Port,DS18B20_Pin,GPIO_PIN_SET):HAL_GPIO_WritePin(DS18B20_GPIO_Port,DS18B20_Pin,GPIO_PIN_RESET))
#define DS18B20_In() HAL_GPIO_ReadPin(DS18B20_GPIO_Port,DS18B20_Pin)
void DS18B20_Init(void);
uint16_t DS18B20_Read_Temperature(void);
#endif
DS18B20.c
DS18B20操作时序实现
1、 复位信号:
此部分在DS18B20.c中实现
/**
* @brief 发送复位信号
* @param none
* @retval none
*/
static void DS18B20_Reset(void)
{
//设置DS18B20为输出模式
DS18B20_IO_OUT();
//拉低总线480-960us
DS18B20_Out(0);
delay_us(660);
//释放总线15-60us
DS18B20_Out(1);
delay_us(30);
}
/**
* @brief 检测DS18B20存在脉冲
* @param none
* @retval 0 DS18B20设备正常
* @retval 1 DS18B20设备响应复位信号失败
* @retval 2 DS18B20设备释放总线失败
*/
static uint8_t DS18B20_Check_Pulse(void)
{
uint8_t cnt = 0;
//检测是否存在脉冲
DS18B20_IO_IN();
//等待DS18B20 拉低总线 (60~240 us 响应复位信号)
while (DS18B20_In() && cnt < 240) {
delay_us(1);
cnt++;
}
if (cnt > 240) {
return 1;
}
/* 2.检测DS18B20是否释放总线 */
cnt = 0;
DS18B20_IO_IN();
//判断DS18B20是否释放总线(60~240 us 响应复位信号之后会释放总线)
while ((!DS18B20_In()) && cnt<240) {
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(void)
{
//发送复位信号
DS18B20_Reset();
//检测是否存在脉冲
return DS18B20_Check_Pulse();
}
/**
* @brief DS18B20初始化
* @param none
* @retval none
*/
void DS18B20_Init(void)
{
if(DS18B20_Check() == 0)
{
printf("DS18B20设备初始化成功!\r\n");
}
else if(DS18B20_Check() == 1)
{
printf("DS18B20设备响应复位信号失败!\r\n");
}
else
{
printf("DS18B20设备释放总线失败!\r\n");
}
}
2、向DS18B20写一个字节时序
代码如下:
/**
* @brief 向DS18B20写一个字节
* @param cmd 要写入的字节
* @retval none
*/
static uint8_t DS18B20_Write_Byte(uint8_t cmd)
{
uint8_t i = 0;
/* 1. 设置总线为输出模式 */
DS18B20_IO_OUT();
/* 2. 发送数据,低位在前 */
for (i = 0; i < 8; i++)
{
DS18B20_Out(0);
delay_us(2);
DS18B20_Out(cmd & 0x01);
delay_us(60);
DS18B20_Out(1);
cmd >>= 1;
delay_us(2);
}
return 0;
}
3、从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_IO_OUT();
DS18B20_Out(0);
delay_us(2);
DS18B20_Out(1);
DS18B20_IO_IN();
delay_us(10);
data >>= 1 ;
if (DS18B20_In())
{
data |= 0x80;
}
delay_us(60);
DS18B20_Out(1);
}
return data;
}
4、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();
DS18B20_Write_Byte(0xCC);
DS18B20_Write_Byte(0x44);
while (DS18B20_Read_Byte() != 0xFF);
DS18B20_Check(); //必须,不能省略
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;
}
Main.c
在头文件包含处添加代码:
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "ds18b20.h"
/* USER CODE END Includes */
添加变量:
/* USER CODE BEGIN PV */
uint16_t temp;
int intT, decT;
/* USER CODE END PV */
在main函数里添加
/* USER CODE BEGIN 2 */
printf("\r\nhi! 物联网小白Jayce!\r\n");
printf("DS18B20 Test!\r\n");
DS18B20_Init();
/* USER CODE END 2 */
在while循环里添加
temp = DS18B20_Read_Temperature();
intT = temp >> 4 ; /*合成实际温度整数部分****精度相对上面的更高*/
decT = temp & 0xF ; /*合成实际温度小数部分*/
HAL_GPIO_TogglePin(LED_B_GPIO_Port,LED_B_Pin);
printf("当前温度为:%d.%d℃\r\n", intT, decT);
HAL_Delay(1000);
HAL_GPIO_TogglePin(LED_B_GPIO_Port,LED_B_Pin);
五、实验效果
开发板上蓝灯每闪一次就打印一次温度信息