最近发现一个好东西,可以用一种全新的接口,0基础上手freeRTOS系统,
对,没错,0基础上手freeRTOS系统,那就是CMSIS_RTOS标准接口,其实是把 freeRTOS 中间件封装成符合 cmsis 标准的接口,好处也太很明显了:
1, cmsis_os 接口很好用,不要多少基础就能自己使用,0基础使用也不难。稍后看代码示例
2, 用户使用更方便了,使用同一套接口可以在所有RTOS 上编程。本来就应该是这样的,操作系统只是一种实现,多种实现方式多个RTOS,应用层面只关注RTOS 的功能,调用标准的与具体实现无关的cmsis_os 接口,具体怎么实现以及RTOS的接口,那是RTOS的事情。愿景还是很美好的,现实还是需要学习freeRTOS代码,可是cmsis_os 是会给你精神希望的,哈哈。
3, 从freeRTOS迁移到另一个 RTOS更方便,只要使用cmsis接口写的用户代码不用动,直接到cobemx中去掉当前的 freeRTOS,勾选其他 RTOS后生成工程,直接编译即可。如果cube库支持其他RTOS的话,当前还不支持。
进入本文主题,看怎么用这一套接口。
本文按照cmsis功能模块分成多个部分,分别列出各个部分接口,和使用方法,后面还有示例代码。
本文中的进程/线程/任务,都是一个概念,等同freeRTOS的 task,cmsis 中的 thread。
本文注解全部是笔者自己做的,表述不一定严谨,仅供参考。
任务有关的接口:
osThreadId osThreadCreate
(const osThreadDef_t *thread_def, void *argument); //创建进程
osStatus osThreadTerminate
(osThreadId thread_id); //终止进程
osThreadId osThreadGetId
(void) //获取进程id/句柄/handler,和 Linux getpid差不多
osPriority osThreadGetPriority
(osThreadId thread_id) //获取进程优先级
osThreadState osThreadGetState(osThreadId
thread_id) //获取进程状态/运行/阻塞/就绪/挂起/删除
osStatus osThreadIsSuspended(osThreadId
thread_id) //进程是否挂起
osStatus osThreadResume
(osThreadId thread_id) //进程恢复
osStatus osThreadResumeAll
(void) //所有进程恢复
osStatus osThreadSetPriority
(osThreadId thread_id, osPriority priority) //设置进程优先级
osStatus osThreadSuspend
(osThreadId thread_id) //挂起进程
osStatus osThreadSuspendAll
(void) //挂起所有进程
osStatus osThreadYield
(void) //调度
结构体:
typedef struct os_thread_def {
char *name; ///< Thread name
os_pthread pthread; ///< start address of thread function
osPriority tpriority; ///< initial thread priority
uint32_t instances; ///< maximum number of instances of that
thread function
uint32_t stacksize; ///< stack size requirements in bytes; 0
is default stack size
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
uint32_t *buffer; ///< stack buffer for static allocation;
NULL for dynamic allocation
osStaticThreadDef_t
*controlblock; ///< control
block to hold thread's data for static allocation; NULL for dynamic allocation
#endif
} osThreadDef_t;
示例代码:
void led1_function(void const * argument); //声明进程函数
osThreadId led1handles; //定义进程句柄
osThreadDef(led1, led1_function,
osPriorityNormal, 0, 128); //定义进程结构体
led1handles = osThreadCreate(osThread(led1),
NULL); //创建进程
void led1_function(void const * argument) //把进程函数实现了
{
for(;;)
{
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_7);
osDelay(300);
}
}
osThreadTerminate (led1handles); //删除函数
演示:
以下是先打印所有进程,然后删除led1进程,然后再打印所有任务,就没有 led1 theread
时钟有关:
osTimerId osTimerCreate
(const osTimerDef_t *timer_def, os_timer_type type, void *argument) //创建时钟
osStatus osTimerDelete
(osTimerId timer_id) //删除时钟
osStatus osTimerStart
(osTimerId timer_id, uint32_t millisec) //启动时钟
osStatus osTimerStop
(osTimerId timer_id) //停止时钟
示例代码:
osTimerId timer1_handle; //定义句柄
osTimerId timer2_handle;
void timer1_function(void const *
argument); //声明函数
void timer2_function(void const *
argument);
osTimerDef(timer1, timer1_function); //定义timer1结构体
osTimerDef(timer2, timer2_function);
timer1_handle =
osTimerCreate(osTimer(timer1), osTimerPeriodic, NULL); //创建时钟,两个参数不一样,periodic 是连续循环时钟,once 是之调用一次,时钟就停了,时钟还在,下次调用,启用载运行一次,又停了。
timer2_handle = osTimerCreate(osTimer(timer2),
osTimerOnce, NULL);
void timer1_function(void const * argument) //时钟回调函数
{
; //随便写点东西
}
void timer2_function(void const * argument)
{
; //随便写点东西
}
创建的时钟不是单独的一个任务,所有创建的时钟,都是有系统的timer svc 时钟系统服务进程调用时钟回调函数实现的,如下图。用户代码写到回调函数中,如上代码注释位置。
系统相关:
static int inHandlerMode
(void) //是否工作在线程模式
static osPriority makeCmsisPriority
(unsigned portBASE_TYPE fpriority)
//freeRTOS 优先级转为CMSIS
static unsigned portBASE_TYPE makeFreeRtosPriority (osPriority priority) //CMSIS优先级转为freeRTOS
int32_t osKernelRunning(void) //内核/系统是否运行
osStatus osKernelStart
(void) //启动内核/系统
uint32_t osKernelSysTick(void) //获取系统心跳数
osKernelStart(void):这个函数会创建idel 任务,创建 tmr svc 任务,开启调度系统自动运行。
Cobemx 生成的工程,会在main函数中的 while(1) 大循环前,最后一句,就是这个函数。
osKernelStart(void) 和 while(1) 之间不允许有用户代码
这是idle 和 tmr svc 进程,从序号能看出来创建顺序;
延时相关:
osStatus osAbortDelay(osThreadId
thread_id) //中止延迟
osStatus osDelay (uint32_t
millisec) //系统延时,ms,不占用CPU
osStatus osDelayUntil
(uint32_t *PreviousWakeTime, uint32_t millisec) //延时到
osDelay 和 osDelayUntil : 这两个函数是有区别的,如下图;
注释掉的两个语句是比较耗时间的,实际运行时候,上面这个led1_function进程循环更快,因为osDelayUntil函数延时的起点是ticks变量的值,也就是耗时间比较多的 timer2_function()函数运行的同时,计数值也在累加,即使timer2_function()函数耗时200ms,这个大循环仍然按照 400ms周期循环。
osDelay 延时,是这个语句需要消耗 400ms,再加上耗时间比较长的timer2_function() 函数运行时间假如是200ms,下面这个led2_function进程大循环一次需要 400+200ms,60ms,循环的更慢。
这两个函数都是freeRTOS 原生特性
两个函数的用法,如上图所示,比较简单
timer2_function()函数其实是我的时钟回调函数,占用cpu 时间比较多,临时拿来当作耗时用的。自己可以随便写,for()嵌套循环耗时也行
消息队列:
osMessageQId osMessageCreate
(const osMessageQDef_t *queue_def, osThreadId thread_id) //创建消息队列
osStatus osMessageDelete
(osMessageQId queue_id) //删除一条消息
osEvent
osMessageGet (osMessageQId queue_id, uint32_t millisec); //获取消息
osEvent osMessagePeek
(osMessageQId queue_id, uint32_t millisec); //获取消息不删除,详细看下面测试
osStatus osMessagePut
(osMessageQId queue_id, uint32_t info, uint32_t millisec) //发送消息
uint32_t osMessageWaiting(myQueue01Handle);
//获取队列中有几条消息
uint32_t osMessageAvailableSpace(osMessageQId
queue_id); //获取队列可用空间
消息结构体:
typedef struct os_messageQ_def {
uint32_t
queue_sz; ///< number of
elements in the queue
uint32_t
item_sz; ///< size of an
item
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
uint8_t
*buffer; ///< buffer for
static allocation; NULL for dynamic allocation
osStaticMessageQDef_t
*controlblock; ///< control
block to hold queue's data for static allocation; NULL for dynamic allocation
#endif
//void
*pool; ///< memory array for
messages
} osMessageQDef_t;
示例代码:
int queue_data_put=21;
char queue_buffer[0x40];
osEvent queue_event;
int count=0;
void keyup_press_callback(void)
{
osMessagePut
(myQueue01Handle, ++queue_data_put, 0);
memset(queue_buffer,0,0x40);
sprintf(queue_buffer,"put queue a message:%d.\r\n",queue_data_put);
while(!(HAL_UART_Transmit_IT(&huart1,
(uint8_t *)queue_buffer, sizeof(queue_buffer))==HAL_OK)){};
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_9);
}
void key0_press_callback(void)
{
memset(&queue_event,0,sizeof(osEvent));
queue_event=osMessagePeek
(myQueue01Handle,0);
memset(queue_buffer,0,0x40);
sprintf(queue_buffer,"peek queue a message:%d.\r\n",queue_event.value.v);
while(!(HAL_UART_Transmit_IT(&huart1,
(uint8_t *)queue_buffer, sizeof(queue_buffer))==HAL_OK)){};
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_10);
}
void key1_press_callback(void)
{
memset(&queue_event,0,sizeof(osEvent));
queue_event=osMessageGet
(myQueue01Handle,0);
memset(queue_buffer,0,0x40);
sprintf(queue_buffer,"get queue a message:%d.\r\n",queue_event.value.v);
while(!(HAL_UART_Transmit_IT(&huart1,
(uint8_t *)queue_buffer, sizeof(queue_buffer))==HAL_OK)){};
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_11);
}
void key2_press_callback(void)
{
count=osMessageAvailableSpace(myQueue01Handle);
memset(queue_buffer,0,0x40);
sprintf(queue_buffer,"queue availd space:%d.\r\n",count);
while(!(HAL_UART_Transmit_IT(&huart1,
(uint8_t *)queue_buffer, sizeof(queue_buffer))==HAL_OK)){};
// osTimerStart
(timer2_handle, 0);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_12);
演示:初始消息队列可用长度16,put 发送一条消息,可用长度减1,peek 获取一条消息,因为不删除,所以不减1,get获取一条消息减1,他会把消息从队列删除。
事件中有个结构体,联合体,避免发生错误,需要判断status,我的代码中没有判断,获取队列是0,其实是消息队列空了,发生了错误。
Staus 是个枚举变量,osOK,event还有三种状态,signal,message,mail,读取消息要根据这个值做相应处理。
信箱:
void *osMailAlloc (osMailQId queue_id,
uint32_t millisec) //分配内存
void *osMailCAlloc (osMailQId queue_id,
uint32_t millisec) //分配内存并清零
osMailQId osMailCreate (const osMailQDef_t
*queue_def, osThreadId thread_id) //创建信箱队列
osStatus osMailFree (osMailQId queue_id,
void *mail) //释放内存
osEvent osMailGet (osMailQId queue_id,
uint32_t millisec) //获取消息
osStatus osMailPut (osMailQId queue_id,
void *mail) //发送消息
信箱队列和消息队列完全一模一样,唯一不同的一点是,这个信箱可以传递任何类型的数据,消息队列只能传递uint32_t 类型的数据。
信箱传递的参数是指针,消息传递的参数是变量。其他的完全一样,状态变量,osEvent 都是同样的类型,一样的。
互斥量:
osMutexId osMutexCreate (const osMutexDef_t
*mutex_def) //创建互斥量
osStatus osMutexDelete (osMutexId mutex_id) //释放互斥量
osStatus osMutexWait (osMutexId mutex_id,
uint32_t millisec) //获取互斥量
osStatus osMutexRelease (osMutexId mutex_id) //释放互斥量
互斥量结构体:
typedef struct os_mutex_def {
uint32_t
dummy; ///< dummy value.
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
osStaticMutexDef_t
*controlblock; ///<
control block for static allocation; NULL for dynamic allocation
#endif
} osMutexDef_t;
示例代码:
osMutexDef(myMutex01); //定义结构体
myMutex01Handle = osMutexCreate(osMutex(myMutex01)); //创建互斥量
void led1_function(void const * argument)
{
for(;;)
{
while(osMutexWait(myMutex01Handle,0)!=osOK);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_7);
osDelay(500);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_7);
while(osMutexRelease(myMutex01Handle)!=osOK);
osThreadYield
();
}
}
void led2_function(void const * argument)
{
for(;;)
{
while(osMutexWait(myMutex01Handle,0)!=osOK);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_8);
osDelay(500);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_8);
while(osMutexRelease(myMutex01Handle)!=osOK);
osThreadYield
();
}
}
void led3_function(void const * argument)
{
for(;;)
{
while(osMutexWait(myMutex01Handle,0)!=osOK);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_9);
osDelay(500);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_9);
while(osMutexRelease(myMutex01Handle)!=osOK);
osThreadYield
();
}
}
void led4_function(void const * argument)
{
for(;;)
{
while(osMutexWait(myMutex01Handle,0)!=osOK);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_10);
osDelay(500);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_10);
while(osMutexRelease(myMutex01Handle)!=osOK);
osThreadYield
();
}
}
基于上面这个代码,单片机 stm32f407 上能跑4个任务,能正常实现功能,任务里面需要加一个osThreadYield ()调度才可以。
获取互斥量的多个任务之间优先级必须一样,这个测试压力比较大,5个任务同时获取互斥量,就出现问题了只有两个任务可以正常拿到互斥量相互切换执行,其他任务获取不到互斥量。
递归互斥量:
osMutexId osRecursiveMutexCreate (const
osMutexDef_t *mutex_def) //创建互斥量
osStatus osRecursiveMutexWait (osMutexId
mutex_id, uint32_t millisec) //获取互斥量
osStatus osRecursiveMutexRelease (osMutexId
mutex_id) //释放互斥量
递归互斥量结构体和互斥量一样。
示例代码:
osMutexDef(myRecursiveMutex01); //定义结构体
myRecursiveMutex01Handle =
osRecursiveMutexCreate(osMutex(myRecursiveMutex01)); //创建递归互斥量
void led3_function(void const * argument)
{
for(;;)
{
while(osRecursiveMutexWait(myRecursiveMutex01Handle,0)!=osOK);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_9);
osDelay(500);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_9);
while(osRecursiveMutexRelease(myRecursiveMutex01Handle)!=osOK);
osThreadYield
();
}
}
void led4_function(void const * argument)
{
for(;;)
{
while(osRecursiveMutexWait(myRecursiveMutex01Handle,0)!=osOK);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_10);
osDelay(500);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_10);
while(osRecursiveMutexRelease(myRecursiveMutex01Handle)!=osOK);
osThreadYield
();
}
}
void led5_function(void const * argument)
{
for(;;)
{
while(osRecursiveMutexWait(myRecursiveMutex01Handle,0)!=osOK);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_11);
osDelay(500);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_11);
while(osRecursiveMutexRelease(myRecursiveMutex01Handle)!=osOK);
osThreadYield
();
}
}
void led6_function(void const * argument)
{
for(;;)
{
while(osRecursiveMutexWait(myRecursiveMutex01Handle,0)!=osOK);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_12);
osDelay(500);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_12);
while(osRecursiveMutexRelease(myRecursiveMutex01Handle)!=osOK);
osThreadYield
();
}
}
递归互斥量,这一段代码,四个 led 灯可以轮番被点亮。第五个任务获取递归信号量,也会出问题。
内存池:
osPoolId osPoolCreate (const osPoolDef_t
*pool_def) //创建内存池
osStatus osPoolFree (osPoolId pool_id, void
*block) //释放内存
void *osPoolAlloc (osPoolId pool_id) //分配内存
void *osPoolCAlloc (osPoolId pool_id) //分配内存清零
这几个好简单
内存池结构体:
typedef struct os_pool_def {
uint32_t
pool_sz; ///< number of
items (elements) in the pool
uint32_t
item_sz; ///< size of an
item
void
*pool; ///< pointer to
memory for pool
} osPoolDef_t;
二值信号量,计数信号量:
osSemaphoreId osSemaphoreCreate (const
osSemaphoreDef_t *semaphore_def, int32_t count) //创建信号量,二值count=1,计数count=100都可以。
osStatus osSemaphoreDelete (osSemaphoreId
semaphore_id) //删除
uint32_t osSemaphoreGetCount(osSemaphoreId
semaphore_id) //获取信号值
int32_t osSemaphoreWait (osSemaphoreId
semaphore_id, uint32_t millisec) //获取
osStatus osSemaphoreRelease (osSemaphoreId
semaphore_id) //释放
二值信号量示例代码:
osSemaphoreDef(myBinarySem01);
myBinarySem01Handle = osSemaphoreCreate(osSemaphore(myBinarySem01), 1);
使用二值信号量,一次点亮一盏灯:
void led1_function(void const * argument)
{
for(;;)
{
while(osSemaphoreWait(myBinarySem01Handle,0)!=osOK);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_7);
osDelay(500);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_7);
while(osSemaphoreRelease(myBinarySem01Handle)!=osOK);
osThreadYield
();
}
}
void led2_function(void const * argument)
{
for(;;)
{
while(osSemaphoreWait(myBinarySem01Handle,0)!=osOK);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_8);
osDelay(500);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_8);
while(osSemaphoreRelease(myBinarySem01Handle)!=osOK);
osThreadYield
();
}
}
void led3_function(void const * argument)
{
for(;;)
{
while(osSemaphoreWait(myBinarySem01Handle,0)!=osOK);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_9);
osDelay(500);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_9);
while(osSemaphoreRelease(myBinarySem01Handle)!=osOK);
osThreadYield
();
}
}
void led4_function(void const * argument)
{
for(;;)
{
while(osSemaphoreWait(myBinarySem01Handle,0)!=osOK);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_10);
osDelay(500);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_10);
while(osSemaphoreRelease(myBinarySem01Handle)!=osOK);
osThreadYield
();
}
}
计数信号量示例代码:
osSemaphoreDef(myCountingSem01);
myCountingSem01Handle = osSemaphoreCreate(osSemaphore(myCountingSem01),
2);
使用计数信号量,初始化为2,一次点亮两盏灯:
void led1_function(void const * argument)
{
for(;;)
{
while(osSemaphoreWait(myCountingSem01Handle,0)!=osOK);
HAL_GPIO_TogglePin(GPIOE,
GPIO_PIN_7);
&n
原创作品,未经权利人授权禁止转载。详情见转载须知。 举报文章
我要举报该内容理由
×