简述网络营销推广的方式都有哪些,冯耀宗seo,绿色农产品网站 模板,百度账号登陆入口#x1f431;作者#xff1a;一只大喵咪1201 #x1f431;专栏#xff1a;《智能家居项目》 #x1f525;格言#xff1a;你只管努力#xff0c;剩下的交给时间#xff01; 目录 #x1f353;多任务系统中使用DHT11#x1f345;关闭调度器#x1f345;使用中断 作者一只大喵咪1201 专栏《智能家居项目》 格言你只管努力剩下的交给时间 目录 多任务系统中使用DHT11关闭调度器使用中断 获取SNTP服务器时间重新设计功能框架总结 多任务系统中使用DHT11
在上篇文章中本喵仅进行了单任务的DHT11温湿度传感器使用相当于裸机使用。 根据上面时序图计算接收一次数据(5个字节)的耗时不考虑主机发送起始信号的耗时
最小时间40 80 80 (5028)*40 3320us 3.32ms。最大时间40 80 80 (5070)*40 5000us 5ms。 在配置文件FreeRTOSConfig.h中configTICK_RATE_HZ的值是1000这表示FreeRTOS中有多个同优先级的就绪任务时它们依次执行1ms。 而读取DHT11时最少耗时3.32ms如果读取DHT11的过程被其他任务打断的话必定失败。 如上图所示在smarthouse.c文件中定义一个DHT11_UpdateValue函数来更新DHT11传感器的值并且通过串口打印出来。 如上图创建三个相同优先级的任务分别执行SmartHomeTask任务DHT11_UpdataValue以及other_task任务。
此时三个任务按照时间片轮转的方式在执行 如上图此时虽然ESP8266在某种巧合下连接成功了但是DHT11读取温湿度数据失败了始终都是0。
有三种解决方法
读取时关中断或者关闭调度。把DHT11的任务优先级设置为最高。修改程序在中断里记录时间、解析数据。
将DHT11任务优先级设置为最高必然能成功因为只有这一个任务在运行所以只介绍其他两种解决方法。
关闭调度器
目前项目中存在SmartHouseTask任务和DHT11_UpdateValue任务在连接WIFI和读取温湿度数据时都不能被打断必须具有原子性否则就会出现错误。 如上图所示在连接WIFI和建立连接之前先调用vTaskSuspendAll()关闭调度器此时其他任务无法调度操作完毕后再调用xTaskResumeAll()打开调度器恢复任务调度。 如上图所示在DHT11读取温湿度数据前将调度器关闭读取完毕后再打开防止其他任务来干扰导致读取出错。 如上图此时WIFI连接和温湿度数据读取都正常。
使用中断
将主机和DHT11传送数据的总线设置成外部中断模式 如上图在主机发出起始信号并将总线拉高时使能中断并且启动定时器。
DHT11每改变一次总线状态就会触发一次中断在中断函数中记录中断发生的时间Tx如上图中的T3-T2就是总线保持高电平时间根据时间判断出该bit是0还是1。
在发出起始信号后使能中断并启动定时器的原因 最理想的使能位置是在T3位置。但是这里使能完中断并启动定时器以后可能就过了50us了。会导致中断丢失从而造成数据读取错误。
所以提前使能中断这样的话一次读取数据的过程中会产生83次中断前3次DHT11做出应答时触发的中断所以在程序中抛弃这三次中断仅处理后面的80次中断即可。 40个bit一个bit会触发两次中断。 编程思路
初始化 设置定时器精度要达到1us量级设置DHT11数据引脚用作开漏输出模式设置DHT11数据引脚用于中断、双边沿触发
定时器前面本喵在上篇文中已经初始化过了这里直接使用DHT11_TIM_Init来初始化微秒定时器。 如上图定义一个函数DHT11_GPIO_Init_As_Output将数据引脚设置成开漏输出模式。 如上图定义一个函数DHT11_GPIO_Init_As_IRQ将数据引脚设置成中断模式。 如上图在DHT11初始化的时候先将引脚初始化为输出引脚并创建一个二进制信号量。 在DHT11_Read函数中 发出Start信号等待ACK使能数据引脚的中断阻塞 如上图先提供使能和失能中断的方式以及启动和停止定时器的方式在每次失能定时器的时候都要将记录中断发生次数的变量dht11_edge_cnt清零。
在启动定时器的时候设置最大超时时间为10ms因为读取DHT11一次数据(40bit)最多耗时5ms如果10ms还没有读成功就说明出问题了。 如上图所示发出起始信号将总线拉高后立即打开中断并启动定时器然后去获取信号量这是为了让该任务阻塞。 在DHT11数据引脚的中断函数中 记录中断发生的时间放在数组里累加中断次数发现40位数据都接收完毕后唤醒任务 如上图先提供一个能读取微秒定时器计数值的函数DHT11_ReadTimer_us。 如上图在中断函数中再增加用于处理DHT11产生的中断。当中断发生后获取一下发生的时间并放入到存放时间的数组中。
当中断发生80次以后就唤醒读取数据的任务调用DHT11_Notify_Task因为40个bit会产生80次中断。 如上图代码归还初始化时创建的信号量唤醒读取温湿度数据的任务。
如果中断发生了83次就直接返回不记录时间因为这时必然接收到了40个bit多发生的3次中断是DHT11应答产生的。 任务被唤醒后根据中断时间解析出得到的数据
在读取温湿度数据DHT11_Read时任务被唤醒后没有立刻处理数据而是又调用vTaskDelay(1)延时了1ms这是因为该任务被唤醒时产生了80次中断这80次中断中包含DHT11应答产生的中断此时数据产生的中断就没有到达80所以要再等一会。
然后再失能中断并停止定时器将数据引脚设置成输出模式再去解析数据。 如上图整体处理裸机和之前的方式差不多数据左移以后如果该bit是1则或等如果是0则不用处理。
但是这里需要处理多出来的3次中断83次中断中只有80次中断发生时记录的时间数据是有用的这里通过校验和来舍弃多发生的3次中断
从记录时间的数组首部开始取80个数据解析出温湿度数据。然后使用校验和来判断如果校验通过则直接采用如果不通过就抛弃数组第一个时间数据从下一个数据开始再取80个解析。最多解析3次就能校验得到正确的温湿度数据。
在判断每个bit是高电平还是低电平的时候让数组中后一个时间值减去前一个时间中得到的就是两次中断之间电平保持的时间如果大于40us则是高电平否则就是低电平。
如上图代码中所示需要的差值仅是高电平保持的时间低电平的不需要也就是只需要算1时刻减0时刻和3时刻减2时刻的时间差值。
所以每计算完一次以后数组下标直接加2而不是加1。 如上图将更新温湿度任务中关闭调度器的部分屏蔽掉因为现在使用的是中断方式。 中断的优先级高于所有任务所以读取温湿度过程中的是实时性是可以保证的。在中断没有将80个时间值处理完毕前该任务由于无法申请到信号量而处于阻塞状态。 如上图此时温湿度数据可以正常读取温度是T16℃湿度是H 38%并且WIFI也成功连接。打印的路径信息是校验错误时打印的这个数据就被抛弃了。 由于温湿度数据并不会剧烈变化所以不需要很高的实时性。 获取SNTP服务器时间
ESP8266带有从SNTP服务器上获取时间的功能查看手册中相关用法
设置 如上图所示是设置时域和SNTP服务器的AT指令设置指令格式
ATCIPSNTPCFG1,8,cn.ntp.org.cn,ntp.sjtu.edu.cn,us.pool.ntp.org 1表示使能相应的填0就表示失能。8表示时域北京时间位于东八区所以是8。引号中的内容是SNTP服务器的IP地址。
对于SNTP服务器可以将3个都填进去也可以不填使用默认的网上有很多SNTP服务器的IP地址这里本喵使用上海市的SNTP服务器IP地址202.120.2.101。发送的指令就是ATCIPSNTPCFG1,8,202.120.2.101。 如上图将指令使用串口发送给ESP8266后回显OK表明设置成功。
查询 如上图所示是查询SNTP时间的指令ATCIPSNTPTIME?回显的是查询到的时间 如上图所示查询时间后返回CIPSNTPTIME:Fri Nov 17 13:44:30 2023当前时间是星期五11月17日13点44分30秒2023年。 接下来就是将获取SNTP服务器时间加入到智能家居项目中了获取SNTP服务器时间是网卡的功能所以这部分代码放在网卡设备及esp8266驱动层。 如上图是描述获取到时间的结构体获取到的时间Fri Nov 17 13:44:30 2023中年是4个字节月是3个字节日是2个字节时是2个字节分是2个字节秒是2个字节使用数组存放。由于是字符串所以每个数组多出一个字节来放\0。 如上图在网卡设备结构体中增加一个unsigned char isConnected来表示网卡状态0表示WIFI没有连接1表示WIFI连接成功只有WIFI连接成功才能获取时间。再增加一个获取时间的函数指针GetTime。 如上图在esp8266.c中定义获取时间的函数ESP8266_GetNetTime在该函数中由于设置时区和服务器比较耗时还需要等待而且也无需重复设置所以只设置一次即可。
从回显的字符串中解析出时间后要判断一下是否是是1970年如果是则说明设置服务器和时区没有成功需要重新设置一下。 如上图初始化ESP8266时增加网络状态和获取时间函数指针的初始化。 如上图在smarthouse.c中连接WIFI的函数中WIFI连接成功就将isConnected标志置一表示WIFI连接成功了。 如上图在smarthouse.c中定义一个GetNetTime函数来获取网络时间获取到以后分别在屏幕和串口上显示。 如上图所示创建一个任务用来获取网络时间。 如上图串口所示此时既能获取温湿度数据又能获取网络时间但是此时还没有充分利用起来FreeRTOS特性所以其他功能会收到影响这里只是验证获取时间的功能是正常的至于OLED屏幕上的显示本喵就不拍照了。
重新设计功能框架 如上图所示是增加了两个功能模块后的框架结构图。
输入事件队列是信息中轴
按键中断触发定时器中断消除抖动后往队列写入按键类事件。使用微信小程序、sscom发出控制命令UART3接收到数据解析出数据后往队列写入网络类事件。SNTP任务每隔1分钟通过网络获取时间修正本地时间。1秒钟周期定时器往队列写入时间类事件以便更新OLED上的时间。DHT11任务每隔几秒钟读取温湿度往队列写入温湿度类事件。SmartHome任务 连接WIFI读取输入事件控制设备LED、风扇、OLED
任务的优先级
SmartHome任务优先级最高它要及时响应用户的操作平时大部分时间都是阻塞状态。定时器后台任务优先级第2高用来更新时间如果时间的更新不及时会影响产品体验。SNTP任务优先级第3高它每隔1分钟执行一次用来修正时间。DHT11任务优先级最低温湿度不会剧烈变化。 使用一个软件定时器作为1秒钟周期定时器将该定时器也看作是一个设备 如上图代码创建一个结构体来描述软件定时器包括设备编号定时周期回调函数软件定时器句柄以及初始化等方法。 如上图定义具体的函数然后来初始化周期定时器全局变量回调函数使用的是外部的vOneSecTimerFunc。其他函数调用内核层相应的函数。 如上图在内核抽象层使用FreeRTOS提供的操作软件定时器的接口即可在初始化的时候将创建软件定时器后生成的句柄填入到上一层的全局变量ptTimer-xPeriodTimer中。 软件定时器超时后会调用回调函数 如上图代码所示在软件定时器的回调函数中会先获取一下当前网卡的时间然后再给这个时间增加1秒因为软件定时器1秒钟超时一次在时间增加的时候要注意进位。
时间增加完毕后再将这个时间设置到网卡时间中并且构造输入事件写入到输入队列中让最上层的智能家居任务来显示这个时间到OLED上。
每超时一次后超时次数OverTimeCounter加1当前其等于60的时候到了一分钟此时要唤醒更新时间任务从SNTP服务器获取新的网络时间来校正本地时间。 在回调函数中获取网卡时间的时候前提是网卡中已经存在时间了 如上图在获取网卡时间时先xSemaphoreTake申请信号量如果成功则返回网卡时间的地址否则就阻塞。 使用一个信号量让更新网络时间的任务和软件定时器回调函数互斥访问网卡时间。 如上图在设置一次网卡时间后就会增加一下信号量好让软件定时器的回调函数能访问而不会阻塞。 那么是谁会设置网卡时间呢 如上图代码在获取网络时间的任务运行后会先获取一次网络时间然后设置到网卡时间中此时定时器的回调还是才能获取到网卡时间在这之前处于阻塞状态。
除此之外还创建了一个xUpDateNetTimeSema信号量用来控制SNTP服务器上获取网络时间。在创建成功后先增加一下方便第一次获取网络时间Take以后再次到了这里后就会阻塞。
当软件定时器超时60次也就是一分钟时 如上图调用UpdateTimeNotify时就会Give一下这个信号量此时就会唤醒任务从网络上获取一下时间来校正本地时间。 更新网络时间的任务大部分时间处于阻塞状态每一分钟会被软件定时器的回调函数唤醒一次来从SNTP服务器上获取一次时间。 如上图在初始化所有子系统和设备的时候需要将软件定时器也初始化。 如上图在智能家居任务将输入事件转换成Json格式的时候再增加一个定时器输入的转换如红色框中所示。 如上图在根据输入事件控制设备时增加定时器输入部分从网卡中获取时间后将时间显示到OLED屏幕的最下面。 总结
至此本喵就带着大家实现了FreeRTOS版本的智能家居项目它的优点主要在于有非常高的实时性。
温湿度部分使用了中断方式这也是一种保证实时性的手段有兴趣的小伙伴可以自行了解一下中断编程。
多任务系统中每个任务在不需要运行的时候要让其处于阻塞状态让出CPU资源可以通过信号量队列等等方式来实现。