ds18b20温度传感器stm32
代码编译无误,检测不到传感器的存在
编译无误只是没有语法错误而已,不代表功能就正常。
可能是硬件连接有问题,也可能是程序逻辑有问题,要结合二者具体分析才行
提供下代码看下!
板卡是用的开发板上的DS18b20还是自己连接的!如果是自己连接,还提供下电路图!
1、初始化配置
初始化包含两步:
1、`DQ`对应的引脚的GPIO配置
2、通过`DQ`对`ds18b20`芯片的初始化
对应驱动代码的这部分:
/**
* @brief 配置DQ并复位DS18B20
* @param 无
* @retval 无
*/
void DS18B20_Init(void)
{
/* 初始化GPIO */
DS18B20_DQGPIOConfig();
/* 复位 */
DS18B20_Rst();
}
DS18B20_DQGPIOConfig()
使能APB2总线时钟和GPIO时钟,配置DQ
对应的引脚为推挽输出模式即可:
/**
* @brief 配置DQ对应的GPIO引脚
* @param 无
* @retval 无
*/
void DS18B20_DQGPIOConfig(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启LED相关的GPIO外设时钟*/
RCC_APB2PeriphClockCmd(DQ_GPIO_CLK, ENABLE);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = PINDQ;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化GPIO*/
GPIO_Init(GPIODQ, &GPIO_InitStructure);
}
上面的代码中包含一些宏定义:
/* DQ引脚相关宏定义 */
#define GPIODQ GPIOB
#define PINDQ GPIO_Pin_8
/* GPIO端口时钟 */
#define DQ_GPIO_CLK RCC_APB2Periph_GPIOB
注:为了不至于看花眼,不会一次性将所有的宏和函数实现贴出来,说到哪儿就贴到那儿。同时也不用担心不好拼凑成完整的驱动程序,因为文末会放出驱动源代码链接。
重点看DS18B20_Rst()
的实现:
/**
* @brief 复位DS18B20
*/
void DS18B20_Rst(void)
{
DS18B20_SetDQMode_OUT();
/* 先将数据线置高电平“1” */
DQ_H;
/* 延时(该时间要求的不是很严格,但是尽可能的短一点) */
Delay_us(1);
/* 数据线拉到低电平“0”。 */
DQ_L;
/* 延时750微秒(该时间的时间范围可以从480到960微秒) */
Delay_us(750);
/* 数据线拉到高电平“1” */
DQ_H;
/* 延时等待(如果初始化成功则在15到60微秒时间之内产生一个由
DS18B20所返回的低电平“0”。据该状态可以来确定它的存在,
但是应注意不能无限的进行等待,不然会使程序进入死循环,
所以要进行超时控制)。 */
/* 超时时间不要设置得太短(500us是足够的),
否则初始化失败,将获取不到环境温度 */
Delay_us(500);
uint8_t dqStatus = GPIO_ReadInputDataBit(GPIODQ,PINDQ);
/* 将数据线再次拉高到高电平“1”后结束。 */
DQ_H;
}
注意看每一步的注释,交代了初始化过程中DQ
的电平变化。
其中最后一个延时函数:
Delay_us(500);
尤为关键,如果这里的等待时间不足,将导致初始化失败,出现温度值不变的怪病。
DS18B20_SetDQMode_OUT()
是把DQ对应的引脚设置为推挽输出模式:
/**
* @brief 设置DQ对应的引脚位推挽输出模式
* @param 无
* @retval 无
*/
static void DS18B20_SetDQMode_OUT()
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = PINDQ;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化GPIO*/
GPIO_Init(GPIODQ, &GPIO_InitStructure);
}
DQ_H()
和DQ_L()
分别是将DQ
对应的引脚设置为高电平和低电平:
#define DQ_H GPIO_SetBits(GPIODQ,PINDQ)
#define DQ_L GPIO_ResetBits(GPIODQ,PINDQ)
Delay_us()
的实现如下:
/**
* @brief 延时函数
* @param us_cnt 设定的延时微秒数
* @retval 无
*/
void Delay_us(uint32_t us_cnt)
{
TIM3->CNT = us_cnt-1;
TIM3->CR1 |= TIM_CR1_CEN;
while((TIM3->SR & TIM_FLAG_Update)!=SET);
TIM3->SR = (uint16_t)~TIM_FLAG_Update;
TIM3->CR1 &= ~TIM_CR1_CEN;
}
这个函数需要配置定时器以支持计时:
/**
* @brief 配置延时定时器
* @param us_cnt 设定的延时微秒数
* @retval 无
*/
void Delay_Timer_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Down;
TIM_TimeBaseInitStruct.TIM_Period = 100-1;
TIM_TimeBaseInitStruct.TIM_Prescaler = (84-1);
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
while((TIM3->SR & TIM_FLAG_Update)!=SET);
TIM3->SR = (uint16_t)~TIM_FLAG_Update;
}
2、通过DQ读取一个字节
/**
* @brief 通过DQ从DS18B20读取一个字节(Byte)
* @param 无
* @retval 读取到的数据(uint8_t类型)
*/
uint8_t DS18B20_ReadByte(void)
{
uint8_t data = 0x00;
for(int i=0;i<8;i++)
{
DS18B20_SetDQMode_OUT();
/* 将数据线拉高“1” */
DQ_H;
/* 延时2微秒 */
Delay_us(2);
/* 将数据线拉低“0” */
DQ_L;
/* 延时3微秒 */
Delay_us(3);
data>>=1;
/* 将数据线拉高“1” */
DQ_H;
DS18B20_SetDQMode_IPU();
/* 延时5微秒 */
Delay_us(5);
/* 读数据线的状态得到1个状态位,并进行数据处理 */
if(GPIO_ReadInputDataBit(GPIODQ,PINDQ))
data|=0x80;
/* 延时60微秒 */
Delay_us(60);
}
return data;
}
同样的,注意阅读注释中的描述。
DS18B20_SetDQMode_IPU()
把DQ对应的引脚设置为输入模式:
/**
* @brief 设置DQ对应的引脚为输入模式
* @param 无
* @retval 无
*/
static void DS18B20_SetDQMode_IPU()
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = PINDQ;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
/*调用库函数,初始化GPIO*/
GPIO_Init(GPIODQ, &GPIO_InitStructure);
}
3、通过DQ发送一个字节
/**
* @brief 通过DQ向DS18B20发送一个字节(Byte)
* @param
@arg data 待发送的1字节数据
* @retval 无
*/
void DS18B20_WriteByte(uint8_t data)
{
for(int i=0;i<8;i++){
/* 数据线先置低电平“0” */
DQ_L;
/* 延时确定的时间为15微秒 */
Delay_us(15);
/* 按从低位到高位的顺序发送字节(一次只发送一位) */
if(data&0x01){
GPIO_WriteBit(GPIODQ,PINDQ,Bit_SET);
}else{
GPIO_WriteBit(GPIODQ,PINDQ,Bit_RESET);
}
/* 延时时间为45微秒 */
Delay_us(45);
/* 将数据线拉到高电平 */
DQ_H;
data>>=1;
}
}
4、读取温度
/**
* @brief 获取温度寄存器的值并转换为温度值返回
* @param 无
* @retval 无
*/
float DS18B20_GetTemp(void)
{
DS18B20_Rst();
DS18B20_WriteByte(SKIPROMCOMMAND);
DS18B20_WriteByte(CONVERTT);
DS18B20_Rst();
DS18B20_WriteByte(SKIPROMCOMMAND);
DS18B20_WriteByte(READSCRATCHPAD);
uint8_t tempL = DS18B20_ReadByte();
uint8_t tempH = DS18B20_ReadByte();
/* 反码 */
if(tempH>0x7f)
{
tempL = ~tempL;
tempH = ~tempH+1;
}
/* 计算温度值 */
float temp = ((tempH<<4)|(tempL>>4))+
(float)(tempL&0x0f)*0.0625;
return temp;
}
其中的SKIPROMCOMMAND
、CONVERTT
、READSCRATCHPAD
都是 ds18b20
的控制指令:
温度计算的部分需要注意一下:
/* 计算温度值 */
float temp = ((tempH<<4)|(tempL>>4))+
(float)(tempL&0x0f)*0.0625;
数据手册的表述如下:
1、黑色-划掉的是多余的符号位
2、红色-对应着整数位
3、蓝色-下划线部分对应小数
首先,(高8位左移4位
)或上(低八位右移4位
),得到8位的整数部分:
((tempH<<4)|(tempL>>4))
然后,低8位去掉左边4位:
(tempL&0x0f)
乘2^(-4)化成小数:
(float)(tempL&0x0f)*0.0625
整数部分和小数部分加起来得到温度值:
float temp = ((tempH<<4)|(tempL>>4))+
(float)(tempL&0x0f)*0.0625;
好啦,ds18b20
的驱动就介绍完了,源代码的链接放在下面:
timer.x提供时序控制支持
ds18b20.x是驱动内容,供以读取温度数据