StmF103C8T6标准库使用FreeRtos----中断管理

一:中断管理理论部分

 

 

 

 

注意:freertos中PendSV作用和优先级设置

在 FreeRTOS 中,PendSV 是一个用于任务切换的中断,它的作用是在任务切换时切换到最高优先级的任务。当一个任务处于等待状态时,调度器会将 CPU 的控制权交给 PendSV 中断服务程序,PendSV 中断服务程序会在任务切换之前执行一些必要的清理和准备工作,然后将控制权交给调度器,由调度器选择下一个最高优先级的任务执行。

在 FreeRTOS 中,PendSV 的优先级是最低的,因为它需要在任务切换时被触发,而不应该被其他中断所打断。因此,PendSV 中断的优先级应该设置为最低,通常为数值为 15。这样,当其他中断发生时,PendSV 中断不会被打断,确保了任务切换的可靠性和正确性。同时,PendSV 中断的优先级也不能太低,否则可能会影响任务切换的响应速度。

注意:freertos中Systick作用和优先级设置

在 FreeRTOS 中,Systick 是一个用于实现系统节拍的定时器中断。它的作用是定期触发中断,在中断处理程序中更新系统时间,并检查是否需要任务切换。

Systick 中断的优先级应该设置为与 PendSV 中断同级或者更低,这是因为 Systick 中断的优先级比 PendSV 中断的优先级高时,会在 PendSV 中断执行期间被打断,这可能会导致任务切换出现问题。另外,由于 Systick 中断是定期触发的,因此其优先级过高也会影响任务切换的响应速度。通常,Systick 中断的优先级设置为 15 或 16。

需要注意的是,在 FreeRTOS 中,Systick 中断的时间间隔应该与系统节拍的时间间隔相同。这样可以确保系统时间的准确性,并且任务切换也会在正确的时间点上进行。在 FreeRTOS 中,通常将系统节拍的时间间隔设置为 1ms,因此 Systick 中断的时间间隔也应该设置为 1ms。

注意:0x50代表0x5左移了4位,因为stm32优先级低四位不生效 

 

 

  

注意:优先级0到4不会被freertos禁止,因为通过configfreertos.h文件我们可以观察到freertos优先级处于5到16。

 

二:实际操作

 

 1、我们将创建动态任务项目中的多余任务删除,只保留开始任务和其中一个任务,首先创建一个Timer.c文件,激活两个定时器,使其能够每隔1s能够在串口打印字符。(我这里将Timer.c文件的定时器3优先级从6改为了15,通过configfreertos.h来看configLIBRARY_KERNEL_INTERRUPT_PRIORITY的值最低为15,和老版本不同,新版最低为15,如果我们仍然照着正点原子做,那么我们的关中断函数将会失效)

 

Timer.c

#include "stm32f10x.h"                  // Device header
#include "Serial.h"



void Timer_Init(void)
{
	//RCC打开时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	//选择时基单元的时钟,内部时钟一般默认初始化可以写可以不写
	TIM_InternalClockConfig(TIM2);
	TIM_InternalClockConfig(TIM3);
	//配置时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	//TIM_CKD_DIV1代表1分屏
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	//代表向上计数
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
	//72MHZ分频7200,就是10k,10k计10000个数就是1s
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
	//高级定时器才有,现在是通用定时器给0
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	
	
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
	
	//如果不加入这一句,会导致复位之后从1开始计数
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	TIM_ClearFlag(TIM3, TIM_FLAG_Update);
	//TIM_IT_Update代表更新中断,中断控制,用来控制某个中断能不能通往NIVC
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(&NVIC_InitStructure);
	
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 15;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(&NVIC_InitStructure);
	//启动定时器
	TIM_Cmd(TIM2, ENABLE);
	TIM_Cmd(TIM3, ENABLE);
}


void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		Serial_Printf("优先级4\r\n");
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

void TIM3_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
	{
		Serial_Printf("优先级15\r\n");
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
	}
}

 Timer.h

#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);

#endif

 测试结果:

 

2:通过下面这个任务函数实现关中断和开中断,关中断之后,优先级为15的定时器不打印,开中断之后继续打印

注意:如果没有使用宏定义将开中断关中断定义为porDISABLE_INTERRUPTS(); 

porENABLE_INTERRUPTS();是不能直接使用的,我们需要使用原始的vPortRaiseBASEPRI();

vPortSetBASEPRI(0);进行关中断和开中断。

 

void led2_task(char * pvParameters)
{
	uint16_t num=0;
	for(;;)
	{
		
		if(num++==5)
		{
            num=0;
			Serial_Printf("关中断\r\n");
			vPortRaiseBASEPRI();
			//porDISABLE_INTERRUPTS(); 
			Delay_ms(5000);
			Serial_Printf("开中断\r\n");
			vPortSetBASEPRI(0);
			//porENABLE_INTERRUPTS();
		}
		
		vTaskDelay(100);
	} 
}

 完整的main.c代码如下:

#include "stm32f10x.h"     // Device header
#include "Delay.h"
#include "LED.h"


#include "OLED.h"
#include "Timer.h"
#include "Serial.h"
#include "FreeRTOS.h"
#include "task.h"





#define START_STK_DEPTH						64
#define START_TASK_PRIO						1
TaskHandle_t start_task_handler;
void start_task(void * pvParameters);

#define LED2_STK_DEPTH						64
#define LED2_TASK_PRIO						2
TaskHandle_t led2_task_handler;
void led2_task(char * pvParameters);


int main(void)
{
	
	LED_Init();
	Serial_Init();
	Timer_Init();
	xTaskCreate(            (TaskFunction_t) start_task,		//创建开始任务
							(const char *  ) "start_task",
							(uint16_t      ) START_STK_DEPTH,
							(void *        ) NULL,
							(UBaseType_t   ) START_TASK_PRIO,
							(TaskHandle_t *) &start_task_handler);
	vTaskStartScheduler();  //开启任务调度器			
}

/*******开始任务函数*****/
void start_task(void * pvParameters)
{
	taskENTER_CRITICAL();
	
	xTaskCreate((TaskFunction_t) led2_task,
							(const char *  ) "led2_task",
							(uint16_t      ) LED2_STK_DEPTH,
							(void *        ) NULL,
							(UBaseType_t   ) LED2_TASK_PRIO,
							(TaskHandle_t *) &led2_task_handler); 			
							
	
							
	vTaskDelete(start_task_handler);
							
  taskEXIT_CRITICAL();					
}

void led2_task(char * pvParameters)
{
	uint16_t num=0;
	for(;;)
	{
		
		if(num++==5)
		{
            num=0;
			Serial_Printf("关中断\r\n");
			vPortRaiseBASEPRI();
			//porDISABLE_INTERRUPTS(); 
			Delay_ms(5000);
			Serial_Printf("开中断\r\n");
			vPortSetBASEPRI(0);
			//porENABLE_INTERRUPTS();
		}
		
		vTaskDelay(100);
	} 
}



实验结果:

 

 

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值