目录
2.1、ESP8266 RTOS 中FreeRTOS的分析 与FreeRTOS的对比
1、问题描述
出现诸如标题所示的ShowCritical:1 HdlMacSig:1 SWReq:0的问题导致ESP8266 复位,查阅网上相关资料和FreeRTOS源代码,有如下总结,现记录如下。
出现ShowCritical:1 HdlMacSig:1 SWReq:0这类的log,是由port.c中的void ICACHE_FLASH_ATTR ShowCritical(void)所示。port.c是对ARM CM3 port的portable.h中函数的实现。
复位的原因的软看门狗复位。
2、网络资料
2.1、ESP8266 RTOS 中FreeRTOS的分析 与FreeRTOS的对比
参考网络资料,与FreeRTOS官方同版本源码逐一比较,ESP8266对FreeRTOS源码修改基于以下几点
2.1.1 开关中断
将源文件中的开中断portDISABLE_INTERRUPTS()与关中断portENABLE_INTERRUPTS()替换为PortEnableInt_NoNest()与PortDisableInt_NoNest();这2个函数在port.c内实现。
注释:这2个函数对NMI中断标志(NMIIrqIsOn)进行了判断,只有在未进入NMI中断情况下才能开关中断。然后调用portDISABLE_INTERRUPTS()和portENABLE_INTERRUPTS(),同时对开关中断次数使用CloseLv1Isr标志进行了保护(开了才可关,关了才可开)。
对于ESP8266而言(Non Maskable Interrupt,不可屏蔽中断)才能开关中断,NMI中断是ESP8266最高优先级中断。
2.1.2 ICACHE_FLASH_ATTR宏的使用
函数前添加ICACHE_FLASH_ATTR定义;该宏通过makefile中的ICACHE_FLASH宏开启。
#define ICACHE_FLASH_ATTR __attribute__((section(".irom0.text)))
在NON-OS版本SDK中,添加了“ICACHE_FLASH_ATTR”宏的函数,存放在IROM中,CPU仅在调用它的时候,将其读取cache中运行。没有添加“ICACHE_FLASH_ATTR”宏的函数,将在一上电就加载到IRAM中运行。由于ESP8266中RAM空间有限,所有无法将所有代码一次性加载到IRAM中运行。故在大部分函数前添加“ICACHE_FLASH_ATTR”,将其放至于IROM中。
而在ESP8266_RTOS_SDK,函数默认存放在IROM中,中断处理函数也可以定义在IROM中,因此无需特意添加“ICACHE_FLASH_ATTR”宏。对于一些频繁调用的函数指定到IRAM中,应在函数前添加"IRAM_ATTR"宏。
3、加入
2.2 、FREERTOS 临界段和开关中断
以本篇标题所示,本文主要解决ShowCritical:1 HdlMacSig:1 SWReq:0出现的问题
2.2.1、临界区的概念
代码的临界段也成为临界区,一旦这部分代码开始执行,则不允许任何中断打断。为确保临界段代码的执行不被中断,在进入临界段之前必须关中断,而在临界段代码执行完毕后,要立即开中断。
临界段在FreeRTOS中的使用:
除了FreeRTOS操作系统源码所带的临界段以外,用户写应用的时候也有临界段的问题。
1、读取或修改变量(特别适用于任务间通信的全局变量)的diamante,一般来说这是最常见的临界代码。
2、调用公共函数的代码,特别是不可重入的函数,如果多个任务都访问这个函数,结果是可想而知的。
总之,对于临界段要做到执行时间越短越好,否则会影响系统的实时性。
2.2.2、源码的说明
对于源码中的
portDISABLE_INTERRUPTS() 宏 关闭中断,将先前状态保存到cpu_sr
portENABLE_INTERRUPTS()宏 将中断恢复到先前保存在cpu_sr的级别
事实上,FreeRTOS通过调用vPortEnterCritical()进入临界区,调用vPortExiteCritical()退出临界区。这两个函数在不同芯片核实现方式不同,在ESP8266中是在port.c中实现。
从uxCriticalNesting 这一静态全局变量管理临界区的嵌套计数。在NMI中断未使能的情况下,默认uxCriticalNesting变量为0,调用vPortEnterCritical()进入临界区后+1, 调用一次vPORTExitCritical() 减1。减到0后,退出临界区,调用portENABLE_INTERRUPTS()使能中断,恢复任务调度。支持中断嵌套。它的存在避免了在开关中断之间的代码里再次执行开关中断造成的 意料意外的中断开启。
2.2.3、参考网络,说明初始化阶段中断状态的变化
参考网上对中断状态的说明
(在FreeRTOS的Cortex-M4F移植版(portable/GCC/ARM_CM4F))的参考下,在初始化阶段CPU中断状态的变化。
复位后中断默认处于开启状态,当建立第一个任务时中断被关闭,开启调度器时重新开启中断。----总体上适用其他Cortex-M系列。
参考见https://blog.csdn.net/zoomdy/article/details/54773866 半斗米的博客
从SDK源码所知
进出临界段的地方
1、创建任务时,为了确保更新任务列表时不被中断打断,使用临界段保护
#define xTaskCreate( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask ) xTaskGenericCreate( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask ), ( NULL ), ( NULL ) )
2、同理,删除任务时,也会使用临界段保护
删除任务是将任务从就绪列表移除,移到终止列表。这就确保这个任务不会被调度。空闲任务运行时会检查终止列表,并释放被TCB和堆栈分配的内存。
3、其他任务调度均有临界段保护
4、
2.2.4 造成题设原因
造成题设问题,可能的原因有: PWM、falsh。SDK源码中