DSP

TI RTOS系统在CC2642上的应用

2019-07-13 19:52发布

        TI针对自家处理器设计了RTOS实时操作系统,适用于TI的众多处理器例如蓝牙芯片CC26XX、MSP43X和多核DSP等等。由于之前在开发c6678时使用过RTOS,所以现在在CC2642上使用就顺畅的多。接下来进入正题,TI RTOS系统在CC2642上使用的介绍。 一、TI RTOS简介         TI 的RTOS实时操作系统原来名称为sys/bios,所以看到TI中说的sys/bios和RTOS是同一概念。RTOS是一个可裁剪的实时系统内核,多任务单线程机制。基于占先的工作模式,执行顺序按照优先级的高低进行,高优先级的任务可以抢占低优先级的线程,线程间的同步可以使用semaphore(信号量),event,queue,mailbox和gate来实现,和通用的RTOS系统相类似。RTOS的组成部分如下图所示:         上图是TI RTOS的基本组件,由线程模块,内存管理单元等部分组成。线程模块中有任务(task),硬件中断事件(HWI)、软件中断事件(SWI),定时器等部分组成。对于CC26XX,并不需要上面所有这些组件。RTOS的设置可以在.cfg的配置文件中进行(静态配置),也可以用C语言配置(动态配置)。CC26XX提供的SDK都是直接使用C语言来配置RTOS,主要原因是使用的简单,并不像多核DSP那样需要更多复杂的功能,RTOS的可裁剪性就体现在按需使用。 二、TI RTOS的三种线程         上文说道RTOS有多种线程模块,在这里重点介绍task、HWI和SWI三种线程的关系。RTOS系统是基于占先机制的,多任务单线程的实时操作系统。多任务体现在在一个工程里可以创建多个task、HWI和SWI,单线程含义是同一时刻只能有一个task、HWI或者SWI处于工作状态,其余的处于standby状态或者终止状态。高优先级的线程可以优先执行,等待高优先级线程执行完成后低优先级的线程才会开始执行。在低优先级线程执行期间,高优先级线程随时可以抢占执行权,此时低优先级的线程保存现场,等高优先级线程执行完成后继续执行未完成的工作。task,HWI和SWI三者之间的关系如下图所示: HWI的优先级最高,其次是SWI,最后是task。也就是说task执行期间随时会被HWI、SWI以及优先级高于自己的线程打断。 HWI主要是硬件中断事件,SWI是软件中断事件,task是使用的基本事件,我们的主要功能实现都是通过建立task,task总共有32个优先级,数字越大优先级越高。 三、线程间的同步         高优先级的线程会抢占低优先级的线程优先执行,我们使用的task的工作状态通常是等待执行和正在执行,如果高优先级的任务一直占据着线程,那么低优先级任务就永远没有机会执行,而一个任务通常不会只执行一次就结束,那么如何实现task间的调度呢?线程间的同步机制semaphore、event、queue、mailbox和gate完美实现任务间的切换。 1、semaphore信号量         信号量是经常使用的一种线程间同步机制,根据模式可以分为二进制信号量和计数信号量。但本质上相同,使用semaphore_post来post信号量,使用semaphore_pend来挂起信号量等待semaphore_post(通俗理解就是post使信号量加1,pend使信号量减1,当信号量大于或等于1时不会阻塞任务执行,当信号量等于0时程序停止在semaphore_pend处来等待semaphore_post).举个例子说明信号量的使用:在优先级为5的task中添加semaphore_pend函数等待信号量,在优先级小于5的task中添加semaphore_post来post信号量。那么程序上电开始运行后高优先级任务被终止,优先执行低优先级任务,当低优先级任务中执行到semaphore_post后,高优先任务才被激活,迅速跳转到高优先级任务中执行,低优先级任务如果还没执行完就处于等待状态,等待高优先级任务执行完成后再继续执行。这就实现了任务间的调度。 掌握了semaphore_post和semaphore_pend的使用,也就掌握了信号量的使用。 2、event事件         在普通函数中通常使用switch语句来实现多条件分支程序,每个case对应一种情况,就避免了使用多层if循环代码的不清晰。event在任务间的调度中就发挥着这样的作用。以文件系统的设计为例,当我们要写文件时会调用write函数,读文件时会调用read函数,那么在任务中难道要为write和read各建立一个task么?完全不需要,使用event事件,可以定义最多32个event ID,每个event ID触发不同代码的执行。那么对于读写文件只需要建立write event和read event即可,在task中判断event ID,如果是write,就执行write函数,如果是read,就执行read函数。         同样的event的使用是event_pend和event_post。event_pend中可以设定要等待的event ID,当执行event_post该event ID后,该task下对应代码就会执行。 3、gate gate在RTOS中的作用就像是一把门锁,当前的task被高优先级task抢占线程时,会立即跳转,可有的时候我们想等待当前task执行完之后再跳转,这个时候就可以用gate了,他可以不使能占先机制,保证当前task执行期间不被抢占。gate针对HWI、SWI和task都有独立的函数来实现门锁,根据线程的不同调用不同的gate。 4、queue和mailbox queue队列基于FIFO机制,先入先出。queue使用Queue_enqueue和Queue_Dequeue来实现入队和出队;mailbox信箱用来实现task间传输buffer。使用mailbox_post函数来将数据放入信箱,使用mailbox_pend来从信箱中取出数据,如果此时信箱中没有数据,那么挂起来等待直到信箱中有数据。由此可见semaphore是基本,几种同步机制原理相同。 5、Implicitly Posted Events
event可以和信号量或者mailbox关联,这样当关联的信号量被post或者mailbox中有数据时,就会触发关联event的执行,接下来看一下sysbios user guide中的例子代码: 首先在main()函数中创建myEvent事件: mboxParams.readerEvent = myEvent;该条语句实现myEvent事件和mailbox的关联: mboxParams.readerEventId = Event_Id_00;该语句将myEvent中的event id 0和mailbox关联; Mailbox_post(mbox, &msgA, BIOS_WAIT_FOREVER);调用该函数就相当于调用了event post(event id 0),触发event id 0.
程序首先执行writetask,当信箱中有数据后,就触发了event id 0,在readtask中,当event id 0被触发,就执行if语句中的内容。总结一下就是Mailbox_post(mbox, &msgA, BIOS_WAIT_FOREVER);即post mailbox又post event id 0。 四、在CC2642上的应用 接下来以CC2642的例程ble5_simple_peripheral_cc26x2r1lp_app为例来介绍RTOS的使用: CC2642中的task都是在C文件中定义,这种方式是动态task的定义,task可以被删除。如果在.cfg文件中定义task,称之为静态task,不能被删除。 头文件中包含了sysbios中即将使用到的组件; 创建task,设置stack,这里stack一定要设置适当,如果小了会导致程序跑飞,这个task定义的1024字节;设置task优先级,这里我定义优先级为4(原工程为1)。Task_construct就创建了simplePeripheral_taskFxn任务。在main函数中执行BIOS_start()后就会跳转到优先级最高的任务中开始执行; 在task中如果想让task不止执行一次,通常要加死循环语句,通过信号量等同步模块实现任务循环执行。该处加了for(;;) Event_pend()会等待SP_ALL_EVENTS事件,如果没有触发,则该任务处于等待执行状态。  可以看到定义了两个event id 30和31的两个事件,任何一个event被post,都会触发该task执行。 在该判断中,如果SP_QUEUE_EVT事件被触发,会执行对应函数。这里的两个事件用来等待Icall传输过来的蓝牙事件(Icall的task等级为6)。  在该工程里,我又添加了两个task,如下图所示: GPStask的优先级是3,在该task里创建了semGPS这个信号量,调用semaphore_pend()去等待信号量post:  在filetask中,创建了write event和read event来触发读和写事件: 至此,关于TI RTOS的在CC2642中的应用就介绍完了,RTOS的核心就是多任务单线程机制。理解了这个概念,也就理解了RTOS的使用。