定时器加状态转移图方式实现DS18B20
感谢硬汉哥提供的方案H7-TOOL同时采样10路DS18B20实现,带CRC校验,很实用
我们知道,DS18B20 是单总线时序通讯,采用严格的信号时序,以保证数据的完整性。因此如果采用操作系统,逻辑读取DS18B20为了避免时序被操作系统的任务调度,或者其他中断打乱,我们都会采用临界区的形式保护时序。
正常情况下,DS18B20通信的时间并不长,也就几十us。时间长的是18b20的转换过程(750ms)。
那么我们可以再和18B20通讯时关闭中断,通讯完毕立马开启中断,转换的过程可以不用管(时序要求再通讯部分)。这样就可以既不影响18b20数据的读取,一起不影响任务的调度。
但是这样的话,我们读取到的温度值就是上一时刻的转换完成的值了。
并且还需要使用各种DelayMs,DelayUs。在RTOS中非常的不方便,因此我们可以使用定时器加状态图的方式实现数据读取,状态图如下:
该种思想我们可以应用到FPGA中,FPGA操作时也是采用状态转移图实现。
时序
初始化复位时序
- 写操作
读操作
代码:
#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 温度1D1 PD15 PA8 PH19 - DIR PG8 温度2D2 PE6 PD0 PB7 - DIR PD9 温度3D3 PE5 PD1 PH11 - DIR PG10 温度4D4 PE4 PE7 PH12 - DIR PG12 温度5 --SWD调试占用D5 PE2 PE8 PI5 - DIR PG7 温度6D6 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-60usDS18B20 将驱动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);
}
定时器加状态转移图方式实现DS18B20相关推荐
- 定时器计数器工作方式
1.方式0 方式0为13位计数,由TL0的低5位(高3位未用)和TH0的8位组成.TL0的低5位溢出时向TH0进位,TH0溢出时,置位TCON中的TF0标志,向CPU发出中断请求. ...
- 中断系统应用实例(1)用定时器T1工作方式1控制两个LED以不同周期闪烁
1.控制目标 利用定时器T1工作方式1控制两个LED以不同周期闪烁,使得LED1每秒闪烁4次(即周期为250ms),LED2每秒闪烁1次(即周期为1s). 2.控制分析 2.1 LED侧分析 若要使L ...
- 利用定时器0工作方式1——独立按键控制流水灯模式
目录 1.前言 1.1 项目资源 1.2 实验现象 2.定时器/计数器 2.1定时器/计数器0&1的相关寄存器 2.2定时器的工作方式(总结) 3.中断系统 3.1中断概念 3.2中断流程: ...
- python ctypes库中动态链接库加载方式
最近看了<Gray hat python>一书,这才知道为什么python是黑客必学的编程语言.通过python的ctypes模块,可以直接调用动态链接库中的导出函数,而且甚至可以直接在p ...
- React Native 的图片加载方式
在做APP的时候,遇到了要加载图片的问题,本来以为很简单,,但是知道真相的我眼泪掉下来.在此记录一下.. 一.本地图片的加载 如上图:./代表当前文件,相当于是在本地项目根据目录找到该图片即可.问题是 ...
- hive--udf函数(开发-4种加载方式)
UDF函数开发 标准函数(UDF):以一行数据中的一列或者多列数据作为参数然后返回解雇欧式一个值的函数,同样也可以返回一个复杂的对象,例如array,map,struct. 聚合函数(UDAF):接受 ...
- 定时器0工作方式2,定时1s
定时器0工作方式2,定时1s
- 定时器0工作方式1,定时1s
定时器0工作方式1,定时1s.
- 动态链接库dll的两种加载方式
在第一篇技术博客"动态链接库简介"中说到了两种加载方式,当时没有详细说明,这里详细说明一下 可以通过两种方式 1.隐式链接(需要.dll,.lib,.h) 2.显式链接(需要.dl ...
- iOS 正确选择图片加载方式
正确选择图片加载方式能够对内存优化起到很大的作用,常见的图片加载方式有下面三种: //方法1 UIImage *imag1 = [UIImage imageNamed:@"image.png ...
最新文章
- 快速设置UITableView不同section对应于不同种类的cell
- android 手机投影演示 ——vysor操作手册
- Visual Studio registry capture utility 已停止工作的解决办法
- Linux Increase The Maximum Number Of Open Files / File Descriptors (FD)
- 【算法】图论学习笔记与代码实现
- 一个技术人的知识管理方法论
- OpenJudge NOI 1.7 08:字符替换
- 关于Excel的最大行数和列数。
- dbms_lob.substr最大能截取多少?_【贱猫圈速6】同款车前驱和四驱,圈速到底差多少?...
- 每日算法系列【LeetCode 825】适龄的朋友
- 最大熵图像复原方法原理(附完整代码)
- syslog可能引起得问题_电动车控制器经常出现的问题分析
- JFlash添加芯片
- vue3笔记(乱七芭蕉更新中)
- 故宫景点功课24:宁寿宫区6
- vue中使用h5 video标签实现弹窗播放本地视频
- 计算机病毒与防治的教案,计算机病毒及防治教案
- strcmp()函数的用法
- A First Course in Database Systems(数据库基础教程 第三版)课后答案——2.3.1\2.3.2\2.4.1
- 微信小程序网络请求配置问题及本地网络请求测试解决方案
热门文章
- dns预获取(dns-prefetch)link rel="dns-prefetch"优化载入速度
- APICloud的config.xml应用配置的说明
- linux安装2870无线网卡,告诉你Ubuntu 12.04下RT5370无线网卡驱动安装的方法及命令
- Wincc声音报警简单方法
- LinkPdf转换器-批量PDF转Word|PDF合并|压缩|拆分
- RQNOJ 能量项链
- javaweb小说阅读网站源码
- MIUI 13 去除输入法广告
- 计算机安装win10配置,安装Win10系统配置的最低要求
- 清华计算机科学技术复试2018,2018清华大学计算机科学与技术系考研复试名单