来看看CMSIS_V1对Freertos的API的封装。
主要是cmsis_os.h文件。
+++++++++++++++++++++++++++++++++++++++++++++++++++++
typedef enum {
osPriorityIdle = -3, ///< priority: idle (lowest)
osPriorityLow = -2, ///< priority: low
osPriorityBelowNormal = -1, ///< priority: below normal
osPriorityNormal = 0, ///< priority: normal (default)
osPriorityAboveNormal = +1, ///< priority: above normal
osPriorityHigh = +2, ///< priority: high
osPriorityRealtime = +3, ///< priority: realtime (highest)
osPriorityError = 0x84 ///< system cannot determine priority or thread has illegal priority
} osPriority;
定义了任务优先级。我们通常设置的7级。
#define osWaitForever 0xFFFFFFFF
定义了MAX_TIMEOUT。
typedef enum {
osOK = 0, ///< function completed; no error or event occurred.
osEventSignal = 0x08, ///< function completed; signal event occurred.
osEventMessage = 0x10, ///< function completed; message event occurred.
osEventMail = 0x20, ///< function completed; mail event occurred.
osEventTimeout = 0x40, ///< function completed; timeout occurred.
osErrorParameter = 0x80, ///< parameter error: a mandatory parameter was missing or specified an incorrect object.
osErrorResource = 0x81, ///< resource not available: a specified resource was not available.
osErrorTimeoutResource = 0xC1, ///< resource not available within given time: a specified resource was not available within the timeout period.
osErrorISR = 0x82, ///< not allowed in ISR context: the function cannot be called from interrupt service routines.
osErrorISRRecursive = 0x83, ///< function called multiple times from ISR with same object.
osErrorPriority = 0x84, ///< system cannot determine priority or thread has illegal priority.
osErrorNoMemory = 0x85, ///< system is out of memory: it was impossible to allocate or reserve memory for the operation.
osErrorValue = 0x86, ///< value of a parameter is out of range.
osErrorOS = 0xFF, ///< unspecified RTOS error: run-time error but no other error message fits.
os_status_reserved = 0x7FFFFFFF ///< prevent from enum down-size compiler optimization.
} osStatus;
定义了状态码。
typedef enum {
osTimerOnce = 0, ///< one-shot timer
osTimerPeriodic = 1 ///< repeating timer
} os_timer_type;
定义了soft timer的类型码。
typedef TaskHandle_t osThreadId;
定义了TCB的句柄的类型。
typedef void (*os_pthread) (void const *argument);
定义了taskfunc的函数指针的类型。
typedef TimerHandle_t osTimerId;
定义了soft timer的句柄的类型。
typedef void (*os_ptimer) (void const *argument);
定义了soft timer的callback的函数指针的类型。
+++++++++++++++++++++++++++++++++++++++++++++++++++
#define osThreadDef(name, thread, priority, instances, stacksz) \
const osThreadDef_t os_thread_def_##name = \
{ #name, (thread), (priority), (instances), (stacksz), NULL, NULL }
宏拟函数,作用是填充一个TCB。包括taskfunc,优先级,taskname,stacksize,taskfuncref等。
#define osThread(name) \
&os_thread_def_##name
宏拟函数,作用是获取一个TCB的句柄。
#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
宏拟函数。作用是控制访问临界区。
osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument);
创建task,返回值是一个TCB的句柄。
osStatus osKernelStart (void);
调度器的启动函数。返回值是状态码。
uint32_t osKernelSysTick (void);
查询SYSTICK的函数。返回值是当前SYSTICK值。
osStatus osThreadTerminate (osThreadId thread_id);
删除一个task。返回值是状态码。
来看看函数实现。
osStatus osThreadTerminate (osThreadId thread_id)
{
vTaskDelete(thread_id);
return osOK;
}
其实就是对vTaskDelete的调用的二次封装。
osStatus osDelay (uint32_t millisec);
阻塞任务,并在延时到期后,解除阻塞。
来看看函数实现。
osStatus osDelay (uint32_t millisec)
{
TickType_t ticks = millisec / portTICK_PERIOD_MS;
vTaskDelay(ticks ? ticks : 1); /* Minimum delay = 1 tick */
return osOK;
}
其实就是对vTaskDelay的二次封装。
osStatus osThreadYield (void);
主动发起一次任务调度。
来看看函数实现。
osStatus osThreadYield (void)
{
taskYIELD();
return osOK;
}
其实就是对taskYIELD的二次封装。
osStatus osThreadSuspend (osThreadId thread_id);
挂起一个task。在管理者任务里调用。
来看看函数实现。
osStatus osThreadSuspend (osThreadId thread_id)
{
vTaskSuspend(thread_id);
return osOK;
}
其实就是对vTaskSuspend的二次封装。
osStatus osThreadResume (osThreadId thread_id);
恢复一个task。在管理者任务里调用。
来看看函数实现。
osStatus osThreadResume (osThreadId thread_id)
{
if(inHandlerMode())
{
if (xTaskResumeFromISR(thread_id) == pdTRUE)
{
portYIELD_FROM_ISR(pdTRUE);
}
}
else
{
vTaskResume(thread_id);
}
return osOK;
}
其实就是对vTaskResume的二次封装。
通过检查是否处于ISR中,判断是否调用fromisr版本的API。
如果是fromisr版本的API,还配套执行一次portYIELD_FROM_ISR,
如果不是处于ISR中,那么就执行常规版本的API。
++++++++++++++++++++++++++++++++++++++++++++++++++++
#define osTimerDef(name, function) \
const osTimerDef_t os_timer_def_##name = \
{ (function), NULL }
宏拟函数,作用是填充一个STDB,包括callback,callbackref等。
#define osTimer(name) \
&os_timer_def_##name
宏拟函数,作用是获取一个STDB的句柄。
osTimerId osTimerCreate (const osTimerDef_t *timer_def, os_timer_type type, void *argument);
创建一个softtimer。返回值是一个soft timer的句柄。
osStatus osTimerDelete (osTimerId timer_id);
删除一个soft timer。返回值是状态码。
osStatus osTimerStart (osTimerId timer_id, uint32_t millisec);
启动一个soft timer。返回值是状态码。
osStatus osTimerStop (osTimerId timer_id);
停止一个soft timer,返回值是状态码。
+++++++++++++++++++++++++++++++++++++++++++++++++++++
static int inHandlerMode (void)
{
return __get_IPSR() != 0;
}
#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) portYIELD()
#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x )
在很多CMSIS封装的API中,都会出现这几个函数。
它们就是进行上下文判断,如果是ISR中,就调用fromisr版本的API,
如果不是ISR中,就调用常规版本的API。
+++++++++++++++++++++++++++++++++++++++++++++++++++
typedef QueueHandle_t osMessageQId;
定义了queue的句柄的类型。
typedef struct QueueDefinition * QueueHandle_t;
typedef struct QueueDefinition xQUEUE;
typedef xQUEUE Queue_t;
所以,在CMSIS下,osMessageQId,是一个指针,是一个Queue的句柄。
const osMessageQDef_t os_messageQ_def_##name = \
{ (queue_sz), sizeof (type), NULL, NULL }
宏拟函数,用来填充一个MsgQDB。
#define osMessageQ(name) \
&os_messageQ_def_##name
宏拟函数,用来获取一个MsgQDB的句柄。
osMessageQId osMessageCreate (const osMessageQDef_t *queue_def, osThreadId thread_id);
创建一个MsgQDB,并返回MsgQDB的句柄。
osStatus osMessageDelete (osMessageQId queue_id);
删除一个MsgQDB,返回值是状态码。
来看看函数实现。
osStatus osMessageDelete (osMessageQId queue_id)
{
if (inHandlerMode()) {
return osErrorISR;
}
vQueueDelete(queue_id);
return osOK;
}
其实就是对vQueueDelete的二次封装。
osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec);
向一个MsgQ中,填充一个Msg。返回值是状态码。
来看看函数实现。
osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec)
{
portBASE_TYPE taskWoken = pdFALSE;
TickType_t ticks;
...
if (inHandlerMode()) {
if (xQueueSendFromISR(queue_id, &info, &taskWoken) != pdTRUE) {
return osErrorOS;
}
portEND_SWITCHING_ISR(taskWoken);
}
else {
if (xQueueSend(queue_id, &info, ticks) != pdTRUE) {
return osErrorOS;
}
}
return osOK;
}
其实内部就是通过检查是否处于ISR中,判断是否调用fromisr版本的API。
如果是fromisr版本的API,还配套执行一次portEND_SWITCHING_ISR,
如果不是处于ISR中,那么就执行常规版本的API。
注意,如果要使用osMessagePut ,传递的info,那么就必须是uint32_t类型。MsgQ会根据ItemSize,从info中取出需要的Byte。如果Msg的长度大于4个byte,那么就需要使用传址方式了。将sendbuffer的基地址,作为info传递。
osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec);
从一个MsgQ中,获取一个Msg。返回
来看看函数实现。
osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec)
{
portBASE_TYPE taskWoken;
TickType_t ticks;
osEvent event;
event.def.message_id = queue_id;
event.value.v = 0;
taskWoken = pdFALSE;
...
if (inHandlerMode()) {
if (xQueueReceiveFromISR(queue_id, &event.value.v, &taskWoken) == pdTRUE) {
/* We have mail */
event.status = osEventMessage;
}
else {
event.status = osOK;
}
portEND_SWITCHING_ISR(taskWoken);
}
else {
if (xQueueReceive(queue_id, &event.value.v, ticks) == pdTRUE) {
/* We have mail */
event.status = osEventMessage;
}
else {
event.status = (ticks == 0) ? osOK : osEventTimeout;
}
}
return event;
}
其实内部就是通过检查是否处于ISR中,判断是否调用fromisr版本的API。
如果是fromisr版本的API,还配套执行一次portEND_SWITCHING_ISR,
如果不是处于ISR中,那么就执行常规版本的API。
返回值是一个osEvent类型的变量。返回值中,event.status携带了状态码,event.value.v则携带了Msg的内容。
注意,如果要使用osMessagePut ,传递的info,那么就必须是uint32_t类型。MsgQ会根据ItemSize,从info中取出需要的Byte。如果Msg的长度大于4个byte,那么就需要使用传址方式了。将sendbuffer的基地址,作为info传递。
来看看event的定义。
typedef struct {
osStatus status; ///< status code: event or error information
union {
uint32_t v; ///< message as 32-bit value
void *p; ///< message or mail as void pointer
int32_t signals; ///< signal flags
} value; ///< event value
union {
osMailQId mail_id; ///< mail id obtained by \ref osMailCreate
osMessageQId message_id; ///< message id obtained by \ref osMessageCreate
} def; ///< event definition
} osEvent;
这是一个结构体对象,并且使用了struct-union技巧。
成员变量value和成员变量def,都是Union。
当我们用成员索引运算符".“或者”->"选择union的member时,本质上,是选取了对union的类型的解析方式,
换句话说,当我们对union使用了成员索引运算符时,实际上是执行了强制类型转换操作。
所以,在表达式event.value.v中,value.v这个子表达式,就将value强制转换成uint32_t类型。
osEvent osMessagePeek (osMessageQId queue_id, uint32_t millisec);
探取但是并不取走Msg。
注意,如果要使用osMessagePut ,传递的info,那么就必须是uint32_t类型。MsgQ会根据ItemSize,从info中取出需要的Byte。如果Msg的长度大于4个byte,那么就需要使用传址方式了。将sendbuffer的基地址,作为info传递。
来看看函数实现。
osEvent osMessagePeek (osMessageQId queue_id, uint32_t millisec)
{
TickType_t ticks;
osEvent event;
event.def.message_id = queue_id;
...
if (xQueuePeek(queue_id, &event.value.v, ticks) == pdTRUE)
{
/* We have mail */
event.status = osEventMessage;
}
else
{
event.status = (ticks == 0) ? osOK : osEventTimeout;
}
return event;
}
其实内部就是通过检查是否处于ISR中,判断是否调用fromisr版本的API。
如果是fromisr版本的API,还配套执行一次portEND_SWITCHING_ISR,
如果不是处于ISR中,那么就执行常规版本的API。
++++++++++++++++++++++++++++++++++++++++++++++++++++
uint32_t osMessageWaiting(osMessageQId queue_id);
判断MsgQ中,有多少Msg等待被取走。返回值是有效的msg的数量。
来看看函数实现。
uint32_t osMessageWaiting(osMessageQId queue_id)
{
if (inHandlerMode()) {
return uxQueueMessagesWaitingFromISR(queue_id);
}
else
{
return uxQueueMessagesWaiting(queue_id);
}
}
函数中会判断上下文是否是ISR,并调用对应的fromisr版本的API。
来看看uxQueueMessagesWaiting函数实现。
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue )
{
UBaseType_t uxReturn;
configASSERT( xQueue );
taskENTER_CRITICAL();
{
uxReturn = ( ( Queue_t * ) xQueue )->uxMessagesWaiting;
}
taskEXIT_CRITICAL();
return uxReturn;
}
typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */
{
...
volatile UBaseType_t uxMessagesWaiting;/*< The number of items currently in the queue. */
...
} xQUEUE;
可见,该函数返回值是有效Msg的数量。
uint32_t osMessageAvailableSpace(osMessageQId queue_id);
判断MsgQ中,有多少MsgSpace可以用来填充。返回值是有效的MsgSpace的数量。
来看看函数实现。
uint32_t osMessageAvailableSpace(osMessageQId queue_id)
{
return uxQueueSpacesAvailable(queue_id);
}
就是对uxQueueSpacesAvailable的二次封装。
来看看uxQueueSpacesAvailable的函数实现。
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue )
{
UBaseType_t uxReturn;
Queue_t * const pxQueue = xQueue;
configASSERT( pxQueue );
taskENTER_CRITICAL();
{
uxReturn = pxQueue->uxLength - pxQueue->uxMessagesWaiting;
}
taskEXIT_CRITICAL();
return uxReturn;
}
可见,该函数返回值是有效MsgSpace的数量。
++++++++++++++++++++++++++++++++++++++++++++++++
typedef SemaphoreHandle_t osSemaphoreId;
定义了semaph的句柄的类型。
#define osSemaphoreDef(name) \
const osSemaphoreDef_t os_semaphore_def_##name = { 0, NULL }
宏拟函数,作用是填充一个SemDB。
#define osSemaphore(name) \
&os_semaphore_def_##name
宏拟函数,作用是获取SemDB的句柄。
osSemaphoreId osSemaphoreCreate (const osSemaphoreDef_t *semaphore_def, int32_t count);
创建一个semaph,返回值是状态码。
osStatus osSemaphoreDelete (osSemaphoreId semaphore_id);
删除一个semaph,返回值是状态码。
int32_t osSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec);
在一个semaph上阻塞,然后等待,直到get semaph或者timeout,就解除阻塞,并返回,返回值是有效的token的数量。
来看看函数实现。
int32_t osSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec)
{
TickType_t ticks;
portBASE_TYPE taskWoken = pdFALSE;
...
if (inHandlerMode()) {
if (xSemaphoreTakeFromISR(semaphore_id, &taskWoken) != pdTRUE) {
return osErrorOS;
}
portEND_SWITCHING_ISR(taskWoken);
}
else if (xSemaphoreTake(semaphore_id, ticks) != pdTRUE) {
return osErrorOS;
}
return osOK;
}
其实内部就是通过检查是否处于ISR中,判断是否调用fromisr版本的API。
如果是fromisr版本的API,还配套执行一次portEND_SWITCHING_ISR,
如果不是处于ISR中,那么就执行常规版本的API。
osStatus osSemaphoreRelease (osSemaphoreId semaphore_id);
释放掉一个semaph,并将阻塞在这个semaph上的task解除阻塞。返回值是状态码。
来看看函数实现。
osStatus osSemaphoreRelease (osSemaphoreId semaphore_id)
{
osStatus result = osOK;
portBASE_TYPE taskWoken = pdFALSE;
if (inHandlerMode()) {
if (xSemaphoreGiveFromISR(semaphore_id, &taskWoken) != pdTRUE) {
return osErrorOS;
}
portEND_SWITCHING_ISR(taskWoken);
}
else {
if (xSemaphoreGive(semaphore_id) != pdTRUE) {
result = osErrorOS;
}
}
return result;
}
其实内部就是通过检查是否处于ISR中,判断是否调用fromisr版本的API。
如果是fromisr版本的API,还配套执行一次portEND_SWITCHING_ISR,
如果不是处于ISR中,那么就执行常规版本的API。
+++++++++++++++++++++++++++++++++++++++++++++++
typedef SemaphoreHandle_t osMutexId;
定义了mutex的句柄的类型。
#define osMutexDef(name) \
const osMutexDef_t os_mutex_def_##name = { 0, NULL }
宏拟函数,作用是填充一个MutDB。
#define osMutex(name) \
&os_mutex_def_##name
宏拟函数,作用是获取一个MutDB的句柄。
osMutexId osMutexCreate (const osMutexDef_t *mutex_def);
创建一个mutex,返回值是状态码。
osStatus osMutexDelete (osMutexId mutex_id);
删除一个mutex。返回值是状态码。
osStatus osMutexWait (osMutexId mutex_id, uint32_t millisec);
在一个mutex上阻塞,然后等待,直到get mutex或者timeout,就解除阻塞,并返回,返回值是状态码。
来看看函数实现。
osStatus osMutexWait (osMutexId mutex_id, uint32_t millisec)
{
TickType_t ticks;
portBASE_TYPE taskWoken = pdFALSE;
...
if (inHandlerMode()) {
if (xSemaphoreTakeFromISR(mutex_id, &taskWoken) != pdTRUE) {
return osErrorOS;
}
portEND_SWITCHING_ISR(taskWoken);
}
else if (xSemaphoreTake(mutex_id, ticks) != pdTRUE) {
return osErrorOS;
}
return osOK;
}
其实内部就是通过检查是否处于ISR中,判断是否调用fromisr版本的API。
如果是fromisr版本的API,还配套执行一次portEND_SWITCHING_ISR,
如果不是处于ISR中,那么就执行常规版本的API。
osStatus osMutexRelease (osMutexId mutex_id);
释放掉一个mutex,并将阻塞在这个mutex上的task解除阻塞。返回值是状态码。
来看看函数实现。
osStatus osMutexRelease (osMutexId mutex_id)
{
osStatus result = osOK;
portBASE_TYPE taskWoken = pdFALSE;
if (inHandlerMode()) {
if (xSemaphoreGiveFromISR(mutex_id, &taskWoken) != pdTRUE) {
return osErrorOS;
}
portEND_SWITCHING_ISR(taskWoken);
}
else if (xSemaphoreGive(mutex_id) != pdTRUE)
{
result = osErrorOS;
}
return result;
}
其实内部就是通过检查是否处于ISR中,判断是否调用fromisr版本的API。
如果是fromisr版本的API,还配套执行一次portEND_SWITCHING_ISR,
如果不是处于ISR中,那么就执行常规版本的API。
++++++++++++++++++++++++++++++++++++++++++
如果使用递归互斥量,那么有对应的API。
osMutexId osRecursiveMutexCreate (const osMutexDef_t *mutex_def);
创建一个递归互斥量。返回一个RMutDB的句柄。
osStatus osRecursiveMutexWait (osMutexId mutex_id, uint32_t millisec);
在一个mutex上阻塞,然后等待,直到get mutex或者timeout,就解除阻塞,并返回,返回值是状态码。
osStatus osMutexDelete (osMutexId mutex_id);
删除一个mutex。返回值是状态码。
osStatus osRecursiveMutexWait (osMutexId mutex_id, uint32_t millisec);
在一个mutex上阻塞,然后等待,直到get mutex或者timeout,就解除阻塞,并返回,返回值是状态码。
如果递归申请,则对RecurMutex进行计数加一。
osStatus osRecursiveMutexRelease (osMutexId mutex_id);
释放掉一个mutex,并将阻塞在这个mutex上的task解除阻塞。返回值是状态码。
如果递归释放,则对RecurMutex进行计数减一。
uint32_t osSemaphoreGetCount(osSemaphoreId semaphore_id);
获取semaph的当前计数值。
来看看函数实现。
uint32_t osSemaphoreGetCount(osSemaphoreId semaphore_id)
{
return uxSemaphoreGetCount(semaphore_id);
}
#define uxSemaphoreGetCount( xSemaphore ) uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) )
其实就是对uxSemaphoreGetCount的二次封装。底层,就是查询MsgQ中有效的Msg的数量。