上一篇文章我们讨论了FreeRTOS任务相关的内容, 接下来让我们探索一下TM4C的中断系统及FreeRTOS的中断管理.
中断在TM4C中的实现
在FreeRTOS中, 实时性和中断的处理都十分重要. 为了保证系统的实时性, 如某些外设初始化时不被打断, FreeRTOS提供了一系列函数来满足中断管理的要求.
由于TM4C单片机为Cortex-M内核, 故其中断的实现方式与STM32单片机十分相似, 故具体原理不详细讲解.
根据官方的芯片数据手册, Tiva C/E系列单片机设计只实现了三位优先级, 故根据这种设置, 可能的优先级为高0x00, 0x40, 0x60, 0x80, 0xA0, 0xC0, 0xE0低共八位.
在官方库中, 可通过 IntPriorityGroupingSet(uint32_t ui32Bits) 函数实现优先级分组的更改. 如, ui32Bits值为2时, 系统具有四位抢占优先级, 两位子优先级. 具体如下表所示:
若要在TM4C单片机中实现中断功能, 需有一下几步设置(以PF4按键中断为例):
- #define USER_INT0 0x00
#define USER_INT1 0x20
#define USER_INT2 0x40
#define USER_INT3 0x60
#define USER_INT4 0x80
#define USER_INT5 0xA0
//中断优先级定义 - IntPriorityGroupingSet(3);
//设置中断优先级分组 - IntMasterEnable();
//向处理器请求开启总中断开关 - IntPrioritySet(INT_GPIOF_TM4C123, USER_INT5);
//设置GPIO F口中断优先级为5(无法按位设置中断优先级) - GPIOIntRegister(GPIO_PORTF_BASE, KEYIRQHandler);
//注册GPIO中断函数 - GPIOIntEnable(GPIO_PORTF_BASE, GPIO_PIN_4);
//使能PF4中断 - IntEnable(INT_GPIOF);
//开启GPIO F口中断
注意: 对于设置中断优先级分组 IntPriorityGroupingSet() 函数, 传参值仅1, 2, 3有效, 对于高于3的值, 系统会默认处理为3, 及8位抢占优先级.
FreeRTOS的中断管理
FreeRTOS对于中断没有特别的处理程序, 但提供一些特性和系统API函数方便用户可以简单地实现和维护中断管理.
需要注意的是, 任意一个最低优先级的中断都比最高优先级的任务权限更高, 故在FreeRTOS中, 我们对于不能被打断的代码有临界段保护的功能. 对于临界段保护, FreeRTOS有一下几个相关函数
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 )
#define portENTER_CRITICAL() vPortEnterCritical()
#define portEXIT_CRITICAL() vPortExitCritical()
#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) vPortSetBASEPRI( x )
这几个函数的具体操作就是改变BASEPRI的值, 来实现中断的开关, 从而实现临界段的保护, 通常情况下, 用 portENTER_CRITICAL() 和 portEXIT_CRITICAL() 两个函数框住想要保护的代码即可实现有关功能.
然而, FreeRTOS并不能通过这些函数暂停一切中断. 我们可以设置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY参数来免于某些中断被FreeRTOS打断. 当中断优先级高于此参数时(即数值上低于此值), 该中断便是免于FreeRTOS管理的.
当然, FreeRTOS中中断管理远不止这些, 大家可以看这篇文章FreeRTOS 从入门到精通9–探讨中断管理来深入了解.
实验探索
我们下面通过一个简单的实验来探索FreeRTOS的中断特性.
中断函数
void KEYIRQHandler(void)
{
uint32_t s=GPIOIntStatus(GPIO_PORTF_BASE, true);
if((s&GPIO_PIN_4)==GPIO_PIN_4)
{
if(TactKey2FLAG==true)
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1);
TactKey2FLAG=false;
}
else
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0);
TactKey2FLAG=true;
portDISABLE_INTERRUPTS();
}
}
GPIOIntClear(GPIO_PORTF_BASE, GPIO_INT_PIN_4);
}
恢复任务
void task3_task(void *pvParameters)
{
bool print_flag1 = 0, print_flag2 = 0;
while (1)
{
if (TactKey2 == 0 && print_flag2 == 0)
{
print_flag1 = 0;
printf("resuming interrupt\n\n");
print_flag2 = 1;
portENABLE_INTERRUPTS();
}
}
}
当我们按下按键SW1(PF4)时, 系统将会进入该中断函数. 当我们按第一次时, 会发现板载LED灯亮. 按第二次时, LED灯熄灭, 同时被portDISABLE_INTERRUPTS()函数关闭中断. 在此之后, 再按下按键, 我们会发现LED不会再次翻转, 说明FreeRTOS的中断管理体系已经将该中断屏蔽.
当我们通过恢复任务(按下SW2)调用portENABLE_INTERRUPTS()函数后, 按下SW1, LED灯会再次亮起.
当我们改变configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY值至更高优先级后, 重新编译烧录程序, 我们会发现持续按下SW1小灯会持续翻转状态, 说明中断并未被FreeRTOS屏蔽.