LPC5411x_FreeRTOS学习笔记

LPC5411x_FreeRTOS学习笔记

文档编号

TN_TEMPLATE0101_A0

关键字

FreeRTOS, MCU, Embedded System,

摘要

本技术笔记对LPC5411x_FreeRTOS学习笔记进行说明

Mars4zhu


目 录

1 总述 1

2 LPCOpen的FreeRTOS范例学习 1

3 SDK_LPC5411x的FreeRTOS范例学习 2

3.1 freertos_hello范例学习 2

3.2 Freertos_event范例学习 3

3.3 freertos_mutex范例学习 7

3.4 freertos_queue范例学习 9

3.5 freertos_sem范例学习 11

3.6 freertos_swtimer范例学习 13

3.7 freertos_tickless 14

3.8 freertos_generic范例学习 16

3.8.1 消息队列与任务管理 16

3.8.2 信号量消息管理 18

3.8.3 定时器管理 19

4 LPC5411x_FreeRTOS+外设驱动例程学习 21

4.1 freertos_i2c 21

4.2 freertos_spi 21

4.3 freertos_usart 21

5 版本历史(Revision History) 28


插图索引

图 21   LPC5411x_LPCOpen_freertos_blinky例程编译下载调试运行效果图 2

图 31   LPC5411x_SDK_freertos_hello例程编译下载调试运行效果图 3

图 32   IAR选中FreeRTOS and OpenRTOS选项 6

图 33   LPC5411x_SDK_freertos_event例程编译下载调试运行效果图 6

图 34   LPC5411x_SDK_freertos_mutex例程编译下载调试运行效果图 8

图 35   LPC5411x_SDK_freertos_mutex例程取消互斥量的运行效果图 8

图 36   LPC5411x_SDK_freertos_queue例程的运行效果图 11

图 37   LPC5411x_SDK_freertos_sem例程量的运行效果图 13

图 37   LPC5411x_SDK_freertos_swtimer例程量的运行效果图 14

图 38   LPC5411x_SDK_freertos_tickless例程编译下载调试运行效果图 16

图 38   LPC5411x_SDK_freertos_generic例程编译下载调试运行效果图 21

图 38   LPC5411x_SDK_freertos_usart例程编译下载调试运行效果图 27


表格索引

错误!未找到目录项。

1 总述

FreeRTOS是广泛使用的RTOS之一。LPC5411x系列MCU的LPCOpen以及SDK软件代码库提供了FreeRTOS的例程,本学习笔记采用截至目前(2017-05-07)最新版(LPCOpen_V3.01, SDK_V2.2)的代码、学习采用FreeRTOS开发LPC5411x系列MCU的应用。

LPC5411x的官方代码LPCOpen和SDK都FreeRTOS的范例代码,其中LPCOpen只是简单的一个Blinky例程。

SDK则提供了更多的范例,位于SDK_2.2_LPC54114J256\boards\lpcxpresso54114\rtos_examples目录下。

2 LPCOpen的FreeRTOS范例学习

使用相应的开发工具打开\lpc5411x_xpresso54114\lpc5411x\prj_xpresso54114\iar\examples_freertos.eww(或keil\freertos_blinky.uvprojx等其他工具的工程项目文件),

该例程创建两个分别按照不同的周期执行的LED亮灭的任务,和一个UART输出tickCnt计数值。

/* LED1 toggle thread */

static void vLEDTask1(void *pvParameters) {

bool LedState = false;

while (1) {

Board_LED_Set(0, LedState);

LedState = (bool) !LedState;

/* About a 3Hz on/off toggle rate */

vTaskDelay(configTICK_RATE_HZ / 6);

}

}

/* LED2 toggle thread */

static void vLEDTask2(void *pvParameters) {

bool LedState = false;

while (1) {

Board_LED_Set(1, LedState);

LedState = (bool) !LedState;

/* About a 7Hz on/off toggle rate */

vTaskDelay(configTICK_RATE_HZ / 14);

}

}

/* UART (or output) thread */

static void vUARTTask(void *pvParameters) {

int tickCnt = 0;

while (1) {

DEBUGOUT("Tick: %d \r\n", tickCnt);

tickCnt++;

/* About a 1s delay here */

vTaskDelay(configTICK_RATE_HZ);

}

}

完成编译后下载调试,点击运行即可看到两个LED在来回闪同时串口输出Tick计数信息。

LPC5411x_FreeRTOS学习笔记_第1张图片

21  LPC5411x_LPCOpen_freertos_blinky例程编译下载调试运行效果图

3 SDK_LPC5411x的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.”信息。

LPC5411x_FreeRTOS学习笔记_第2张图片

31  LPC5411x_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");

}

}

}

完成编译后下载调试,点击运行即可串口输出相关信息。

其中在IAR中可以在Options->Debugs->Plugins中选中FreeRTOS and OpenRTOS选项,这样在调试中就可以通过这个插件查看FreeRTOS中的任务Task、队列Queue等系统组件的信息。如下图:

LPC5411x_FreeRTOS学习笔记_第3张图片

32  IAR选中FreeRTOS and OpenRTOS选项

LPC5411x_FreeRTOS学习笔记_第4张图片

33  LPC5411x_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();

}

}

完成编译后下载调试,点击运行即可串口输出相关信息。

LPC5411x_FreeRTOS学习笔记_第5张图片

34  LPC5411x_SDK_freertos_mutex例程编译下载调试运行效果图

如果注释掉xMutex的获取和释放之后,则任务执行乱序,输出的字符串就有了混杂。

LPC5411x_FreeRTOS学习笔记_第6张图片

35  LPC5411x_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++;

}

}

完成编译后下载调试,点击运行即可串口输出相关信息。

LPC5411x_FreeRTOS学习笔记_第7张图片

36  LPC5411x_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);

}

}

}

完成编译后下载调试,点击运行即可串口输出相关信息。

LPC5411x_FreeRTOS学习笔记_第8张图片

37  LPC5411x_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任务中。

LPC5411x_FreeRTOS学习笔记_第9张图片

38  LPC5411x_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.)

LPC5411x_FreeRTOS学习笔记_第10张图片

39  LPC5411x_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等信息。

LPC5411x_FreeRTOS学习笔记_第11张图片

310  LPC5411x_SDK_freertos_generic例程编译下载调试运行效果图

4 LPC5411x_FreeRTOS+外设驱动例程学习

4.1 freertos_i2c

TODO

4.2 freertos_spi

TODO

4.3 freertos_usart

FreeRTOS与USART驱动的集成采用了NXP(原Freescale软件开发部门,被NXP收购,但是代码依然保留fsl等前缀)提供的一套类似框架的代码。

USART在FreeRTOS的驱动主要是fsl_usart_freertos.h/fsl_usart_freertos.c,提供了usart_rtos_handle_t和rtos_usart_config等结构体,以及USART_RTOS_Init(), USART_RTOS_Deinit(), USART_RTOS_SendI(), USART_RTOS_Receive()等函数,采用了FreeRTOS的系统服务组件如Semaphore、Queue、EventGroup等,作为USART的读/写/中断等的消息传递机制。

/*! @brief FLEX USART FreeRTOS handle */

typedef struct _usart_rtos_handle

{

    USART_Type *base; /*!< USART base address */

    usart_transfer_t txTransfer; /*!< TX transfer structure */

    usart_transfer_t rxTransfer; /*!< RX transfer structure */

    SemaphoreHandle_t rxSemaphore; /*!< RX semaphore for resource sharing */

    SemaphoreHandle_t txSemaphore; /*!< TX semaphore for resource sharing */

#define RTOS_USART_COMPLETE 0x1

#define RTOS_USART_RING_BUFFER_OVERRUN 0x2

#define RTOS_USART_HARDWARE_BUFFER_OVERRUN 0x4

    EventGroupHandle_t rxEvent; /*!< RX completion event */

    EventGroupHandle_t txEvent; /*!< TX completion event */

void *t_state; /*!< Transactional state of the underlying driver */

} usart_rtos_handle_t;

在ISR中断服务例程中调用回调函数USART_RTOS_Callback,通过设置rxEvent/txEvent的各个标志位,通知应用程序底层硬件的相关状态变化。

static void USART_RTOS_Callback(USART_Type *base, usart_handle_t *state, status_t status, void *param)

{

    usart_rtos_handle_t *handle = (usart_rtos_handle_t *)param;

    BaseType_t xHigherPriorityTaskWoken, xResult;

    xHigherPriorityTaskWoken = pdFALSE;

    xResult = pdFAIL;

if (status == kStatus_USART_RxIdle)

{

        xResult = xEventGroupSetBitsFromISR(handle->rxEvent, RTOS_USART_COMPLETE, &xHigherPriorityTaskWoken);

}

else if (status == kStatus_USART_TxIdle)

{

        xResult = xEventGroupSetBitsFromISR(handle->txEvent, RTOS_USART_COMPLETE, &xHigherPriorityTaskWoken);

}

else if (status == kStatus_USART_RxRingBufferOverrun)

{

        xResult = xEventGroupSetBitsFromISR(handle->rxEvent, RTOS_USART_RING_BUFFER_OVERRUN, &xHigherPriorityTaskWoken);

}

else if (status == kStatus_USART_RxRingBufferOverrun)

{

/* Clear Overrun flag (OR) in USART S1 register */

// USART_ClearStatusFlags(base, kStatus_USART_RxRingBufferOverrun);

// xResult =

//    xEventGroupSetBitsFromISR(handle->rxEvent, RTOS_USART_HARDWARE_BUFFER_OVERRUN,

//    &xHigherPriorityTaskWoken);

}

if (xResult != pdFAIL)

{

        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

}

}

USART_RTOS_Send通过获取txSemaphore才允许发送,这样防止了多个任务发送USART造成的混乱顺序,然后调用通过USART_TransferSendNonBlocking()发送,最后验证txEvent的RTOS_USART_COMPLETE标志位代表完成发送后才释放txSemaphore,完成通过USART发送数据的功能。

/*FUNCTION**********************************************************************

*

* Function Name : USART_RTOS_Send

* Description   : Initializes the USART instance for application

*

*END**************************************************************************/

int USART_RTOS_Send(usart_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 */

    USART_TransferSendNonBlocking(handle->base, handle->t_state, &handle->txTransfer);

    ev = xEventGroupWaitBits(handle->txEvent, RTOS_USART_COMPLETE, pdTRUE, pdFALSE, portMAX_DELAY);

if (!(ev & RTOS_USART_COMPLETE))

{

        retval = kStatus_Fail;

}

if (pdFALSE == xSemaphoreGive(handle->txSemaphore))

{

/* We could not post the semaphore, exit with error */

        retval = kStatus_Fail;

}

return retval;

}

同样,USART_RTOS_Receive通过获取rxSemaphore才允许接受,这样防止了多个任务同时接受USART造成接收到的数据混乱顺序,然后调用通过USART_TransferReceiveNonBlocking(),最后验证rxEvent的 RTOS_USART_COMPLETE | RTOS_USART_RING_BUFFER_OVERRUN | RTOS_USART_HARDWARE_BUFFER_OVERRUN标志位代表完成发送(或者缓冲区溢出)后才释放rxSemaphore,完成通过USART接受数据的功能。

/*FUNCTION**********************************************************************

*

* Function Name : USART_RTOS_Recv

* Description   : Receives chars for the application

*

*END**************************************************************************/

int USART_RTOS_Receive(usart_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 */

    USART_TransferReceiveNonBlocking(handle->base, handle->t_state, &handle->rxTransfer, &n);

    ev = xEventGroupWaitBits(handle->rxEvent,

                             RTOS_USART_COMPLETE | RTOS_USART_RING_BUFFER_OVERRUN | RTOS_USART_HARDWARE_BUFFER_OVERRUN,

                             pdTRUE, pdFALSE, portMAX_DELAY);

if (ev & RTOS_USART_HARDWARE_BUFFER_OVERRUN)

{

/* Stop data transfer to application buffer, ring buffer is still active */

// USART_TransferAbortReceive(handle->base, handle->t_state);

/* Prevent false indication of successful transfer in next call of USART_RTOS_Receive.

           RTOS_USART_COMPLETE flag could be set meanwhile overrun is handled */

        xEventGroupClearBits(handle->rxEvent, RTOS_USART_COMPLETE);

        retval = kStatus_USART_RxRingBufferOverrun;

        local_received = 0;

}

else if (ev & RTOS_USART_RING_BUFFER_OVERRUN)

{

/* Stop data transfer to application buffer, ring buffer is still active */

// USART_TransferAbortReceive(handle->base, handle->t_state);

/* Prevent false indication of successful transfer in next call of USART_RTOS_Receive.

           RTOS_USART_COMPLETE flag could be set meanwhile overrun is handled */

        xEventGroupClearBits(handle->rxEvent, RTOS_USART_COMPLETE);

        retval = kStatus_USART_RxRingBufferOverrun;

        local_received = 0;

}

else if (ev & RTOS_USART_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等信息。

LPC5411x_FreeRTOS学习笔记_第12张图片

41  LPC5411x_SDK_freertos_usart例程编译下载调试运行效果图

5 版本历史(Revision History)

版本号

发布时间

内容

A0

2017-05-08

初次编写

你可能感兴趣的:(LPC5411x_FreeRTOS学习笔记)