KW41Z_FreeRTOS學習筆記

KW41Z_FreeRTOS學習筆記 架構

文檔編號app

TN_TEMPLATE0101_A0框架

關鍵字less

FreeRTOS, MCU, Embedded System, KW41Zide

摘要函數

本技術筆記對KW41Z_FreeRTOS學習筆記進行說明工具

Mars4zhupost


目 錄學習

1 總述 1ui

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-KW41Z\boards\frdmkw41z\rtos_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-KW41Z\boards\frdmkw41z\rtos_examples\freertos_hello\iar\freertos_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:\software\gcc_arm_none_eabi\bin;D:\software\cmake\bin;D:\software\mingw\bin

set ARMGCC_DIR=D:\software\gcc_arm_none_eabi

Build_debug.bat

便可生成gcc編譯器支持的elf格式文件,爲了方便下載,使用objcopy將elf轉化爲hex文件,命令以下:

arm-none-eabi-objcopy -O ihex debug\freertos_hello.elf debug\freertos_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.\r\n");

        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.\r\n");

}

else if ((event_bits & B1) == B1)

{

            PRINTF("Bit B1 is set.\r\n");

}

}

}

完成編譯後下載調試,點擊運行便可串口輸出相關信息。

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\r\n");

        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\r\n");

        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\r\n", 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.\r\n");

        vTaskSuspend(NULL);

}

    xSemaphore_consumer = xSemaphoreCreateBinary();

if (xSemaphore_consumer == NULL)

{

        PRINTF("xSemaphore_consumer creation failed.\r\n");

        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.\r\n");

}

else

{

            PRINTF("Producer is waiting for customer.\r\n");

}

}

}

/*!

* @brief Task consumer_task.

*/

static void consumer_task(void *pvParameters)

{

    PRINTF("Consumer number: %d\r\n", 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.\r\n", pvParameters);

}

else

{

            PRINTF("Consumer %d is waiting for producer.\r\n", 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.\r\n");

}

完成編譯後下載調試,點擊運行便可串口輸出相關信息。同時可見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.\r\n", 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.\r\n");

}

}

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

初次編寫

相關文章
相關標籤/搜索