一、FreeRTOS之使用初体验
1. FreeRTOS之全局配置文件FreeRTOSConfig.h
对于全新的RTOS首先第一步首先需要会用才能深度学习其底层实现,所以本章节我们先来看一下FreeRTOS最重要的全局配置文件:FreeRTOSConfig.h
本章节以官方/FreeRTOS/Demo/CORTEX_STM32F103_Keil 为例说明:
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#define configUSE_PREEMPTION 1 //使能抢占调度器
#define configUSE_IDLE_HOOK 0 //不使能空闲任务钩子函数
#define configUSE_TICK_HOOK 0 //不时间片钩子函数
#define configCPU_CLOCK_HZ ( ( unsigned long ) 72000000 ) //设置CPU运行频率
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) //设置调度周期频率1000hz->10ms
#define configMAX_PRIORITIES ( 5 )
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) //任务最小栈大小128字 = 128*4 Bytes
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) ) //设置堆大小
#define configMAX_TASK_NAME_LEN ( 16 )
#define configUSE_TRACE_FACILITY 0
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1 //空闲任务让出时间片给同优先级任务,建议关闭
/* 协程相关,逐步废弃 */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
#define INCLUDE_vTaskPrioritySet 1 //使能设置优先级API
#define INCLUDE_uxTaskPriorityGet 1 //使能获取优先级API
#define INCLUDE_vTaskDelete 1 //使能删除任务API
#define INCLUDE_vTaskCleanUpResources 0 //
#define INCLUDE_vTaskSuspend 1 //使能任务挂起API
#define INCLUDE_vTaskDelayUntil 1 //
#define INCLUDE_vTaskDelay 1 //使能设置任务延迟API
/* This is the raw value as per the Cortex-M3 NVIC. Values can be 255
(lowest) to 0 (1?) (highest). */
#define configKERNEL_INTERRUPT_PRIORITY 255
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 /* equivalent to 0xb0, or priority 11. */
/* This is the value being used as per the ST library which permits 16
priority values, 0 to 15. This must correspond to the
configKERNEL_INTERRUPT_PRIORITY setting. Here 15 corresponds to the lowest
NVIC value of 255. */
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15
#endif /* FREERTOS_CONFIG_H */
2. FreeRTOS之创建任务
知道FreeRTOS的全局配置都是干什么的以后,来到了我们最重要的部分,使用rtos就是为了使用多任务,本小节就看如何使用创建任务的API来创建多个不同的任务。
FreeRTOS 任务不允许以任何方式从实现函数中返回,也不能执行到函数末尾。如果一个任务不再需要,可以显式地将其删
除。
任务的原型为:
void ATaskFunction( void *pvParameters )
{
while(1){
vTaskDelay(pdMS_TO_TICKS(10UL)); //延迟10ms
}
vTaskDelete( NULL );
}
其中pvParameters为给该任务传递的参数。
创建任务的API为xTaskCreate:
portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode,
const signed portCHAR * const pcName,
unsigned portSHORT usStackDepth,
void *pvParameters,
unsigned portBASE_TYPE uxPriority,
xTaskHandle *pxCreatedTask );
pvTaskCode: 任务函数
pcName: 任务名称,调试可用
usStackDepth: 任务堆栈为多少字,先非配大点,后续可以通过系统提供的接口查询实际使用的栈大小
pvParameters: 任务参数
uxPriority: 任务优先级
xTaskHandle:任务句柄,后续可用于删除任务等操作
3. FreeRTOS之使用队列完成任务通信
可以创建任务以后我们就有了一个个小程序,通常这些程序需要能够互相通信起来才会发挥大作用。这里介绍最常用的通信手段 队列。
队列可以存储有限确定长度的数据单元。可被多任务存取,读写都可以阻塞。
3.1创建队列的API函数为xQueueCreate:
xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength,
unsigned portBASE_TYPE uxItemSize );
uxQueueLength:队列能够存储的最大单元数目,即队列深度
uxItemSize:队列中数据单元的长度,以字节为单位。
xQueueHandle:创建成功的队列句柄
3.2发送数据到队列API:
xQueueSendToBack()和xQueueSendToFront()
portBASE_TYPE xQueueSendToFront( xQueueHandle xQueue,
const void * pvItemToQueue,
portTickType xTicksToWait );
portBASE_TYPE xQueueSendToBack( xQueueHandle xQueue,
const void * pvItemToQueue,
portTickType xTicksToWait );
pvItemToQueue:发送数据的指针。其指向将要复制到目标队列中的数据单元。拷贝的长度为uxItemSize
xTicksToWait:阻塞超时时间。
3.3接受队列数据API:
xQueueReceive()与 xQueuePeek()
xQueueReceive 用于从队列中接收(读取)数据单元。接收到的单元同时会从队列中删除。
xQueuePeek 也是从从队列中接收数据单元,不同的是并不从队列中删出接收到的单元。
portBASE_TYPE xQueueReceive( xQueueHandle xQueue,
const void * pvBuffer,
portTickType xTicksToWait );
portBASE_TYPE xQueuePeek( xQueueHandle xQueue,
const void * pvBuffer,
portTickType xTicksToWait );
pvBuffer:接收缓存指针。其指向一段内存区域,用于接收从队列中拷贝来的数据。
xTicksToWait:阻塞超时时间。
3.4查询队列数据个数API:
uxQueueMessagesWaiting()
unsigned portBASE_TYPE uxQueueMessagesWaiting( xQueueHandle xQueue );
3.5 常用接受队列数据代码结构
接受数据侧:
void ATaskFunction( void *pvParameters )
{
while(1){
if( uxQueueMessagesWaiting( xQueue ) != 0 )
{
vPrintString( "Queue should have been empty!\r\n" );
}
xStatus = xQueueReceive( xQueue, &lReceivedValue, xTicksToWait );
if( xStatus == pdPASS )
vPrintStringAndNumber( "Received = ", lReceivedValue );
else
vPrintString( "Could not receive from the queue.\r\n" );
vTaskDelay(pdMS_TO_TICKS(10UL)); //延迟10ms
}
vTaskDelete( NULL );
}