FreeRTOS+STM32F103中断中发送任务通知单片机死机问题

最近在调试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 变量要么定义在中断函数外,如果要定义在中断函数内部时,必须声明为静态变量。

你可能感兴趣的:(FreeRTOS学习笔记)