本帖最后由 zero949079783 于 2021-10-27 09:19 编辑
开发环境:VSCODE(gcc编译链)+STM32CubeMX(也可以使用HUAWEI-LiteOS-Studio) 。
代码:链接:https://pan.baidu.com/s/1uXfIR0GFQOBZPl1NfQP08w
提取码:6b0c
DS18B20的工作时序:
初始化时序
主机首先发出一个480-960微秒的低电平脉冲,然后释放总线变为高电平,并在随后的480微秒时间内对总线进行检测,如果有低电平出现说明总线上有器件已做出应答。
若无低电平出现一直都是高电平说明总线上无器件应答。
做为从器件的DS18B20在一上电后就一直在检测总线上是否有480-960微秒的低电平出现,如果有,在总线转为高电平后等待15-60微秒后将总线电平拉低60-240微秒做出响应存在脉冲,告诉主机本器件已做好准备。若没有检测到就一直在检测等待。
复位代码:/**
* [url=home.php?mod=space&uid=247401]@brief[/url] 复位DS18B20
* @param
* @arg
* @retval dat :返回1:未检测到DS18B20的存在 返回0:存在
*/
uint8_t DS18B20_Rst(void)
{
uint8_t dat;
DS18B20_Write_Out_Input(DS18B20_WRITE); //设为输出
DS18B20_OUT_L();
delay_us(48); //480us
DS18B20_OUT_H();
delay_us(6); //60us
DS18B20_Write_Out_Input(DS18B20_READ);//设为输入
dat = DS18B20_IN();
delay_us(48); //480us
return dat;
}
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] DS18B20 IO方向设置 输出输出
* @param cmd :DS18B20_WRITE /DS18B20_READ
* @arg
* @retval 无
*/
void DS18B20_Write_Out_Input(uint8_t cmd)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(cmd == DS18B20_WRITE )
{
GPIO_InitStruct.Pin = DS18B20_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DS18B20_GPIO_Port, &GPIO_InitStruct);
}
else
{
GPIO_InitStruct.Pin = DS18B20_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(DS18B20_GPIO_Port, &GPIO_InitStruct);
}
}
写操作
写周期最少为60微秒,最长不超过120微秒。写周期一开始做为主机先把总线拉低1微秒表示写周期开始。随后若主机想写0,则继续拉低电平最少60微秒直至写周期结束,然后释放总线为高电平。若主机想写1,在一开始拉低总线电平1微秒后就释放总线为高电平,一直到写周期结束。而做为从机的DS18B20则在检测到总线被拉底后等待15微秒然后从15us到45us开始对总线采样,在采样期内总线为高电平则为1,若采样期内总线为低电平则为0。
写操作
/**
* @brief 写一个字节到DS18B20
* @param dat:要写入的字节
* @arg
* @retval
*/
void DS18B20_Write_Byte(uint8_t dat)
{
uint8_t i;
for(i=0;i<8;i++)
{
DS18B20_Write_Out_Input(DS18B20_WRITE);
DS18B20_OUT_L();
delay_us(2);
if(dat &0x01)
DS18B20_OUT_H();//写1
else
DS18B20_OUT_L();//写0
delay_us(6);
DS18B20_OUT_H();
dat >>=1;
}
}
读操作
对于读数据操作时序也分为读0时序和读1时序两个过程。读时隙是从主机把单总线拉低之后,在1微秒之后就得释放单总线为高电平,以让DS18B20把数据传输到单总线上。DS18B20在检测到总线被拉低1微秒后,便开始送出数据,若是要送出0就把总线拉为低电平直到读周期结束。若要送出1则释放总线为高电平。主机在一开始拉低总线1微秒后释放总线,然后在包括前面的拉低总线电平1微秒在内的15微秒时间内完成对总线进行采样检测,采样期内总线为低电平则确认为0。采样期内总线为高电平则确认为1。完成一个读时序过程,至少需要60us才能完成
读操作代码:
/**
* @brief 从DS18B20读取一个字节
* @param
* @arg
* @retval 读到的数据
*/
uint8_t DS18B20_Read_Byte(void)
{
uint8_t dat=0,i;
for(i=0;i<8;i++)
{
DS18B20_Write_Out_Input(DS18B20_WRITE);
DS18B20_OUT_L();
delay_us(2);
DS18B20_OUT_H(); //总线释放
dat >>=1;
DS18B20_Write_Out_Input(DS18B20_READ);
if(DS18B20_IN() == 1){
dat |=0x80;
}
else{
dat &=0x7F;
}
delay_us(6);
}
return dat;
}
读取RAM内的温度数据。同样,这个操作也要接照三个步骤。
1、主机发出复位操作并接收DS18B20的应答(存在)脉冲。
2、主机发出跳过对ROM操作的命令(CCH)。
3、主机发出读取RAM的命令(BEH),随后主机依次读取DS18B20发出的从第0一第8,共九个字节的数据。如果只想读取温度数据,那在读完第0和第1个数据后就不再理会后面DS18B20发出的数据即可。同样读取数据也是低位在前的。
//开始温度转换
float DS18B20_Get_Temp(void)
{
uint8_t MSB =0 ,MSL=0;
uint16_t Temp=0;
float Ftemp = 0;
if(DS18B20_Rst() == 0){
DS18B20_Rst();
DS18B20_Write_Byte(0XCC); //跳过ROM
DS18B20_Write_Byte(0X44);
delay_ms(750);
DS18B20_Rst();
DS18B20_Write_Byte(0XCC); //跳过ROM
DS18B20_Write_Byte(0XBE);
MSL = DS18B20_Read_Byte(); //低8位
MSB = DS18B20_Read_Byte(); //高8位
Temp = MSB;
Temp = (Temp<<8) |MSL;
if((Temp&0xf800) == 0xf800){ //1111 1000 0000 0000 前5为1,为负温度
Temp = (((~Temp) + 0X01) * -0.0625);
}
else{
Ftemp =Temp *0.0625;
//Temp = Ftemp *100+0.5;
}
}
else{
printf("DS18B20 未检测到 \n\r");
}
return Ftemp;
}
微秒延时函数:
#include "delay.h"
#include "core_cm3.h"
static __IO uint32_t TimingDelay;
/**
* @brief 启动系统滴答定时器 SysTick
* @param 无
* @retval 无
*/
void SysTick_Init(void)
{
/* SystemFrequency / 1000 1ms中断一次
* SystemFrequency / 100000 10us中断一次
* SystemFrequency / 1000000 1us中断一次
*/
// if (SysTick_Config(SystemFrequency / 100000)) // ST3.0.0库版本
if (SysTick_Config(SystemCoreClock / 100000)) // ST3.5.0库版本 //HAL库只能使用10us中断一次
{
/* Capture error */
while (1);
}
}
/**
* @brief us延时程序,1us为一个单位
* @param
* [url=home.php?mod=space&uid=2817080]@ARG[/url] nTime: Delay_us( 1 ) 则实现的延时为 1 * 10us = 10us
* @retval 无
*/
void delay_us(__IO uint32_t nTime)
{
TimingDelay = nTime;
// 使能滴答定时器
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
while(TimingDelay != 0);
}
/**
* @brief 获取节拍程序
* @param 无
* @retval 无
* [url=home.php?mod=space&uid=93590]@Attention[/url] 在 SysTick 中断函数 SysTick_Handler()调用
*/
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
#if 0
// 这个 固件库函数 在 core_cm3.h中
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
// reload 寄存器为24bit,最大值为2^24
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1);
// 配置 reload 寄存器的初始值
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;
// 配置中断优先级为 1<<4-1 = 15,优先级为最低
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
// 配置 counter 计数器的值
SysTick->VAL = 0;
// 配置systick 的时钟为 72M
// 使能中断
// 使能systick
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
return (0);
}
#endif
// couter 减1的时间 等于 1/systick_clk
// 当counter 从 reload 的值减小到0的时候,为一个循环,如果开启了中断则执行中断服务程序,
// 同时 CTRL 的 countflag 位会置1
// 这一个循环的时间为 reload * (1/systick_clk)
void SysTick_Delay_Us( __IO uint32_t us)
{
uint32_t i;
SysTick_Config(SystemCoreClock/1000000);
for(i=0;i<us;i++)
{
// 当计数器的值减小到0的时候,CRTL寄存器的位16会置1
while( !((SysTick->CTRL)&(1<<16)) );
}
// 关闭SysTick定时器
SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
}
void SysTick_Delay_Ms( __IO uint32_t ms)
{
uint32_t i;
SysTick_Config(SystemCoreClock/1000);
for(i=0;i<ms;i++)
{
// 当计数器的值减小到0的时候,CRTL寄存器的位16会置1
// 当置1时,读取该位会清0
while( !((SysTick->CTRL)&(1<<16)) );
}
// 关闭SysTick定时器
SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}
//static uint8_t fac_us = RESET;
//static uint16_t fac_ms = RESET;
//void delay_UserConfig(uint8_t SYSCLK)
//{
// SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //8分频 72/8=9
// fac_us = SYSCLK /8;//计数 9个数
// fac_ms = (uint16_t)fac_us *1000; //1S= 1000MS *9=9000 1ms= 1000us*9 =9000
//}
//void delay_ms(uint16_t ms)
//{
// uint32_t temp;
// SysTick ->LOAD = fac_ms*ms; // 重载
// SysTick ->VAL = 0x00; //清空
// SysTick ->CTRL |= SysTick_CTRL_ENABLE_Msk;//开启定时器
//
// do
// {
// temp = SysTick ->CTRL;
// }
// while((temp&0x01) &&!(temp&(1<<16)));//COUNTFLAG==1
// SysTick ->VAL =0X00;
// SysTick ->CTRL &= ~SysTick_CTRL_ENABLE_Msk;//关闭定时器
//}
//void delay_us(uint32_t us)
//{
// uint32_t temp;
// SysTick ->LOAD = fac_us*us; // 重载
// SysTick ->VAL = 0x00; //清空
// SysTick ->CTRL |= SysTick_CTRL_ENABLE_Msk;//开启定时器
//
// do
// {
// temp = SysTick ->CTRL;
// }
// while((temp&0x01) &&!(temp&(1<<16)));//COUNTFLAG==1
// SysTick ->VAL =0X00;
// SysTick ->CTRL &= ~SysTick_CTRL_ENABLE_Msk;//关闭定时器
//}
|