基于LL库以及DS18B20数据手册编写单总线协议读取DS18B20温度传感器数据,使用CUBEMX配置引脚,通过修改系统时基配置设定1us的时基,精准延时实现单总线协议驱动。
<系统时钟配置>
void SystemClock_Config(void)
{
LL_FLASH_SetLatency(LL_FLASH_LATENCY_2);
while(LL_FLASH_GetLatency()!= LL_FLASH_LATENCY_2)
{
}
LL_RCC_HSE_Enable();
/* Wait till HSE is ready */
while(LL_RCC_HSE_IsReady() != 1)
{
}
LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE_DIV_1, LL_RCC_PLL_MUL_9);
LL_RCC_PLL_Enable();
/* Wait till PLL is ready */
while(LL_RCC_PLL_IsReady() != 1)
{
}
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_2);
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
/* Wait till System clock is ready */
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
{
}
LL_Init1usTick(72000000);
LL_SetSystemCoreClock(72000000);
LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_PLLCLK_DIV_2);
}
由于CUBEMX配置时钟时默认时基是1ms,所以修改时钟配置,定义1us时基函数。
定义好1us时基后可直接采用LL库提供的延时函数LL_mDelay,为了避免写代码时的误区所以重新命名一个延时函数,函数内容不变,因为此时时基已经被修改为1us所以延时函数也是1us。
时基确定之后接下来写DS18B20驱动,本文只提供示例代码,具体可以根据个人需求修改GPIO端口,数据手册上的内容在此不在赘述,感兴趣的可以在网上搜索,有很多,本文只实现单DS18B20的驱动,多个DS18B20根据数据手册发送不同指令寻址即可。
<DS18B20.c>
#define DS18B20_Pin_Port GPIO_Port
#define DS18B20_Pin GPIO_Pin
#define DS18B20_MTX GPIOx -> CRL = 0xXX; //配置寄存器的方式修改GPIO模式,查询ST编程手册GPIO部分寄存器
#define DS18B20_MRX GPIOx -> CRL = 0xXX; //配置寄存器的方式修改GPIO模式,查询ST编程手册GPIO部分寄存器
#define DS18B20_H LL_GPIO_SetOutputPin(GPIO_Port , GPIO_Pin);
#define DS18B20_L LL_GPIO_ResetOutputPin(GPIO_Port , GPIO_Pin);
#define DS18B20_CHECK_EN LL_GPIO_IsInputPinSet(GPIO_Port ,GPIO_Pin) //检查单总线是高电平还是低电平
#define DS18B20_RX_DATA LL_GPIO_IsInputPinSet(GPIO_Port , GPIO_Pin) //检查单总线是高电平还是低电平
#define DS18B20_EN_FAIL 0 //失能标志,本程序未使用
#define DS18B20_EN_SUESS 1 //使能标志,本程序未使用
uint8_t DS18B20_EN_Flag = 0;
uint8_t Temperature_L = 0x00;
uint8_t Temperature_H = 0x00;
float Temperature = 0;
uint16_t Temperature_sum = 0;
void DS18B20_Reset(void) //复位DS18B20
{
DS18B20_L
LL_usDelay(1000);
DS18B20_MRX
LL_usDelay(60);
if(DS18B20_CHECK_EN)
{
DS18B20_EN_Flag = DS18B20_EN_FAIL;
}
else
{
DS18B20_EN_Flag = DS18B20_EN_SUESS;
}
LL_usDelay(240);
DS18B20_H
DS18B20_MTX
}
void DS18B20_W_0(void) //主机向DS18B20写0
{
DS18B20_L
LL_usDelay(60);
DS18B20_H
}
void DS18B20_W_1(void) //主机向DS18B20写1
{
DS18B20_L
LL_usDelay(1);
DS18B20_MRX
LL_usDelay(59);
DS18B20_H
DS18B20_MTX
}
uint8_t DS18B20_Read(uint8_t receive) //读取DS18B20数据
{
uint8_t data = 0;
receive = 0;
for(int i = 0 ; i < 8 ; i ++)
{
DS18B20_L
LL_usDelay(1);
DS18B20_MRX
LL_usDelay(9);
data = DS18B20_RX_DATA;
LL_usDelay(5);
data <<= i;
receive |= data;
LL_usDelay(45);
DS18B20_H
DS18B20_MTX
}
return receive;
}
void DS18B20_TX_COMMAND(uint8_t command) //发送8位数据
{
for(int i = 0 ; i < 8 ; i ++)
{
if(command & 0x01)
{
DS18B20_W_1();
}
else
{
DS18B20_W_0();
}
command >>= 1;
}
}
void DS18B20_Temp_Get(void) //温度数据读取
{
DS18B20_Reset();
DS18B20_TX_COMMAND(0xCC); //跳过ROM
DS18B20_TX_COMMAND(0x44); //执行温度转换
DS18B20_Reset();
DS18B20_TX_COMMAND(0xCC); //跳过ROM
DS18B20_TX_COMMAND(0xBE); //执行温度转换
Temperature_L = DS18B20_Read(Temperature_L);
Temperature_H = DS18B20_Read(Temperature_H);
}
void Temperature_cal(void) //温度高低字节组合并转换计算成实际摄氏度(12位精度×系数0.0625)
{
Temperature_sum = Temperature_H << 8;
Temperature_sum |= Temperature_L;
if(Temperature_sum & 0xF000)
{
Temperature_sum = ~(Temperature_sum);
Temperature = (float)(Temperature_sum) * (- 0.0625);
}
else
{
Temperature = (float)Temperature_sum * 0.0625;
}
}
<DS18B20.h>
#ifndef __DS18B20_H
#define __DS18B20_H
#include "main.h"
extern uint16_t Temperature_sum;
extern uint16_t Temperature_hex;
extern float Temperature;
extern uint8_t Temperature_L;
extern uint8_t Temperature_H;
void DS18B20_W_0(void);
void DS18B20_W_1(void);
void DS18B20_Reset(void);
uint8_t DS18B20_Read(uint8_t receive);
void DS18B20_Temp_Get(void);
void DS18B20_TX_COMMAND(uint8_t command);
void Temperature_cal(void);
#endif
读取后的数据保存在Temperature中,使用串口打印或者串口屏的方式显示出来,本文不作赘述。
Author:李家琦
Date:2025/7/26