admin 管理员组

文章数量: 887021

STM32第二十九课(Freertos, HAL, cubemx,CMSIS)

来看看CMSIS_V1对Freertos的API的封装。
主要是cmsis_os.h文件。

+++++++++++++++++++++++++++++++++++++++++++++++++++++

typedef enum  {osPriorityIdle          = -3,          ///< priority: idle (lowest)osPriorityLow           = -2,          ///< priority: lowosPriorityBelowNormal   = -1,          ///< priority: below normalosPriorityNormal        =  0,          ///< priority: normal (default)osPriorityAboveNormal   = +1,          ///< priority: above normalosPriorityHigh          = +2,          ///< priority: highosPriorityRealtime      = +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 timerosTimerPeriodic         =     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 informationunion  {uint32_t                    v;     ///< message as 32-bit valuevoid                       *p;     ///< message or mail as void pointerint32_t               signals;     ///< signal flags} value;                             ///< event valueunion  {osMailQId             mail_id;     ///< mail id obtained by \ref osMailCreateosMessageQId       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的数量。

本文标签: STM32第二十九课(Freertos HAL cubemx,CMSIS)