【智能家居项目】FreeRTOS版本——将裸机程序改造成FreeRTOS程序 | DHT11温湿度传感器

🐱作者:一只大喵咪1201
🐱专栏:《智能家居项目》
🔥格言:你只管努力,剩下的交给时间!
图

图
如上图所示是裸机版本的智能家居项目总体框架结构,这篇文章开始,本喵要带着大家逐渐将智能家居项目从逻辑版本改为FreeRTOS版本,并且要增加温湿度显示和网络时间获取两个功能。

目录

  • 😸裸机程序的不足
  • 😸合并代码
  • 😸改造
    • 😹思路
    • 😹将环形缓冲区改为队列
    • 😹队列调试
  • 😸DHT11温湿度传感器
    • 😹时序及驱动层代码
    • 😹其他层代码
  • 😸总结

😸裸机程序的不足

int main()
{
	while(1)
	{
		if(按键按下)
		{
			点亮LED灯();
		}
		
		if(网络输入)
		{
			OLED显示();
			LED控制();
			风扇控制();
			...
		}
	}
}

上面就是本喵前面用裸机方式实现智能家居本质逻辑的伪代码,在while(1)不停检测是否有输入事件产生,如果有则判断是按键输入还是网络输入,不同类型的输入事件对应不同的处理方式。

按键输入时,点亮LED灯的逻辑很简单,所以执行的非常快,当网络输入时,OLED显示等处理则比较复杂,执行速度相对较慢。

此时程序是顺序执行的,如果在处理网络输入时,又产生了按键输入,此时CPU并不会立刻去处理按键,而是需要等处理完网络输入后再去处理按键,这样一来就会感觉整个系统比较卡顿,延时比较高。

  • 缺点非常明显:不同任务模块之间相互影响。

int main()
{
	while(1)
	{
		检测输入事件();	
	}
}

void USART3_IRQHandler()
{
	构造输入事件();
}

如上面伪代码,在裸机版本智能家居中,输入事件是在中断函数中构造的,尤其是在处理ESP8266的串口3中断函数中,构造事件比较复杂。

当在中断函数中构造网络输入事件时,后台程序(main函数)是处于中断状态不执行的,如果此时按键输入发生了,后台程序也无法处理按键,只有构造完网络数据从中断函数中退出才能处理按键。

  • 缺点非常明显:中断函数执行时间太长,就会导致:更低优先级的中断无法得到及时处理,后台程序无法及时执行。

用软件定时器解决:

创建一个硬件定时器(TIMx),依据该定时器设计软件定时器,软件定时器:

  • 软件定时器周期性触发。
  • 每个软件定时器都有一个回调函数。

图
如上图所示实现了两个软件定时器:

  • 软件定时器1:每4个tick,执行一次f1()
  • 软件定时器2:每2个tick,执行一次f2()

如果f1()f2()执行时间很短,不超过1个tick时,效果很好。一旦f1()f2()执行的时间比较长,就会出问题。

比如f2()需要执行3个tick,当它执行到第二个tick的时候,下一次f2()该执行了,但是当前的f2()还没有结束,所以下一次的f2()就得不到执行。

  • 缺点:周期性执行的函数执行时间不能太长。

总的来说,裸机程序的主要缺点就是实时性不够高。

😸合并代码

在文章CubeMX对FreeRTOS的适配中本喵介绍了如何使用CubeMX创建FreeRTOS工程,此时打开前面裸机版本智能家居项目的CubeMX工程:

图
如上图,然后选择FREERTOS并且选择CMSIS_V2接口,然后生成代码:

图
如上图红色框,此时原本的裸机项目工程中就有了FreeRTOS了,文件多了一个Middlewares/FreeRTOS,代码中多了内核以及FreeRTOS初始化部分代码。

在FreeRTOS执行之前,调用SmartHomeTask函数,此时仍然没有使用到FreeRTOS,并且效果和裸机版本一样。

此时就实现了将裸机版本代码合并到FreeRTOS中,只是还没有使用任何和RTOS有关的东西。


接下来创建一个任务用来执行SmartHomeTask函数:

tu

如上图,使用xTaskCreate创建一个新任务来执行智能家居函数SmartHome,此时就将原本的裸机程序简单地变成了一个FreeRTOS程序了,执行效果和之前的裸机程序一样。

😸改造

😹思路

完全没有必要重新使用FreeRTOS实现一遍智能家居项目,只需要在裸机版本的基础上进行改造即可。

图
如上图所示,左边黑色框是智能家居系统的输入,中间红色是业务子系统,业务子系统任务和输入方通过输入队列来通信,右边黑色框是输出。

输入:

  • 按键输入:使用中断来处理,把按键输入事件写入"输入队列"。
    • 按键输入执行快,耗时短,不会影响其他任务的执行,所以直接操作即可。
  • 网络输入:使用网络任务来处理,把网络输入写入到“输入队列”。
    • 微信小程序发来的控制命令,解析等操作耗时长,裸机方式会影响其他任务的实时性。
  • 温湿度输入:使用温湿度任务来处理,把温湿度结果写入"输入队列"。
    • 温湿度的获取比较耗时,裸机方式会影响其他任务。
  • 时间输入:使用时钟任务来处理,把获取的时间结果写入"输入队列"。
    • 从互联网获取时间比较耗时,裸机方式会影响其他任务。

业务子系统:

  • 从"输入队列"获取按键输入、微信控制信息,从而直接控制设备。
  • 从"输入队列"获取温湿度、时间,发送给OLED任务显示内容。

为了保证整个业务子系统能够实时处理输入队列中的数据,毫无疑问,业务子系统也是一个任务。

输出:

  • LDE灯和风扇:直接控制外设来控制设备。
    • 只需要改变相关引脚的电平状态,执行时间很短,不会对其他任务造成影响。
  • OLED任务:使用OLED任务来处理。
    • OLED上要显示的内容很多,有IP地址、温湿度、时间
    • 需要一个统筹管理的OLED任务:它负责操作OLED
    • 其他任务只能向OLED任务发出显示请求

以上就整个项目的改造思路。

😹将环形缓冲区改为队列

裸机版本的智能家居中,输入事件会被放入一个环形缓冲区中,业务子系统从这个环形缓冲区中读取事件并作出判断。

现在将原本的环形缓冲区改成队列,用来存放输入事件:

图
如上图,将input_buffer.c中原本的环形缓冲区改成一个队列g_xQueueInput,并且增加一个InitInputQueue函数用来初始化输入队列,在其内部调用xQueueCreate创建队列,创建成功返回0,失败返回-1并打印错误信息。


图
如上图所示,在写入输入事件的函数PutInputEvent中,原本是将输入事件写入到环形缓冲区中,此时改成使用xQueueSendFromISR写入到输入队列中。

按键输入:

图
如上图代码,当按键中断发生以后,会触发定时器消抖,定时器超时后会调用回调函数使用PutInputEvent将输入事件放入到输入队列中。

  • 输入事件是在中断中放入输入队列的。

网络输入:

tu
如上图代码所示,当网络数据到来时,会产生串口3中断,在中断函数中调用回调函数,回调函数中再调用PutInputEvent将输入事件放入到输入队列中。

  • 输入事件也是在中断中放入队列的。

可以看到,站在输入队列的角度来看,输入事件都来自中断,所以在PutInputEvent函数中使用xQueueSendFromISR将输入事件放入到输入队列中。


图
如上图代码,SmartHomeTask是一个普通任务,在该任务中调用GetInputEvent来读取输入队列中的输入事件,所以在该函数中使用的就是xQueueReceive来读取。

😹队列调试

此时虽然改造完毕了,编译也没有报错,但是烧录到板子里并不能正常运行,本喵带着大家来调试一下:

tu
如上图,在FreeRTOSConfig.h中改造一下宏函数configASSERT,在里面加一个printf函数打印代码信息,再将程序烧录开发板中。

图

如上图,使用sscom工具当作服务端和开发板建立UDP连接,然后发送控制指令。

图
如上图,此时在串口助手中将会看到configASSERT中打印的代码信息,然后程序死机,也没有产生预想的动作。

图
如上图,定位到configASSERT打印的代码信息处,发现这里调用了configASSERT,条件是ucCurrentPriority >= ucMaxSysCallPriority

  • ucCurrentPriority是当前中断的优先级。
  • ucMaxSysCallPriority,是允许使用系统调用中断的最高优先级。

既然程序死机了,说明执行了configASSERT陷入了for(;;)循环中,进而说明条件不满足,也就是当前中断的优先级在编号上小于允许使用系统调用中断的优先级编号。

  • 当前中断的优先级高于允许使用系统调用中断的优先级,这是不被允许的,所以死机。

图
如上图所示,在调用xQueueGenericSendFromISR向队列中写数据时,会调用portASSERT_IF_INTERRUPT_PRIORITY_INVALID,这是一个宏函数,会调用vPortValidateInterruptPriority,在该函数内会调用configASSERT来判断当前中断优先级和允许使用系统调用优先级的关系。

图
如上图,当开发板收到网络数据后会产生串口3中断,然后在中断函数中调用回调函数来将输入事件写入到队列中,所以此时当前中断就是串口3中断。

  • 串口3中断的优先级高于允许使用系统调用的中断优先级,所以不被允许,程序死机。

图
如上图,将串口3中的优先级改成14,仅比Tick中断15高一级,但是比允许使用系统调用的中断优先级低,此时就允许在串口3中断函数中使用xQueueSnendFormISR向队列中写数据了。

此时程序就可以正常运行了,configASSERT中的打印信息也不再打印,程序也不会死机,而且会产生预想的动作。

  • 凡是会在中断中使用系统调用的,都必须把它的优先级设置成低于允许使用系统调用的中断优先级。

😸DHT11温湿度传感器

图
如上图是DHT11的接线图,只需要三根线,VDD,GND,以及DATA,传感器和MCU共用一根数据线来传送数据。

图
如上图,本喵的开发板用PF6与DHT11的DATA相连。

😹时序及驱动层代码

初始化:
图
如上图所示通讯过程示意图,用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后DHT11发送响应信号。

然后DHT11发送出40bit的数据,用户可选择读取部分数据.从模式下DHT11接收到开始信号触发一次温湿度采集如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集,采集数据后转换到低速模式。

这个过程中:

  • 主机发送开始信号后将DATA线拉高,释放总线控制权。
  • DHT11拉低总线发出响应信号后,主动拉高总线准备输出数据。
  • DHT11开始发送40bit(5 bytes)数据。
  • 从机释放总线,等待下一次开始。

对于MCU而言:

  • 主机GPIO输出低电平,然后拉高,转为输入模式。
  • 主机读取GPIO电平,读到低电平之后再等待读取到高电平,确认收到DHT11的响应。
  • 主机确认得到响应后,通过读取到GPIO高低电平的时长,来判断收到的是逻辑1还是逻辑0,来组成真正的数据。
  • 主机连续读取到40个bit之后,转为输出模式,准备为下一次读取数据发出开始信号。

可以看到,主机的GPIO存在输入模式和输出模式的转换,所以该IO口需要设置成开漏输出模式,当将IO电平拉高后就成为了输入模式。


图
如上图所示,总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测到起始信号。

DHT11接收到主机的开始信号后,等待主机开始信号结束,然后发送80us低电平响应信号。

主机发送开始信号结束后,将总线拉高保持20-40us,释放总线控制权,然后读取DHT11的响应信号。

总线为低电平,说明 DHT11发送响应信号,DHT11发送响应信号后,再把总线拉高80us,准备发送数据。

  • 这里的延时是微妙级别的,而TICK中断一次的时间是1ms,所以需要配置一个定时器进行微妙级别的延时。

图
如上图,使用CubeMX配置定时器3作为微秒级别的定时器,分频系数是72,此时定时器的频率为72M/72=1MHZCNT计数值增加一次,用时1us。

图
如上图,在driver_dht11.c中定义DHT11_TIM_Init函数来初始微秒定时器,并且定义微秒延时函数DHT11_usDelay,传入的us值是多少,就延时多长时间。

图
如上图,定义HDT11_Init函数来初始化DHT11引脚,设置为开漏输出模式,同时初始化微秒定时器。

图
如上图,在driver_dht11.h中定义宏DHT11_H用来拉高总线,DHT11_L用来拉低总线,DHT11_IN用来读取总线上的电平。

图
如上图,定义DHT11_Start来发出起始信号,主机将总线拉低20ms,大于18ms

图
如上图所示,主机按照该流程图进行操作,操作完毕后就可以开始读取DHT11发送来的数据了。

图
如上图代码所示,主机发出起始信号后,将总线拉高保持40us释放总线控制权,然后读取总线电平,读取到低电平说明从机开始应答,然后进入第一个while不断读取总线电平,当电平变为高以后说明从机结束应答。

再进入第二个while,当电平变成低以后说明这是从机开始发送一个数据,接下来就是判断该bit的数据是0还是1。

读数据:

图
如上图,当DHT11开始控制总线后,每一bit数据都以 50us低电平时隙开始,高电平的长短定了数据位是0还是1。

  • 50us低电平过后,高电平保持的时间在26us~28us之间表示该bit是低电平。

图

  • 50us过后,高电平保持时间是70us表示该bit是高电平。

如上图,在读数据时,从机会先发维持50us的低电平,然后再维持一定时间的高电平,所以需要读取总线上电平变化:
图
如上图代码,val是要等待的目标总线电平状态,timeout是超时时间,单位是us

  • val是1时,说明在调用该函数时,总线电平状态是0,当总线电平状态变为1以后返回。
  • val是0时,说明在调用该函数时,总线电平状态是1,当总线电平状态变为0以后返回。
  • 接收数据时以字节为单位,先接收到的bit是高位,后接收的是低位。

图
如上图所示,开始接收一个字节时,总线处于低电平,所以要先等其变为高,然后再延时40us,如果总线电平仍然是高,已经超过0所规定的28us,说明该bit是1。

由于先收到的bit是高位,所以每次接收一个bit时需要将data左移一位,当前bit是1则与data或等,是0的话则不用,只需要左移即可。

bit数据组合到data中后,再等待总线电平为低,开始下一个bit的接收。


数据格式:

一次完整的数据传输为40bit,先接收到的是高位bit。

  • 数据格式:
  • 8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验和。

数据传送正确时校验和数据等于8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据所得结果的末8位。

DHT11中小数部分是保留不用的,所以这两个字节的数据始终为0。

图
如上图代码所示,使用DHT11_Read来读取温湿度数据,使用一个5个字节的数组来接收温湿度数据。

当主机发送完开始信号接收数据时,将接收到的5个字节数据放入到数组中,然后进行校验,如果校验通过则给输出型参数湿度和温度赋值。

😹其他层代码

设备层:

图
如上图所示,在dht11_device.h中定义温湿度数据结构体DHT11_VAL,以及描述DHT11设备结构体DHT11Dev,包含设备号,温湿度数据,设备初始化方法,以及获取数据的方法。

图
如上图所示,在dht11_device.c中,创建全局的DHT11结构体变量并初始化。

内核抽象层:

tu
如上图所示是内核抽象层的DHT11设备初始化和获取数据的方法。

芯片抽象层:

tu
如上图所示是芯片抽象层的DHT11设备初始化和获取温湿度数据的方法,其本质是在调用驱动层的DHT11设备初始化和获取数据的方法。

tu
如上图所示,在dht11_test.c中定义dht11_test函数来测试DHT11模块,将接收到的温湿度数据通过串口打印出来。

图
如上图,在main.c中创建一个任务来执行DHT11的测试函数。
图
如上图所示,可以看到本喵所在位置的温度T=18℃,湿度H=49%。和天气预报上面的大致相符。

😸总结

本篇文章主要介绍了如何将之前的裸机版本智能家居改造成FreeRTOS版本,要注意使用系统调用的中断优先级,不能太高。

除此之外还介绍了DHT11温湿度传感器的使用方法,以及使用代码实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/153308.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

服装供应链管理的革新利器—超高频RFID技术

一、行业概述 服装行业一直被视为低技术含量的劳动密集型产业,但实际上,科学技术在整个行业的发展中起着至关重要的作用,从服装面料的制作到服装设计、生产制作、物流到终端销售,科技力量贯穿于每一个环节。然而,传统…

Codeforces Round 908 (Div 2——AB)

A. Secret Sport 题目 AB二人玩游戏,每一局(plays)游戏会有一个获胜者,首先获胜X局(play)的玩家得一分(赢得一轮sets)。率先获得Y分的玩家获得最终胜利。 给你整场游戏的每局&…

树状图PPT怎么做?用这个树状图制作软件轻松拿捏!

在我们的日常工作和学习中,PPT已经成为了我们常见的展示方式。 在制作PPT时,树状图PPT是非常重要和常用的一种,并且在商务、教育等领域都非常受欢迎。那么,究竟什么是树状图PPT,如何使用树状图制作软件来快速绘制树状…

2022年12月 Scratch(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

一、单选题(共25题,每题2分,共50分) 第1题 小明想在开始表演之前向大家问好并做自我介绍,应运行下列哪个程序?( ) A: B: C: D: 答案:D 外观积木配合显示时间,才能看清楚内容。 第2题 舞台有两个不同的背景,小猫角色的哪个

网站提示Internal Server Error的原因和解决方法分享

解决方法 登陆FTP或文件管理器,检查站点目录文件权限设置,将文件夹权限设置为755,单个文件权限设置为644。 这样设置644后,问题就可以解决,同时也不会影响网站的所需要的写入权限,满足网站正常运行。如果你的站点中有很多权限都要需要设置,为了提高效率。 应用导致分析…

案例分析:大疆的电子产品说明书在组织分类结构方面值得借鉴的地方

大疆科技作为全球领先的无人机和航拍设备制造商,其产品众多,包括无人机、相机等电子产品。为了提供给用户详尽准确的产品信息和操作指导,大疆在其官方网站上建立了一个电子产品说明书的帮助中心。在组织分类结构方面,大疆的电子产…

【Spring】 Spring中的IoC(控制反转)

以往在定义业务层实现时,在指定具体地Dao时候需要具体地定义出其实现: public class BookServiceImpl implements BookService{private BookDao bookDao new BookDaoImpl();public void save(){bookDao.save()} }public class BookDaoImpl implements …

什么是美国服务器,有哪些优势,适用于什么场景?

​  在互联网发展的过程中,服务器扮演着至关重要的角色。而美国作为全球信息技术的中心,其服务器在全球范围内受到广泛关注。  美国服务器是指在美国本土机房搭建并运行的服务器。其拥有带宽大、优质硬件、售后运维好、位置优越、数据安全性高以及免备…

命令行中引导用户指定选择文档

背景 在python中,我们如果需要操作文档,则需要用户指定文档,那么,如何引导用户指定或者选择文档呢? 导入包 本次我们即将演示的代码,使用了 DebugInfo python包,我们需要导入 DebugInfo 包 …

接口返回的二进制音频数据转url

const data await textToVoice({tts1: { input: "你好吗", speed: "1.0" }, });//data 接口返回的二进制音频数据 const blobData new Blob([data], { type: "audio/mpeg" }); const url window.URL.createObjectURL(blobData); // url >…

Flex布局---看一篇就够了

1. flex布局是什么? flex是flexible Box的缩写,用来为盒装模型提供的最大的灵活性,任何一个容器都可以指定为flex布局。Flex布局称为flex容器,所有的子元素为容器成员Flex项目(flex item); 当为…

城市生命线丨城市燃气管网监测系统功能效果

11月6日,福建泉州某小区发生煤气闪爆导致1死三伤,这起事故再次为我们敲响了城市燃气管网安全监测的警钟。在城市生命线的建设中,城市燃气管网监测系统以其独特的作用和价值,成为保障城市安全的重要一环。 根据应急管理部《全国城镇…

zookeeper的安装部署

目录 简介 Zookeeper架构设计及原理 1.Zookeeper定义 2.Zookeeper的特点 3.Zookeeper的基本架构 4.Zookeeper的工作原理 5.Zookeeper的数据模型 (1)临时节点 (2)顺序节点 (3)观察机制 Zookeeper集…

【知识增强】A Survey of Knowledge-Enhanced Pre-trained LM 论文笔记

A Survey of Knowledge-Enhanced Pre-trained Language Models Linmei Hu, Zeyi Liu, Ziwang Zhao, Lei Hou, Liqiang Nie, Senior Member, IEEE and Juanzi Li 2023年8月的一篇关于知识增强预训练模型的文献综述 论文思维导图 思维导图网页上看不清的话,可以存…

systemverilog:interface中端口方向理解

(1)从testbench的角度看,tb中信号的输入输出方向与interface中信号输入输出方向一致: (2)从DUT角度看,DUT中信号输入输出方向与interface中信号输入输出方向相反。简单图示如下: 代…

用哈希表封装unordered_map(以及set)【C++】

目录 一,前言 二,封装层框架(哈希底层以哈希桶为例) 三,迭代器 1. operator 2. operator[] 3. 仿函数优化 3. 解决unordered_set中Key可以修改的Bug 代码区 Hash_map_set.h HashTable.h 下节预告&#xff1…

Ganache结合内网穿透实现远程不同局域网公网访问

文章目录 前言1. 安装Ganache2. 安装cpolar3. 创建公网地址4. 公网访问连接5. 固定公网地址 正文开始前给大家推荐个网站,前些天发现了一个巨牛的 人工智能学习网站, 通俗易懂,风趣幽默,忍不住分享一下给大家。 点击跳转到网站…

3GPP协议解读(一)_23.501_23.502_PDU Session_SMF与UDP的交互

UE发起计算服务申请后,网络侧处理的流程 UE发起服务的流程:service request网络侧处理服务涉及的通信数据通过PDU Session进行传输,涉及到SMF与UPF的交互。PDU Session的建立、管理全部由SMF(Session Management Function&#x…

在混料配料输送系统中使用485modbus转profinet网关案例

485Modbus转Profinet网关是一种在工业自动化控制系统中常用的设备,能够实现不同通信协议之间的转换,对于混料配料输送系统的优化和控制具有重要作用。 通过使用485Modbus转Profinet网关,混料配料输送系统能够实现与不同设备之间的通信和数据交…

Android 框架

MVC: MVP MVVM Model 数据以及业务数据 View 视图 Control 控制器 simple code MVP OnFinishInflate ViewGroup 加载完成 MVC 优化 Struts MVC- MVP MVC-单次调用逻辑把 MVP / 把C拆分出来 MVVM 2017Google推出ViewModel DataBind MVVM是一种框架规则,双向绑定 Model…