定时器加状态转移图方式实现DS18B20

感谢硬汉哥提供的方案H7-TOOL同时采样10路DS18B20实现,带CRC校验,很实用
我们知道,DS18B20 是单总线时序通讯,采用严格的信号时序,以保证数据的完整性。因此如果采用操作系统,逻辑读取DS18B20为了避免时序被操作系统的任务调度,或者其他中断打乱,我们都会采用临界区的形式保护时序。

正常情况下,DS18B20通信的时间并不长,也就几十us。时间长的是18b20的转换过程(750ms)。
image-20220423100345368
那么我们可以再和18B20通讯时关闭中断,通讯完毕立马开启中断,转换的过程可以不用管(时序要求再通讯部分)。这样就可以既不影响18b20数据的读取,一起不影响任务的调度。

但是这样的话,我们读取到的温度值就是上一时刻的转换完成的值了。

并且还需要使用各种DelayMs,DelayUs。在RTOS中非常的不方便,因此我们可以使用定时器加状态图的方式实现数据读取,状态图如下:
image-20220423091225381

该种思想我们可以应用到FPGA中,FPGA操作时也是采用状态转移图实现。

时序

  • 初始化复位时序

image-20220423093409405

  • 写操作

image-20220423093847051

  • 读操作

    image-20220423094141486

代码:

#include "bsp.h"
#include "param.h"

#define DS18B20_IRQ_HANDLE                TIM16_IRQHandler

#define TIM_HARD                                TIM16
#define TIM_HARD_IRQn                        TIM16_IRQn

#define DQ_DIR_OUTPUT(ch)       EIO_ConfigPort(ch, ES_GPIO_OUT)
#define DQ_DIR_INPUT(ch)        EIO_ConfigPort(ch, ES_GPIO_IN)

#define DQ_0(ch)                    EIO_SetOutLevel(ch, 0)
#define DQ_1(ch)                        EIO_SetOutLevel(ch, 1)

/* 判断DQ输入是否为低 */
#define DQ_IS_LOW(ch)                (EIO_GetInputLevel(ch) == 0)

/*
    D0  PD14 PA15 PI0    - DIR PH8      温度1
    D1  PD15 PA8 PH19    - DIR PG8      温度2
    
    D2  PE6  PD0 PB7     - DIR PD9      温度3
    D3  PE5  PD1 PH11    - DIR PG10     温度4
    D4  PE4  PE7 PH12    - DIR PG12     温度5        --SWD调试占用
    D5  PE2  PE8 PI5     - DIR PG7      温度6
    D6  PE9  PD3 PA0     - DIR PD10     温度7        --SWD调试占用
    D7  PE10 PI6         - DIR PI1      温度8  (有上拉)
    D8  PE11 PD4 PI3     - DIR PG9      温度9         --SWD调试占用
    D9  PE12 PD5         - DIR PI12     温度10 (有上拉) 
*/

#define DS_CHAN_NUM     10       /* 最多10个通道 */

static float s_DS18B20_TempReg[DS_CHAN_NUM];
static uint16_t s_err[DS_CHAN_NUM] = {0};
    
static void DS18B20_StartTimerIRQ(void);
static void DS18B20_StopTimerIRQ(void);

/* 必须添加 volatile, 中断和主任务同时访问时,部分等待语句会被优化掉 */
static volatile uint8_t s_ch = 0;
static volatile uint16_t s_status = 0;

/*
*********************************************************************************************************
*        函 数 名: DS18B20_ReadTempReg
*        功能说明: 读温度寄存器的值(原始数据). 循环读,返回上次结果.
*        形    参: 无
*        返 回 值: 0表示失败,1表示OK
*********************************************************************************************************
*/
uint8_t DS18B20_ReadTemp(uint8_t _ch, float *_result)
{
    uint8_t re = 0;
    
    if (_ch >= DS_CHAN_NUM)
    {
        return re;
    }
    
    EIO_ConfigPort(_ch, ES_GPIO_OUT);    /* DQ方向输出 */
    
    s_err[_ch] = 1;
    s_ch = _ch;
    s_status = 0;
    
    /* 定时周期10us,频率100KHz */
    DS18B20_StartTimerIRQ();
    
    while (s_status != 10)
    {
        //bsp_Idle();
    }
      
    DS18B20_StopTimerIRQ();
    
    if (s_err[s_ch] == 1)
    {
        re = 0; /* 返回异常温度值 */
    }
    else
    {
        *_result = (float)s_DS18B20_TempReg[s_ch] / 16;
        re = 1;
    }

    return re;
}

/*
*********************************************************************************************************
*        函 数 名: DS18B20_CaculCRC
*        功能说明: CRC校验
*        形    参: _buf: 数据缓冲区
*                          _len : 数据长度
*        返 回 值: 校验值
*********************************************************************************************************
*/
uint8_t DS18B20_CaculCRC(uint8_t *_buf, uint8_t _len)
{
    uint8_t crc = 0, i, j;
        
    for (i = 0; i < _len; i++)
    {
        crc = crc ^ _buf[i];
        for (j = 0; j < 8; j++)
        {
            if (crc & 0x01) 
            {
                crc = (crc >> 1) ^ 0x8C;
            }
            else
            {
                crc >>= 1;
            }
        }
    }
    return crc;
}

/*
*********************************************************************************************************
*        函 数 名: DS18B20_IRQ_HANDLE
*        功能说明: 10us定时中断服务程序,读取DS18B20温度值
*        形    参: 无
*        返 回 值: 无
*********************************************************************************************************
*/
void DS18B20_IRQ_HANDLE(void)
{
        static volatile uint16_t s_time = 0;
        static volatile uint16_t s_call_ret = 0;
        static volatile uint16_t s_write_value = 0;
        static volatile uint8_t s_i;
        static volatile uint8_t s_read;
        static volatile uint8_t s_rx_buf[9];
        static volatile uint8_t s_rx_len = 0;
        
        if (TIM_HARD->SR & TIM_IT_UPDATE)
        {
                TIM_HARD->SR = (uint16_t)~TIM_IT_UPDATE;
        }
        
        /*
                复位时序, 见DS18B20 page 15

                首先主机拉低DQ,持续最少 480us
                然后释放DQ,等待DQ被上拉电阻拉高,约 15-60us
                DS18B20 将驱动DQ为低 60-240us, 这个信号叫 presence pulse  (在位脉冲,表示DS18B20准备就绪 可以接受命令)
                如果主机检测到这个低应答信号,表示DS18B20复位成功
        */
        s_time++;
        switch (s_status)
        {
        //                DS18B20_Reset(_ch);

        //                DS18B20_WriteByte(_ch, 0xcc);        /* 发命令 - 跳过ROM */
        //                DS18B20_WriteByte(_ch,0x44);        /* 发转换命令 */

        //                DS18B20_Reset(_ch);                /* 总线复位 */

        //                DS18B20_WriteByte(_ch, 0xcc);        /* 发命令 */
        //                DS18B20_WriteByte(_ch,0xbe);        /* 读温度 */

        //                temp1 = DS18B20_ReadByte(_ch);        /* 读温度值低字节 */
        //                temp2 = DS18B20_ReadByte(_ch);        /* 读温度值高字节 */

        //                return ((temp2 << 8) | temp1);        /* 返回16位寄存器值 */        
        
        case 0:
                s_status++;
                break;
        
        case 1:
                s_call_ret = s_status + 1;      /* 执行完毕返回状态 */
                s_status = 100;                            /*         去执行DS18B20_Reset */
                break;

        case 2:
                s_call_ret = s_status + 1;
                s_write_value = 0xcc;
                s_status = 110;                /* DS18B20_WriteByte(_ch, 0xcc); */
                break;

        case 3:
                s_call_ret = s_status + 1;
                s_write_value = 0x44;
                s_status = 110;                /* DS18B20_WriteByte(_ch,0x44); */
                break;        
        
        case 4:
                s_call_ret = s_status + 1;
                s_status = 100;                /*         DS18B20_Reset */
                break;        
        
        case 5:
                s_call_ret = s_status + 1;
                s_write_value = 0xcc;
                s_status = 110;                /* DS18B20_WriteByte(_ch, 0xcc); */
                break;

        case 6:
                s_call_ret = s_status + 1;
                s_write_value = 0xBE;
                s_status = 110;                /* DS18B20_WriteByte(_ch,0xbe); */
                break;        
        
        case 7:
                s_call_ret = s_status + 1;
                s_status = 120;                /* DS18B20_ReadByte(_ch); 读温度值 */
                break;        

        case 8:
                if (DS18B20_CaculCRC((uint8_t *)s_rx_buf, 8) == s_rx_buf[8] && s_rx_buf[4] == 0x7F)
                {
            		int16_t reg16;
            
            		reg16 = (s_rx_buf[1] << 8) + s_rx_buf[0];
                    s_DS18B20_TempReg[s_ch] = reg16;
            
            		s_err[s_ch] = 0;                /* 读取成功清0 */
                }
        		else
        		{
            		s_err[s_ch] = 1;
        		}
        		s_status++;
                break;
                
        case 9:
                /* 任务结束,关闭定时中断 */
                DS18B20_StopTimerIRQ();
                s_status++;
                break;
    
        case 10:    /* 执行一次结束,再此处等待 */
                ;
                break;    

        case 99:        /* 异常处理 */
                s_err[s_ch] = 1;                /* 错误 */
        s_status = 9;                /* 结束任务 */        
                break;        
                
        /************ DS18B20_Reset 总线复位子程序 ************/
        case 100:
        		DQ_DIR_OUTPUT(s_ch);    /* DQ方向输出 */
                DQ_0(s_ch);                                /* 拉低DQ */
                s_time = 0;
                s_status++;
                break;

        case 101:                                    /* 延迟 520uS, 要求这个延迟大于 480us */
                if (s_time >= 52)
                {
            DQ_DIR_INPUT(s_ch);     /* DQ方向输入, 外部上拉会拉到高 */
                        //DQ_1(s_ch);                            /* 释放DQ */
                        
                        s_time = 0;
                        s_status++;
                }
                break;

        case 102:                                                /* 延时60us,等待总线电平归位 */
                if (s_time >= 6)        
                {
                        s_time = 0;
                        s_status++;
                }
                if (!DQ_IS_LOW(s_ch))            /* 总线已经拉高 */        
                {
                        s_time = 0;
                        s_status++;
                }
                break;                
        
        case 103:                                                /* 等待DS18B20拉低总线 */
                if (DQ_IS_LOW(s_ch))                
                {
                        s_time = 0;
                        s_status++;
                }
                if (s_time > 8)                            /* 80us 没检测到应答,则认为DS18B20异常 */
                {
                        s_status = 99;                    /* 错误处理 */
                }
                break;
                
        case 104:                                                /* 等待DS18B20释放总线 */
                if (!DQ_IS_LOW(s_ch))        
                {
                        s_time = 0;
                        s_status++;
                }
                if (s_time > 30)                /* 300us 没释放,则认为DS18B20异常 */
                {
                        s_status = 99;                /* 错误处理 */
                }
                break;                

        case 105:                                                /* 复位成功,下面可以开始读数据了 */
                s_status = s_call_ret;
                break;        

        /************ DS18B20_WriteByte ************/
        //        for (i = 0; i < 8; i++)
        //        {
        //                DQ_0(_ch);
        //                bsp_DelayUS(2);

        //                if (_val & 0x01)
        //                {
        //                        DQ_1(_ch);
        //                }
        //                else
        //                {
        //                        DQ_0(_ch);
        //                }
        //                bsp_DelayUS(60);
        //                DQ_1(_ch);
        //                bsp_DelayUS(2);
        //                _val >>= 1;
        //        }        
        case 110:
                s_i = 0;
                s_status++;
                break;
        
        case 110 + 1:
        DQ_DIR_OUTPUT(s_ch);    /* DQ方向输出 */
                DQ_0(s_ch);                            /* 拉低DQ */
                bsp_DelayUS(2);
                if (s_write_value & 0x01)
                {
                        DQ_1(s_ch);
                }
                else
                {
                        DQ_0(s_ch);
                }
                s_time = 0;
                s_status++;
                break;        
        
        case 110 + 2:
                if (s_time >= 6)
                {
                        DQ_1(s_ch);                                
                        s_write_value >>= 1;
                        
                        if (++s_i >= 8)
                        {
                                s_status++;                
                        }
                        else
                        {
                                s_status--;                
                        }
                }
                break;
        
        case 110 + 3:
                s_status = s_call_ret;        
                break;
        
        /************ DS18B20_ReadByte ************/
        //        uint8_t i;
        //        uint8_t read = 0;

        //        for (i = 0; i < 8; i++)
        //        {
        //                read >>= 1;

        //                DQ_0(_ch);
        //                bsp_DelayUS(3);
        //                DQ_1(_ch);
        //                bsp_DelayUS(3);

        //                if (DQ_IS_LOW(_ch))
        //                {
        //                        ;
        //                }
        //                else
        //                {
        //                        read |= 0x80;
        //                }
        //                bsp_DelayUS(60);
        //        }
        //        return read;
        
        case 120:
                s_rx_len = 0;
                s_status++;        
                break;
        
        case 120 + 1:
                s_i = 0;
                s_read = 0;
                s_status++;        
                break;

        case 120 + 2:
                s_read >>= 1;
        		DQ_DIR_OUTPUT(s_ch);        /* DQ方向输出 */
                DQ_0(s_ch);
                bsp_DelayUS(3);
                //DQ_1(s_ch);
       			DQ_DIR_INPUT(s_ch);         /* DQ方向输入 */
                bsp_DelayUS(3);
                if (DQ_IS_LOW(s_ch))
                {
                        ;
                }
                else
                {
                        s_read |= 0x80;
                }                
                s_time = 0;
                s_status++;                
                break;
                
        case 120 + 3:
                if (s_time >= 6)                /* 延迟60us */
                {
                        if (++s_i >= 8)
                        {
                                s_status++;        
                        }
                        else
                        {
                                s_status--;        
                        }                        
                }
                break;

        case 120 + 4:
                s_rx_buf[s_rx_len] = s_read;
                if (++s_rx_len >= 9)
                {
                        s_status++;
                }
                else
                {
                        s_status = 120 + 1;
                }
                break;
                
        case 120 + 5:
                //s_err[s_ch] = 0;                /* 读取成功清0 */
                s_status = s_call_ret;
                break;
        }
}

/*
*********************************************************************************************************
*        函 数 名: DS18B20_StartTimerIRQ
*        功能说明: 启动TIM定时中断
*        形    参: 无
*        返 回 值: 无
*********************************************************************************************************
*/
static void DS18B20_StartTimerIRQ(void)
{
        bsp_SetTIMforInt(TIM_HARD, 100*1000, 0, 0);
}

/*
*********************************************************************************************************
*        函 数 名: DS18B20_StopTimerIRQ
*        功能说明: 关闭TIM定时中断
*        形    参: 无
*        返 回 值: 无
*********************************************************************************************************
*/
static void DS18B20_StopTimerIRQ(void)
{
        bsp_SetTIMforInt(TIM_HARD, 0, 0, 0);
}

  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
DS18B20是一种数字温度传感器,它可以通过单总线接口与微控制器通信。它不支持定时器中断方式,因为它的通信方式是基于单总线的协议,需要主动发送和接收命令。 要使用DS18B20传感器,您可以使用GPIO引脚来控制传感器的通信。您可以通过软件方式实现定时器功能,例如在特定时间间隔内读取传感器数据。 以下是一个示例代码片段,展示了如何使用DS18B20传感器并实现定时器功能(以Arduino为例): ```cpp #include <OneWire.h> #include <DallasTemperature.h> #define ONE_WIRE_BUS 2 // DS18B20连接到2号引脚 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); unsigned long previousMillis = 0; // 上一次读取温度的时间 const long interval = 1000; // 每隔1秒读取一次温度 void setup() { Serial.begin(9600); sensors.begin(); } void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; sensors.requestTemperatures(); // 请求温度数据 float temperatureC = sensors.getTempCByIndex(0); Serial.print("Temperature: "); Serial.println(temperatureC); } } ``` 这段代码使用了OneWire库和DallasTemperature库来与DS18B20传感器通信。在loop()函数中,通过使用millis()函数来实现定时器功能,并在每隔1秒读取一次温度数据。 请注意,具体的实现方式可能因所使用的硬件平台和编程语言而有所不同。以上示例代码仅供参考,您可能需要根据自己的需求进行适当的修改和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值