出现ShowCritical:1 HdlMacSig:1 SWReq:0

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhejfl/article/details/82752793

目录

 

1、问题描述

2、网络资料

2.1、ESP8266 RTOS 中FreeRTOS的分析  与FreeRTOS的对比

2.1.1 开关中断

2.1.2 ICACHE_FLASH_ATTR宏的使用

2.2 、FREERTOS 临界段和开关中断

2.2.1、临界区的概念

2.2.2、源码的说明

2.2.3、参考网络,说明初始化阶段中断状态的变化

2.2.4 造成题设原因

3、


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源码中

3、解决方法——题设问题

猜你喜欢

转载自blog.csdn.net/zhejfl/article/details/82752793