|
1.首先说明:本工程是使用FreeRTOS V8.2.3。
2.问题起源:在bsp_Init();硬件初始化里面初始化外设,并且使用printf函数打印,导致死机。
3.问题描述:
在启动任务之前,我工程使用printf函数(串口FIFO使用串口发送中断了)
测试发现在操作系统启动之前,如果调用开关中断,会导致中断关闭,之后无法打开。无法进入串口发送中断;
4.因为本工程是使用FreeRTOS操作系统,使用
#define DISABLE_INT() taskENTER_CRITICAL()
#define ENABLE_INT() taskEXIT_CRITICAL()
完成开关中断操作的。这一点要跟裸机下使用 __set_PRIMASK(0) __set_PRIMASK(1)是不一样的;
5.对于上述问题归根到底是因为:
static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;【port.c 157行】
中断嵌套默认是0xaaaaaaaa,而开中断函数:
void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );
uxCriticalNesting--;
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS();
}
}
这里判断 uxCriticalNesting 等于0 的时候 才会执行portENABLE_INTERRUPTS();进行开中断。
所以简而言之就是系统启动之前,只能关闭中断,而无法打开中断;
我尝试把static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;更改为
static UBaseType_t uxCriticalNesting = 0x00000000;,这样哪怕在操作系统启动之前,开关中断都没有什么问题;
6.为什么说操作系统启动之后,开关中断就没问题了呢,需要进一步看:
void vTaskStartScheduler( void )【task.c 1543行】是启动操作系统的,这里面调用
BaseType_t xPortStartScheduler( void )【port.c 332行】,这个函数有初始化uxCriticalNesting = 0;
所以说,必须启动操作系统,操作开关中断才会都有效,否则只能关闭中断,无法打开中断。
/***********************************************************/
我还遇见另外一个问题,一起记录下:
还有一个问题:测试发现在操作系统启动之前,如果前面有大量printf打印函数,会死机,比如:
for(u8 i = 0;i<100;i++) 循环100次
printf("11112554645235695659+\r\n");
一点打印函数就不会死机;比如:
for(u8 i = 0;i<10;i++) 循环10次
printf("11112554645235695659+\r\n");
分析:
1.经过仿真发现是大量打印函数会导致串口FIFO缓冲区满,而串口里面有检测:如果缓冲区满,会等待缓冲区不满;而中断由被屏蔽了,导致死等,那么就死机;
仔细想一下,串口FIFO不是边填充,边串口发送中断里面发送吗?归根到底还是串口中断被屏蔽了导致的。
2.那么小量打印,缓冲区不会满,但是串口在操作系统启动之前不会打印东西的,也是因为串口中断被屏蔽了。这种情况下,当系统启动之后,会立即进行发送。因为启动操作系统后,在函数prvStartFirstTask();【port.c 410行】里面会打开所有中断。
/***********************************************************/
注意:
FreeRTOS操作系统使用 basepri 进行开关中断,而一般裸机使用 primask 开关中断。区别如下:
名字 功能描述
primask 这是个只有 1 个 bit 的寄存器。 在它被置 1 后,就关掉所有可屏蔽的异常,只剩下
NMI 和硬 fault 可以响应。它的缺省值是 0,表示没有关中断。
faultmask 这是个只有 1 个 bit 的寄存器。当它置 1 时,只有 NMI 才能响应,所有其它的异
常,甚至是硬 fault,也通通闭嘴。它的缺省值也是 0,表示没有关异常。
basepri 这个寄存器最多有 9 位(由表达优先级的位数决定)。它定义了被屏蔽优先级的阈值。
当它被设成某个值后,所有优先级号大于等于此值的中断都被关(优先级号越大,优
先级越低)。但若被设成 0,则不关闭任何中断, 0 也是缺省值。
使用basepri屏蔽中断之后,就算使用primask开启所有中断,也是无效的。
可以这样理解 primask 和 basepri的关系:学过51的人都知道,51有个总中断开关EA,这个EA就相当于primask,是总开关;
而 basepri 可以理解为是串口,定时器1中断开关;如ES、ET1这些分开关。
所以:
就算总开关开了, 但是外设中断被屏蔽了,还是进不去中断;
就算外设开关开了,但是总中断关了, 还是进不去中断;
/***********************************************************/
进一步深入:
prvStartFirstTask();【port.c 410行】中打开了总开关primask,并且开异常faultmask。没有发现操作basepri的。
进一步看里面有一句:svc 0 这会导致进入SVC中断,
__asm void vPortSVCHandler( void )函数是SVC中断函数,这里面有:
mov r0, #0
msr basepri, r0
所以总得来说,函数prvStartFirstTask();里面对中断而言,对primask faultmask basepri都有操作,操作结果是:
primask 为 0,开中断;
faultmask 为 0,开异常;
basepri 为 0,不关闭任何中断;
/**************************************************************/
附录:
在汇编代码中,CPSID CPSIE 用于快速的开关中断。
CPSID I; PRIMASK=1, ;关中断
CPSIE I; PRIMASK=0, ;开中断
CPSID F; FAULTMASK=1,;关异常
CPSIE F; FAULTMASK=0,;开异常
好了,大概就这么多,不进去不知道,一进去就出不来了。
分享一下,防止大家遇到跟我一样的问题。
|
评分
-
查看全部评分
|