freeRTOS总结(四)中断管理

1、什么是中断

打断CPU正常运行程序,转而处理紧急的事件(中断服务函数)。
中断执行机制3步
1、中断请求
2、响应中断
3、退出中断

2 中断优先级

cortex-M使用8位寄存器配置中断优先级
stm32只用到高4位

freeRTOS总结(四)中断管理_第1张图片
stm32优先级分为抢占优先级和子优先级
抢占:高优先级抢低优先级
子优先级:同时抢占优先级,子优先级数值越小越优先执行。

优先级分组设置
freeRTOS总结(四)中断管理_第2张图片
特点:
1、通过调用函数HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4)即可完成设置
在HAL_Init中设置

2、低于configMAX_SYSCALL_INTERRUPT_PRIORITY优先级的中断里才允许调用FreeRTOS 的API函数
3、中断优先级数值越小越优先,任务优先级数值越大越优先

#如何配置pendsv处理任务切换和systick中断优先级
中断优先级寄存器

3,中断相关寄存器(熟悉)

freeRTOS总结(四)中断管理_第3张图片
表出自:《Cortex M3权威指南(中文)》第286页

三个系统中断优先级配置寄存器,分别为 SHPR1、 SHPR2、 SHPR3
SHPR1寄存器地址:0xE000ED18
SHPR2寄存器地址:0xE000ED1C
SHPR3寄存器地址:0xE000ED20

这里补充个知识
1、一个地址对应一个存储单元,一个存储单元一般对应8位所以四个单元是32位也就是说用8位表示优先级而八位中又只用到高4位。
2、比如SHPR3这个32位寄存器排列应该是0xE000ED23-0xE000ED20
这里来解释为什么要左移4位 和左移16位24 位
FreeRTOS如何配置PendSV和Systick中断优先级?

   /* Make PendSV and SysTick the lowest priority interrupts. */
    portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;
    portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;

#define portNVIC_PENDSV_PRI                   ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) 
#define portNVIC_SYSTICK_PRI                  ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )
//portNVIC_SHPR3_REG 的地址
#define portNVIC_SHPR3_REG                    ( *( ( volatile uint32_t * ) 0xe000ed20 ) )
#ifdef __NVIC_PRIO_BITS
    #define configPRIO_BITS __NVIC_PRIO_BITS
#else
    #define configPRIO_BITS 4
#endif

#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY         15                  /* 中断最低优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5                   /* FreeRTOS可管理的最高中断优先级 */
#define configKERNEL_INTERRUPT_PRIORITY                 ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY            ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_API_CALL_INTERRUPT_PRIORITY           configMAX_SYSCALL_INTERRUPT_PRIORITY

以上这些是设置好的宏在port.c中已经写好了。

3.中断相关寄存器(熟悉)

freeRTOS总结(四)中断管理_第4张图片
FreeRTOS所使用的中断管理就是利用的BASEPRI这个寄存器
BASEPRI:屏蔽优先级低于某一个阈值的中断
比如: BASEPRI设置为0x50,代表中断优先级在5-15内的均被屏蔽,0~4的中断优先级正常执行
BASEPRI:屏蔽优先级低于某一个阈值的中断,当设置为0时,则不关闭任何中断(在上面的宏中我们实现了系统抵达定时器中断和pendsv中断优先级为15,而且设置5-15内的优先级能被屏蔽)

开中断

#define portDISABLE_INTERRUPTS() 		vPortRaiseBASEPRI()
static portFORCE_INLINE void vPortRaiseBASEPRI( void ) 
{ 
	uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; 
	__asm 
	{
		msr basepri, ulNewBASEPRI 
		dsb 
		isb
	} 
}
#define configMAX_SYSCALL_INTERRUPT_PRIORITY            ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY        5      /* FreeRTOS可管理的最高中断优先级 */ 

关中断

#define portENABLE_INTERRUPTS()		 vPortSetBASEPRI( 0 )
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI ) 
{ 
	__asm
	{
		msr basepri, ulBASEPRI
	} 
}

所以我们关中断和开中断只需要调用portDISABLE_INTERRUPTS()
portENABLE_INTERRUPTS()就行。

实验

1、实验目的:学会使用FreeRTOS的中断管理!

本实验会使用两个定时器,一个优先级为4,一个优先级为6,注意:系统所管理的优先级范围:5~15,
现象:两个定时器每1s,打印一段字符串,当关中断时,停止打印,开中断时持续打印。

2、实验设计:将设计2个任务:start_task、task1

start_task 用来创建task1任务
task1 中断测试任务,任务中将调用关中断和开中断函数来体现对中断的管理作用!

这里只放部分代码
任务1

void task1( void * pvParameters )
{
 uint8_t task1_num = 0;
	while(1)
	{
		//前置自增先自增后使用
		if(++task1_num==5)
		{
			task1_num=0;
			portDISABLE_INTERRUPTS();
			delay_ms(5000);//这里必须是delay_ms不能是vTaskDelay 因为vTaskDelay会开中断
			portENABLE_INTERRUPTS();
		
		}
			vTaskDelay(1000);
	}

}


初始化


#include "./BSP/LED/led.h"
#include "./BSP/TIMER/btim.h"
#include "stdio.h"

TIM_HandleTypeDef g_timx_handler;         /* 定时器参数句柄 */
TIM_HandleTypeDef g_tim7_handler;         /* 定时器参数句柄 */
/**
 * @brief       基本定时器TIMX定时中断初始化函数
 * @note
 *              基本定时器的时钟来自APB1,当PPRE1 ≥ 2分频的时候
 *              基本定时器的时钟为APB1时钟的2倍, 而APB1为42M, 所以定时器时钟 = 84Mhz
 *              定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
 *              Ft=定时器工作频率,单位:Mhz
 *
 * @param       arr : 自动重装值。
 * @param       psc : 时钟预分频数
 * @retval      无
 */
void btim_timx_int_init(uint16_t arr, uint16_t psc)
{
    g_timx_handler.Instance = BTIM_TIMX_INT;                      /* 定时器x */
    g_timx_handler.Init.Prescaler = psc;                          /* 分频 */
    g_timx_handler.Init.CounterMode = TIM_COUNTERMODE_UP;         /* 递增计数模式 */
    g_timx_handler.Init.Period = arr;                             /* 自动装载值 */
    HAL_TIM_Base_Init(&g_timx_handler);
    
    HAL_TIM_Base_Start_IT(&g_timx_handler);                       /* 使能定时器x和定时器更新中断 */
}


/**
 * @brief       基本定时器TIM7定时中断初始化函数
 * @note
 *              基本定时器的时钟来自APB1,当PPRE1 ≥ 2分频的时候
 *              基本定时器的时钟为APB1时钟的2倍, 而APB1为42M, 所以定时器时钟 = 84Mhz
 *              定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
 *              Ft=定时器工作频率,单位:Mhz
 *
 * @param       arr : 自动重装值。
 * @param       psc : 时钟预分频数
 * @retval      无
 */
void btim_tim7_int_init(uint16_t arr, uint16_t psc)
{
    g_tim7_handler.Instance = BTIM_TIM7_INT;                      /* 定时器x */
    g_tim7_handler.Init.Prescaler = psc;                          /* 分频 */
    g_tim7_handler.Init.CounterMode = TIM_COUNTERMODE_UP;         /* 递增计数模式 */
    g_tim7_handler.Init.Period = arr;                             /* 自动装载值 */
    HAL_TIM_Base_Init(&g_tim7_handler);
    
    HAL_TIM_Base_Start_IT(&g_tim7_handler);                       /* 使能定时器x和定时器更新中断 */
}






/**
 * @brief       定时器底层驱动,开启时钟,设置中断优先级
                此函数会被HAL_TIM_Base_Init()函数调用
 * @param       无
 * @retval      无
 */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == BTIM_TIMX_INT)
    {
        BTIM_TIMX_INT_CLK_ENABLE();                     /* 使能TIMx时钟 */
        HAL_NVIC_SetPriority(BTIM_TIMX_INT_IRQn, 4, 0); /* 抢占1,子优先级3 */
        HAL_NVIC_EnableIRQ(BTIM_TIMX_INT_IRQn);         /* 开启ITMx中断 */
    }
		
		    if (htim->Instance == BTIM_TIM7_INT)
    {
        BTIM_TIM7_INT_CLK_ENABLE();                     /* 使能TIMx时钟 */
        HAL_NVIC_SetPriority(BTIM_TIM7_INT_IRQn, 6, 0); /* 抢占1,子优先级3 */
        HAL_NVIC_EnableIRQ(BTIM_TIM7_INT_IRQn);         /* 开启ITMx中断 */
    }
}

/**
 * @brief       基本定时器TIMX中断服务函数
 * @param       无
 * @retval      无
 */
void BTIM_TIMX_INT_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_timx_handler);  /* 定时器回调函数 */
}



/**
 * @brief       基本定时器TIMX中断服务函数
 * @param       无
 * @retval      无
 */
void BTIM_TIM7_INT_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_tim7_handler);  /* 定时器回调函数 */
}
/**
 * @brief       回调函数,定时器中断服务函数调用
 * @param       无
 * @retval      无
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{ 
 //无论哪个定时器处理计数溢出中断都会调用这个回调函数
    if (htim->Instance == BTIM_TIMX_INT)
    {
			printf("TIM6优先级为4的正在运行!!!!\r\n");
    }
		if (htim->Instance == BTIM_TIM7_INT)
    {
			printf("TIM7优先级为6的正在运行!!!!\r\n");
    }
}

注意printf耗时长请尽量不要在中断回调函数中调用。

你可能感兴趣的:(freeRTOS,单片机,嵌入式硬件,学习)