12下一页
返回列表 发新帖我要提问本帖赏金: 10.00元(功能说明)

[应用相关] 如何看懂时序图,以DHT21为例

[复制链接]
3676|40
 楼主| gaoyang9992006 发表于 2020-9-17 16:51 | 显示全部楼层 |阅读模式
#申请原创# @21小跑堂   @21小跑堂    @21小跑堂

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

比如常用的单线协议的温湿度传感器DHT21.
584615f631ce52f39d.png
可以看到一共40BIT,并注意到是以8BIT为单位的,因此我们可以先规划成每次读取8BIT,读取5次,完成读取。
683915f631e9d45280.png

开始读取时候,假设传感器是空闲的,那么这个时候传感器就是在高电平,主控想要发起读取,要给传感器一个读取的信号,
这个信号就是先拉低至少500us,然后拉高20到40us.
因此这个时候,主控的IO要处于输出状态,我们可以输出1,也可以输出0,先输出1,然后输出0,将0持续的事件大于500us,然后输出1
保持20us到40us.
为了靠谱,我这里拉低持续2ms,拉高持续30us.
先设置IO的模式为输出模式。
  1.     Write_AM2301_PIN_Init();

拉低这个端口,即输出0
  1.     RESET_AM2301_PIN();

保持2ms,这样就满足最少500us了。
  1.     HAL_Delay(2);

然后拉高它,输出1
  1.     SET_AM2301_PIN();

保持30us
  1.     rt_hw_us_delay(30);

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

读取相应,因为接下来器件会主动拉低总线80us,然后再拉高80us.
我们先切换主控的这个IO到输入模式,进行读取。然后判断
器件准备好的这个拉低拉高信号。
第一步,切断刀输入模式,准备读取IO信号
  1.     Read_AM2301_PIN_Init();
  2.     Sensor_AnswerFlag=0;


判断是否传感器拉低了总线,拉低表示传感器要发送准备好信号了
  1.     if(Read_AM2301_PIN()==GPIO_PIN_RESET)
  2.     {
  3.         Sensor_AnswerFlag=1;
  4.         Sys_CNT=0;

等待准备好的拉低段80us结束,并计数,看看是否超时。
  1.        while(Read_AM2301_PIN()==GPIO_PIN_RESET)
  2.         {
  3.             if(++Sys_CNT>3000)
  4.             {
  5.                 Sensor_ErrorFlag=1;
  6.                 return 0;
  7.             }
  8.         }
  9.         Sys_CNT=0;

如果准备拉低状态顺利结束,再看看准备信号的拉高状态是否OK
  1.         while(Read_AM2301_PIN()==GPIO_PIN_SET)
  2.         {
  3.             if(++Sys_CNT>3000)
  4.             {
  5.                 Sensor_ErrorFlag=1;
  6.                 return 0;
  7.             }
  8.         }

一切OK的话,就该读取实际的传感器输出值了。这个时候要写入到存储传感器40BIT数值的变量里了
每次读取8BIT,一共5此,所以用个循环。方到准备好的变量数组里。
  1.         for(i=0;i<5;i++)
  2.         {
  3.             AM2301_Data<i> = Read_AM2301_Data();</i>
  4. <i>        }</i>


接下来我们还要实现什么呢,当然是基本的读取8BIT的操作了。
854575f63216061840.png
根据这个时序图,可以看出来什么是1,什么是0.
我们看到总线在传输数据时候,拉低都是50us,只有拉高长短不同,长的表示1,短的表示0.
因此我们读取每一位时候,只要先判断是不是低电平或者高电平,就行了。
在低电平时候我们等待,当高电平到来我们判断是否大于28us,因为26us~28us表示0,70us标志1.
所以我们找一个介于28到70us之间的判断阈值。
比如我以30us作为阈值,当低电平结束后,我延时30us,如果是0,这个时候高电平肯定结束了,
如果是1,高电平还在持续。
因此我通过这个思路判断是0还是1.
因为我要读取是8BIT,因此我用循环8次的操作。

  1. unsigned char Read_AM2301_Data(void)
  2. {
  3.     unsigned char i,cnt,buffer,tmp;
  4. //要读取8次
  5.     for (i = 0; i < 8; i++)
  6.     {
  7.         cnt=0;
  8. //判断低电平是否结束
  9.         while(!Read_AM2301_PIN())
  10.         {
  11.            if(++cnt>=3000)
  12.                break;
  13.         }
  14. //低电平结束后,进入高电平,开始计时30us
  15.         rt_hw_us_delay(30);
  16.         tmp=0;
  17. //如果此时还是高电平,那么肯定是大于28us,确定是1来了,赋值1
  18.         if(Read_AM2301_PIN())
  19.             tmp=1;
  20.         cnt=0;
  21. //等待高电平结束,号进入下一位的读取
  22.         while(Read_AM2301_PIN())
  23.         {
  24.             if(++cnt>=2000)
  25.                 break;
  26.         }
  27. //移位写入刚刚得到的1个BIT
  28.         buffer<<=1;
  29.         buffer|=tmp;
  30.     }
  31.     return buffer;
  32. }



接下来实现什么呢?
实现读取IO状态和写高低电平。
  1. unsigned char Read_AM2301_PIN(void)
  2. {
  3.     return HAL_GPIO_ReadPin(AM2301_PORT, AM2301_PIN);
  4. }

  5. void SET_AM2301_PIN(void)
  6. {
  7.     HAL_GPIO_WritePin(AM2301_PORT, AM2301_PIN,GPIO_PIN_SET);
  8. }

  9. void RESET_AM2301_PIN(void)
  10. {
  11.     HAL_GPIO_WritePin(AM2301_PORT, AM2301_PIN,GPIO_PIN_RESET);
  12. }


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

  2. //读传感器 端口位定义,可修改
  3. //*
  4. #define AM2301_PIN  GPIO_PIN_10
  5. #define AM2301_PORT GPIOA
  6. #define AM2301_GPIO_CLK_ENABLE()     __HAL_RCC_GPIOA_CLK_ENABLE()
  7. #define AM2301_GPIO_CLK_DISABLE()    __HAL_RCC_GPIOA_CLK_DISABLE()


  8. unsigned char Sensor_AnswerFlag;  //收到起始标志位
  9. unsigned char Sensor_ErrorFlag;   //读取传感器错误标志
  10. unsigned int  Sys_CNT;
  11. unsigned char AM2301_Data[5]={0x00,0x00,0x00,0x00,0x00};

  12. void Read_AM2301_PIN_Init(void)
  13. {
  14.     AM2301_GPIO_CLK_ENABLE();
  15.     GPIO_InitTypeDef GPIO_InitStruct;
  16.     GPIO_InitStruct.Mode    = GPIO_MODE_INPUT;
  17.     GPIO_InitStruct.Pull    = GPIO_PULLUP;
  18.     GPIO_InitStruct.Speed   = GPIO_SPEED_FREQ_HIGH;
  19.     GPIO_InitStruct.Pin     = AM2301_PIN;
  20.     HAL_GPIO_Init(AM2301_PORT, &GPIO_InitStruct);
  21. }

  22. void Write_AM2301_PIN_Init(void)
  23. {
  24.     AM2301_GPIO_CLK_ENABLE();
  25.     GPIO_InitTypeDef GPIO_InitStruct;
  26.     GPIO_InitStruct.Mode    = GPIO_MODE_OUTPUT_PP;
  27.     GPIO_InitStruct.Pull    = GPIO_PULLUP;
  28.     GPIO_InitStruct.Speed   = GPIO_SPEED_FREQ_HIGH;
  29.     GPIO_InitStruct.Pin     = AM2301_PIN;
  30.     HAL_GPIO_Init(AM2301_PORT, &GPIO_InitStruct);
  31. }

  32. unsigned char Read_AM2301_PIN(void)
  33. {
  34.     return HAL_GPIO_ReadPin(AM2301_PORT, AM2301_PIN);
  35. }

  36. void SET_AM2301_PIN(void)
  37. {
  38.     HAL_GPIO_WritePin(AM2301_PORT, AM2301_PIN,GPIO_PIN_SET);
  39. }

  40. void RESET_AM2301_PIN(void)
  41. {
  42.     HAL_GPIO_WritePin(AM2301_PORT, AM2301_PIN,GPIO_PIN_RESET);
  43. }

  44. unsigned char Read_AM2301_Data(void)
  45. {
  46.     unsigned char i,cnt,buffer,tmp;
  47.     for (i = 0; i < 8; i++)
  48.     {
  49.         cnt=0;
  50.         while(!Read_AM2301_PIN())
  51.         {
  52.            if(++cnt>=3000)
  53.                break;
  54.         }
  55.         rt_hw_us_delay(30);
  56.         tmp=0;
  57.         if(Read_AM2301_PIN())
  58.             tmp=1;
  59.         cnt=0;
  60.         while(Read_AM2301_PIN())
  61.         {
  62.             if(++cnt>=2000)
  63.                 break;
  64.         }
  65.         buffer<<=1;
  66.         buffer|=tmp;
  67.     }
  68.     return buffer;
  69. }

  70. unsigned char Read_Sensor(void)
  71. {
  72.     unsigned char i;
  73.     Write_AM2301_PIN_Init();
  74.     RESET_AM2301_PIN();
  75. //    rt_thread_mdelay(2);
  76.     HAL_Delay(2);
  77.     SET_AM2301_PIN();
  78.     rt_hw_us_delay(30);
  79.     SET_AM2301_PIN();

  80.     Read_AM2301_PIN_Init();
  81.     Sensor_AnswerFlag=0;
  82.     if(Read_AM2301_PIN()==GPIO_PIN_RESET)
  83.     {
  84.         Sensor_AnswerFlag=1;
  85.         Sys_CNT=0;
  86.         while(Read_AM2301_PIN()==GPIO_PIN_RESET)
  87.         {
  88.             if(++Sys_CNT>3000)
  89.             {
  90.                 Sensor_ErrorFlag=1;
  91.                 return 0;
  92.             }
  93.         }
  94.         Sys_CNT=0;
  95.         while(Read_AM2301_PIN()==GPIO_PIN_SET)
  96.         {
  97.             if(++Sys_CNT>3000)
  98.             {
  99.                 Sensor_ErrorFlag=1;
  100.                 return 0;
  101.             }
  102.         }
  103.         for(i=0;i<5;i++)
  104.         {
  105.             AM2301_Data[i] = Read_AM2301_Data();
  106.         }
  107.     }
  108.     else
  109.     {
  110.         Sensor_AnswerFlag=0;
  111.     }
  112.     return 1;
  113. }


打赏榜单

21小跑堂 打赏了 10.00 元 2020-09-22
理由:恭喜通过优秀原创文章审核!奖励10元!

小灵通2018 发表于 2020-9-17 17:36 | 显示全部楼层
多谢分享。
数据采集存储 发表于 2020-9-17 18:27 | 显示全部楼层
这篇**写得太好了,不错,不错的分享。
幸福小强 发表于 2020-9-17 22:14 | 显示全部楼层
太好了,好贴。
幸福小强 发表于 2020-9-17 22:20 | 显示全部楼层
看完了,顿时觉得容易多了。
wahahaheihei 发表于 2020-9-17 22:21 | 显示全部楼层
讲的很好,通俗易懂的好贴
里面有晴雨 发表于 2020-9-18 09:03 | 显示全部楼层
讲的通俗易懂的好帖子,不错的。我要学习一下。
 楼主| gaoyang9992006 发表于 2020-9-18 09:15 | 显示全部楼层
wahahaheihei 发表于 2020-9-17 22:21
讲的很好,通俗易懂的好贴

Thanks♪(・ω・)ノ支持,多谢,多谢捧场
 楼主| gaoyang9992006 发表于 2020-9-18 09:16 | 显示全部楼层
里面有晴雨 发表于 2020-9-18 09:03
讲的通俗易懂的好帖子,不错的。我要学习一下。

Thanks♪(・ω・)ノ支持,多谢,多谢捧场。。。。
 楼主| gaoyang9992006 发表于 2020-9-18 09:16 | 显示全部楼层
幸福小强 发表于 2020-9-17 22:20
看完了,顿时觉得容易多了。

Thanks♪(・ω・)ノ支持,多谢,多谢捧场······
 楼主| gaoyang9992006 发表于 2020-9-18 09:16 | 显示全部楼层
数据采集存储 发表于 2020-9-17 18:27
这篇**写得太好了,不错,不错的分享。

Thanks♪(・ω・)ノ支持,多谢,多谢捧场######
便携手到老 发表于 2020-9-18 09:21 | 显示全部楼层
大神,真的很不错,浅显易懂,能够学到很多东西。
便携手到老 发表于 2020-9-18 09:30 | 显示全部楼层
利用IO来完成读写操作。完成的思路是模块化编程思想,将问题逐个分解。由大化小,实现小的功能。
 楼主| gaoyang9992006 发表于 2020-9-18 09:31 | 显示全部楼层
便携手到老 发表于 2020-9-18 09:21
大神,真的很不错,浅显易懂,能够学到很多东西。

多谢多谢,感兴趣的话,我会多发点类似的内容。我以前也走过不少弯路,其实电路设计也可以模块化,重复利用,提高设计效率。
小明的同学 发表于 2020-9-21 15:24 | 显示全部楼层
通俗易懂啊,给力。
单片小菜 发表于 2020-9-21 21:16 | 显示全部楼层
通俗易懂,很好学习的。现在学习了。不错的选择。

天灵灵地灵灵 发表于 2020-9-23 17:51 | 显示全部楼层
通俗易懂啊。
gejigeji521 发表于 2020-9-23 17:52 | 显示全部楼层
好贴,学习学习。
engineerDC 发表于 2020-9-23 18:45 | 显示全部楼层
学习学习!!!
xukun977 发表于 2020-9-27 08:17 | 显示全部楼层
本帖最后由 xukun977 于 2020-9-27 08:19 编辑

这个帖子我没看懂,跟文言文一样:


529775f6fd993a4cd7.png





自己百度,一遍看懂了:




977495f6fd9b250be9.png




建议网站找专业人士把把关,帖子内容方面,最起码文字要通顺,错别字少一点,不然这个万元红包活动很尴尬。



实话实说,不喜勿喷!






评论

这货不是在模电混的吗,难道还懂单片机?  发表于 2020-9-27 09:05
真有意思,大家都能理解,你还号称大佬级别的,竟然说看不懂了。 我这贴是讲如何根据时序图模块化写程序。  发表于 2020-9-27 08:55
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:如果你觉得我的分享或者答复还可以,请给我点赞,谢谢。

2052

主题

16403

帖子

222

粉丝
快速回复 在线客服 返回列表 返回顶部