最近在调试FreeRTOS系统遇到了一个比较奇怪的问题,在STM32F103最小系统上调试任务通知模拟事件标志组功能时,中断一触发,单片机就会死机。通过查询方式发送任务通知没任何问题,一旦用按键触发外部中断,在中断中发送任务通知时,单片机就死机。在编译时程序无任何报错。
相关代码如下:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "exti.h"
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
TaskHandle_t EventGroupHandler; //事件标志组句柄
#define EVENTBIT_0 (1<<0) //事件位
#define EVENTBIT_1 (1<<1)
#define EVENTBIT_2 (1<<2)
#define EVENTBIT_ALL (EVENTBIT_0|EVENTBIT_1|EVENTBIT_2)
void start_task(void *pvParameters);
void eventSetBit_task(void *pvParameters);
void eventGroup_task(void *pvParameters);
void eventQuery_task(void *pvParameters);
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
delay_init(); //延时函数初始化
uart_init(115200); //初始化串口
LED_Init(); //初始化LED
KEY_Init();
IO_Exti_Init();
//创建开始任务
xTaskCreate(start_task, //任务函数的指针
"start_task", //任务的文本名字,只会在调试中用到
100, //栈深度
NULL, //没有任务参数
1, //任务优先级
NULL //不会用到任务句柄
);
vTaskStartScheduler(); //开启任务调度
}
void start_task(void *pvParameters)
{
taskENTER_CRITICAL();
xTaskCreate(eventSetBit_task, "eventSetBit_task", 100, NULL, 2, NULL); //创建设置事件位任务
xTaskCreate(eventGroup_task, "eventGroup_task", 100, NULL, 3, &EventGroupHandler); //创建事件标志组处理任务
vTaskDelete(NULL);
taskEXIT_CRITICAL();
}
//设置事件位任务
void eventSetBit_task(void *pvParameters)
{
u8 key = 0;
u8 i = 0;
while(1)
{
if(EventGroupHandler != NULL)
{
key = KEY_Scan( 0 );
switch( key )
{
case 1:
xTaskNotify(EventGroupHandler, EVENTBIT_1, eSetBits);
break;
case 2:
xTaskNotify(EventGroupHandler, EVENTBIT_2, eSetBits);
break;
}
}
i++;
if(i > 50)
{
i = 0;
LED1 = !LED1;
}
vTaskDelay(10);
}
}
//事件标志组处理任务
void eventGroup_task(void *pvParameters)
{
u8 num = 0;
BaseType_t err;
uint32_t NotifyValue;
static u8 event0flag, event1flag, event2flag;
while(1)
{
if(EventGroupHandler != NULL)
{
err = xTaskNotifyWait((uint32_t )0x00, //进入函数不清除bit
(uint32_t )0xFFFFFFFF, //退出函数清除所有bit
(uint32_t* )&NotifyValue, //存储任务通知值
(TickType_t )portMAX_DELAY); //阻塞时间
if(err == pdPASS)
{
if((NotifyValue & EVENTBIT_0) != 0)
{
event0flag = 1;
printf("事件0发生\r\n");
}
if((NotifyValue & EVENTBIT_1) != 0)
{
event1flag = 1;
printf("事件1发生\r\n");
}
if((NotifyValue & EVENTBIT_2) != 0)
{
event2flag = 1;
printf("事件2发生\r\n");
}
if(event0flag && event1flag && event2flag)
{
printf("所有事件都发生了\r\n");
event0flag = 0;
event1flag = 0;
event2flag = 0;
}
}
else
{
printf("任务获取失败\r\n");
}
}
else
{
vTaskDelay(10);
}
}
}
中断部分代码如下:
#include "exti.h"
#include "key.h"
#include "led.h"
#include "delay.h"
#include "FreeRTOS.h"
#include "task.h"
#define EVENTBIT_0 (1<<0)
extern TaskHandle_t EventGroupHandler;
void IO_Exti_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
KEY_Init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x05; //抢占优先级6
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI0_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken;
delay_xms(20);
if(KEY == 1)
{
xTaskNotifyFromISR((TaskHandle_t )EventGroupHandler, //任务句柄
(uint32_t )EVENTBIT_0, //要更新的位
(eNotifyAction )eSetBits, //按位更新
(BaseType_t* )xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
通过单步调试后发现代码一执行到
xTaskNotifyFromISR((TaskHandle_t )EventGroupHandler, //任务句柄
(uint32_t )EVENTBIT_0, //要更新的位
(eNotifyAction )eSetBits, //按位更新
(BaseType_t* )xHigherPriorityTaskWoken);
这块时单片机就会死机,也就是在中断中一发送任务通知,程序出现了异常,通过对比原子的代码和PDF上的说明没发现这段代码有什么问题,对比FreeRTOS的配置文件也没有发现有什么问题,网上搜索了一下,也没找到有效的解决办法,最后没办法了只能把中断函数中的代码一行一行屏蔽找原因,后来发现将 BaseType_t xHigherPriorityTaskWoken; 这个变量的定义放到中断外部时,代码就可以正常运行,难道是在中断执行的时候,内部变量xHigherPriorityTaskWoken存储地址被覆盖了?于是将这个变量在中断函数内部声明为静态变量,代码也可以正常运行。
这说明了xHigherPriorityTaskWoken这个变量的确是在中断函数运行过程中存储的数据丢失了,导致任务通知的返回值存储失效,代码出现了异常。
通过观察FreeRTOS中调试的其他项目代码,这个变量一般都是在函数内部定义的,在中断中也是在函数内部定义的,代码都可以正常运行。唯独在使用发送任务通知xTaskNotifyFromISR()这个功能时,出现了这样情况。不知道是代码其他地方有问题,还是这时FreeRTOS本身的BUG。
不过程序死机的问题暂时只能这样解决了。
修改后的代码如下:
BaseType_t xHigherPriorityTaskWoken; //必须定义在中断外面,如果定义在中断代码内,程序会死机
void EXTI0_IRQHandler(void)
{
//static BaseType_t xHigherPriorityTaskWoken; //如果要定义在函数内部时,这个变量必须声明为静态变量,否则程序会死机
delay_xms(20);
if(KEY == 1)
{
xTaskNotifyFromISR((TaskHandle_t )EventGroupHandler, //任务句柄
(uint32_t )EVENTBIT_0, //要更新的位
(eNotifyAction )eSetBits, //按位更新
(BaseType_t* )xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
解决方法暂时为 xHigherPriorityTaskWoken 变量要么定义在中断函数外,如果要定义在中断函数内部时,必须声明为静态变量。