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

CMSIS-RTOS V2学习笔记

[复制链接]
mylovemcu 发布时间:2019-9-29 15:26
CMSIS-RTOS V2是keil软件自带的操作系统,相较其他操作系统,该系统的有点在于移植方便,代码开源,可实时响应中断,之前查看网上资料,发现这个系统的内容特别少,个人用了一段时间,感觉特别好,写一下学习笔记,作为备用吧。写的有什么问题的,希望大神及时指正。接下来是操作系统的学习笔记。




第一篇:操作系统的移植

第一步:添加操作系统API,勾选CMSIS—RTOS2—keil RTX5


112.png
完成以后如下图显示

113.png

第二步:添加头文件,
#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文件中,配置参数需根据需要配置


114.png

至此,操作系统的移植完成。
需要注意的是原工程里面不能有SysTick,PendSV和SVC三个系统中断的使用,因为RTX系统要使用这三个中断。


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

下载

4.33 MB, 下载次数: 49

评分

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

查看全部评分

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

举报

11个回答
mylovemcu 回答时间:2019-9-29 15:27:54
第二篇:线程的管理
系统配置完成以后,首先需要建立线程,至少建立一个线程才可以正常运行

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

10181.png


新建线程如下
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,操作更加方便。

一个最简单的线程就建立完成了,线程建立以后就可以运行系统了。

创建线程的时候也可以利用参数属性,同时设置线程的优先级,变量大小等
设置线程优先级表达式:
const osThreadAttr_t thread1_attr = {
  .priority = osPriorityHigh //设置高优先级   
};

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); //使用自定义尺寸大小创建线程
}
更改线程优先级:
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:不能从中断服务例程中调用函数该函数
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被设置,但不清除标志.
}

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);//一直等待事件标志就绪
  }
}
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延时。

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)  {// 定时器启动失败
    }
  } ;
}

fulangsuowa 回答时间:2019-11-1 17:51:41
楼主,使用事件是不是也可以使用信号量(可用最大数目设置为1)???
mylovemcu 回答时间:2020-3-12 13:17:32
虚拟定时器例程分享

附件是例程

系统是CMSIS RTOS2

keil版本5.28
例程只是空模板  

Demo.rar

下载

871.72 KB, 下载次数: 19

sldxxr 回答时间:2020-3-12 16:19:32
mylovemcu 发表于 2020-3-12 13:17
虚拟定时器例程分享

附件是例程

Thank you so much!
12下一页

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版