目录
FreeRTOS的中断处理方式和RT Thread有点不同,在中断处理函数中Free RTOS都会有对应的API,例如xQueueSend对应xQueueSendFromISR,即API函数名加上后缀“FromISR”就是中断中的API函数。它们的区别是“FromISR” API函数执行完后不会立即进行任务调度,只是标记,等退出中断后再进行任务调度。
1. 中断配置
FreeRTOSConfig.h中的宏定义configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
这个宏定义的含义是指FreeRTOS能管理优先级在 x ~ 15 之间的的优先级,比如设置为x = 5,则表示FREERTOS不能管理优先级在0~4的中断,FreeRTOS打断不了0-4的中断。
a. 在0-4的中断里不能调用freertos的api函数
b. 可以在5-15的中断中调用以FromISR结尾的api函数,并且可以中断嵌套
2. 中断处理原则
对于RTOS来说,中断处理原则上要尽可能短,尽可能不调用OS的API函数。一般设计成发送事件、邮箱之类的通知任务处理即可。
2.1 发送事件
#define rtosEventSendFromISR( \
handle, \
set \
) \
{ \
xEventGroupSetBitsFromISR(handle, set, NULL); \
}
xEventGroupSetBitsFromISR比xEventGroupSetBits多一个参数,第三个参数是一个BOOL型的返回值,为pdTRUE时表示中断结束时需要任务切换,否则表示不需要。这里为了兼容性,不用这个参数,设置为NULL。
2.2 发送邮件
#define rtosMailboxSendFromISR( \
handle, \
value \
) \
{ \
uint32_t *pValue = (void *)&value; \
uint32_t ret = \
xQueueSendFromISR(handle, (void *)&pValue, \
NULL); \
}
xQueueSendFromISR和xQueueSend的区别是第三个参数不同,xQueueSend是设置超时时间,而xQueueSendFromISR的含义与xEventGroupSetBitsFromISR相同。
2.3 发送消息队列
#define rtosMQSendFromISR( \
handle, \
buffer, \
size, \
ret \
) \
{ \
ret = xQueueSendFromISR(handle, \
(void *)&buffer, NULL); \
}
#define rtosMQSendUrgentFromISR( \
handle, \
buffer, \
size, \
ret \
) \
{ \
ret = xQueueSendToFrontFromISR(handle, \
(void *)&buffer, NULL); \
}
对应NULL的参数含义与xEventGroupSetBitsFromISR相同。
4. 进入中断和离开中断
#define rtosIntEnter()
#define rtosIntLeave() portYIELD_FROM_ISR(pdTRUE)
为了保持和RTT的兼容性、中断处理的及时性和程序简单化,在退出中断时默认做任务切换。不过这种方式实际是破坏了FreeRTOS在设计中断时的切换速度。
5. 禁止中断和使能中断
#define rtosIntEnable() portENABLE_INTERRUPTS()
#define rtosIntDisable() portDISABLE_INTERRUPTS()
6. 实例
利用按键的GPIO中断检测,当发生GPIOI中断时,进入中断发送消息。
GPIO中断初始化代码如下:
void gpioIntInit(void)
{
//GPIOA0 is user key input pin.
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC->APB2ENR |= RCC_APB2Periph_SYSCFG;
RCC->AHB1ENR |= RCC_AHB1Periph_GPIOA;
GPIOA->OSPEEDR |= ((uint32_t)0x0000000F << (6 * 2)); //All are 50MHz
GPIOA->MODER |= (uint32_t)GPIO_Mode_IN << (0 * 2);
GPIOA->PUPDR &= ~((uint32_t)0x00000003 << (6 * 2)); //GPIO_PuPd_NOPULL
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
注意,这里中断优先级设置为5。
中断处理函数根据FreeRTOS的特性改一下(RTT的也要加上这些FromISR的宏定义)
void EXTI0_IRQHandler(void)
{
/* enter interrupt */
rtosIntEnter();
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
static char str1[] = "gpio int\n";
EXTI_ClearITPendingBit(EXTI_Line0);
rtosMailboxSendFromISR(mailbox, str1);
}
/* leave interrupt */
rtosIntLeave();
}
验证结果和RTT的一样。
将中断优先级改为0
vNVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
可以看到程序会死在rtosMailboxSendFromISR(mailbox, str1);