FreeRTOS 中断配置项
FreeRTOSConfig.h 文件中有 6 个与中断相关的 FreeRTOS 配置项,如表 3.2.7.1 所示。这 6 个 FreeRTOS 配置项在 3.2.7 小节“中断嵌套行为相关定义”中已经做了相应的描述,本小节主 要根据本章 4.1 小节“ARM Cortex-M 中断”,为读者分析,如何配置这 6 个中断相关的 FreeRTOS 配置项。
1. configPRIO_BITS 此宏是用于辅助配置的宏,主要用于辅助配置宏 configKERNEL_INTERRUPT_PRIORITY 和宏configMAX_SYSCALL_INTERRUPT_PRIORITY 的,此宏应定义为MCU的8位优先级配 置寄存器实际使用的位数,因为 STM32 只使用到了中断优先级配置寄存器的高4位,因此,此宏应配置为4。
2. configLIBRARY_LOWEST_INTERRUPT_PRIORITY 此宏是用于辅助配置宏 configKERNEL_INTERRUPT_PRIORITY 的,此宏应设置为 MCU 的最低优先等级,因为 STM32 只使用了中断优先级配置寄存器的高 4 位,因此 MCU 的最低优 先等级就是 2^4-1=15,因此,此宏应配置为 15。
3. configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 此宏是用于辅助配置宏 configMAX_SYSCALL_INTERRUPT_PRIORITY 的,此宏适用于配置 FreeRTOS 可管理的最高优先级的中断,此功能就是操作 BASEPRI 寄存器来实现的。此宏的 值可以根据用户的实际使用场景来决定,本教程的配套例程源码全部将此宏配置为 5,即中断 优先级高于 5 的中断不受 FreeRTOS 影响,如下图所示:
4. configKERNEL_INTERRUPT_PRIORITY 此宏应配置为 MCU 的最低优先级在中断优先级配置寄存器中的值,在 FreeRTOS 的源码 中,使用此宏将 SysTick 和 PenSV 的中断优先级设置为最低优先级。因为 STM32 只使用了中 断优先级配置寄存器的高 4 位,因此,此宏应配置为最低中断优先级在中断优先级配置寄存器 高 4 位的表示,即(configLIBRARY_LOWEST_INTERRUPT_PRIORITY<<(8-configPRIO_BITS))。
5. configMAX_SYSCALL_INTERRUPT_PRIORITY 此宏用于配置 FreeRTOS 可管理的最高优先级的中断,在 FreeRTOS 的源码中,使用此宏来 打开和关闭中断。因为 STM32 只使用了中断优先级配置寄存器的高 4 位,因此,此宏应配置为 (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY<<(8-configPRIO_BITS))。
6. configMAX_API_CALL_INTERRUPT_PRIORITY 此宏为宏 configMAX_SYSCALL_INTERRUPT_PRIORITY 的新名称,只被用在 FreeRTOS 官方一些新的移植当中,此宏于宏 configMAX_SYSCALL_INTERRUPT_PRIORITY 是等价的。
STM32F103C8T6实际源码如下图所示:
FreeRTOS 开关中断
前面说过,FreeRTOS 使用 BASEPRI 寄存器来管理受 FreeRTOS 管理的中断,而不受 FreeRTOS 管理的中断不受 FreeRTOS 开关中断的影响,那么 FreeRTOS 开关中断是如何操作的 呢?首先来看一下 FreeRTOS 开关中断的宏定义,代码如下所示:
FreeRTOS 进出临界区
临界区是指那些必须完整运行的区域,在临界区中的代码必须完整运行,不能被打断。
例如一些使用软件模拟的通信协议,通信协议在通信时,必须严格按照通信协议的时序进行,不能被打断。
FreeRTOS 在进出临界区的时候,通过关闭和打开受 FreeRTOS 管理的中断,以保护临界区中的代码。FreeRTOS 的源码中就包含了许多临界区的代码,这部分代码都是用临界区进 行保护,用户在使用 FreeRTOS 编写应用程序的时候,也要注意一些不能被打断的操作,并为这部分代码加上临界区进行保护。 对于进出临界区, FreeRTOS 的源码中有四个相关的宏定义 , 分别为 taskENTER_CRITICAL() 、 taskENTER_CRITICAL_FROM_ISR() 、 taskEXIT_CRITICAL() 、 taskEXIT_CRITICAL_FROM_ISR(x),这四个宏定义分别用于在中断和非中断中进出临界区,定 义代码如下所示:
FreeRTOS 中断测试实验
TIM_TimeBaseInitTypeDef TIM3_TimeBaseStructure;
TIM_TimeBaseInitTypeDef TIM2_TimeBaseStructure;
NVIC_InitTypeDef TIM3_NVIC_InitStructure;
NVIC_InitTypeDef TIM2_NVIC_InitStructure;
void TIM3_Init(unsigned int arr, unsigned int psc)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 , ENABLE);
TIM_DeInit(TIM3);
TIM3_TimeBaseStructure.TIM_Prescaler = psc;
TIM3_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM3_TimeBaseStructure.TIM_Period = arr;
TIM3_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3, &TIM3_TimeBaseStructure);
TIM_ClearFlag(TIM3, TIM_FLAG_Update);
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
TIM_Cmd(TIM3, ENABLE);
TIM3_NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
TIM3_NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;
TIM3_NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
TIM3_NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&TIM3_NVIC_InitStructure);
}
void TIM2_Init(unsigned int arr, unsigned int psc)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);
TIM_DeInit(TIM2);
TIM2_TimeBaseStructure.TIM_Prescaler = psc;
TIM2_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM2_TimeBaseStructure.TIM_Period = arr;
TIM2_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2, &TIM2_TimeBaseStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
TIM_Cmd(TIM2, ENABLE);
TIM2_NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
TIM2_NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4;
TIM2_NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
TIM2_NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&TIM2_NVIC_InitStructure);
}
TIM2和TIM3中断配置函数参数调用:
计算方式为:72000000 / (7200)* 10000 = 1S。即TIM2和TIM3都会以1S的频率产生中断。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
TIM2_Init(10000 - 1, 7200 - 1);
TIM3_Init(10000 - 1, 7200 - 1);
TIM2和TIM3中断响应和处理函数:
注:一定要注意清空中断标志位,不然中断程序会一直触发!
void TIM2_IRQHandler(void)
{
if ( TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET )
{
TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update);
HAL_TIM_PeriodElapsedCallback(&TIM2_TimeBaseStructure);
}
}
void TIM3_IRQHandler(void)
{
if ( TIM_GetITStatus(TIM3 , TIM_IT_Update) != RESET )
{
TIM_ClearITPendingBit(TIM3 , TIM_FLAG_Update);
HAL_TIM_PeriodElapsedCallback(&TIM3_TimeBaseStructure);
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_TimeBaseInitTypeDef *htim)
{
if(htim == (&TIM3_TimeBaseStructure))
{
printf("\r\n");
printf("TIM3输出......\r\n");
}else if(htim == (&TIM2_TimeBaseStructure))
{
printf("\r\n");
printf("TIM2输出......\r\n");
}
}
us和ms级的延时函数:
void delay_us(u32 nus)
{
uint32_t fac_us;
uint32_t ticks;
uint32_t told, tnow, tcnt = 0;
uint32_t reload = SysTick->LOAD;
fac_us = SystemCoreClock / 1000000;
ticks = nus * fac_us;
told = SysTick->VAL;
while (1) {
tnow = SysTick->VAL;
if (tnow != told) {
if (tnow < told)tcnt += told - tnow;
else tcnt += reload - tnow + told;
told = tnow;
if (tcnt >= ticks)break;
}
};
}
void delay_xms(u32 nms)
{
u32 i;
for(i=0;i<nms;i++) delay_us(1000);
}
TIM2和TIM3中断实验调试任务函数:
unsigned char led_change_flag = 0;
void interrupt_task(void *pvParameters)
{
static u32 total_num = 0;
while(1)
{
total_num += 1;
if(total_num == 5)
{
printf("关闭中断......\r\n");
portDISABLE_INTERRUPTS();
delay_xms(5000);
printf("打开中断......\r\n");
portENABLE_INTERRUPTS();
}
led_change_flag = ~led_change_flag;
if(led_change_flag)
{
LED_GREEN_ON;
LED_RED_ON;
}else
{
LED_GREEN_OFF;
LED_RED_OFF;
}
vTaskDelay(1000);
}
}
实验验证效果:
结论:验证成功!