有很多传感器手册给了我们时序图,我们只要按照时序图操作就行了,还有一些是标准接口,例如SPI,IIC,UART,这些可以利用硬件提供的收发器通信,还有一些我们没有足够的接口,或者没有对应的接口与之通信,我们可以按照手册提供的时序图,利用IO来完成读写操作。完成的思路是模块化编程思想,将问题逐个分解。由大化小,实现小的功能。

比如常用的单线协议的温湿度传感器DHT21.

可以看到一共40BIT,并注意到是以8BIT为单位的,因此我们可以先规划成每次读取8BIT,读取5次,完成读取。

开始读取时候,假设传感器是空闲的,那么这个时候传感器就是在高电平,主控想要发起读取,要给传感器一个读取的信号,

这个信号就是先拉低至少500us,然后拉高20到40us.

因此这个时候,主控的IO要处于输出状态,我们可以输出1,也可以输出0,先输出1,然后输出0,将0持续的事件大于500us,然后输出1

保持20us到40us.

为了靠谱,我这里拉低持续2ms,拉高持续30us.

先设置IO的模式为输出模式。

复制

Write_AM2301_PIN_Init();

拉低这个端口,即输出0

复制

RESET_AM2301_PIN();

保持2ms,这样就满足最少500us了。

复制

HAL_Delay(2);

然后拉高它,输出1

复制

SET_AM2301_PIN();

保持30us

复制

rt_hw_us_delay(30);

接下来传感器就该响应这个请求了,这个时候就要让主控读取信号的模式了

读取相应,因为接下来器件会主动拉低总线80us,然后再拉高80us.

我们先切换主控的这个IO到输入模式,进行读取。然后判断

器件准备好的这个拉低拉高信号。

第一步,切断刀输入模式,准备读取IO信号

复制

Read_AM2301_PIN_Init();

Sensor_AnswerFlag=0;

判断是否传感器拉低了总线,拉低表示传感器要发送准备好信号了

复制

if(Read_AM2301_PIN()==GPIO_PIN_RESET)

{

Sensor_AnswerFlag=1;

Sys_CNT=0;

等待准备好的拉低段80us结束,并计数,看看是否超时。

复制

while(Read_AM2301_PIN()==GPIO_PIN_RESET)

{

if(++Sys_CNT>3000)

{

Sensor_ErrorFlag=1;

return 0;

}

}

Sys_CNT=0;

如果准备拉低状态顺利结束,再看看准备信号的拉高状态是否OK

复制

while(Read_AM2301_PIN()==GPIO_PIN_SET)

{

if(++Sys_CNT>3000)

{

Sensor_ErrorFlag=1;

return 0;

}

}

一切OK的话,就该读取实际的传感器输出值了。这个时候要写入到存储传感器40BIT数值的变量里了

每次读取8BIT,一共5此,所以用个循环。方到准备好的变量数组里。

复制

for(i=0;i<5;i++)

{

AM2301_Data = Read_AM2301_Data();

}

接下来我们还要实现什么呢,当然是基本的读取8BIT的操作了。

根据这个时序图,可以看出来什么是1,什么是0.

我们看到总线在传输数据时候,拉低都是50us,只有拉高长短不同,长的表示1,短的表示0.

因此我们读取每一位时候,只要先判断是不是低电平或者高电平,就行了。

在低电平时候我们等待,当高电平到来我们判断是否大于28us,因为26us~28us表示0,70us标志1.

所以我们找一个介于28到70us之间的判断阈值。

比如我以30us作为阈值,当低电平结束后,我延时30us,如果是0,这个时候高电平肯定结束了,

如果是1,高电平还在持续。

因此我通过这个思路判断是0还是1.

因为我要读取是8BIT,因此我用循环8次的操作。

复制

unsigned char Read_AM2301_Data(void)

{

unsigned char i,cnt,buffer,tmp;

//要读取8次

for (i = 0; i < 8; i++)

{

cnt=0;

//判断低电平是否结束

while(!Read_AM2301_PIN())

{

if(++cnt>=3000)

break;

}

//低电平结束后,进入高电平,开始计时30us

rt_hw_us_delay(30);

tmp=0;

//如果此时还是高电平,那么肯定是大于28us,确定是1来了,赋值1

if(Read_AM2301_PIN())

tmp=1;

cnt=0;

//等待高电平结束,号进入下一位的读取

while(Read_AM2301_PIN())

{

if(++cnt>=2000)

break;

}

//移位写入刚刚得到的1个BIT

buffer<<=1;

buffer|=tmp;

}

return buffer;

}

接下来实现什么呢?

实现读取IO状态和写高低电平。

复制

unsigned char Read_AM2301_PIN(void)

{

return HAL_GPIO_ReadPin(AM2301_PORT, AM2301_PIN);

}

void SET_AM2301_PIN(void)

{

HAL_GPIO_WritePin(AM2301_PORT, AM2301_PIN,GPIO_PIN_SET);

}

void RESET_AM2301_PIN(void)

{

HAL_GPIO_WritePin(AM2301_PORT, AM2301_PIN,GPIO_PIN_RESET);

}

这里我直接调用的HAL库函数,其实这么做是方便移植,如果你要去其他芯片下使用,你只需要实现这3个函数以及延时函数就行了。逻辑顺序无需修改。最后奉上源码

复制

#include "stm32f0xx_hal.h"

//读传感器 端口位定义,可修改

//*

#define AM2301_PIN GPIO_PIN_10

#define AM2301_PORT GPIOA

#define AM2301_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()

#define AM2301_GPIO_CLK_DISABLE() __HAL_RCC_GPIOA_CLK_DISABLE()

unsigned char Sensor_AnswerFlag; //收到起始标志位

unsigned char Sensor_ErrorFlag; //读取传感器错误标志

unsigned int Sys_CNT;

unsigned char AM2301_Data[5]={0x00,0x00,0x00,0x00,0x00};

void Read_AM2301_PIN_Init(void)

{

AM2301_GPIO_CLK_ENABLE();

GPIO_InitTypeDef GPIO_InitStruct;

GPIO_InitStruct.Mode = GPIO_MODE_INPUT;

GPIO_InitStruct.Pull = GPIO_PULLUP;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

GPIO_InitStruct.Pin = AM2301_PIN;

HAL_GPIO_Init(AM2301_PORT, &GPIO_InitStruct);

}

void Write_AM2301_PIN_Init(void)

{

AM2301_GPIO_CLK_ENABLE();

GPIO_InitTypeDef GPIO_InitStruct;

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Pull = GPIO_PULLUP;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

GPIO_InitStruct.Pin = AM2301_PIN;

HAL_GPIO_Init(AM2301_PORT, &GPIO_InitStruct);

}

unsigned char Read_AM2301_PIN(void)

{

return HAL_GPIO_ReadPin(AM2301_PORT, AM2301_PIN);

}

void SET_AM2301_PIN(void)

{

HAL_GPIO_WritePin(AM2301_PORT, AM2301_PIN,GPIO_PIN_SET);

}

void RESET_AM2301_PIN(void)

{

HAL_GPIO_WritePin(AM2301_PORT, AM2301_PIN,GPIO_PIN_RESET);

}

unsigned char Read_AM2301_Data(void)

{

unsigned char i,cnt,buffer,tmp;

for (i = 0; i < 8; i++)

{

cnt=0;

while(!Read_AM2301_PIN())

{

if(++cnt>=3000)

break;

}

rt_hw_us_delay(30);

tmp=0;

if(Read_AM2301_PIN())

tmp=1;

cnt=0;

while(Read_AM2301_PIN())

{

if(++cnt>=2000)

break;

}

buffer<<=1;

buffer|=tmp;

}

return buffer;

}

unsigned char Read_Sensor(void)

{

unsigned char i;

Write_AM2301_PIN_Init();

RESET_AM2301_PIN();

// rt_thread_mdelay(2);

HAL_Delay(2);

SET_AM2301_PIN();

rt_hw_us_delay(30);

SET_AM2301_PIN();

Read_AM2301_PIN_Init();

Sensor_AnswerFlag=0;

if(Read_AM2301_PIN()==GPIO_PIN_RESET)

{

Sensor_AnswerFlag=1;

Sys_CNT=0;

while(Read_AM2301_PIN()==GPIO_PIN_RESET)

{

if(++Sys_CNT>3000)

{

Sensor_ErrorFlag=1;

return 0;

}

}

Sys_CNT=0;

while(Read_AM2301_PIN()==GPIO_PIN_SET)

{

if(++Sys_CNT>3000)

{

Sensor_ErrorFlag=1;

return 0;

}

}

for(i=0;i<5;i++)

{

AM2301_Data[i] = Read_AM2301_Data();

}

}

else

{

Sensor_AnswerFlag=0;

}

return 1;

}

此文章已获得原创/原创奖标签,著作权归21ic所有,未经允许禁止转载。

---------------------

作者:gaoyang9992006

链接:https://bbs.21ic.com/icview-3027014-1-1.html

来源:21ic.com

此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

如何看懂时序图,以DHT21为例的更多相关文章

  1. 看懂UML图

    看懂UML类图和时序图 这里不会将UML的各种元素都提到,我只想讲讲类图中各个类之间的关系: 能看懂类图中各个类之间的线条.箭头代表什么意思后,也就足够应对 日常的工作和交流: 同时,我们应该能将类图 ...

  2. 看懂类图——UML类图基础

    类图 要学懂设计模式,就需要先看得懂类图,类与类之间的关系是学习设计模式的基础,而在软件工程中,类与类之间的关系是通过UML中的类图来体现. 这篇笔记包含的不会是类图的所有东西,包含的只是各个类之间的 ...

  3. (转)看懂类图——UML类图基础

    类图 要学懂设计模式,就需要先看得懂类图,类与类之间的关系是学习设计模式的基础,而在软件工程中,类与类之间的关系是通过UML中的类图来体现. 这篇笔记包含的不会是类图的所有东西,包含的只是各个类之间的 ...

  4. 0160 十分钟看懂时序数据库(I)-存储

    摘要:2017年时序数据库忽然火了起来.开年2月Facebook开源了beringei时序数据库:到了4月基于PostgreSQL打造的时序数据库TimeScaleDB也开源了,而早在2016年7月, ...

  5. 看懂UML类图与时序图

    看懂UML类图和时序图 这里不会将UML的各种元素都提到,我只想讲讲类图中各个类之间的关系: 能看懂类图中各个类之间的线条.箭头代表什么意思后,也就足够应对 日常的工作和交流: 同时,我们应该能将类图 ...

  6. 看懂UML类图和时序图

    看懂UML类图和时序图 这里不会将UML的各种元素都提到,我只想讲讲类图中各个类之间的关系: 能看懂类图中各个类之间的线条.箭头代表什么意思后,也就足够应对 日常的工作和交流: 同时,我们应该能将类图 ...

  7. [转] 看懂UML类图和时序图

    PS: 组合关系:实心,一个类A属于另一个类,或多个类,但是类A不能单独存在去使用,A一般是一种抽象的东西 聚合关系:空心,一个类A可以单独存在使用 不论组合聚合,A的方法都会被直接调用. 看懂UML ...

  8. [转]看懂UML类图

    这里不会将UML的各种元素都提到,我只想讲讲类图中各个类之间的关系: 能看懂类图中各个类之间的线条.箭头代表什么意思后,也就足够应对 日常的工作和交流: 同时,我们应该能将类图所表达的含义和最终的代码 ...

  9. 看懂UML类图

    这里不会将UML的各种元素都提到,我只想讲讲类图中各个类之间的关系: 能看懂类图中各个类之间的线条.箭头代表什么意思后,也就足够应对 日常的工作和交流: 同时,我们应该能将类图所表达的含义和最终的代码 ...

随机推荐

  1. 接口神器之 Json Server 详细指南

    简介 json-server 是一款小巧的接口模拟工具,一分钟内就能搭建一套 Restful 风格的 api,尤其适合前端接口测试使用. 只需指定一个 json 文件作为 api 的数据源即可,使用起 ...

  2. CTF中的变量覆盖问题

    0x00 前言 最近在干代码审计,于是就把之前学习的CTF题目中有关变量覆盖的题目结合下进一步研究. 通常将可以用自定义的参数值替换原有变量值的情况称为变量覆盖漏洞.经常导致变量覆盖漏洞场景有:$$使 ...

  3. prtotype原型对象

    js每个对象都拥有一个原型对象,每个对象都能从原型对象继承方法和属性,原型链就是基于原型对象而产生的, 也就是说,每个对象都能继承原型对象的方法和属性,这样一层一层的继承,就形成了原型链 当然,你也可 ...

  4. 《剑指offer》面试题40. 最小的k个数

    问题描述 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 示例 1: 输入:arr = [3,2,1], k ...

  5. java单例模式(饿汉式和懒汉式)

    1 /* 2 * 设计模式:对问题行之有效的解决方式.其实它是一种思想. 3 * 4 * 1,单例设计模式 5 * 解决的问题:就是可以保证一个类在内容中的对象唯一性. 6 * 7 * 必须对于多个程 ...

  6. 集合框架-Map集合-TreeMap存储自定义对象

    1 package cn.itcast.p8.treemap.demo; 2 3 4 import java.util.Iterator; 5 import java.util.Map; 6 impo ...

  7. 第03讲:Flink 的编程模型与其他框架比较

    Flink系列文章 第01讲:Flink 的应用场景和架构模型 第02讲:Flink 入门程序 WordCount 和 SQL 实现 第03讲:Flink 的编程模型与其他框架比较 本课时我们主要介绍 ...

  8. python网络爬虫-解析网页(六)

    解析网页 主要使用到3种方法提取网页中的数据,分别是正则表达式.beautifulsoup和lxml. 使用正则表达式解析网页 正则表达式是对字符串操作的逻辑公式 .代替任意字符 . *匹配前0个或多 ...

  9. python 小兵内置函数进制转换

    Python内置函数进制转换的用法 使用Python内置函数:bin().oct().int().hex()可实现进制转换. 先看Python官方文档中对这几个内置函数的描述: bin(x)Conve ...

  10. python3调用js的库之execjs

    执行JS的类库:execjs,PyV8,selenium,node execjs是一个比较好用且容易上手的类库(支持py2,与py3),支持 JS runtime. 1.安装: pip install ...