
FreeRTOS 在 AVR 平台上的 MPLAB X 演示工程Blinky / Minimal / Full 三档 Demo 完全指南【免费下载链接】FreeRTOSClassic FreeRTOS distribution. Started as Git clone of FreeRTOS SourceForge SVN repo. Submodules the kernel.项目地址: https://gitcode.com/GitHub_Trending/fr/FreeRTOS本篇技术指南聚焦于 FreeRTOS 官方仓库中面向 AVR 单片机AVR128DA48 Curiosity Nano 及 AVR-Dx / megaAVR-0 系列的 MPLAB XXC8 编译器演示工程系统讲解AVR_ATMega4809_MPLAB.X工程内通过一个宏即可切换的三套示例程序Blinky、Minimal、Full的架构、任务组成、check 任务与 LED 反馈机制、串口配置与快速上手指南。读完本文你将能够独立搭建工具链、在 Curiosity Nano 板上构建并运行任一 Demo并能依据源码定位每个示例背后的 FreeRTOS 内核机制队列、信号量、任务通知、递归互斥量、软件定时器等。工程概览面向 AVR128DA48 的 MPLAB X 演示工程本演示工程位于 FreeRTOS/Demo/AVR_ATMega4809_MPLAB.X 目录是一个使用MPLAB® X IDE XC8 编译器的裸机 FreeRTOS 示例项目目标硬件为AVR128DA48 Curiosity Nano 开发板搭载 AVR128DA48 微控制器128 KB Flash、16 KB SRAM、512 字节 EEPROM。仓库 ReadMe 同时将 readme.md 的参考资料指向了 Atmega4809 / Atmega4808 / Atmega3208 / Atmega3209 等 AVR 器件页面表明该工程同样适用于这一系列 AVR-Dx 与 megaAVR-0 器件。工程的最大特点是一个工程、三套示例通过修改 main.c 中的mainSELECTED_APPLICATION宏即可在三套演示程序之间切换每一套示例对应一个独立的main-***demo_name***.c源文件宏取值演示名称入口源文件定位0Blinky Demomain_blinky.c面向初学者的最小示例仅两个任务 一个队列1Minimal Demomain_minimal.c任务数较多但复杂度仍较低的功能演示2Full Demomain_full.c覆盖 FreeRTOS 大量特性的综合演示与测试入口 main() 如何选择 Demo从 main.c 源码可以看到三套示例的调度逻辑#define mainSELECTED_APPLICATION 0 #if ( mainSELECTED_APPLICATION 0 ) extern void main_blinky( void ); extern void init_blinky( void ); #elif ( mainSELECTED_APPLICATION 1 ) extern void main_minimal( void ); extern void init_minimal( void ); #elif ( mainSELECTED_APPLICATION 2 ) extern void main_full( void ); extern void init_full( void ); #else #error Invalid mainSELECTED_APPLICATION setting. ... #endif int main( void ) { prvSetupHardware(); #if ( mainSELECTED_APPLICATION 0 ) main_blinky(); #elif ( mainSELECTED_APPLICATION 1 ) main_minimal(); #elif ( mainSELECTED_APPLICATION 2 ) main_full(); #endif return 0; }每套示例都有两个入口init_***demo_name***()硬件初始化与main_***demo_name***()创建任务并启动调度器。main()首先通过prvSetupHardware()完成底层准备——调用portDISABLE_INTERRUPTS()确保调度器启动前没有中断打断内核状态的建立注释明确说明调度器启动时中断会自动使能随后执行CLK_init()完成时钟配置最后再调用对应 Demo 的init_***demo_name***()。如果宏取值为非法值非 0/1/2编译期会直接报错提示。时钟与内核配置FreeRTOSConfig.h 与 clk_config.h示例对系统时钟有明确的限定与配置。在 FreeRTOSConfig.h 中configCPU_CLOCK_HZ默认为1000000010 MHz注释明确说明可选的时钟频率为20 MHz、10 MHz、5 MHz、2 MHz四种其他频率需要自行修改clock_config.hconfigTICK_RATE_HZ为1000即 1 kHz 系统节拍configMAX_PRIORITIES为4configMINIMAL_STACK_SIZE为110字AVR 平台为 8 位 MCU栈以字节为单位考量configUSE_16_BIT_TICKS为1AVR 8 位平台上 TickType_t 使用 16 位节省 RAM内存管理采用动态分配configSUPPORT_DYNAMIC_ALLOCATION 1堆大小configTOTAL_HEAP_SIZE为0x10004 KB软件定时器相关configUSE_TIMERS 1、定时器队列长度5、定时器任务栈configMINIMAL_STACK_SIZE * 2头文件顶部还有一个 AVR 平台特有的处理为避免 FreeRTOS 任务控制块类型TCB_t与 AVR 的 Timer Counter B 类型TCB_t冲突先将TCB_t重定义为avrTCB_t包含avr/io.h后再#undef恢复。对应的 clk_config.h 使用_PROTECTED_WRITE()受保护寄存器写入操作CLKCTRL.MCLKCTRLA/CLKCTRL.MCLKCTRLB寄存器将时钟源选为内部 20 MHz 振荡器再通过预分频器得到目标频率20 MHz 不分频PDIV关闭、10 MHz 二分频CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm、5 MHz 四分频、2 MHz 十分频。值得注意的还有定时器实例的选择configUSE_TIMER_INSTANCE 0表示使用TCB0作为 FreeRTOS 系统节拍定时器注释给出了实例与取值的对应表TCB00、TCB11、TCB22、TCB33、RTC4。Blinky Demo两个任务与一个队列的最小内核演示#define mainSELECTED_APPLICATION 0Blinky Demo 专为初学者设计是理解 FreeRTOS 任务与队列机制的最佳起点。整个示例只创建两个任务和一个队列全部代码集中在 main_blinky.c 单个 C 文件中。任务角色与数据流队列发送任务prvQueueSendTask优先级tskIDLE_PRIORITY 1每隔200 ms向队列写入一个预定义值100队列接收任务prvQueueReceiveTask优先级tskIDLE_PRIORITY 2阻塞等待队列中的消息收到后校验值是否为预期的100若匹配则翻转板载 LED引脚 PF5。两个任务通过队列解耦接收方以portMAX_DELAY无限期阻塞在xQueueReceive()上只有发送方写入数据时才会被唤醒——因此 LED 恰好以200 ms 为周期翻转。源码关键点解读#define mainQUEUE_RECEIVE_TASK_PRIORITY ( tskIDLE_PRIORITY 2 ) #define mainQUEUE_SEND_TASK_PRIORITY ( tskIDLE_PRIORITY 1 ) #define mainQUEUE_SEND_FREQUENCY_MS ( 200 / portTICK_PERIOD_MS ) #define mainQUEUE_LENGTH ( 1 ) /* 队列深度为 1 */ static QueueHandle_t xQueue NULL; void main_blinky( void ) { xQueue xQueueCreate( mainQUEUE_LENGTH, sizeof( uint32_t ) ); if( xQueue ! NULL ) { xTaskCreate( prvQueueReceiveTask, Rx, configMINIMAL_STACK_SIZE, NULL, mainQUEUE_RECEIVE_TASK_PRIORITY, NULL ); xTaskCreate( prvQueueSendTask, TX, configMINIMAL_STACK_SIZE, NULL, mainQUEUE_SEND_TASK_PRIORITY, NULL ); vTaskStartScheduler(); } for( ; ; ) { } /* 正常情况下调度器启动后不会执行到这里 */ }队列深度设为1是有意为之接收任务会及时取走数据发送任务写入时队列总是空的因此发送时使用0阻塞时间也不会失败发送任务使用vTaskDelayUntil()周期性延时而非相对延时配合xNextWakeTime xTaskGetTickCount()的初始化可以避免节拍漂移、实现精确的 200 ms 周期LED 翻转直接操作 AVR 寄存器PORTF.OUTTGL | PIN5_bm硬件初始化init_blinky()中将PORTF.DIRSET PIN5_bmPF5 设为输出——这与工程通用的 LED 驱动 ParTest.c 使用的 PORTF 端口一致。Minimal Demo多任务功能演示与 check 任务#define mainSELECTED_APPLICATION 1Minimal Demo 的任务数量明显多于 Blinky但整体复杂度仍然较低。全部功能集中在 main_minimal.c 中它创建了以下任务整数运算任务Integer.c反复执行一组整型运算并将结果与已知期望值比对用于验证上下文切换机制是否完好实现见 FreeRTOS/Demo/Common/Minimal/integer.c运算常量与期望答案定义在文件头部寄存器测试任务Regtest.c创建两个测试任务用内联汇编给大量寄存器写入已知值然后调用taskYIELD()强制切换恢复运行后逐一检查寄存器值是否被破坏——这是对上下文切换正确性的直接压力测试完整实现见 regtest.c一旦发现异常即将xRegTestError置为非零轮询队列任务PollQ.c以轮询方式测试队列操作串口通信任务Serial.c测试基于中断驱动的串口收发check 任务本文件内的vErrorChecks周期性地检查上述任务是否仍在无错误运行。从 main_minimal.c 可以看到任务创建顺序void main_minimal( void ) { prvIncrementResetCount(); /* EEPROM 复位计数 */ vStartIntegerMathTasks( tskIDLE_PRIORITY ); vAltStartComTestTasks( mainCOM_TEST_PRIORITY, mainCOM_TEST_BAUD_RATE, mainCOM_TEST_LED ); vStartPolledQueueTasks( mainQUEUE_POLL_PRIORITY ); vStartRegTestTasks(); xTaskCreate( vErrorChecks, Check, configMINIMAL_STACK_SIZE, NULL, mainCHECK_TASK_PRIORITY, NULL ); vStartFlashCoRoutines( mainNUM_FLASH_COROUTINES ); /* 3 个闪烁协程 */ vTaskStartScheduler(); }该示例还包含两个值得注意的细节EEPROM 复位计数prvIncrementResetCount()在启动时从地址0x1400读取复位计数并加 1 写回用于检测 CPU 是否发生意外复位协程Co-routine演示创建 3 个 LED 闪烁协程vStartFlashCoRoutines并且空闲钩子vApplicationIdleHook()中调用vCoRoutineSchedule()驱动协程调度对应的内核配置configUSE_CO_ROUTINES 1、configMAX_CO_ROUTINE_PRIORITIES 2。check 任务与 LED 状态反馈check 任务优先级tskIDLE_PRIORITY 3是这套示例的健康监测员它每隔约1 秒mainCHECK_PERIOD 1000 / portTICK_PERIOD_MS调用一次prvCheckOtherTasksAreStillRunning()依次查询整数运算、串口通信、轮询队列、寄存器测试四类任务的状态查询函数xAreIntegerMathsTaskStillRunning()、xAreComTestTasksStillRunning()、xArePollingQueuesStillRunning()、xAreRegTestTasksStillRunning()任何一项返回异常都会将xErrorHasOccurred置位并永远不再翻转 LED。因此板载 LED 的翻转行为就是系统状态的指示灯LED 约每秒翻转一次check 任务未发现任何问题所有任务运行正常LED 停止翻转check 任务在某一个或多个任务中发现了错误。串口控制台参数若需查看串口通信任务的输出串口USART应按如下参数配置参数取值波特率9600数据位8 bit校验位none停止位1 bit流控none对应源码中的mainCOM_TEST_BAUD_RATE定义为9600。底层串口驱动位于 serial/serial.c是一个基于USART3的中断驱动驱动波特率通过公式USART_BAUD_RATE(BAUD_RATE) configCPU_CLOCK_HZ * 64 / (16 * BAUD_RATE)换算后写入USART3.BAUD接收完成中断USART3_RXC_vect中通过xQueueSendFromISR()把字符投递到接收队列并在有更高优先级任务被唤醒时调用portYIELD_FROM_ISR()触发上下文切换发送采用数据寄存器空中断USART3_DRE_vect驱动任务调用xSerialPutChar()将字符放入发送队列并开中断ISR 逐字符取出发送队列空时关闭中断硬件初始化init_minimal()中将 PB0 配置为 Tx、PB1 配置为 RxPORTB.DIR方向设置。Full DemoFreeRTOS 特性的综合演示与测试#define mainSELECTED_APPLICATION 2Full Demo 是对 FreeRTOS 大量特性的综合演示与测试覆盖任务间直接通知direct task to task notifications、队列、信号量、递归互斥量、软件定时器等机制。从 main_full.c 可以看到它创建的任务void main_full( void ) { vStartSemaphoreTasks( mainSEM_TEST_PRIORITY ); /* 信号量测试任务 */ vStartTaskNotifyTask(); /* 任务通知测试任务 */ vStartRegTestTasks(); /* 寄存器上下文切换测试 */ vStartRecursiveMutexTasks(); /* 递归互斥量测试任务 */ xTaskCreate( prvCheckTask, Check, configMINIMAL_STACK_SIZE, NULL, mainCHECK_TASK_PRIORITY, NULL ); vTaskStartScheduler(); }各测试任务的完整实现位于 FreeRTOS 仓库的公共演示代码中信号量测试Semtest.c队列信号量/互斥量相关任务通知测试FreeRTOS/Demo/Common/Minimal/TaskNotify.c测试任务通知的位操作、数值累加、通知等待等行为涉及ULONG_MAX环绕、挂起/恢复等边界场景寄存器测试与 Minimal Demo 共用 regtest.c递归互斥量测试Recmutex.c对应配置configUSE_RECURSIVE_MUTEXES 1其任务栈大小宏recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE定义为configMINIMAL_STACK_SIZE * 2。check 任务与 LED 状态反馈与 Minimal Demo 的机制相同Full Demo 也使用check 任务优先级为configMAX_PRIORITIES - 1即最高优先级周期巡检所有任务。区别在于巡检周期更长约3 秒mainCHECK_PERIOD 3000 / portTICK_PERIOD_MS且错误记录采用位标志方式——每类任务对应一个独立的位if( xAreSemaphoreTasksStillRunning() ! pdTRUE ) ulErrorFound | 1UL 0UL; if( xAreTaskNotificationTasksStillRunning() ! pdTRUE ) ulErrorFound | 1UL 1UL; if( xAreRegTestTasksStillRunning() ! pdTRUE ) ulErrorFound | 1UL 2UL; if( xAreRecursiveMutexTasksStillRunning() ! pdTRUE ) ulErrorFound | 1UL 3UL; if( ulErrorFound pdFALSE ) vParTestToggleLED( mainCHECK_TASK_LED ); /* LED5 */LED 反馈规则LED 约每 3 秒翻转一次check 任务未发现任何问题LED 停止翻转check 任务在某个或多个任务中发现了错误。中断与任务通知的联动Full Demo 还有一个值得注意的设计在 main_full.c 中实现了vApplicationTickHook()tick 钩子由内核在每个系统节拍中断中调用其中调用xNotifyTaskFromISR()——这正是演示从 ISR 向任务发送任务通知这一机制的入口让任务通知测试能够在中断上下文中被驱动验证configUSE_TICK_HOOK 1配置下的 ISR 通知路径。快速上手指南要在 AVR128DA48 Curiosity Nano 平台上运行本演示工程按以下步骤操作安装MPLAB® X IDE参考资料建议 5.40 或更新版本安装MPLAB® XC8编译器参考资料建议 2.20 或更新版本在 MPLAB X IDE 中打开工程文件FreeRTOS/Demo/AVR_ATMega4809_MPLAB.X目录下的工程工程目录包含由 MPLAB X 自动管理的 Makefile实际构建逻辑由nbproject/Makefile-impl.mk与Makefile-variables.mk提供支持build、clean、clobber、all等标准目标按上文说明在 main.c 中通过#define mainSELECTED_APPLICATION选择期望的 Demo0 Blinky1 Minimal2 Full构建build并将固件烧录/调试debug到目标板。版本与硬件要求来自原文档 References运行本工程需要以下环境与硬件MPLAB® X IDE 5.40 或更新版本MPLAB® XC8 2.20 或更新版本目标器件为 AVR128DA48Curiosity Nano 板载参考资料同时覆盖 Atmega4809、Atmega4808、Atmega3208、Atmega3209 等 AVR 器件页面可按需选用同一系列的开发板。说明工程目录名沿用了AVR_ATMega4809而 ReadMe 正文的目标硬件为 AVR128DA48 Curiosity Nano128 KB Flash / 16 KB SRAM / 512 B EEPROM参考资料亦同时列出 megaAVR-0 系列器件使用时请以实际板卡型号为准并参照 FreeRTOS 官网对应 RTOS port 文档页完成目标硬件搭建、固件下载与运行。调试钩子栈溢出与内存分配失败处理main.c 中还预置了两个可选的调试钩子方便开发阶段排查问题vApplicationStackOverflowHook栈溢出时被内核调用需要将FreeRTOSConfig.h中的configCHECK_FOR_STACK_OVERFLOW设为1。钩子进入临界区后死循环等待调试器可将局部变量ulSetToNonZeroInDebuggerToContinue改为非零值以继续执行vApplicationMallocFailedHook动态内存分配失败时被调用需要configUSE_MALLOC_FAILED_HOOK设为1处理方式同上。这两个钩子当前在 FreeRTOSConfig.h 中对应配置均为关闭状态configCHECK_FOR_STACK_OVERFLOW 0、configUSE_MALLOC_FAILED_HOOK 0但代码已就绪按需打开即可在开发期获得故障定位能力。小结AVR_ATMega4809_MPLAB.X演示工程以一个工程、三档 Demo的方式为 AVR 平台的 FreeRTOS 入门到进阶提供了循序渐进的路径BlinkymainSELECTED_APPLICATION 0任务 队列的最小范例验证工具链与调度器是否正常工作MinimalmainSELECTED_APPLICATION 1整数运算、寄存器测试、轮询队列、串口通信、协程与 check 任务的组合演示验证上下文切换与多种内核对象FullmainSELECTED_APPLICATION 2信号量、任务通知、递归互斥量等特性的综合压力测试。三套 Demo 共用同一套check 任务 LED 翻转的健康反馈机制Minimal 每 1 秒、Full 每 3 秒通过 LED 是否持续翻转即可直观判断系统是否运行无误是理解 FreeRTOS 任务调度、内核对象与 AVR 移植层配合的优秀参考实现。更完整的上下文切换压力测试寄存器级验证可继续研读 regtest.c内核任务通知的深度测试则见 FreeRTOS/Demo/Common/Minimal/TaskNotify.c。【免费下载链接】FreeRTOSClassic FreeRTOS distribution. Started as Git clone of FreeRTOS SourceForge SVN repo. Submodules the kernel.项目地址: https://gitcode.com/GitHub_Trending/fr/FreeRTOS创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考