怎样用stm32驱动ds18b20温度芯片 |
您所在的位置:网站首页 › 温度传感器连接 › 怎样用stm32驱动ds18b20温度芯片 |
一、DS18B20简介
DS18B20是一一款常用的数字温度传感器,使用单个引脚完成主从机的双向通信是这款芯片的特点。 物理连接简单,那自然有特殊的时序需要满足,这也是使用这款芯片时麻烦的一点。 由于DS18B20不支持我们熟悉的IIC和SPI协议,那也就没有现成的库函数来支持我们的读写操作,也就是需要我们自行编写驱动。 但本文并不打算从解读数据手册中的时序图开始说太多废话,只给把代码给出,介绍一下驱动编写时可能遇到的一些问题,结合代码给出解决方案。 根据数据手册,这款芯片通常有三种封装: 三者都是通过DQ这个引脚来进行与主机的通信。所以问题简化为: 控制与DQ连接的引脚的电平变化 实际上,ds18b20支持六个操作指令: 只需要按照一定电平协议把这些命令发出去,就能够从DQ读取到想要的数据。而这些指令都刚好只有8位(bit),也就是一个字节。于是乎,ds18b20驱动的编写进一步简化为四个小任务: 1、初始化配置 2、通过DQ读取一个字节 3、通过DQ发送一个字节 4、读取温度 二、驱动代码介绍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>=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>=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 = ((tempH4))+ (float)(tempL&0x0f)*0.0625; return temp; }其中的SKIPROMCOMMAND、CONVERTT、READSCRATCHPAD都是 ds18b20 的控制指令:
温度计算的部分需要注意一下: /* 计算温度值 */ float temp = ((tempH4))+ (float)(tempL&0x0f)*0.0625;数据手册的表述如下: 首先,(高8位左移4位)或上(低八位右移4位),得到8位的整数部分: ((tempH4))然后,低8位去掉左边4位: (tempL&0x0f)乘2^(-4)化成小数: (float)(tempL&0x0f)*0.0625整数部分和小数部分加起来得到温度值: float temp = ((tempH4))+ (float)(tempL&0x0f)*0.0625;好啦,ds18b20的驱动就介绍完了,源代码的链接放在下面: 基于stm32F1标准库开发的DS18B20驱动 timer.x提供时序控制支持 ds18b20.x是驱动内容,供以读取温度数据 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |