你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

CMSIS-RTOS V2学习笔记

[复制链接]
mylovemcu 发布时间:2019-9-29 15:26
CMSIS-RTOS V2是keil软件自带的操作系统,相较其他操作系统,该系统的有点在于移植方便,代码开源,可实时响应中断,之前查看网上资料,发现这个系统的内容特别少,个人用了一段时间,感觉特别好,写一下学习笔记,作为备用吧。写的有什么问题的,希望大神及时指正。接下来是操作系统的学习笔记。
1 E$ Q' I3 N0 S6 M# {' X/ U9 ?& {  T! x. u+ n& b/ h& M( K9 J
5 J; r6 N3 I7 G  F! i  @: F

8 t7 n6 h: J5 x+ f& R$ m" \
' [" \9 V- r! G& ~第一篇:操作系统的移植
" t9 V, x3 U# q, _+ r$ L0 g: B/ G5 N3 B0 I
第一步:添加操作系统API,勾选CMSIS—RTOS2—keil RTX5+ b) W6 [' w% Q" }  Y- {) o3 ^8 `

+ Y; `* ~0 J- I2 {/ R* ?8 U
# g: o& e" i: S4 \
112.png
, Q. x! T" A; T) A) h, Q+ L$ ?% I
完成以后如下图显示

7 a; G) S# k# E9 } 113.png ( |& A6 \" W1 D  `3 @9 Z# d

2 e. l$ a- n& X# D, a( u
第二步:添加头文件,
#include"cmsis_os2.h"
#include"RTE_Components.h"
#include  CMSIS_device_header
第三步:添加main函数,V2操作系统中,有两层main函数,第一层是系统的main函数,系统上电以后,首次进入main函数,进行初始化时钟和操作系统,同时启动第二次main函数。第二层main函数是用户创建的main函数,进行用户函数的初始化和任务的创建。
//第一层main函数,不需要修改
int main (void) {
  // System Initialization
  SystemCoreClockUpdate();//初始化系统时钟
  // ...
  osKernelInitialize();//初始化系统
  osThreadNew(app_main,(void *)1,0);//启动main进程
  osKernelStart();//启动系统
  for (;;);
}
//第二层main函数,初始化用户底层驱动,建立任务等
voidapp_main(void  *argument){
   
    osKernelInitialize();
  // initialize peripherals here
       /* 初始化外设 */
   
}
第四步:系统配置,V2操作系统需对运行方式进行配置,配置文件在RTX_Config.h文件中,配置参数需根据需要配置/ J; a8 ?# r$ s- m3 w! o( C

; o( L& @, w0 |+ U

4 N" ?3 z3 C7 L* M 114.png
4 v, w0 }5 C  E  q- X9 |; ?/ ^
% g1 ^) X1 O3 G! c
至此,操作系统的移植完成。
需要注意的是原工程里面不能有SysTick,PendSV和SVC三个系统中断的使用,因为RTX系统要使用这三个中断。

. m( ]7 P# J+ \7 x% Y3 l. M2 w5 e8 a, m& i( s2 f

嵌入式实时操作系统uCOS-II[邵贝贝].pdf

下载

4.33 MB, 下载次数: 62

评分

参与人数 1 ST金币 +2 收起 理由
wu18946796976 + 2

查看全部评分

1 收藏 3 评论11 发布时间:2019-9-29 15:26

举报

11个回答
mylovemcu 回答时间:2019-10-25 15:05:13
第六篇:定时器管理
系统中可以实现虚拟定时器功能,虚拟定时器最小计时周期为系统滴答周期,由于虚拟定时器会产生中断,优先级较高,所以定时器频率不宜过高,定时器中不宜处理太多事情。虚拟定时器的个数可以设定,每个定时器也可以设定为单次定时器或者重复定时器,单次定时器每次执行完都会停止。
建立定时器
osTimerId_t osTimerNew(osTimerFunc_t   func,osTimerType_t     type,void * argument,const osTimerAttr_t *     attr )
各参数定义
func  回调函数名,也是虚拟定时器中断处理函数
type  定时器类型,     osTimerOnce-单次定时器   osTimerPeriodic-重复定时器
argument      回调函数传递参数
attr  虚拟定时器属性设置,NULL是使用系统默认值
启动定时器
osStatus_t osTimerStart       (      osTimerId_t  timer_id,uint32_t  ticks )      
建立好定时器以后就可以启动定时器了,启动定时器时需要定时器名和定时器的频率
timer_id是定时器的ID,ticks是定时器进入中断的滴答值。
函数返回值有以下几种:
osOK: 定时器已经被启动
osErrorISR: 不能从中断服务程序中调用osTimerStart。
osErrorParameter:参数timer_id为NULL或无效或滴答不正确。
osErrorResource:计时器处于无效状态。
停止定时器
osStatus_t osTimerStop (    osTimerId_t   timer_id  )   
timer_id就是定时器的ID
函数返回值有以下几种
osOK:指定的定时器已停止。
osErrorISR: 不能从中断服务程序中调用osTimerStop。
osErrorParameter:参数timer_id为NULL或无效。
osErrorResource:计时器未运行(您只能停止正在运行的计时器)。
示例
void Timer_Callback (void *arg) // 定时器回调函数
{                          
}
uint32_t  exec;             // 定时器函数回调参数
void TimerStart_example (void) {
  osTimerId_t id;                                         // 定时器ID
  uint32_t    timerDelay;                                   // 定时器周期
  osStatus_t  status;                                       // 函数返回标志
  // 建立周期定时器
  exec = 1;
  id = osTimerNew(Timer_Callback, osTimerPeriodic, &exec, NULL);
  if (id)  {
    timerDelay = 1000;
    status = osTimerStart (id,timerDelay);                 // 启动定时器
    if (status != osOK)  {// 定时器启动失败
    }
  } ;
}

( b. Y8 [  H' B/ O$ E9 V+ @
mylovemcu 回答时间:2019-9-29 15:27:54
第二篇:线程的管理
系统配置完成以后,首先需要建立线程,至少建立一个线程才可以正常运行

8 C) C: B1 P0 |5 [
线程可以处于以下状态:
运行:当前正在运行的线程处于运行状态。一次只能有一个线程处于此状态。
就绪:运行准备的线程处于就绪状态一旦。运行线程终止或被阻塞,一个下优先级最高的就绪线程将成为运行线程。
挂起:被中断的线程被延迟,等待事件发生或挂起处于挂起状态。
终止:当osThreadTerminate被调用时,线程终止与尚未发布的资源。
根本:未创建或已释放所有资源的情况终止的线程处于先前状态。

0 F/ x8 @0 a$ W
10181.png

0 I; m. `, x. e1 P) V3 K( K; Y  R3 ^% d9 ?# |" @, s! u& \
新建线程如下
osThreadId_t osThreadNew(osThreadFunc_t func,void * argument,const osThreadAttr_t * attr)
osThreadNew函数返回变量函数id,有三个参数其中func是函数名,argument是传参指针,无参数的时候是NULL,attr是线程属性,变量是NULL
体现如下:
id_SysResetThread = osThreadNew(Sys_Reset,NULL,NULL);
id_SysResetThread是线程的ID,可以写也可以不写,根据函数需求决定,如果后续会调用该函数,则需要id,反之则不需要。后续该线程的操作都可以通过id进行调用。
线程新建以后就需要编写线程函数,线程函数是一个独立的函数,编写方法和没有系统时一样的。
无效Sys_Reset(无效*参数)
{
//编写用户函数
虽然(1)
{
osDelay(100);
}
}
编写线程需要注意几点:
1,操作系统中的线程等同于裸机中的主函数,都是由上至下执行一次,所以在函数中如果需要重复执行的,一定要加同时循环,一直执行,否则执行一次函数以后线程关闭了,只有在下次重复调用的时候才会再次执行一次。
2,操作系统中多个线程宏上是同时执行的,但是微观上还是单独执行的,只不过执行过程中通过预定的机制转换的切换线程,微小的只有一个线程在运行,这样就需要一个机制去控制线程的切换,当线程是分片式切换时,线程中必须有堆栈函数,可以是线程等待,时间等待,时序函数等。这样线程才能释放控制权,否则会造成系统死机。
3,线程有传参,并且参数是无类型的指针,可以传递所有的数据类型,根据传参,同一个函数名可以实现多个函数功能。
CMSIS-RTOSV2系统相较RTX系统更简洁,RTX系统在新建线程需要先申明线程名,之后才能建立线程。CMSIS-RTOSV2系统不需要申明线程,直接建立线程就可以,建议如果使用RTX系统的话可以换成CMSIS-RTOSV2,操作更加方便。

% B/ ]* }' J  _, a
一个最简单的线程就建立完成了,线程建立以后就可以运行系统了。

) [  n8 v! A& z0 |9 z! D- K
创建线程的时候也可以利用参数属性,同时设置线程的优先级,变量大小等
设置线程优先级表达式:
const osThreadAttr_t thread1_attr = {
  .priority = osPriorityHigh //设置高优先级   
};

5 h. S  d: |% p% F; c) |" Z
int main(void){
  osThreadNew(thread1,NULL,&thread1_attr); //使用设定优先级创建线程
}
设置尺寸大小示例:
const osThreadAttr_t thread1_attr = {
  .stack_size = 1024 //设置多个大小为1024字节
};
int main(void){
  osThreadNew(thread1,NULL,&thread1_attr); //使用自定义尺寸大小创建线程
} 5 G( O6 y, d4 f: B# S% j
更改线程优先级:
osPriority_t osThreadGetPriority(osThreadId_t thread_id,osPriority_t优先级)
thread_id就是需要修改线程的ID,priority是修改的优先级,优先级有很多级,参数表如下
osPriorityNone
无优先级(未初始化)。
osPriorityIdle
为空闲线程保留。
  
此最低优先级不应用于任何其他线程。
osPriorityLow
优先级:低。
osPriorityLow1
优先级:低+1。
osPriorityLow2
优先级:低+ 2。
osPriorityLow3
优先级:低+ 3。
osPriorityLow4
优先级:低+ 4。
osPriorityLow5
优先级:低+ 5。
osPriorityLow6
优先级:低+ 6。
osPriorityLow7
优先级:低+ 7。
osPriorityBelowNormal
优先:低于正常水平。
osPriorityBelowNormal1
优先级:低于正常值+1。
osPriorityBelowNormal2
优先级:低于正常水平+ 2。
osPriorityBelowNormal3
优先级:低于正常水平+ 3。
osPriorityBelowNormal4
优先级:低于正常水平+ 4。
osPriorityBelowNormal5
优先级:低于正常水平+ 5。
osPriorityBelowNormal6
优先级:低于正常水平+ 6。
osPriorityBelowNormal7
优先级:低于正常水平+ 7。
osPriorityNormal
优先:正常。
osPriorityNormal1
优先级:正常+1。
osPriorityNormal2
优先级:正常+ 2。
osPriorityNormal3
优先级:正常+ 3。
osPriorityNormal4
优先级:正常+ 4。
osPriorityNormal5
优先级:正常+ 5。
osPriorityNormal6
优先级:正常+ 6。
osPriorityNormal7
优先级:正常+ 7。
osPriorityAboveNormal
优先:高于正常水平。
osPriorityAboveNormal1
优先级:高于正常+ 1。
osPriorityAboveNormal2
优先级:高于正常水平+ 2。
osPriorityAboveNormal3
优先级:高于正常水平+ 3。
osPriorityAboveNormal4
优先级:高于正常水平+ 4。
osPriorityAboveNormal5
优先级:高于正常水平+ 5。
osPriorityAboveNormal6
优先级:高于正常水平+ 6。
osPriorityAboveNormal7
优先级:高于正常水平+ 7。
osPriorityHigh
优先级:高。
osPriorityHigh1
优先级:高+1。
osPriorityHigh2
优先级:高+ 2。
osPriorityHigh3
优先级:高+ 3。
osPriorityHigh4
优先级:高+ 4。
osPriorityHigh5
优先级:高+ 5。
osPriorityHigh6
优先级:高+ 6。
osPriorityHigh7
优先级:高+ 7。
osPriorityRealtime
优先级:实时。
osPriorityRealtime1
优先级:实时+1。
osPriorityRealtime2
优先级:实时+ 2。
osPriorityRealtime3
优先级:实时+ 3。
osPriorityRealtime4
优先级:实时+ 4。
osPriorityRealtime5
优先级:实时+ 5。
osPriorityRealtime6
优先级:实时+ 6。
osPriorityRealtime7
优先级:实时+ 7。
优先权
为ISR延迟线程保留。
  
RTOS实施可能会使用此最高优先级
  
但不得用于任何用户线程。
osPriorityError
系统无法确定优先级或非法优先级。
osPriorityReserved
  
防止枚举缩小编译器优化。
  
返回的参数有如下:
osOK:设置成功
osErrorParameter: 线程ID为空或无效或优先级不正确
osErrorResource 线程处于无效状态的盖的状态。
osErrorISR:不能从中断服务例程中调用函数该函数
- I3 k  d4 Q; ~! e
zhy233090130 回答时间:2019-9-30 11:17:47
感谢分享,学习
mylovemcu 回答时间:2019-10-25 15:02:52
第三篇:线程标志
线程建立以后就可以使用线程标志,类似的标志还有一个是事件标志,应该说事件标志可包含线程标志,但线程标志使用方法比事件标志简单一些,所以在一些简单应用中,线程标志使用也比较多。
线程标志设置函数:
uint32_t osThreadFlagsSet   (      osThreadId_t       thread_id,uint32_t        flags )     
thread_id:是线程ID
flags:是线程标志,根据需要设置
线程标志清除函数:
uint32_t osThreadFlagsClear(uint32_t flags)
返回当前线程标志:
uint32_t osThreadFlagsGet(void )
等待线程标志:
uint32_t osThreadFlagsWait (      uint32_t flags,uint32_t        options,uint32_t   timeout)
函数osThreadFlagsWait会暂停当前正在运行的线程的执行,直到设置了由参数标志指定的任何或所有线程标志为止。当已经设置了这些线程标志时,该函数立即返回。否则,线程将进入状态BLOCKED。
每个线程最多可设置31个线程标志,通过flags参数进行设置
Options参数指定等待的条件,有三种方式
osFlagsWaitAny等待任何标志(默认)。
osFlagsWaitAll等待所有标志。
osFlagsNoClear不要清除已指定要等待的标志。
Timeout参数设置了超时等待时间,单位是单位滴答时间。0是不等待,osWaitForever是一直等待
void Thread (void* arg) {
osThreadFlagsWait (0x00000001U, osFlagsWaitAny, osWaitForever); // 一直等待标志1被设置
osThreadFlagsWait (0x00000003U, osFlagsWaitAny, osWaitForever); //一直等待标志0或1被设置  ;
osThreadFlagsWait (0x00000003U, osFlagsWaitAll, 10); //10个时钟周期内等待0和1同时被设置,否则超时
osThreadFlagsWait (0x00000003U, osFlagsWaitAll | osFlagsNoClear,osWaitForever); // 一直等待0和1被设置,但不清除标志.
}
* S: J7 A1 F3 |0 i0 j
mylovemcu 回答时间:2019-10-25 15:04:06
第四篇:事件标志
事件标志在系统中有很重要的位置。在裸机编程的时候,在多个函数之间的数据标志的传递依靠全局变量来实现,但在系统中,由于全局变量不受系统的控制,调用全局变量会增加系统的不确定性。事件标志就是为了替代全局变量实现线程之间的通信,并且事件变量是有系统统一调度的,完全受系统的控制。使用事件标志可以增加系统可靠性。每个信号最多产生31个事件标志,如果没有指定了事件标志选项osFlagsNoClear,当线程唤醒并恢复执行时,信号标志将自动清除。
建立事件
osEventFlagsId_t osEventFlagsNew    (      constosEventFlagsAttr_t *  attr  )      
attr是属性设置,用于分配内存大小,默认是NULL,在使用事件之前必须先建立事件,建立事件指定事件ID,就可以对该ID事件进行操作。简单的事件交流如下图
1025.jpg
设定事件
uint32_t osEventFlagsSet     (      osEventFlagsId_t  ef_id,      uint32_t flags )
ef_id是事件的ID名,flag是事件标志,32位的空间,最多设置31个事件标志。
设定事件之前需要先建立事件。
设定事件错误返回值
osFlagsErrorUnknown:未指定的错误。
osFlagsErrorParameter:参数ef_id无法标识有效的事件标志对象或标志的最高位设置。
osFlagsErrorResource:事件标志对象处于无效状态。
等待事件
uint32_t osEventFlagsWait   (osEventFlagsId_t  ef_id,      uint32_t  flags,      uint32_t options,  uint32_t timeout )      
ef_id是事件的ID名,flag是事件标志,32位的空间,最多设置31个事件标志。
Option是事件选项,有以下三种选项:
osFlagsWaitAny    等待任何一个标志就绪(默认NULL等同osFlagsWaitAny)
osFlagsWaitAll      等待所有标志就绪
osFlagsNoClear     标志到达以后不清零该标志(默认情况下,标志到达后清零标志)
timeout是超时标志,单位是1个滴答周期,
timeout等于0时,该事件立即返回,没有等待事件
timeout等于osWaitForever时,该事件一直等待事件标志就绪
等待事件错误返回数值
osFlagsErrorUnknown:未指定的错误。
osFlagsErrorTimeout:在给定的时间内尚未设置等待的标志。
osFlagsErrorResource:未指定超时时,尚未设置等待的标志。
osFlagsErrorParameter:参数ef_id无法标识有效的事件标志对象或标志的最高位设置。
等待事件一定要在线程中进行,非线程中只可以设定事件,不可以等待事件,否则系统会跑飞
清除事件标志
uint32_t osEventFlagsClear  (osEventFlagsId_t ef_id,      uint32_t flags )     
获取事件标志
uint32_t osEventFlagsGet    (      osEventFlagsId_t   ef_id       )      
删除事件标志
osStatus_t osEventFlagsDelete    (      osEventFlagsId_t   ef_id       )      
获取事件名称
const char * osEventFlagsGetName    (      osEventFlagsId_t   ef_id       )      
事件示例:
#define FLAGS_MSK1 0x00000001ul
void Thread_EventSender (void *argument)
{
  evt_id =osEventFlagsNew(NULL);                //建立事件
  while (1) {   
    osEventFlagsSet(evt_id,FLAGS_MSK1);      //设置事件标志
    osDelay (1000);                                           //suspend thread
  }
}
void Thread_EventReceiver (void *argument)
{
  uint32_t flags;
  while (1) {
    flags = osEventFlagsWait(evt_id,FLAGS_MSK1,osFlagsWaitAny, osWaitForever);//一直等待事件标志就绪
  }
} 5 c) O' u0 b& ^0 {# a9 ]+ Q2 X7 x( ]
mylovemcu 回答时间:2019-10-25 15:04:37
第五篇:等待函数
等待函数的使用非常简单,并且等待函数使用的是滴答计数,延时时间也很精确。在使用等待函数的时候,当前任务处于挂起的状态,其他任务可以进行调度。
系统的等待函数有两个,一个是相对等待时间,一个是绝对等待时间
相对等待函数
osStatus_t osDelay (      uint32_t  ticks )      
函数osDelay等待内核滴答中指定的时间段。对于值1,系统将等待直到下一个计时器滴答发生。实际的时间延迟最多可能比指定的时间间隔少一个计时器滴答,即osDelay(1)在下一个系统滴答发生之前立即调用,线程会立即重新调度。
延迟的线程进入BLOCKED状态,并立即进行上下文切换。在经过给定的滴答数之后,线程将自动返回到READY状态。如果线程在READY状态下具有最高优先级,则将立即对其进行调度。
void Thread_1 (void *arg)  {               
osStatus_t status;                       
uint32_t   delayTime;                     // 延时时间
delayTime = 1000;                        // 延时1000个滴答
status = osDelay (delayTime);            // 调用延时函数
}
绝对等待函数
osStatus_t osDelayUntil (      uint32_t ticks )
函数osDelayUntil等待直到达到绝对时间(在内核ticks中指定)。
内核滴答计数器溢出时的特殊情况由osDelayUntil处理。最大延迟限制为(2的 31次方)-1个滴答声。
延迟的线程进入BLOCKED状态,并立即进行上下文切换。达到给定时间后,线程将自动返回到READY状态。如果线程在READY状态下具有最高优先级,则将立即对其进行调度。
void Thread_1 (void *arg)  {
  uint32_ttick;
tick = osKernelGetTickCount();           // 返回当前滴答计数值
  for(;;) {
   tick += 1000;                          // 延迟1000个滴答
   osDelayUntil(tick);
  }
}
由于一般的滴答计数都是1ms周期的,所以最小的延迟时间就是1ms,那如果需要us级延时的话可以调用osKernelGetSysTimerCount()函数,osKernelGetSysTimerCount()函数是返回内核计数器的值,频率是内核的频率。延时函数如下:
微妙级相对等待函数
void delay_us(uint16 nus)
{
   uint32_t Buf,Buf2;
   Buf = osKernelGetSysTimerCount();//读取当前计数值
   Buf += nus*72;//当前频率是72MHz,运行一次是1/72us
   do
    {
      Buf2 = osKernelGetSysTimerCount();
   }while(Buf > Buf2);//等待计数到达
}
当然也可以使用for循环延时等待,实现us延时。

3 J- p+ p  _8 I: ]4 z9 B
fulangsuowa 回答时间:2019-11-1 17:51:41
楼主,使用事件是不是也可以使用信号量(可用最大数目设置为1)???
mylovemcu 回答时间:2020-3-12 13:17:32
虚拟定时器例程分享* h2 s6 v; P- V

: H6 f- |7 Q& U3 P附件是例程# \1 {, U+ s; G( y5 `5 N' P" ~- y
3 V5 P7 ?0 O4 N0 {7 c/ X3 T/ ^
系统是CMSIS RTOS2) H5 w6 M" ?1 c. D
6 ?, d% Z7 }( K  t; M, D) e* U# r
keil版本5.28* k4 t/ g/ w7 }& U! ^. a
例程只是空模板  ) N& W, [/ u* q0 R5 I

8 N, j1 Q/ U4 f, C- p4 k8 k# b  n) d

Demo.rar

下载

871.72 KB, 下载次数: 37

Heyboy 回答时间:2020-3-12 16:19:32
mylovemcu 发表于 2020-3-12 13:17
! j- Y' t7 T; M/ ^( X1 W虚拟定时器例程分享
' G( n- ~3 q4 \( \" s/ }" Y/ ^5 @
) e6 W0 o7 `% j" m附件是例程
' @5 P& G/ Q& v$ P4 I4 Y9 H' I
Thank you so much!
wdliming-222461 回答时间:2020-4-13 17:09:27
谢谢分享~~~
plj213 回答时间:2020-5-2 23:56:50
+ X) Q, z  j$ \8 L- b( g
感谢分享,学习学习。。

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版