NXP

KW41Z_FreeRTOS学习笔记

2019-07-12 12:06发布

KW41Z_FreeRTOS学习笔记 文档编号 TN_TEMPLATE0101_A0 关键字 FreeRTOS, MCU, Embedded System, KW41Z 摘要 本技术笔记对KW41Z_FreeRTOS学习笔记进行说明 Mars4zhu
目 录 1 总述 1 2 使用嵌入式开发软件编译下载调试SDK_KW41Z程序 1 2.1 使用IAR开发SDK_KW41Z程序 1 2.2 使用Keil开发SDK_KW41Z程序 2 2.3 使用ARM-GCC开发SDK_KW41Z程序 3 2.4 使用MUCXpresso开发SDK_KW41Z程序 5 2.5 使用KSDK开发SDK_KW41Z程序 5 2.6 使用OpenOCD+JLink+arm-none-eabi-gdb调试KW41Z程序 5 2.7 使用Eclipse + OpenOCD + arm-none-eabi-gcc + Jlink开发KW41Z程序 5 3 SDK_KW41Z的FreeRTOS范例学习 5 3.1 freertos_hello范例学习 5 3.2 Freertos_event范例学习 7 3.3 freertos_mutex范例学习 9 3.4 freertos_queue范例学习 11 3.5 freertos_sem范例学习 13 3.6 freertos_swtimer范例学习 15 3.7 freertos_tickless范例学习 17 3.8 freertos_generic范例学习 18 3.8.1 消息队列与任务管理 18 3.8.2 信号量消息管理 20 3.8.3 定时器管理 21 4 KW41Z_FreeRTOS+外设驱动例程学习 23 4.1 freertos_i2c 23 4.2 freertos_dspi 23 4.3 freertos_lpuart 23 5 版本历史(Revision History) 30
插图索引 图 21   KW41Z_SDK_freertos_hello例程的IAR编译下载调试运行效果图 2 图 22   KW41Z_SDK_freertos_hello例程的Keil工程项目与DFP安装截图 2 图 23   KW41Z_SDK_freertos_hello例程的Keil编译下载调试运行效果图 3 图 24   KW41Z_SDK_freertos_hello例程的ARM-GCC编译效果图 4 图 25 使用JFlash创建KW41Z项目并破解license示意图 4 图 26 使用JFlash下载运行KW41Z_SDK_freertos_hello例程的gcc编译后的hex文件 4 图 31   IAR选中FreeRTOS and OpenRTOS选项 6 图 32   KW41Z_SDK_freertos_hello例程编译下载调试运行效果图 6 图 33   KW41Z_SDK_freertos_event例程编译下载调试运行效果图 9 图 34   KW41Z_SDK_freertos_mutex例程编译下载调试运行效果图 10 图 35   KW41Z_SDK_freertos_mutex例程取消互斥量的运行效果图 11 图 36   KW41Z_SDK_freertos_queue例程的运行效果图 13 图 37   KW41Z_SDK_freertos_sem例程量的运行效果图 15 图 38   KW41Z_SDK_freertos_swtimer例程量的运行效果图 16 图 39   KW41Z_SDK_freertos_tickless例程编译下载调试运行效果图 18 图 310   KW41Z_SDK_freertos_generic例程编译下载调试运行效果图 23 图 41   KW41Z_SDK_freertos_LPUART例程编译下载调试运行效果图 29
表格索引 错误!未找到目录项。

1 总述

FreeRTOS是广泛使用的RTOS之一。KW41Z系列MCU的SDK软件代码库提供了FreeRTOS的例程,本学习笔记采用截至目前(2017-05-07)最新版(SDK_V2.2)的代码、学习采用FreeRTOS开发KW41Z系列MCU的应用。 KW41Z的官方代码SDK提供了更多的范例,位于SDK_2.2_FRDM-KW41Zoardsfrdmkw41z tos_examples目录下。

2 使用嵌入式开发软件编译下载调试SDK_KW41Z程序

KW41Z的SDK支持多种嵌入式开发软件来进行编译下载调试,包括老牌的Keil、IAR、NXP自家的KDS(收购自原FreeScale公司)和McuXpresso(NXP原有的LPCXpresso与收购的Freescale的KDS合并之后的开发软件),以及开源的ARM-GCC。本章节简要描述如何使用这些开发软件来对SDK的例程进行编译、下载和调试。

2.1 使用IAR开发SDK_KW41Z程序

SDK_KW41Z几乎大部分例程都支持IAR开发,特别是最重要的无线应用如BLE、Thread等使用IAR最适用。 双击打开一个例程的IAR工程项目.eww文件,以freertos_hello例程为例,双击SDK_2.2_FRDM-KW41Zoardsfrdmkw41z tos_examplesfreertos_helloiarfreertos_hello.eww,就可以使用IAR打开该例程的工程项目。 菜单栏点击Project->Make(快捷键F7),即可完成项目的编译。然后在菜单栏选择Project->Download and Debug(快捷键Ctrl+D),即可下载编译好的程序到开发板并启动调试,默认在main函数停止,可以在菜单栏的Debug->中或者工具栏的wpsE196.tmp进行单步跨越、单步进入、单步跳出、持续运行、中断暂停等调试操作,以及在源代码窗口左边行号上双击设置断点,本例程可以选择在PRINTF语句中,点击运行到断点处后会停下,再点击单步跨越即可看到串口输出信息。 wpsE1A7.tmp 21  KW41Z_SDK_freertos_hello例程的IAR编译下载调试运行效果图

2.2 使用Keil开发SDK_KW41Z程序

Keil打开后,首先需要安装KW41Z系列的设备支持包DFP,在Pack Installer中点击安装即可。安装后打开的Keil工程项目截图如下: wpsE1A8.tmp 22  KW41Z_SDK_freertos_hello例程的Keil工程项目与DFP安装截图 点击Project->Build(快捷键F7)即可完成freertos_hello例程的编译。 在菜单栏选择Debug->Start/Stop Debug Session(快捷键Ctrl+F5),即可下载编译好的程序到开发板并启动调试,默认在main函数停止,可以在菜单栏的Debug->中或者工具栏的wpsE1A9.tmp进行单步跨越、单步进入、单步跳出、持续运行、中断暂停等调试操作,以及在源代码窗口左边行号上单击设置断点,本例程设置在PRINTF语句前,则每次Systick延时中断发生时调试器暂停程序运行,点击运行到断点处后会停下,再点击单步跨越即可看到串口输出信息。 wpsE1B9.tmp 23  KW41Z_SDK_freertos_hello例程的Keil编译下载调试运行效果图

2.3 使用ARM-GCC开发SDK_KW41Z程序

GCC是著名的开源编译器,ARM提供了针对ARM Cortex系列各种CPU架构的编译器。KW41Z是ARM Cortex-M0架构,适用的ARM-GCC编译器是arm-none-eabi-gcc编译器,从https://launchpad.net/gcc-arm-embedded下载解压后即可(最新版本从https://developer.arm.com/open-source/gnu-toolchain/gnu-rm下载)。并且还需要mingw32-make,可以安装mingw编译器即可。 KW41Z的ARM-GCC例程使用了cmake作为gcc的项目工程文件Makefile的生成器。同样下载cmake安装后,在Windows命令行输入以下命令即可完成编译。 Set PATH=%PATH%;D:softwaregcc_arm_none_eabiin;D:softwarecmakein;D:softwaremingwin set ARMGCC_DIR=D:softwaregcc_arm_none_eabi Build_debug.bat 即可生成gcc编译器支持的elf格式文件,为了方便下载,使用objcopy将elf转化为hex文件,命令如下: arm-none-eabi-objcopy -O ihex debugfreertos_hello.elf debugfreertos_hello.hex 结果如下图: wpsE1CA.tmp 24  KW41Z_SDK_freertos_hello例程的ARM-GCC编译效果图 生成hex文件后,可以使用板载Jlink,利用JFlash软件进行下载运行。 打开JFlash软件,新建一个Project并设定MCU为KW41Z,SWD接口等设置,并连接Target->Connect(如果提示FRDM-KW41Z板载的Jlink不支持某些功能需要license的话,可以从网上下载破解器进行破解)如下图: wpsE1CB.tmp 25 使用JFlash创建KW41Z项目并破解license示意图 分别选择Target->Production Progamming和Target->Manual Programing->Start Application即可完成下载和运行,效果图如下: wpsE1CC.tmp 26 使用JFlash下载运行KW41Z_SDK_freertos_hello例程的gcc编译后的hex文件

2.4 使用MUCXpresso开发SDK_KW41Z程序

TODO

2.5 使用KSDK开发SDK_KW41Z程序

TODO

2.6 使用OpenOCD+JLink+arm-none-eabi-gdb调试KW41Z程序

TODO

2.7 使用Eclipse + OpenOCD + arm-none-eabi-gcc + Jlink开发KW41Z程序

TODO

3 SDK_KW41Z的FreeRTOS范例学习

3.1 freertos_hello范例学习

SDK提供的freertos_hello是最简单的freertos程序,该程序创建一个执行函数为hello_task的任务,该执行函数向串口输出”Hello world.”字符串之后即挂起,之后任务调度器无限循环调度FreeRTOS的idle_task空闲任务。 /*! * @brief Task responsible for printing of "Hello world." message. */ static void hello_task(void *pvParameters) { for (;;) {         PRINTF("Hello world. ");         vTaskSuspend(NULL); } } 完成编译后下载调试,点击运行即可串口输出”Hello world.”信息。 其中在IAR中可以在Options->Debugs->Plugins中选中FreeRTOS and OpenRTOS选项,这样在调试中就可以通过这个插件查看FreeRTOS中的任务Task、队列Queue等系统组件的信息。如下图: wpsE1CD.tmp 31  IAR选中FreeRTOS and OpenRTOS选项 wpsE1DE.tmp 32  KW41Z_SDK_freertos_hello例程编译下载调试运行效果图

3.2 Freertos_event范例学习

EventGroup事件组合消息提供比队列消息Queue、信号量消息Semaphore、更灵活的任务通讯机制,多个消息一起组合成一个组合消息等特点。 本例程跟FreeRTOS官方的source-code-for-book-examples有关EventGroup的例程类似。 首先创建一个事件组合消息event_group,然后创建三个任务,两个写入组合消息,一个读取该组合消息。     event_group = xEventGroupCreate();     xTaskCreate(write_task_1, "WRITE_TASK_1", configMINIMAL_STACK_SIZE + 38, NULL, tskIDLE_PRIORITY + 1, NULL);     xTaskCreate(write_task_2, "WRITE_TASK_2", configMINIMAL_STACK_SIZE + 38, NULL, tskIDLE_PRIORITY + 1, NULL);     xTaskCreate(read_task, "READ_TASK", configMINIMAL_STACK_SIZE + 38, NULL, tskIDLE_PRIORITY + 2, NULL);ranho 然后两个写任务分别独立设置event_group组合消息的B0和B1两个位,同时读任务读取event_group组合消息,并判断哪个bit位”1”,输出相关判断信息。 /*! * @brief write_task_1 function */ static void write_task_1(void *pvParameters) { while (1) {         xEventGroupSetBits(event_group, B0); } } /*! * @brief write_task_2 function */ static void write_task_2(void *pvParameters) { while (1) {         xEventGroupSetBits(event_group, B1); } } /*! * @brief read_task function */ static void read_task(void *pvParameters) {     EventBits_t event_bits; while (1) {         event_bits = xEventGroupWaitBits(event_group, /* The event group handle. */                                          B0 | B1, /* The bit pattern the event group is waiting for. */                                          pdTRUE, /* BIT_0 and BIT_4 will be cleared automatically. */                                          pdFALSE, /* Don't wait for both bits, either bit unblock task. */                                          portMAX_DELAY); /* Block indefinitely to wait for the condition to be met. */ if ((event_bits & (B0 | B1)) == (B0 | B1)) {             PRINTF("Both bits are set."); } else if ((event_bits & B0) == B0) {             PRINTF("Bit B0 is set. "); } else if ((event_bits & B1) == B1) {             PRINTF("Bit B1 is set. "); } } } 完成编译后下载调试,点击运行即可串口输出相关信息。 wpsE1EE.tmp 33  KW41Z_SDK_freertos_event例程编译下载调试运行效果图

3.3 freertos_mutex范例学习

Mutex互斥量信息同一时刻只允许一个任务访问某个资源,防止资源乱序访问。 首先创建一个互斥信号量xMutex,然后创建两个任务,通过xMutex来互斥执行,即如果一个任务获得xMutex后,另一个任务必须等待它释放才能得以执行。     xMutex = xSemaphoreCreateMutex();     xTaskCreate(write_task_1, "WRITE_TASK_1", configMINIMAL_STACK_SIZE + 128, NULL, tskIDLE_PRIORITY + 1, NULL);     xTaskCreate(write_task_2, "WRITE_TASK_2", configMINIMAL_STACK_SIZE + 128, NULL, tskIDLE_PRIORITY + 1, NULL); 然后两个任务各自的执行函数分别是先获取互斥量xMutex,获得后才可以执行后续程序,执行完毕后释放。 /*! * @brief Write Task 1 function */ static void write_task_1(void *pvParameters) { while (1) {         xSemaphoreTake(xMutex, portMAX_DELAY);         PRINTF("ABCD |");         taskYIELD();         PRINTF(" EFGH ");         xSemaphoreGive(xMutex);         taskYIELD(); } } /*! * @brief Write Task 2 function */ static void write_task_2(void *pvParameters) { while (1) {         xSemaphoreTake(xMutex, portMAX_DELAY);         PRINTF("1234 |");         taskYIELD();         PRINTF(" 5678 ");         xSemaphoreGive(xMutex);         taskYIELD(); } } 完成编译后下载调试,点击运行即可串口输出相关信息。 wpsE1EF.tmp 34  KW41Z_SDK_freertos_mutex例程编译下载调试运行效果图 如果注释掉xMutex的获取和释放之后,则任务执行乱序,输出的字符串就有了混杂。 wpsE200.tmp 35  KW41Z_SDK_freertos_mutex例程取消互斥量的运行效果图

3.4 freertos_queue范例学习

队列消息是任务间通讯常用的方式,通过一个个消息发送到队列中,则接受消息的任务可以获取该队列,并且顺序与发送顺序一样。实现任务间通讯。 首先创建一个队列,并提供添加到队列的函数log_add()。 /*! * @brief log_init function */ void log_init(uint32_t queue_length, uint32_t max_log_lenght) {     log_queue = xQueueCreate(queue_length, max_log_lenght);     xTaskCreate(log_task, "log_task", configMINIMAL_STACK_SIZE + 166, NULL, tskIDLE_PRIORITY + 1, NULL); } /*! * @brief log_add function */ void log_add(char *log) {     xQueueSend(log_queue, log, 0); } 然后创建两个任务write_task_1和write_task_2,分别向队列中发送消息。 /*! * @brief write_task_1 function */ static void write_task_1(void *pvParameters) { char log[MAX_LOG_LENGTH + 1]; uint32_t i = 0; for (i = 0; i < 5; i++) {         sprintf(log, "Task1 Message %d", (int)i);         log_add(log);         taskYIELD(); }     vTaskSuspend(NULL); } /*! * @brief write_task_2 function */ static void write_task_2(void *pvParameters) { char log[MAX_LOG_LENGTH + 1]; uint32_t i = 0; for (i = 0; i < 5; i++) {         sprintf(log, "Task2 Message %d", (int)i);         log_add(log);         taskYIELD(); }     vTaskSuspend(NULL); } 最后创建一个消息接受任务log_task,不断从队列中读取消息并显示。 /*! * @brief log_print_task function */ static void log_task(void *pvParameters) { uint32_t counter = 0; char log[MAX_LOG_LENGTH + 1]; while (1) {         xQueueReceive(log_queue, log, portMAX_DELAY);         PRINTF("Log %d: %s ", counter, log);         counter++; } } 完成编译后下载调试,点击运行即可串口输出相关信息。 wpsE210.tmp 36  KW41Z_SDK_freertos_queue例程的运行效果图

3.5 freertos_sem范例学习

Semaphore信号量消息作为最简单的任务间通讯,通过给任务发送一个信号后,该任务执行该信号量相应的程序。 首先创建一个producer_task任务和三个consumer_task任务,然后创建两个信号量,xSemaphore_producer和xSemaphore_consumer。 if (xSemaphore_producer == NULL) {         PRINTF("xSemaphore_producer creation failed. ");         vTaskSuspend(NULL); }     xSemaphore_consumer = xSemaphoreCreateBinary(); if (xSemaphore_consumer == NULL) {         PRINTF("xSemaphore_consumer creation failed. ");         vTaskSuspend(NULL); } 在producer_task和consumer_task中,分别对两个信号量进行提供和获取。 /*! * @brief Task producer_task. */ static void producer_task(void *pvParameters) { ... while (1) { /* Producer is ready to provide item. */         xSemaphoreGive(xSemaphore_consumer); /* Producer is waiting when consumer will be ready to accept item. */ if (xSemaphoreTake(xSemaphore_producer, portMAX_DELAY) == pdTRUE) {             PRINTF("Producer released item. "); } else {             PRINTF("Producer is waiting for customer. "); } } } /*! * @brief Task consumer_task. */ static void consumer_task(void *pvParameters) {     PRINTF("Consumer number: %d ", pvParameters); while (1) { /* Consumer is ready to accept. */         xSemaphoreGive(xSemaphore_producer); /* Consumer is waiting when producer will be ready to produce item. */ if (xSemaphoreTake(xSemaphore_consumer, portMAX_DELAY) == pdTRUE) {             PRINTF("Consumer %d accepted item. ", pvParameters); } else {             PRINTF("Consumer %d is waiting for producer. ", pvParameters); } } } 完成编译后下载调试,点击运行即可串口输出相关信息。 wpsE221.tmp 37  KW41Z_SDK_freertos_sem例程量的运行效果图

3.6 freertos_swtimer范例学习

Software_Timer软件定时器是FreeRTOS提供的一个定时服务,通过xTimerCreate()创建一个定时器,并设置回调函数SwTimerCallback(),执行定时循环程序。 int main(void) {     TimerHandle_t SwTimerHandle = NULL; /* Init board hardware. */ /* attach 12 MHz clock to FLEXCOMM0 (debug console) */     CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);     BOARD_InitPins();     BOARD_BootClockFROHF48M();     BOARD_InitDebugConsole();     SystemCoreClockUpdate(); /* Create the software timer. */     SwTimerHandle = xTimerCreate("SwTimer", /* Text name. */                                  SW_TIMER_PERIOD_MS, /* Timer period. */                                  pdTRUE, /* Enable auto reload. */ 0, /* ID is not used. */                                  SwTimerCallback); /* The callback function. */ /* Start timer. */     xTimerStart(SwTimerHandle, 0); /* Start scheduling. */     vTaskStartScheduler(); for (;;) ; } /*! * @brief Software timer callback. */ static void SwTimerCallback(TimerHandle_t xTimer) {     PRINTF("Tick. "); } 完成编译后下载调试,点击运行即可串口输出相关信息。同时可见Software_Timer定时服务运行在独立的TmrSvc任务中。 wpsE232.tmp 38  KW41Z_SDK_freertos_swtimer例程量的运行效果图

3.7 freertos_tickless范例学习

Tickless实现了FreeRTOS对低功耗的需求,通过关闭tick,停止了整个FreeRTOS的调度,只通过外部中断来实现唤醒。 程序首先注册了RTC中断和EXT外部按键中断的回调函数,分别为pint_intr_callback()和BOARD_RTC_IRQ_HANDLER()。 pint_intr_callback提供信号量xSWSemaphore给SW_task,然后调度器调度执行SW_task,执行结束后进入Idle_Task,在里面调用__WFI()进入设定好的低功耗模式。 /*! * @brief Call back for PINT Pin interrupt 0-7. */ void pint_intr_callback(pint_pin_int_t pintr, uint32_t pmatch_status) {     portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; /* Clear external interrupt flag. */     PINT_PinInterruptClrFallFlag(PINT, kPINT_PinInt0);     xSemaphoreGiveFromISR(xSWSemaphore, &xHigherPriorityTaskWoken); } BOARD_RTC_IRQ_HANDLER()则调用vPortRtcIsr(),该函数设置ulLPTimerInterruptFired=true,该条件触发FreeRTOS重新滴答Tick一次并调度。 /*! * @brief Interrupt service fuction of UTICK timer. * * This function to call vPortUtickIsr */ void BOARD_RTC_IRQ_HANDLER(void) {     vPortRtcIsr(); } void vPortRtcIsr(void) {   ulLPTimerInterruptFired = true;   RTC_ClearStatusFlags(RTC, kRTC_WakeupFlag); } 最后完成编译后下载调试,点击运行即可看到串口输出各类信息。并且IAR中显示对FreeRTOS系统内部Task、Queue等信息。 (注:其中对于万利电子的LPC54114开发板,需要注意led、key的引脚定义,可以修改代码中的配置,不修改的话,SW3为Manley_LPC54114开发板上的JP3.) wpsE242.tmp 39  KW41Z_SDK_freertos_tickless例程编译下载调试运行效果图

3.8 freertos_generic范例学习

Freertos_generic例程提供了对FreeRTOS的Task、Queue、Semaphore、Timer等多种系统服务组件的综合使用示范。
3.8.1 消息队列与任务管理
该例程通过创建一个xQueue队列消息,来实现队列消息通讯机制。 /* Create the queue used by the queue send and queue receive tasks. */     xQueue = xQueueCreate(/* The number of items the queue can hold. */                           mainQUEUE_LENGTH, /* The size of each item the queue holds. */ sizeof(uint32_t)); 同时创建两个Task,一个Task循环延时后发送队列,另一个Task则持续监听队列,收到队列消息后则打印累计收到的队列消息数量。 /*! * @brief Task prvQueueSendTask periodically sending message. */ static void prvQueueSendTask(void *pvParameters) {     TickType_t xNextWakeTime; const uint32_t ulValueToSend = 100UL; /* Initialise xNextWakeTime - this only needs to be done once. */     xNextWakeTime = xTaskGetTickCount(); for (;;) { /* Place this task in the blocked state until it is time to run again.         The block time is specified in ticks, the constant used converts ticks         to ms.  While in the Blocked state this task will not consume any CPU         time. */         vTaskDelayUntil(&xNextWakeTime, mainQUEUE_SEND_PERIOD_MS); /* Send to the queue - causing the queue receive task to unblock and         increment its counter.  0 is used as the block time so the sending         operation will not block - it shouldn't need to block as the queue         should always be empty at this point in the code. */         xQueueSend(xQueue, &ulValueToSend, 0); } } /*! * @brief Task prvQueueReceiveTask waiting for message. */ static void prvQueueReceiveTask(void *pvParameters) { uint32_t ulReceivedValue; for (;;) { /* Wait until something arrives in the queue - this task will block         indefinitely provided INCLUDE_vTaskSuspend is set to 1 in         FreeRTOSConfig.h. */         xQueueReceive(xQueue, &ulReceivedValue, portMAX_DELAY); /*  To get here something must have been received from the queue, but         is it the expected value?  If it is, increment the counter. */ if (ulReceivedValue == 100UL) { /* Count the number of items that have been received correctly. */             ulCountOfItemsReceivedOnQueue++;             PRINTF("Receive message counter: %d. ", ulCountOfItemsReceivedOnQueue); } } }
3.8.2 信号量消息管理
创建一个信号量xEventSemaphore。 /* Create the semaphore used by the FreeRTOS tick hook function and the     event semaphore task. */     vSemaphoreCreateBinary(xEventSemaphore); 并在vApplicationTickHook函数中发送信号量,该函数每次系统的滴答tick中都调用一次。 /*! * @brief tick hook is executed every tick. */ void vApplicationTickHook(void) {     BaseType_t xHigherPriorityTaskWoken = pdFALSE; static uint32_t ulCount = 0; /* The RTOS tick hook function is enabled by setting configUSE_TICK_HOOK to     1 in FreeRTOSConfig.h.     "Give" the semaphore on every 500th tick interrupt. */     ulCount++; if (ulCount >= 500UL) { /* This function is called from an interrupt context (the RTOS tick         interrupt),    so only ISR safe API functions can be used (those that end         in "FromISR()".         xHigherPriorityTaskWoken was initialised to pdFALSE, and will be set to         pdTRUE by xSemaphoreGiveFromISR() if giving the semaphore unblocked a         task that has equal or higher priority than the interrupted task. */         xSemaphoreGiveFromISR(xEventSemaphore, &xHigherPriorityTaskWoken);         ulCount = 0UL; } /* If xHigherPriorityTaskWoken is pdTRUE then a context switch should     normally be performed before leaving the interrupt (because during the     execution of the interrupt a task of equal or higher priority than the     running task was unblocked).  The syntax required to context switch from     an interrupt is port dependent, so check the documentation of the port you     are using.     In this case, the function is running in the context of the tick interrupt,     which will automatically check for the higher priority task to run anyway,     so no further action is required. */ } 最后创建一个接受信号量的任务,该任务持续监听信号量,并输出信息。 /*! * @brief task prvEventSemaphoreTask is waiting for semaphore. */ static void prvEventSemaphoreTask(void *pvParameters) { for (;;) { /* Block until the semaphore is 'given'. */         xSemaphoreTake(xEventSemaphore, portMAX_DELAY); /* Count the number of times the semaphore is received. */         ulCountOfReceivedSemaphores++;         PRINTF("Event task is running. "); } }
3.8.3 定时器管理
创建一个xExampleSoftwareTimer的定时器,周期为mainSOFTWARE_TIMER_PERIOD_MS,定时器触发时调用vExampleTimerCallback()函数。 /* Create the software timer as described in the comments at the top of     this file. */     xExampleSoftwareTimer = xTimerCreate(/* A text name, purely to help                                        debugging. */ "LEDTimer", /* The timer period, in this case                                          1000ms (1s). */                                          mainSOFTWARE_TIMER_PERIOD_MS, /* This is a periodic timer, so                                          xAutoReload is set to pdTRUE. */                                          pdTRUE, /* The ID is not used, so can be set                                          to anything. */ (void *)0, /* The callback function that switches                                          the LED off. */                                          vExampleTimerCallback); /* Start the created timer.  A block time of zero is used as the timer     command queue cannot possibly be full here (this is the first timer to     be created, and it is not yet running). */     xTimerStart(xExampleSoftwareTimer, 0); 在vExampleTimerCallback()函数中,对定时器触发次数进行累计计数。 /*! * @brief Timer callback. */ static void vExampleTimerCallback(TimerHandle_t xTimer) { /* The timer has expired.  Count the number of times this happens.  The     timer that calls this function is an auto re-load timer, so it will     execute periodically. */     ulCountOfTimerCallbackExecutions++; } 最后完成编译后下载调试,点击运行即可看到串口输出各类信息。并且IAR中显示对FreeRTOS系统内部Task、Queue等信息。 wpsE282.tmp 310  KW41Z_SDK_freertos_generic例程编译下载调试运行效果图

4 KW41Z_FreeRTOS+外设驱动例程学习

4.1 freertos_i2c

TODO

4.2 freertos_dspi

TODO

4.3 freertos_lpuart

FreeRTOS与LPUART驱动的集成采用了NXP(原Freescale软件开发部门,被NXP收购,但是代码依然保留fsl等前缀)提供的一套类似框架的代码。 LPUART在FreeRTOS的驱动主要是fsl_lpuart_freertos.h/fsl_lpuart_freertos.c,提供了lpuart_rtos_handle_t和rtos_lpuart_config等结构体,以及LPUART_RTOS_Init(), LPUART_RTOS_Deinit(), LPUART_RTOS_Send(), LPUART_RTOS_Receive()等函数,采用了FreeRTOS的系统服务组件如Semaphore、Queue、EventGroup等,作为LPUART的读/写/中断等的消息传递机制。 /*! @brief LPUART RTOS configuration structure. */ typedef struct _lpuart_rtos_config {     LPUART_Type *base; /*!< UART base address */ uint32_t srcclk; /*!< UART source clock in Hz*/ uint32_t baudrate; /*!< Desired communication speed */     lpuart_parity_mode_t parity; /*!< Parity setting */     lpuart_stop_bit_count_t stopbits; /*!< Number of stop bits to use */ uint8_t *buffer; /*!< Buffer for background reception */ uint32_t buffer_size; /*!< Size of buffer for background reception */ } lpuart_rtos_config_t; 在ISR中断服务例程中调用回调函数LPUART_RTOS_Callback,通过设置rxEvent/txEvent的各个标志位,通知应用程序底层硬件的相关状态变化。 static void LPUART_RTOS_Callback(LPUART_Type *base, lpuart_handle_t *state, status_t status, void *param) {     lpuart_rtos_handle_t *handle = (lpuart_rtos_handle_t *)param;     BaseType_t xHigherPriorityTaskWoken, xResult;     xHigherPriorityTaskWoken = pdFALSE;     xResult = pdFAIL; if (status == kStatus_LPUART_RxIdle) {         xResult = xEventGroupSetBitsFromISR(handle->rxEvent, RTOS_LPUART_COMPLETE, &xHigherPriorityTaskWoken); } else if (status == kStatus_LPUART_TxIdle) {         xResult = xEventGroupSetBitsFromISR(handle->txEvent, RTOS_LPUART_COMPLETE, &xHigherPriorityTaskWoken); } else if (status == kStatus_LPUART_RxRingBufferOverrun) {         xResult =             xEventGroupSetBitsFromISR(handle->rxEvent, RTOS_LPUART_RING_BUFFER_OVERRUN, &xHigherPriorityTaskWoken); } else if (status == kStatus_LPUART_RxHardwareOverrun) { /* Clear Overrun flag (OR) in LPUART STAT register */         LPUART_ClearStatusFlags(base, kLPUART_RxOverrunFlag);         xResult =             xEventGroupSetBitsFromISR(handle->rxEvent, RTOS_LPUART_HARDWARE_BUFFER_OVERRUN, &xHigherPriorityTaskWoken); } if (xResult != pdFAIL) {         portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } LPUART_RTOS_Send通过获取txSemaphore才允许发送,这样防止了多个任务发送LPUART造成的混乱顺序,然后调用通过LPUART_TransferSendNonBlocking()发送,最后验证txEvent的RTOS_LPUART_COMPLETE标志位代表完成发送后才释放txSemaphore,完成通过LPUART发送数据的功能。 /*FUNCTION********************************************************************** * * Function Name : UART_RTOS_Send * Description   : Initializes the UART instance for application * *END**************************************************************************/ int LPUART_RTOS_Send(lpuart_rtos_handle_t *handle, const uint8_t *buffer, uint32_t length) {     EventBits_t ev; int retval = kStatus_Success; if (NULL == handle->base) { /* Invalid handle. */ return kStatus_Fail; } if (0 == length) { return 0; } if (NULL == buffer) { return kStatus_InvalidArgument; } if (pdFALSE == xSemaphoreTake(handle->txSemaphore, 0)) { /* We could not take the semaphore, exit with 0 data received */ return kStatus_Fail; }     handle->txTransfer.data = (uint8_t *)buffer;     handle->txTransfer.dataSize = (uint32_t)length; /* Non-blocking call */     LPUART_TransferSendNonBlocking(handle->base, handle->t_state, &handle->txTransfer);     ev = xEventGroupWaitBits(handle->txEvent, RTOS_LPUART_COMPLETE, pdTRUE, pdFALSE, portMAX_DELAY); if (!(ev & RTOS_LPUART_COMPLETE)) {         retval = kStatus_Fail; } if (pdFALSE == xSemaphoreGive(handle->txSemaphore)) { /* We could not post the semaphore, exit with error */         retval = kStatus_Fail; } return retval; } 同样,LPUART_RTOS_Receive通过获取rxSemaphore才允许接受,这样防止了多个任务同时接受LPUART造成接收到的数据混乱顺序,然后调用通过LPUART_TransferReceiveNonBlocking(),最后验证rxEvent的 RTOS_LPUART_COMPLETE | RTOS_LPUART_RING_BUFFER_OVERRUN | RTOS_LPUART_HARDWARE_BUFFER_OVERRUN标志位代表完成发送(或者缓冲区溢出)后才释放rxSemaphore,完成通过LPUART接受数据的功能。 /*FUNCTION********************************************************************** * * Function Name : LPUART_RTOS_Recv * Description   : Receives chars for the application * *END**************************************************************************/ int LPUART_RTOS_Receive(lpuart_rtos_handle_t *handle, uint8_t *buffer, uint32_t length, size_t *received) {     EventBits_t ev; size_t n = 0; int retval = kStatus_Fail; size_t local_received = 0; if (NULL == handle->base) { /* Invalid handle. */ return kStatus_Fail; } if (0 == length) { if (received != NULL) { *received = n; } return 0; } if (NULL == buffer) { return kStatus_InvalidArgument; } /* New transfer can be performed only after current one is finished */ if (pdFALSE == xSemaphoreTake(handle->rxSemaphore, portMAX_DELAY)) { /* We could not take the semaphore, exit with 0 data received */ return kStatus_Fail; }     handle->rxTransfer.data = buffer;     handle->rxTransfer.dataSize = (uint32_t)length; /* Non-blocking call */     LPUART_TransferReceiveNonBlocking(handle->base, handle->t_state, &handle->rxTransfer, &n);     ev = xEventGroupWaitBits(         handle->rxEvent, RTOS_LPUART_COMPLETE | RTOS_LPUART_RING_BUFFER_OVERRUN | RTOS_LPUART_HARDWARE_BUFFER_OVERRUN,         pdTRUE, pdFALSE, portMAX_DELAY); if (ev & RTOS_LPUART_HARDWARE_BUFFER_OVERRUN) { /* Stop data transfer to application buffer, ring buffer is still active */         LPUART_TransferAbortReceive(handle->base, handle->t_state); /* Prevent false indication of successful transfer in next call of LPUART_RTOS_Receive.            RTOS_LPUART_COMPLETE flag could be set meanwhile overrun is handled */         xEventGroupClearBits(handle->rxEvent, RTOS_LPUART_COMPLETE);         retval = kStatus_LPUART_RxHardwareOverrun;         local_received = 0; } else if (ev & RTOS_LPUART_RING_BUFFER_OVERRUN) { /* Stop data transfer to application buffer, ring buffer is still active */         LPUART_TransferAbortReceive(handle->base, handle->t_state); /* Prevent false indication of successful transfer in next call of LPUART_RTOS_Receive.            RTOS_LPUART_COMPLETE flag could be set meanwhile overrun is handled */         xEventGroupClearBits(handle->rxEvent, RTOS_LPUART_COMPLETE);         retval = kStatus_LPUART_RxRingBufferOverrun;         local_received = 0; } else if (ev & RTOS_LPUART_COMPLETE) {         retval = kStatus_Success;         local_received = length; } /* Prevent repetitive NULL check */ if (received != NULL) { *received = local_received; } /* Enable next transfer. Current one is finished */ if (pdFALSE == xSemaphoreGive(handle->rxSemaphore)) { /* We could not post the semaphore, exit with error */         retval = kStatus_Fail; } return retval; } 最后完成编译后下载调试,点击运行即可看到串口输出各类信息。并且IAR中显示对FreeRTOS系统内部Task、Queue等信息。 wpsE2C1.tmp 41  KW41Z_SDK_freertos_LPUART例程编译下载调试运行效果图

5 版本历史(Revision History)

版本号 发布时间 内容 A0 2017-05-08 初次编写