[STM32F1]

VSCODE STM32 裸机之DS18B20

[复制链接]
1672|2
手机看帖
扫描二维码
随时随地手机跟帖
zero949079783|  楼主 | 2021-10-23 22:36 | 显示全部楼层 |阅读模式
本帖最后由 zero949079783 于 2021-10-27 09:19 编辑

开发环境:VSCODE(gcc编译链)+STM32CubeMX(也可以使用HUAWEI-LiteOS-Studio) 。
代码:链接:https://pan.baidu.com/s/1uXfIR0GFQOBZPl1NfQP08w  
提取码:6b0c


DS18B20的工作时序:

1.png
初始化时序
主机首先发出一个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);
    }
}







2.png
写操作
写周期最少为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;
    }

}


3.png
读操作
对于读数据操作时序也分为读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;//关闭定时器

//}








  

使用特权

评论回复
小叶三千| | 2021-10-26 08:17 | 显示全部楼层
感谢分享,不过代码贴的有点问题啊,看着不是很舒服,应该用代码格式

使用特权

评论回复
zero949079783|  楼主 | 2021-10-26 19:46 | 显示全部楼层
小叶三千 发表于 2021-10-26 08:17
感谢分享,不过代码贴的有点问题啊,看着不是很舒服,应该用代码格式

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

29

主题

75

帖子

1

粉丝