一、参数配置
1、配置RCC、USART1、时钟84M
2、配置SYS,将Timebase Source修改为除滴答定时器外的其他定时器。
3、初始化LED的两个引脚、两个按键引脚
4、开启FreeRTOS,v1与v2版本不同,一般选用v1即可
5、创建一个队列
6、创建两个线程,一个接收消息,一个发送消息
7、创建一个二值信号量
8、配置两个按键,外部中断模式
9、生成代码
二、FreeRTOS中断介绍
1、FreeRTOS的中断支持:开/关中断、恢复中断、中断使能、中断屏蔽、可选择系统管理的中断优先级。
2、FreeRTOS的中断管理
FreeRTOS 中的中断使用跟裸机差不多,需要自己配置中断,并且使能中断,编写中断服务函数,在中断服务函数中使用内核 IPC 通信机制,一般建议使用信号量、消息或事件标志组等标志事件的发生,将事件发布给处理任务,退出中断后再由相关处理任务具体处理中断。
用户可以自定义配置 系统可管理的最高中断优 先 级 的宏定义 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ,它是用于配置内核中的 basepri 寄存器的,当 basepri 设置为某个值的时候,NVIC 不会响应比该优先级低的中断,而优先级比之更高的中断则不受影响。就是说当这个宏定义配置为 5 的时候,中断优先级数值在 0、1、2、3、4 的这些中断是不受 FreeRTOS 屏蔽的,也就是说即使在系统进入临界段的时候,这些中断也能被触发而不是等到退出临界段的时候才被触发,当然,这些中断服务函数中也不能调用 FreeRTOS 提供的 API 函数接口,而中断优先级在 5 到 15 的这些中断是可以被屏蔽的,也能安全调用 FreeRTOS 提供的 API 函数接口。
3、FreeRTOS开关中断
FreeRTOS开关中断函数为portENABLE_INTERRUPTS()和portDISABLE_INTERRUPTS(),这两个函数其实是宏定义,在portmacro.h中有定义,如下:
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
#define portENABLE_INTERRUPTS() vPortSetBASEPRI(0)
函数vPortBASEPRI()传递了一个0,这就对应了上面说到的,开启中断是将0写入BASEPRI寄存器。
4、临界区
CMSIS-RTOS没有临界区,但FreeRTOS与临界段代码保护有关的函数有4个:taskENTER_CRITICAL()、
和taskEXIT_CRITICAL()、taskENTER_CRITICAL_FROM_ISR()、taskEXIT_CRITICAL_FROM_ISR(),这四个函数其实是宏定义,在task.h文件中有定义。这四个函数的区别是前两个是任务级的临界段代码保护,后两个是中断级的临界段代码保护。所以必要时要是加上。
5、任务级临界段代码保护
taskENTER_CRITICAL()和taskEXIT_CRITICAL()是任务级的临界代码保护,一个是进入临界段,
一个是退出临界段,这两个函数是成对使用的,这函数的定义如下:
#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()
而portENTER_CRITICAL()和portEXIT_CRITICAL()也是宏定义,在文件 portmacro.h中有定义。
任务级临界代码保护使用方法如下:
void CriticalTask_TEST(void const * argv)
{
taskENTER_CRITICAL(); //进入临界区
total_num += 0.01f;
printf("total_num 的值为: %.4f\r\n",total_num);
taskEXIT_CRITICAL(); //退出临界区
vTaskDelay(1000);
}
当进入临界区时,中断被屏蔽,临界区代码无法被打断,只有当所有的临界段代码都退出以后才
使能中断!
注:当进入临界区时,优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断得不到及响应,所以临界区代码一定要精简。
6、中断级临界段代码保护
函数taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR()中断级别临界段代
码保护,是用在中断服务程序中的,而且这个中断的优先级一定要低于
configMAX_SYSCALL_INTERRUPT_PRIORITY。这两个函数在文件task.h中有如下定义:
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL_FROM_ISR(x) portCLEAR_INTERRUPT_MASK_FROM_ISR(x)
中断级临界代码保护使用方法如下:
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
{
status_value=taskENTER_CRITICAL_FROM_ISR(); //进入临界区
total_num += 1;
printf("float_num 的值为: %d\r\n",total_num);
taskEXIT_CRITICAL_FROM_ISR(status_value); //退出临界区
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位
}
三、代码示例
1、it.h
#include "cmsis_os.h"
#include <string.h>
extern osMessageQId TestQueueHandle;
static uint32_t send_data1=1;
static uint32_t send_data2=1;
/* USER CODE BEGIN 1 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
osEvent xReturn;
uint32_t ulReturn;
//进入临界段,且临界段可以嵌套
ulReturn=taskENTER_CRITICAL_FROM_ISR();
if(GPIO_Pin==GPIO_PIN_8)
{
xReturn.status=osMessagePut(TestQueueHandle,send_data1,0);
}
else if(GPIO_Pin==GPIO_PIN_9)
{
xReturn.status=osMessagePut(TestQueueHandle,send_data2,0);
}
//退出临界段
taskEXIT_CRITICAL_FROM_ISR(ulReturn);
}
2、main
uint32_t key_value;
void Receive_thread_entry(void const * argument)
{
/* USER CODE BEGIN Receive_thread_entry */
/* Infinite loop */
osEvent event;
for(;;)
{
event=osMessageGet(TestQueueHandle,osWaitForever);
key_value=event.value.v;
}
/* USER CODE END Receive_thread_entry */
}
过程即为队列在中断中使用,进行传递按键数值。且需要注意,中断优先级不能小于5