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

来看看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的数量。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值