个人名片:
🎓作者简介:嵌入式领域优质创作者
🌐个人主页:妄北y📞个人QQ:2061314755
💌个人邮箱:[mailto:2061314755@qq.com]
📱个人微信:Vir2025WBY🖥️个人公众号:科技妄北
🖋️本文为妄北y原创佳作,独家首发于CSDN🎊🎊🎊
💡座右铭:改造世界固然伟大,但改造自我更为可贵。
专栏导航:
妄北y系列专栏导航:
物联网嵌入式开发项目:大学期间的毕业设计,课程设计,大创项目,各种竞赛项目,全面覆盖了需求分析、方案设计、实施与调试、成果展示以及总结反思等关键环节。📚💼💡
QT基础入门学习:对QT的基础图形化页面设计进行了一个简单的学习与认识,利用QT的基础知识进行了翻金币小游戏的制作。🛠️🔧💭
Linux基础编程:初步认识什么是Linux,为什么学Linux,安装环境,进行基础命令的学习,入门级的shell编程。🍻🎉🖥️
深耕Linux应用开发:分享Linux的基本概念、命令行操作、文件系统、用户和权限管理等,网络编程相关知识,TCP/IP 协议、套接字(Socket)编程等,可以实现网络通信功能。常见开源库的二次开发,如libcurl、OpenSSL、json-c、freetype等💐📝💡
Linux驱动开发:Linux驱动开发是Linux系统不可或缺的组成部分,它专注于编写特殊的程序——驱动程序。这些程序承载着硬件设备的详细信息,并扮演着操作系统与硬件间沟通的桥梁角色。驱动开发的核心使命在于确保硬件设备在Linux系统上顺畅运作,同时实现与操作系统的无缝集成,为用户带来流畅稳定的体验。🚀🔧💻
Linux项目开发:Linux基础知识的实践,做项目是最锻炼能力的一个学习方法,这里我们会学习到一些简单基础的项目开发与应用,而且都是毕业设计级别的哦。🤸🌱🚀
非常期待与您一同在这个广阔的互联网天地里,携手探索知识的海洋,互相学习,共同进步。🌐💫🌱 熠熠星光,照亮我们的成长之路
✨✨ 欢迎订阅本专栏,对专栏内容任何问题都可以随时联系博主,共同书写属于我们的精彩篇章!✨✨
文章介绍:
📚本篇文章将深入剖析FreeRTOS学习的精髓与奥秘,与您一同分享相关知识!🎉🎉🎉
若您觉得文章尚可入目,期待您能慷慨地送上点赞、收藏与分享的三连支持!您的每一份鼓励,都是我创作路上源源不断的动力。让我们携手并进,共同奔跑,期待在顶峰相见的那一天,共庆辉煌!🚀🚀🚀
🙏衷心感谢大家的点赞👍、收藏⭐和评论✍️,您的支持是我前进的动力!
目录:
目录:
一、任务挂起与恢复
1.1 函数vTaskSuspend()
1.1.1 参数:
1.1.2 返回值:
1.1.3 使用说明:
1.1.4 注意事项:
1.2 函数vTaskResume()
1.2.1 参数:
1.2.2 返回值:
1.2.3 使用说明:
1.2.4 注意事项:
1.3 函数xTaskResumeFromISR()
1.3.1 参数:
1.3.2 返回值:
1.3.3 使用说明:
1.3.4 注意事项:
二、程序设计
2.1 设计目的:
2.1.1 任务设计:
2.1.2 按键设计:
2.2 实验程序分析:
2.2.1 任务设置:
2.2.2 main()函数:
2.2.3 任务函数:
三、程序结果分析:
一、任务挂起与恢复
在某些情况下,我们可能需要暂时暂停一个任务的执行,然后在一段时间后重新启动它。如果我们选择删除任务并重新创建,这样会导致任务中所保存的变量值丢失。为了解决这个问题,FreeRTOS提供了任务挂起和恢复的机制。
当一个任务需要暂停运行时,可以将其挂起;待到需要恢复该任务时,再进行恢复。FreeRTOS中提供了相关的API函数来实现任务的挂起和恢复,具体的函数可以参考表。
1.1 函数vTaskSuspend()
vTaskSuspend
函数是 FreeRTOS 中用于挂起任务的一个重要函数。
void vTaskSuspend(TaskHandle_t xTaskToSuspend);
1.1.1 参数:
xTaskToSuspend: 指向要挂起的任务的任务句柄。每个创建的任务都有一个唯一的任务句柄,可以在创建任务时通过 xTaskCreate
或 xTaskCreateStatic
获得该句柄。
如果使用函数xTaskCreate()创建任务的话那么函数的参数 pxCreatedTask就是此任务的任务句柄,如果使用函数xTaskCreateStatic() 创建任务的话那么函数的返回值就是此任务的任务句柄。
也可以使用 xTaskGetHandle
函数根据任务名称获取任务句柄。
1.1.2 返回值:
此函数没有返回值。
1.1.3 使用说明:
挂起任务: 调用 vTaskSuspend()
函数后,指定的任务将进入挂起状态。处于挂起状态的任务不会被调度运行,直到它被恢复。
恢复任务: 要恢复处于挂起状态的任务,您需要调用 vTaskResume()
或 xTaskResumeFromISR()
。只有通过这些函数,任务才能退出挂起状态。
1.1.4 注意事项:
使用任务句柄时,需要确保句柄有效,如果句柄无效,调用 vTaskSuspend()
可能会导致未定义行为。
如果参数为NULL的话表示挂起任务自己。
1.2 函数vTaskResume()
vTaskResume
函数是 FreeRTOS 中用于将一个挂起的任务恢复到就绪状态的函数。
void vTaskResume(TaskHandle_t xTaskToResume);
1.2.1 参数:
xTaskToResume: 这是一个任务句柄,指向要恢复的任务。该任务必须之前通过 vTaskSuspend
或 vTaskSuspendAll
方法被挂起。
1.2.2 返回值:
此函数没有返回值。
1.2.3 使用说明:
只有通过 vTaskSuspend 函数挂起的任务才能使用 vTaskResume 恢复。
如果尝试恢复一个未挂起的任务,函数不会产生任何效果。
使用此函数时,确保传入的任务句柄有效,以避免不确定的行为。
1.2.4 注意事项:
任务的优先级和调度策略将决定恢复后的调度行为。如果有多个就绪任务,调度器将根据优先级选择下一个要运行的任务。
在多核系统中,注意任务的运行可能会被分配到不同的核心。
1.3 函数xTaskResumeFromISR()
xTaskResumeFromISR
函数是 FreeRTOS 中用于在中断服务例程 (ISR) 中恢复一个挂起任务的函数。
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume);
1.3.1 参数:
xTaskToResume: 这是一个任务句柄,指向要恢复的任务。该任务必须之前通过 vTaskSuspend
或 vTaskSuspendAll
方法被挂起。
1.3.2 返回值:
pdTRUE: 表示恢复的任务的优先级等于或高于当前正在运行的任务(即被中断打断的任务)。这意味着在退出中断服务例程后必须进行一次上下文切换。
pdFALSE: 表示恢复的任务的优先级低于当前正在运行的任务。这意味着在退出中断服务例程后不需要进行上下文切换。
1.3.3 使用说明:
xTaskResumeFromISR 适用于需要在中断上下文中恢复任务的场景,例如在接收到中断信号后需要唤醒某个任务来处理数据。
在 ISR 中调用该函数时,应注意中断的优先级,确保不会导致优先级反转或其他调度问题。
使用此函数时需要根据返回值决定是否需要在 ISR 退出时调用 taskYIELD() 或相应的上下文切换处理,以确保新的优先级任务能够被调度。
1.3.4 注意事项:
由于 ISR 的特性,xTaskResumeFromISR
不会直接进行上下文切换,而是通过返回值指示是否需要在 ISR 结束时进行切换。
确保在 ISR 中的任务恢复操作是快速和高效的,以避免影响系统的实时性能。
二、程序设计
2.1 设计目的:
学习使用FreeRTOS的任务挂起和恢复相关API函数,包括VTaskSuspend()、VTaskResume()
和xTaskResumeFromISR()。
本设计的目标是利用 FreeRTOS 的任务管理功能,创建和管理多个任务,并通过按键控制这些任务的挂起和恢复。
2.1.1 任务设计:
1. Start Task:
- 功能: 负责创建其他任务(key_task、task1_task 和 task2_task)。
- 作用: 作为系统的起始点,确保其他任务的初始化和运行,以便后续的按键操作可以被正确处理。
2. Key Task:
- 功能: 负责检测按键输入。根据不同按键的状态执行相应的操作。
- 作用: 作为系统的主控制逻辑,识别用户的输入并决定如何响应,从而影响其他任务的状态。
3. Task1 Task:
- 功能: 执行应用逻辑1。
- 作用: 具体的业务逻辑或功能,可能涉及数据处理、状态更新等。
4. Task2 Task:
- 功能: 执行应用逻辑2。
- 作用: 另一种业务逻辑或操作,可能与 Task1 的功能有所不同。
2.1.2 按键设计:
1. KEY0:
- 功能: 中断模式,恢复 Task2 的运行。
- 分析: 通过中断服务程序快速响应按键事件,适合需要即时处理的场景。该按键的使用可以使 Task2 在适当时刻恢复运行,处理可能的事件或数据。
2. KEY1:
- 功能: 输入模式,恢复 Task1 的运行。
- 分析: 可以通过主控制逻辑(Key Task)来实现,适用于需要用户主动操作的情况。当用户按下该键时,Task1 将被恢复,以继续其工作。
3. KEY2:
- 功能: 输入模式,挂起 Task2 的运行。
- 分析: 允许用户通过按键控制 Task2 的执行,可能用于暂停某些操作以避免资源冲突或处理优先级问题。
4. KEY UP:
- 功能: 输入模式,挂起 Task1 的运行。
- 分析: 类似于 KEY2,但控制的是 Task1,这表明系统可能需要根据当前状态动态调整任务的执行,以优化资源使用和响应时间。
通过设计四个任务和四个按键,实现了较为灵活的任务管理机制。任务之间的相互控制提供了系统对外部事件的响应能力,特别是在多任务环境中,通过挂起和恢复任务来动态调整系统行为。
2.2 实验程序分析:
2.2.1 任务设置:
设计中任务优先级、堆栈大小和任务句柄等的设置如下:
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);//任务优先级
#define KEY_TASK_PRIO 2
//任务堆栈大小
#define KEY_STK_SIZE 128
//任务句柄
TaskHandle_t KeyTask_Handler;
//任务函数
void key_task(void *pvParameters);//任务优先级
#define TASK1_TASK_PRIO 3
//任务堆栈大小
#define TASK1_STK_SIZE 128
//任务句柄
TaskHandle_t Task1Task_Handler;
//任务函数
void task1_task(void *pvParameters);//任务优先级
#define TASK2_TASK_PRIO 4
//任务堆栈大小
#define TASK2_STK_SIZE 128
//任务句柄
TaskHandle_t Task2Task_Handler;
//任务函数
void task2_task(void *pvParameters);
2.2.2 main()函数:
int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4 delay_init(); //延时函数初始化 uart_init(115200); //初始化串口LED_Init(); //初始化LEDKEY_Init(); //初始化按键EXTIX_Init(); //初始化外部中断LCD_Init(); //初始化LCDPOINT_COLOR = RED;LCD_ShowString(30,10,200,16,16,"ATK STM32F103/407"); LCD_ShowString(30,30,200,16,16,"FreeRTOS Examp 6-3");LCD_ShowString(30,50,200,16,16,"Task Susp and Resum");LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");LCD_ShowString(30,90,200,16,16,"2016/11/25");//创建开始任务xTaskCreate((TaskFunction_t )start_task, //任务函数(const char* )"start_task", //任务名称(uint16_t )START_STK_SIZE, //任务堆栈大小(void* )NULL, //传递给任务函数的参数(UBaseType_t )START_TASK_PRIO, //任务优先级(TaskHandle_t* )&StartTask_Handler); //任务句柄 vTaskStartScheduler(); //开启任务调度
}
在main函数中我们主要完成硬件的初始化,在硬件初始化完成以后创建了任务start task()
并且开启了FreeRTOS的任务调度。
2.2.3 任务函数:
1. 开始任务(start_task
)
//开始任务任务函数
void start_task(void *pvParameters)
{taskENTER_CRITICAL(); //进入临界区//创建KEY任务xTaskCreate((TaskFunction_t )key_task, (const char* )"key_task", (uint16_t )KEY_STK_SIZE, (void* )NULL, (UBaseType_t )KEY_TASK_PRIO, (TaskHandle_t* )&KeyTask_Handler); //创建TASK1任务xTaskCreate((TaskFunction_t )task1_task, (const char* )"task1_task", (uint16_t )TASK1_STK_SIZE, (void* )NULL, (UBaseType_t )TASK1_TASK_PRIO, (TaskHandle_t* )&Task1Task_Handler); //创建TASK2任务xTaskCreate((TaskFunction_t )task2_task, (const char* )"task2_task", (uint16_t )TASK2_STK_SIZE,(void* )NULL,(UBaseType_t )TASK2_TASK_PRIO,(TaskHandle_t* )&Task2Task_Handler); vTaskDelete(StartTask_Handler); //删除开始任务taskEXIT_CRITICAL(); //退出临界区
}
- 功能: 负责初始化和创建其他三个任务。
- 关键操作:
- 临界区:
taskENTER_CRITICAL()
和taskEXIT_CRITICAL()
用于保护任务创建过程,避免中断或任务调度影响创建过程。
- 任务创建:
- 使用
xTaskCreate
函数创建key_task
、task1_task
和task2_task
,分别负责按键处理、执行任务1和任务2的逻辑。 - 每个任务的栈大小和优先级由宏定义(如
KEY_STK_SIZE
、TASK1_TASK_PRIO
等)指定。
- 使用
- 删除开始任务:
vTaskDelete(StartTask_Handler)
删除开始任务自身,释放资源。
- 临界区:
2. 按键任务(key_task
)
//key任务函数
void key_task(void *pvParameters)
{u8 key,statflag=0;while(1){key=KEY_Scan(0);switch(key){case WKUP_PRES:statflag=!statflag;if(statflag==1){vTaskSuspend(Task1Task_Handler);//挂起任务printf("挂起任务1的运行!\r\n");}else if(statflag==0){vTaskResume(Task1Task_Handler); //恢复任务1printf("恢复任务1的运行!\r\n");} break;case KEY1_PRES:vTaskSuspend(Task2Task_Handler);//挂起任务2printf("挂起任务2的运行!\r\n");break;}vTaskDelay(10); //延时10ms }
}
- 功能: 监测按键状态,并控制其他任务的挂起与恢复。
- 工作流程:
- 无限循环:
KEY_Scan(0)
调用,获取当前按键状态。- 使用
switch
语句处理不同的按键输入:- WKUP_PRES:
- 使用
statflag
切换状态:- 如果
statflag
为1,挂起task1_task
,并打印输出。 - 如果
statflag
为0,恢复task1_task
,并打印输出。
- 如果
- 使用
- KEY1_PRES:
- 挂起
task2_task
,并打印输出。
- 挂起
- WKUP_PRES:
vTaskDelay(10)
用于防止忙等待,降低 CPU 占用率。
- 无限循环:
3. 任务1 (task1_task
)
//task1任务函数
void task1_task(void *pvParameters)
{u8 task1_num=0;POINT_COLOR = BLACK;LCD_DrawRectangle(5,110,115,314); //画一个矩形 LCD_DrawLine(5,130,115,130); //画线POINT_COLOR = BLUE;LCD_ShowString(6,111,110,16,16,"Task1 Run:000");while(1){task1_num++; //任务执1行次数加1 注意task1_num1加到255的时候会清零!!LED0=!LED0;printf("任务1已经执行:%d次\r\n",task1_num);LCD_Fill(6,131,114,313,lcd_discolor[task1_num%14]); //填充区域LCD_ShowxNum(86,111,task1_num,3,16,0x80); //显示任务执行次数vTaskDelay(1000); //延时1s,也就是1000个时钟节拍 }
}
- 功能: 执行主逻辑,并定期进行状态更新。任务1的任务函数,用于观察任务挂起和恢复的过程。
- 工作流程:
- 初始化一些图形显示(如矩形和线)和文本。
- 无限循环:
- 每次执行时将
task1_num
递增,记录任务执行次数。 - 切换 LED 状态以提供视觉反馈。
- 打印任务执行次数到控制台。
- 使用
LCD_Fill
和LCD_ShowxNum
更新显示内容。 vTaskDelay(1000)
延迟 1 秒用于控制任务的执行频率。
- 每次执行时将
4. 任务2 (task2_task
)
//task2任务函数
void task2_task(void *pvParameters)
{u8 task2_num=0;POINT_COLOR = BLACK;LCD_DrawRectangle(125,110,234,314); //画一个矩形 LCD_DrawLine(125,130,234,130); //画线POINT_COLOR = BLUE;LCD_ShowString(126,111,110,16,16,"Task2 Run:000");while(1){task2_num++; //任务2执行次数加1 注意task1_num2加到255的时候会清零!!LED1=!LED1;printf("任务2已经执行:%d次\r\n",task2_num);LCD_ShowxNum(206,111,task2_num,3,16,0x80); //显示任务执行次数LCD_Fill(126,131,233,313,lcd_discolor[13-task2_num%14]); //填充区域vTaskDelay(1000); //延时1s,也就是1000个时钟节拍 }
}
- 功能: 和任务1类似,执行另一段逻辑。任务2的任务函数,用于观察任务挂起和恢复的过程(中断方式)。
- 工作流程:
- 初始化图形显示(矩形和线)和文本。
- 无限循环:
- 每次执行时将
task2_num
递增,记录任务2的执行次数。 - 切换 LED 状态。
- 打印任务2的执行次数。
- 更新显示内容。
vTaskDelay(1000)
延迟 1 秒用于控制任务的执行频率。
- 每次执行时将
5. 任务函数小结:
- 任务管理: 通过创建多个任务,实现了对不同功能的模块化处理,确保系统的可扩展性和可维护性。
- 按键控制: 通过按键任务动态控制任务的运行状态,增加了用户交互性。
- 显示更新: 结合 LCD 显示,任务不仅仅是逻辑处理,还涉及到与用户的视觉交互。
- 注意事项:
- 在多任务环境中,需要注意优先级和资源共享,确保没有优先级反转或资源竞争的问题。
- 中断和任务调度的结合使用要确保系统的实时性和稳定性。
三、程序结果分析:
编译并下载程序到开发板中,通过按不同的按键来观察任务的挂起和恢复的过程。
一开始任务1和任务2都正常运行,当挂起任务1或者任务2以后,任务1或者任务2就会停止运行,直到下一次重新恢复任务1或者任务2的运行。重点是,保存任务运行次数的变量都没有发生数据丢失,如果用任务删除和重建的方法这些数据必然会丢失掉的!
📝大佬觉得本文有所裨益,不妨轻点一下👍给予鼓励吧!
❤️❤️❤️本人虽努力,但能力尚浅,若有不足之处,恳请各位大佬不吝赐教,您的批评指正将是我进步的动力!😊😊😊
💖💖💖若您认为此篇文章对您有所帮助,烦请点赞👍并收藏🌟,您的支持是我前行的最大动力!
🚀🚀🚀任务在默默中完成,价值在悄然间提升。让我们携手共进,一起加油,迎接更美好的未来!🌈🌈🌈
![]()