FreeRTOS学习笔记【1】

本文章为本人学习FreeRTOS时的笔记,学习时使用 STM32 SPL库+Keil开发环境。
之前发过这篇文章但不知为何在CSDN上MD格式无法显示,故重新发一次。(真不是水浏览量)

文章目录

  • 操作系统启动步骤
    • 1.定义任务函数
    • 2.空闲任务与定时器任务堆栈函数实现
    • 3.定义任务栈、控制块、句柄
    • 4.编写AppTaskCreate任务
    • 5.创建任务
      • 静态创建任务
      • 动态创建任务
    • 6.启动任务
  • 任务管理
    • 任务状态切换
    • 删除任务
    • 任务轮转
    • 延时函数
  • 消息队列使用步骤
    • 1.创建消息队列
      • 静态创建队列
      • 动态创建队列
    • 2.读队列
    • 3.写列队
    • 4.消息队列删除函数
    • 5.复位
    • 6.查询
  • 信号量
    • 二值信号量
      • 创建二值信号量
    • 互斥信号量
      • 互斥信号量应用
    • 计数信号量
      • 创建计数信号量
        • 静态创建
        • 动态创建
        • 查询计数信号量的当前计数值
    • 递归信号量
    • 删除信号量
    • 释放信号量
      • 任务中释放信号量
      • 中断中释放信号量
    • 获取信号量
      • 任务中获取信号量
      • 中断中获取信号量
  • 软件定时器
  • 中断管理
    • 基本介绍
    • 配置可管理中断
  • 一些奇怪的发现

操作系统启动步骤

1.定义任务函数

模块化,写在自己的.c文件中

/*** @brief    LED_Task任务主体* @param    parameter* @retval   void*/
static void LED_Task(void* parameter)
{	while (1){LED1_ON;vTaskDelay(500);   /* 延时500个tick */printf("LED_Task Running,LED1_ON\r\n");LED1_OFF;     vTaskDelay(500);   /* 延时500个tick */		 		printf("LED_Task Running,LED1_OFF\r\n");}
}

2.空闲任务与定时器任务堆栈函数实现

当我们使用了静态创建任务的时候,configSUPPORT_STATIC_ALLOCATION 这个宏 定 义 必 须为 1 (在 FreeRTOS.h 文 件 中 ) , 并且 我 们需 要 实 现两 个 函数 : vApplicationGetIdleTaskMemory()与 vApplicationGetTimerTaskMemory(),这两个函数是用 户设定的空闲(Idle)任务与定时器(Timer)任务的堆栈大小,必须由用户自己分配,而 不能是动态分配

写在main.c,函数在main函数前声明


/* 空闲任务任务堆栈 */
static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE];
/* 定时器任务堆栈 */
static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH];/* 空闲任务控制块 */
static StaticTask_t Idle_Task_TCB;	
/* 定时器任务控制块 */
static StaticTask_t Timer_Task_TCB;/*** @brief    获取空闲任务的任务堆栈和任务控制块内存* @param    ppxIdleTaskTCBBuffer		:	任务控制块内存* @param    ppxIdleTaskStackBuffer	:	任务堆栈内存* @param    pulIdleTaskStackSize		:	任务堆栈大小* @retval   void*/
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize)
{*ppxIdleTaskTCBBuffer=&Idle_Task_TCB;/* 任务控制块内存 */*ppxIdleTaskStackBuffer=Idle_Task_Stack;/* 任务堆栈内存 */*pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;/* 任务堆栈大小 */
}/*** @brief    获取定时器任务的任务堆栈和任务控制块内存* @param    ppxTimerTaskTCBBuffer	:		任务控制块内存* @param    ppxTimerTaskStackBuffer	:	任务堆栈内存* @param    pulTimerTaskStackSize	:		任务堆栈大小* @retval   void*/
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize)
{*ppxTimerTaskTCBBuffer=&Timer_Task_TCB;/* 任务控制块内存 */*ppxTimerTaskStackBuffer=Timer_Task_Stack;/* 任务堆栈内存 */*pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;/* 任务堆栈大小 */
}

3.定义任务栈、控制块、句柄

写在main.c

/* AppTaskCreate任务堆栈 */
static StackType_t AppTaskCreate_Stack[128];
/* LED任务堆栈 */
static StackType_t LED_Task_Stack[128];/* AppTaskCreate 任务控制块 */
static StaticTask_t AppTaskCreate_TCB;
/* AppTaskCreate 任务控制块 */
static StaticTask_t LED_Task_TCB;/* 创建任务句柄 */
static TaskHandle_t AppTaskCreate_Handle;
/* LED任务句柄 */
static TaskHandle_t LED_Task_Handle;

4.编写AppTaskCreate任务

这个任务是用于创建用户任务,为了方便管理,我们的所有的任务创建都统一放在这个函数中,在这个函数中创建成功的任务就可以直接参与任务调度了

写在main.c,在main函数前声明


static void AppTaskCreate(void)
{taskENTER_CRITICAL();           //进入临界区/* 创建LED_Task任务 */LED_Task_Handle = xTaskCreateStatic((TaskFunction_t	)LED_Task,		//任务函数(const char* 	)"LED_Task",		//任务名称(uint32_t 		)128,					//任务堆栈大小(void* 		  	)NULL,				//传递给任务函数的参数(UBaseType_t 	)4, 				//任务优先级(StackType_t*   )LED_Task_Stack,	//任务堆栈(StaticTask_t*  )&LED_Task_TCB);	//任务控制块   if(NULL != LED_Task_Handle)/* 创建成功 */printf("LED_Task任务创建成功!\n");elseprintf("LED_Task任务创建失败!\n");vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务taskEXIT_CRITICAL();            //退出临界区
}

5.创建任务

静态创建任务

xTaskCreateStatic

在main函数内执行创建 AppTaskCreate 任务

/* 创建 AppTaskCreate 任务 */
AppTaskCreate_Handle = xTaskCreateStatic((TaskFunction_t	)AppTaskCreate,		//任务函数(const char* 	)"AppTaskCreate",		//任务名称(uint32_t 		)128,	//任务堆栈大小(void* 		  	)NULL,				//传递给任务函数的参数(UBaseType_t 	)3, 	//任务优先级(StackType_t*   )AppTaskCreate_Stack,	//任务堆栈(StaticTask_t*  )&AppTaskCreate_TCB);	//任务控制块						

在AppTaskCreate任务中创建其余任务

/* 创建LED_Task任务 */
LED_Task_Handle = xTaskCreateStatic((TaskFunction_t	)LED_Task,			//任务函数(const char* 	)"LED_Task",		//任务名称(uint32_t 		)128,				//任务堆栈大小(void* 		  	)NULL,				//传递给任务函数的参数(UBaseType_t 	)4, 				//任务优先级(StackType_t*   )LED_Task_Stack,	//任务堆栈(StaticTask_t*  )&LED_Task_TCB);	//任务控制块   
if(LED_Task_Handle != NULL)/* 创建成功 */printf("LED_Task任务创建成功!\n");
elseprintf("LED_Task任务创建失败!\n");

动态创建任务

xTaskCreate

程序跑起来内存会溢出,未查明原因

在AppTaskCreate任务中创建其余任务

/* 创建Receive_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )Receive_Task, /* 任务入口函数 */(const char*    )"Receive_Task",/* 任务名字 */(uint16_t       )512,   /* 任务栈大小 */(void*          )NULL,	/* 任务入口函数参数 */(UBaseType_t    )2,	    /* 任务的优先级 */(TaskHandle_t*  )&Receive_Task_Handle);/* 任务控制块指针 */

6.启动任务

//这里建议先判断AppTaskCreate任务是否创建成功

跟在创建任务语句后

if(AppTaskCreate_Handle != NULL)/* 创建成功 */vTaskStartScheduler();   /* 开启调度器 */

任务管理

任务状态切换

(1):创建任务→就绪态

(2):就绪态→运行态

(3):运行态→就绪态

(4):运行态→阻塞态:(挂起、延时、 读信号量等待)

(5):阻塞态→就绪态:(任务恢复、延时时间超时、读 信号量超时或读到信号量等),

(6) (7) (8):就绪态、阻塞态、运行态→挂起态:调用 vTaskSuspend() 或 vTaskSuspendAll()

(9):挂起态→就绪态:调用 vTaskResume() 或 vTaskResumeFromISR()

删除任务

调用 vTaskDelete() 形参为要删除任 务创建时返回的任务句柄,如果是删除自身, 则形参为 NULL。

任务轮转

任务轮转即将运行态任务转就绪态,并运行最高优先级就绪态任务

以下是几个触发轮转的条件(满足一个即可)

  • 任务由运行态转阻塞态(挂起、延时、 读信号量等待)
  • 运行态→挂起态

延时函数

vTaskDelay 和 vTaskDelayUntil 函数的参数是以系统节拍为单位的延时时间,而不是以毫秒为单位。要将毫秒转换为系统节拍,可以使用portTICK_PERIOD_MS常量,例如vTaskDelay(500 / portTICK_PERIOD_MS)表示延时500毫秒。

相对延时vTaskDelay()

指每次延时都是从任务执行函数vTaskDelay()开始,延时指定的时间结束。

绝对延时vTaskDelayUntil

指每隔指定的时间,执行一次调用vTaskDelayUntil()函数的任务。

消息队列使用步骤

要使用消息队列功能需要引入头文件
#include “queue.h”

1.创建消息队列

静态创建队列

xQueueCreateStatic()

使 用 xQueueCreateStatic()创建队列时,使用的是静态内存分配,所以要想使用该函数必须在 FreeRTOSConfig.h 中把 configSUPPORT_STATIC_ALLOCATION 定义为 1 来使能。

实际项目中常常是在AppTaskCreate执行vATask函数的内容,这里为了便于观察直接用一个函数框出

/* 创建一个可以最多可以存储 10 个 64 位变量的队列 */
#define QUEUE_LENGTH 10
#define ITEM_SIZE sizeof( uint64_t )
/*消息队列句柄*/
QueueHandle_t Test_QueueHandle = NULL;
/* 消息队列数据结构指针 */
static StaticQueue_t xStaticQueue_Structure;
/* 该数组作为队列的存储区域,大小至少有 uxQueueLength * uxItemSize 个字节 */
uint8_t ucQueueStorageArea[ QUEUE_LENGTH * ITEM_SIZE ]; void vATask( void *pvParameters ){/* 创建一个队列 */ Test_QueueHandle = xQueueCreateStatic(QUEUE_LENGTH, /* 队列深度 */ ITEM_SIZE, /* 队列数据单元的单位 */ ucQueueStorageArea,/* 队列的存储区域 */ &xStaticQueue_Structure ); /* 队列的数据结构 */ /* 剩下的其他代码 */while(1){}
}

这里解释下 ucQueueStorageArea 和 xStaticQueue的区别:

ucQueueStorageArea:就是一个数组,存放队列的实际内容

xStaticQueue:是一个结构体变量,内涵了队列的属性、ucQueueStorageArea的首地址,后续操作队列实际上也就是操作xStaticQueue

动态创建队列

程序跑起来内存会溢出,未查明原因

xQueueCreate()

使用 xQueueCreate()创建队列时,使用的是动态内存分配,所以要想使用该函数必须在 FreeRTOSConfig.h 中把 configSUPPORT_DYNAMIC_ALLOCATION 定义为 1 来使能,这 是个用于使能动态内存分配的宏,通常情况下,在 FreeRTOS 中,凡是创建任务,队列, 信号量和互斥量等内核对象都需要使用动态内存分配,所以这个宏默认在 FreeRTOS.h 头文 件中已经使能(即定义为 1)。

在AppTaskCreate中执行

BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
taskENTER_CRITICAL();           //进入临界区/* 创建Test_Queue */
Test_Queue = xQueueCreate((UBaseType_t ) QUEUE_LEN,/* 消息队列的长度 */(UBaseType_t ) QUEUE_SIZE);/* 消息的大小 */
if(NULL != Test_Queue)printf("创建Test_Queue消息队列成功!\r\n");
taskEXIT_CRITICAL();     //退出临界区

2.读队列

使用 xQueueReceive() 或 xQueueReceiveFromISR() 函数读队列,读到一个数据后,队列中该数据会被移除。两个函数分别为:在任务中使用、在ISR中使用。

这里举出xQueueReceive函数的应用实例

/*** @brief    ReadQueue_Task任务主体* @param    parameter* @retval   void*/
static void ReadQueue_Task(void* parameter){BaseType_t xReturn = pdTRUE;// 定义一个创建信息返回值,默认为pdTRUEu8 rQueue;//接收消息的变量TickType_t lastTick = xTaskGetTickCount();//获取当前时间while(1){xReturn = xQueueReceive( 	Test_Queue,    /* 消息队列的句柄 */&rQueue,      /* 收到的消息内容存放处 */0); /* 等待时间 0ms */if(xReturn == pdTRUE)printf("成功读取消息,内容为:%d\r\n",rQueue);elseprintf("读取消息失败,错误码为:0x%lx\r\n",xReturn);vTaskDelay(1000/portTICK_PERIOD_MS);}
}

3.写列队

可以把数据写到队列头部,也可以写到尾部,注意,一次只能写一个数据,如果要写数组的话要循环遍历数组写入

xQueueSend 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait

xQueueSendToBack 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait

xQueueSendToBackFromISR 往队列尾部写入数据,此函数可以在中断函数中使用,不可阻塞

xQueueSendToFront 往队列头部写入数据,如果没有空间,阻塞时间为xTicksToWait

xQueueSendToFrontFromISR 往队列头部写入数据,此函数可以在中断函数中使用,不可阻塞

阻塞时间表示着等待队列空闲的最大超时时间。如果队列满并且xTicksToWait 被设置成0,函数立刻返回

xQueueSend()函数使用实例

/*** @brief    KeyScan_Task任务主体(简易消抖版)* @param    parameter* @retval   void*/static void KeyScan_Task(void* parameter){BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */u8 wQueue;//写入队列的变量while(1){if(Key1 == 0){wQueue = 1;vTaskDelay(20/portTICK_PERIOD_MS);xReturn = xQueueSend(	Test_Queue,&wQueue,0);if(xReturn == pdPASS)printf("消息1发送成功!\r\n");elseprintf("消息1发送失败,错误码为0x%lx!\r\n",xReturn);while(Key1 == 0);vTaskDelay(20/portTICK_PERIOD_MS);}}
}

4.消息队列删除函数

需要注意的是调用删除消息队列函 数前,系统应存在 xQueueCreate()或 xQueueCreateStatic()函数创建的消息队列。此外 vQueueDelete()也可用于删除信号量。如果删除消息队列时,有任务正在等待消息,则不应 该进行删除操作。

消息队列删除函数 vQueueDelete()使用实例

#define QUEUE_LENGTH 5
#define QUEUE_ITEM_SIZE 4int main( void )
{QueueHandle_t xQueue;/* 创建消息队列 */xQueue = xQueueCreate( QUEUE_LENGTH, QUEUE_ITEM_SIZE );if ( xQueue == NULL ) {/* 消息队列创建失败 */} else {/* 删除已创建的消息队列 */ vQueueDelete( xQueue ); }
}

5.复位

xQueueReset()

队列刚被创建时,里面没有数据;使用过程中可以调用xQueueReset()把队列恢复为初始状态

6.查询

可以查询队列中有多少个数据、有多少空余空间。

/*返回队列中可用数据的个数*/
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );/*返回队列中可用空间的个数*/
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );

信号量

二值信号量

二值信号量和互斥信号量(以下使用互斥量表示互斥信号量)非常相似,但是有一些细 微差别:互斥量有优先级继承机制,二值信号量则没有这个机制。

创建二值信号量

xSemaphoreCreateBinary()

使用该函数创建的二值信号量是空的,在使用函数 xSemaphoreTake()获取之前必须先调用函数 **xSemaphoreGive()**释放后才可以获取。

互斥信号量

互斥信号量其实是特殊的二值信号量,由于其特有的优先级继承机制从而使它更适用 于简单互锁,也就是保护临界资源(临界资源是指任何时刻只能被一个任务访问的资源)

互斥信号量需要打开
#define configUSE_MUTEXES 1
才能使用

互斥信号量应用

本实验通过互斥信号量解决串口被相同优先级的任务访问导致的串口乱码

两个任务都使用printf:
二值信号量市藕帕炕袢⊥放~
成功,亮1秒灯

两个任务都使用xPrintf:
二值信号量释放~
信号量获取成功,亮1秒灯

#include  <stdarg.h>//标准C库文件,让函数能够接收可变参数
static SemaphoreHandle_t printfSemphr_Handle = NULL;/* 串口打印互斥信号量句柄*//* 信号量数据结构指针 */
static StaticSemaphore_t printfSemphr_Structure;/*串口打印互斥信号量*/int main(){printfSemphr_Handle = xSemaphoreCreateMutexStatic(&printfSemphr_Structure);//创建互斥信号量if(printfSemphr_Handle != NULL)printf("串口打印互斥信号量创建成功~\r\n");elseprintf("串口打印互斥创建失败~\r\n");while(1);
}void xPrintf(char *format, ...)
{char  buf_str[200 + 1];va_list   v_args;va_start(v_args, format);(void)vsnprintf((char       *)&buf_str[0],(size_t      ) sizeof(buf_str),(char const *) format,v_args);va_end(v_args);/* 互斥信号量 */xSemaphoreTake(printfSemphr_Handle, portMAX_DELAY);printf("%s", buf_str);xSemaphoreGive(printfSemphr_Handle);
}

计数信号量

计数信号量需要打开
#define configUSE_COUNTING_SEMAPHORES 1
才能使用

创建计数信号量

静态创建

xSemaphoreCreateCountingStatic()

参数说明:

uxMaxCount可以达到的最大计数值。 当信号量达到此值时,它不能再被“给定”。
uxInitialCount创建信号量时分配给信号量的计数值。
pxSemaphoreBuffer必须指向 StaticSemaphore_t 类型的变量, 该变量然后用于保存信号量的数据结构体。

代码案例

#define vSensorCountMax 1000static SemaphoreHandle_t vSensorLCountHandle = NULL;//左测速传感器计数信号量句柄
static StaticSemaphore_t vSensorLCount_Structure;/*左测速传感器计数信号量*/static void AppTaskCreate(void){taskENTER_CRITICAL();           //进入临界区vSensorLCountHandle = xSemaphoreCreateCountingStatic(vSensorCountMax,			//最大计数值0,							//初始计数值&vSensorLCount_Structure);	//信号量的数据结构体taskEXIT_CRITICAL();            //退出临界区
}
动态创建

xSemaphoreCreateCounting()

要想使用该函数必须在 FreeRTOSConfig.h 中把宏 configSUPPORT_DYNAMIC_ALLOCATION 定义为 1,即开启动 态内存分配。

查询计数信号量的当前计数值

uxSemaphoreGetCount()

  • 参数:

    xSemaphore 正在查询的信号量的句柄。

  • 返回:

    如果信号量是计数信号量,则返回信号量的当前计数值 。 如果信号量是二进制信号量, 则当信号量可用时,返回 1,当信号量不可用时, 返回 0。

递归信号量

删除信号量

vSemaphoreDelete()用于删除一个信号量,包括二值信号量,计数信号量,互斥量和递 归互斥量。如果有任务阻塞在该信号量上,那么不要删除该信号量。

释放信号量

任务中释放信号量

xSemaphoreGive()

该函数是一个用于释放信号量的宏,真正的实现过程是调用消息队列通用发送函数,释放的信号量对象必须是已经被创建的,可以用于二值信号量、计数信号量、互斥量的释放,但不能释放由函数 xSemaphoreCreateRecursiveMutex()创建的递归互斥量。

static void Send_Task(void* parameter)
{	 BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */while (1){/* K1 被按下 */if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ){xReturn = xSemaphoreGive( BinarySem_Handle );//给出二值信号量 xSemaphoreGive 释放信号量if( xReturn == pdTRUE )printf("BinarySem_Handle二值信号量释放成功!\r\n");elseprintf("BinarySem_Handle二值信号量释放失败!\r\n");} /* K2 被按下 */if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ){xReturn = xSemaphoreGive( BinarySem_Handle );//给出二值信号量if( xReturn == pdTRUE )printf("BinarySem_Handle二值信号量释放成功!\r\n");elseprintf("BinarySem_Handle二值信号量释放失败!\r\n");}vTaskDelay(20);}
}

中断中释放信号量

xSemaphoreGiveFromISR()

该函数用于释放一个信号量,带中断保护。被释放的信号量可以是二进制信号量和计数信号量。和普通版本的释放信号量 API 函数有些许不同,它不能释放互斥量,这是因为互斥量不可以在中断中使用,互斥量的优先级继承机制只能在任务中起作用,而在中断中毫无意义。

void vTestISR( void ){BaseType_t pxHigherPriorityTaskWoken;uint32_t ulReturn;/* 进入临界段,临界段可以嵌套 */ulReturn = taskENTER_CRITICAL_FROM_ISR();/* 判断是否产生中断 */{/* 如果产生中断,清除中断标志位 *///释放二值信号量,发送接收到新数据标志,供前台程序查询 xSemaphoreGiveFromISR(BinarySem_Handle,& pxHigherPriorityTaskWoken); //如果需要的话进行一次任务切换,系统会判断是否需要进行切换 portYIELD_FROM_ISR(pxHigherPriorityTaskWoken); }/* 退出临界段 */taskEXIT_CRITICAL_FROM_ISR( ulReturn );}

获取信号量

任务中获取信号量

xSemaphoreTake()

该函数用于获取信号量,不带中断保护。获取的信号量对象可以是二 值信号量、计数信号量和互斥量,但是递归互斥量并不能使用这个 API 函数获取。

static void Receive_Task(void* parameter)
{	BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */while (1){//获取二值信号量 xSemaphore,没获取到则一直等待xReturn = xSemaphoreTake(BinarySem_Handle,/* 二值信号量句柄   xSemaphoreTake获取一个信号量,可以是二值信号量、计数信号量、互斥量。*/portMAX_DELAY); /* 等待时间 */if(pdTRUE == xReturn)printf("BinarySem_Handle二值信号量获取成功!\n\n");LED1_TOGGLE;}
}

中断中获取信号量

xSemaphoreTakeFromISR()

此函数用于获取信号量,是 一个不带阻塞机制获取信号量的函数,获取对象必须由是已经创建的信号量,信号量类型 可以是二值信号量和计数信号量,它与 xSemaphoreTake()函数不同,它不能用于获取互斥量,因为互斥量不可以在中断中使用,并且互斥量特有的优先级继承机制只能在任务中起 作用,而在中断中毫无意义。

void vTestISR( void ){BaseType_t pxHigherPriorityTaskWoken;uint32_t ulReturn;/* 进入临界段,临界段可以嵌套 */ulReturn = taskENTER_CRITICAL_FROM_ISR();/* 判断是否产生中断 */{/* 如果产生中断,清除中断标志位 *///释放二值信号量,发送接收到新数据标志,供前台程序查询 xSemaphoreGiveFromISR(BinarySem_Handle,& pxHigherPriorityTaskWoken); //如果需要的话进行一次任务切换,系统会判断是否需要进行切换 portYIELD_FROM_ISR(pxHigherPriorityTaskWoken); }/* 退出临界段 */taskEXIT_CRITICAL_FROM_ISR( ulReturn );}

软件定时器

中断管理

基本介绍

ARM Cortex-M 系列内核的中断是由硬件管理的,而 FreeRTOS 是软件,它并不接管由 硬件管理的相关中断,只支持简单的开 关中断等,所以 FreeRTOS 中的中断使用其实跟裸机差不多的,需要我们自己配置中断,并且使能中断,编写中断服务函数,在中断服务函数中使用内核 IPC 通信机制,一般建议 使用信号量、消息或事件标志组等标志事件的发生,将事件发布给处理任务,等退出中断 后再由相关处理任务具体处理中断。

FreeRTOS 的中断管理支持:

● 开/关中断。

●恢复中断。

●中断使能。

●中断屏蔽。

●可选择系统管理的中断优先级。

配置可管理中断

用 户 可 以 自 定 义 配 置 系 统 可 管 理 的 最 高 中 断 优 先 级 的 宏 定 义 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY , 它 是 用 于 配 置 内 核 中 的 basepri 寄存器的,当 basepri 设置为某个值的时候,NVIC 不会响应比该优先级低的中断, 而优先级比之更高的中断则不受影响。就是说当这个宏定义配置为 5 的时候,中断优先级 数值在 0、1、2、3、4 的这些中断是不受 FreeRTOS 屏蔽的,也就是说即使在系统进入临 界段的时候,这些中断也能被触发而不是等到退出临界段的时候才被触发,当然,这些中 断服务函数中也不能调用 FreeRTOS 提供的 API 函数接口,而中断优先级在 5 到 15 的这些 中断是可以被屏蔽的,也能安全调用 FreeRTOS 提供的 API 函数接口。

一些奇怪的发现

  • 挂起一个任务后,再重新解挂,这个任务函数会从头执行
  • stm32f103c6芯片动态创建任务或队列都会导致ZI-data区达到20000多,程序直接炸

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/20923.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

8岁孩子学编程有什么用:开启未来科技之旅的钥匙

8岁孩子学编程有什么用&#xff1a;开启未来科技之旅的钥匙 在科技日新月异的今天&#xff0c;编程已经成为一项不可或缺的技能。对于8岁的孩子来说&#xff0c;学习编程不仅能够培养他们的逻辑思维能力&#xff0c;还能激发他们的创新思维和解决问题的能力。那么&#xff0c;…

六大全开源的工作流引擎Activiti、Flowable、jBPM、Camunda、JFlow、osworkflow对比

六大全开源的工作流引擎Activiti、Flowable、jBPM、Camunda、JFlow、osworkflow在多个方面有着各自的特点和优势&#xff0c;以下是对这些工作流引擎的对比&#xff1a; Activiti&#xff1a; 概述&#xff1a;Activiti是一个开源的工作流引擎&#xff0c;实现了BPMN 2.0规范&…

设计模式基础知识点(七大原则、UML类图)

Java设计模式&#xff08;设计模式七大原则、UML类图&#xff09; 设计模式的目的设计模式七大原则单一职能原则&#xff08;SingleResponsibility&#xff09;接口隔离原则&#xff08;InterfaceSegreation&#xff09;依赖倒转原则&#xff08;DependenceInversion&#xff0…

R语言绘图 --- 柱状图(Biorplot 开发日志 --- 3)

「写在前面」 在科研数据分析中我们会重复地绘制一些图形&#xff0c;如果代码管理不当经常就会忘记之前绘图的代码。于是我计划开发一个 R 包&#xff08;Biorplot&#xff09;&#xff0c;用来管理自己 R 语言绘图的代码。本系列文章用于记录 Biorplot 包开发日志。 相关链接…

目标检测——农业障碍物检测数据集

引言 亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 …

Kubernetes——YAML文件编写

目录 一、创建Kubernetes对象YAML文件必备字段 1.apiVersion 2.kind 3.metadata 4.spec 二、YAML格式基本规范 1.结构表示 2.键值对 3.列表&#xff08;数组&#xff09; 4.字典&#xff08;映射&#xff09; 5.数据类型 6.注释 7.多文档支持 8.复杂结构 9.示例 …

Vuex 的安装与配置

聚沙成塔每天进步一点点 本文内容 ⭐ 专栏简介Vuex 的安装与配置1. 安装 Vuex使用 npm 安装使用 yarn 安装 2. 配置 Vuex创建和配置 store将 store 注入到 Vue 实例中 3. 在组件中使用 Vuex访问 State提交 Mutation分发 Action使用 Getter 原理解析小结 ⭐ 写在最后 ⭐ 专栏简介…

Proxmox 虚拟环境下1Panel Linux 服务器运维管理面板的安装

简介 以前安装服务器管理面板用的都是宝塔&#xff0c;今天发现 1Panel Linux 服务器运维管理面板也很好&#xff0c;面板清晰整洁&#xff0c;使用的技术比较先进&#xff0c;所以我决定亲自安装一下看看效果就竟如何&#xff1f; 1Panel Linux 服务器运维管理面板是一个开源…

vulhub中Jenkins CLI 接口任意文件读取漏洞复现(CVE-2024-23897)

Jenkins是一个开源的自动化服务器。 Jenkins使用[args4j](https://github.com/kohsuke/args4j)来解析命令行输入&#xff0c;并支持通过HTTP、Websocket等协议远程传入命令行参数。args4j中用户可以通过字符来加载任意文件&#xff0c;这导致攻击者可以通过该特性来读取服务器…

目标检测——植物幼种数据集

引言 亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 …

推荐网站(19)anytools图片分辨率处理网站

今天&#xff0c;我要向您推荐一个非常实用的在线图片处理工具网站——AnyTools。这个网站提供了一站式的图片分辨率处理服务&#xff0c;并且具备添加各种过滤器的功能&#xff0c;非常适合需要快速调整图片大小和风格优化的场合。 多分辨率支持&#xff1a;用户可以自定义图片…

人工智能与【肿瘤免疫微环境】结合,探索免疫治疗的新方向|24年6月·顶刊速递·06-02

罗小罗同学说 24-06-02&#xff5c;文献速递 今天分享的文章&#xff0c;主题是——人工智能&肿瘤免疫微环境。解释一下这张图&#xff0c;左列是文献标题&#xff0c;右侧是发表的年月&#xff0c;放心&#xff0c;都是顶刊&#xff0c;不然我也不会选的。 PS&#xff1a…

力扣刷题--2085. 统计出现过一次的公共字符串【简单】

题目描述 给你两个字符串数组 words1 和 words2 &#xff0c;请你返回在两个字符串数组中 都恰好出现一次 的字符串的数目。 示例 1&#xff1a; 输入&#xff1a;words1 [“leetcode”,“is”,“amazing”,“as”,“is”], words2 [“amazing”,“leetcode”,“is”] 输出…

一键实现文件夹批量高效重命名:轻松运用随机一个字母命名,让文件管理焕然一新!

在数字化时代&#xff0c;文件夹管理是我们日常生活和工作中不可或缺的一部分。然而&#xff0c;随着文件数量的不断增加&#xff0c;文件夹命名的繁琐和重复成为了一个让人头疼的问题。你是否曾因为手动一个个重命名文件夹而感到枯燥乏味&#xff1f;你是否曾渴望有一种方法能…

开发一个comfyui的自定义节点-支持输入中文prompt

文章目录 目标功能开发环境实现过程翻译中文CLIP编码拓展仓库地址完整代码目标功能 目前comfyui的prompt提示词输入节点 CLIP Text Encode 只支持输入英文的prompt,而有时候我们需要自己制定一些prompt,所以就得将我们想要的提示词翻译为英文后再复制粘贴到该节点的输入框中…

什么是迭代器?它有什么优点?

一、技术难点 迭代器&#xff08;Iterator&#xff09;是Java中一种设计模式&#xff0c;它使得我们能够顺序访问一个聚合对象中的各个元素&#xff0c;而又不需要暴露该对象的内部表示。迭代器的技术难点主要体现在以下几个方面&#xff1a; 抽象与封装&#xff1a;迭代器需要…

Spring (33)CSRF(跨站请求伪造)保护

跨站请求伪造&#xff08;CSRF&#xff09;是一种常见的网络攻击手段&#xff0c;它允许攻击者在不知情的用户浏览器中发起恶意请求。这种攻击利用了网站对用户浏览器的信任。如果用户在浏览器中已经登录了一个网站&#xff0c;攻击者就可以构造一个请求&#xff0c;这个请求能…

RTA GMH系列 SERIE MOTION电机驱动板手侧 英文版

RTA GMH系列 SERIE MOTION电机驱动板手侧 英文版

华为OD刷题C卷 - 每日刷题 5

1、&#xff08;最接近的三数之和&#xff09;&#xff1a; 这段代码是解决“最接近的三数之和”问题。它提供了一个Java类Solution&#xff0c;其中包含一个方法threeSumClosest&#xff0c;该方法接收一个整数数组nums和一个目标整数target。任务是找到一个由三个数组元素组…

【Python系列】Python的多返回值

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…