本帖最后由 okwh 于 2019-3-23 15:34 编辑
【IRD-LPC1768-DEV】 从裸机(bare metal)到RTOS B -- 2
上篇实验了裸机编程,本篇试验嵌入实时操作系统
1 嵌入实时操作系统: 通常需要设计比较复杂控制逻辑的、需要控制众多不同响应要求的硬件设备时,且内存比较充足,才需要以操作系统的方式编程嵌入软件。 目前可用的免费RTOS较多,如Keil RTX、mbed-os、freeRTOS、uCOS I/II/III、RT-Thread等等。我们主要关心Contex-M芯片上可用的RTOS. 就RTOS核来说,其实所有OS都差不多,不外乎 1) 任务/线程调度:基于函数(内含永远循环运行的代码)线程的建立,优先级、轮转调度、抢先调度、序列传递执行,线程状态:就绪态READY、运行态RUNNING、阻塞等待态WAIT、不活跃态INACTIVE或挂起锁定lock与恢复resume。 2) 任务同步和通讯机制:信号signal或事件event:用于异步的触发或组合触发;semaphore(信号灯):用于对单或多的执行许可;mutex(互斥锁)用于某资源通常是代码资源的独占执行;message消息或mail box邮箱或share momery:以队列、缓冲池、链表等方式用于线程间通讯。 3) 时间管理:系统硬定时器、软间定时器,优先级与时间片,等待、锁定、延时和条件延时、超时处理等。 4) 静态动态内存管理:内存池、内存堆管理、零拷贝数据传送; 5) 中断:通常,中断响应中尽可能仅设置信号或事件标志就退出,后在高优先线程中做后续完全处理。注意中断中可使用的OS函数; 6) 调试诊断与软件开发支持; 7) 设备管理:非芯片、板级设备驱动或中间件,可选或可不包含在核中; 8) 其他:出错恢复、存储器保护、多核多处理器支持等。
诚然,学习OS是间不容易的事,但对比前文、由易出发、按需要配置,可以很容易循序渐进掌握。 本篇就以堪称最容易最方便的Keil RTX为例,初步理解RTOS。
2 KeilRTX 众所周知,ARM® Cortex™ 微控制器软件接口标准(CMSIS--Cortex Microcontroller Software Interface Standard) 是 Cortex-M 处理器系列的与供应商无关的硬件抽象层。CMSIS 可实现与处理器和外设之间的一致且简单的软件接口,从而简化软件的重用,缩短微控制器开发人员新手的学习过程,并缩短新设备的上市时间。而且CMSIS、传统的寄存器直接编程、厂家提供的驱动等,可以任意混合或独立使用,随你喜好。CMSIS包含多部分CMSIS-CORE、CMSIS-DSP、CMSIS-RTOS API、CMSIS-SVD(这个SVD用于厂家建立芯片的标准信息-系统视图描述--或者说给软件看的芯片手册,进而可由软件工具自动生成芯片的全部驱动代码,就是pack中的 device 目录和RTE_Driver目录)、Driver、PACK、DAP等。目前可用于Contex-M和部分A系列。 显然,CMSIS已经包含了一个统一的CMSIS-RTOSAPI,用于实时操作系统的标准化或者说方便化。 任何第三方都可不太难的设计一些实时调度算法核,无需提供源代码,而且用户只需使用统一的CMSIS-RTOS API即可,甚至即使换了硬件,都无需多大改动。 Keil就实现了一个Keil RTX,使用极其方便,上手也堪称极其容易,且提供源代码(总共十几个文件,合计200多k源代码),甚至可以从中学习自己设计RTOS。
细节参考: C:\Keil\ARM\Pack\ARM\CMSIS\5.4.0\CMSIS\Pack http://github.com/ARM-software/CMSIS_5
仅使用的话,其实可以不管Keil RTX,只需使用CMSIS-RTOSAPI即可。 Keil甚至提供通过无需硬件、可纯软件模拟运行的例子,用于很容易一步步地学习。uVision Simulation的例子:Keil\ARM\Pack\Hitex\CMSIS_RTOS_Tutorial\1.1.0\Examples 优缺点: 在uVsion环境中,可以很容易很方便的配置组合开发,目前主要用于Contex-M和Contex- A。
uVision ARM-MDK体系: CMSIS体系:
3 例程 这里以RTX库,使用CMSIS-RTOSV1 实现4LED 序列明灭 Keil uVision MDK- ARM 5.22, 生成新工程,选 lpc1768,Keil.LPC1700_DFP.2.5.0, 从运行时环境添加CMSIS Core、startup、PIN、GPIO、RTOS Keil RTX库组件。
创建4个线程tid_phaseA/B/C/D分别对应控制4个LED的亮灭,并依次以signal循环触发,一个后续线程tid_Other用于可能的后续工作。
从调试堆栈stack, 可以看出总共有8个线程,4个线程tid_phaseA/B/C/D,一个后续线程tid_Other,一个main本身,一个计时器线程osTimerThread,一个后备工作线程os_idle_Demon。后两个个是缺省的,可配置
RTX当然是可配置的,配置RTX_Conf_CM.C记录,可以看出RTX 配置很简单。
程序代码如下,几乎是裸机版的翻版,但拥有8个线程,可以做为很复杂系统的基本框架了。
- /*----------------------------------------------------------------------------
- * RL-ARM - RTX5 IRD-LPC1768 test
- *---------------------------------------------------------------------------*/
- #include "cmsis_os.h" // ARM::CMSIS:RTOS:Keil RTX5
- //#include "cmsis_os2.h" // ARM::CMSIS:RTOS2:Keil RTX5
- #include "LPC17xx.h"
- #include "PIN_LPC17xx.h"
- #include "GPIO_LPC17xx.h"
- #define LED_COUNT (4)
- const PIN LED_PIN[] = { //后3个 相关USB,做led 就不要用usb
- {0, 7},
- {1, 18},
- {2, 9},
- {1, 22 },
- };
- int32_t LED_Initialize (void) { // led初始化 GPIO
- uint32_t n;
- GPIO_PortClock (1); /* Enable GPIO clock */
- for (n = 0; n < LED_COUNT; n++) { /* Configure pins: Output Mode with Pull-down resistors */
- PIN_Configure (LED_PIN[n].Portnum, LED_PIN[n].Pinnum, PIN_FUNC_0, PIN_PINMODE_PULLDOWN, PIN_PINMODE_NORMAL);
- GPIO_SetDir (LED_PIN[n].Portnum, LED_PIN[n].Pinnum, GPIO_DIR_OUTPUT);
- GPIO_PinWrite (LED_PIN[n].Portnum, LED_PIN[n].Pinnum, 0);
- }
- GPIO_PinWrite (LED_PIN[1].Portnum, LED_PIN[1].Pinnum, 1); // P1.18 led负端, 1灭
- GPIO_PinWrite (LED_PIN[2].Portnum, LED_PIN[2].Pinnum, 1); // P2.9 led负端, 1灭
- return 0;
- }
- osThreadId tid_phaseA; /* Thread id of task: phase_a */
- osThreadId tid_phaseB; /* Thread id of task: phase_b */
- osThreadId tid_phaseC; /* Thread id of task: phase_c */
- osThreadId tid_phaseD; /* Thread id of task: phase_d */
- osThreadId tid_Other; /* Thread id of task: anyOther */
- /*----------------------------------------------------------------------------
- * Function 'signal_func' called from multiple threads
- *---------------------------------------------------------------------------*/
- void signal_func (osThreadId tid) { // 做公共处理、触发后续其他线程等
- osSignalSet(tid_Other, 0x0100); /* set signal to any other thread */
- osDelay(4500); /* delay 500ms */
- osSignalSet(tid, 0x0001); /* set signal to thread next one of ABCD 'threads' */
- osDelay(500); /* delay 500ms */
- }
- /*
- void signal_func (osThreadId tid) { // 只简单用于设置触发后续其他线程的标志
- osSignalSet(tid, 0x0001); // set signal to thread 'thread'
- }
- */
- /*----------------------------------------------------------------------------
- * Thread 1 'phaseA': Phase A output
- *---------------------------------------------------------------------------*/
- void phaseA (void const *argument) {
- for (;;) {
- osSignalWait(0x0001, osWaitForever); /* wait for an event flag 0x0001 for start this thread */
- GPIO_PinWrite(LED_PIN[3].Portnum, LED_PIN[3].Pinnum,1); //On
- signal_func(tid_phaseB); /* call common signal function to trigger next thread*/
- GPIO_PinWrite (LED_PIN[3].Portnum, LED_PIN[3].Pinnum, 0); //Off
- }
- }
- /*----------------------------------------------------------------------------
- * Thread 2 'phaseB': Phase B output
- *---------------------------------------------------------------------------*/
- void phaseB (void const *argument) {
- for (;;) {
- osSignalWait(0x0001, osWaitForever); /* wait for an event flag 0x0001 for start this thread*/
- GPIO_PinWrite(LED_PIN[2].Portnum, LED_PIN[2].Pinnum,0); //On
- signal_func(tid_phaseC); /* call common signal function to trigger next thread*/
- GPIO_PinWrite(LED_PIN[2].Portnum, LED_PIN[2].Pinnum,1); //Off
- }
- }
- /*----------------------------------------------------------------------------
- * Thread 3 'phaseC': Phase C output
- *---------------------------------------------------------------------------*/
- void phaseC (void const *argument) {
- for (;;) {
- osSignalWait(0x0001, osWaitForever); /* wait for an event flag 0x0001 */
- GPIO_PinWrite(LED_PIN[1].Portnum, LED_PIN[1].Pinnum,0); //On
- signal_func(tid_phaseD); /* call common signal function */
- GPIO_PinWrite(LED_PIN[1].Portnum, LED_PIN[1].Pinnum,1); //Off
- }
- }
- /*----------------------------------------------------------------------------
- * Thread 4 'phaseD': Phase D output
- *---------------------------------------------------------------------------*/
- void phaseD (void const *argument) {
- for (;;) {
- osSignalWait(0x0001, osWaitForever); /* wait for an event flag 0x0001 */
- GPIO_PinWrite(LED_PIN[0].Portnum, LED_PIN[0].Pinnum,1); //On
- signal_func(tid_phaseA); /* call common signal function */
- GPIO_PinWrite(LED_PIN[0].Portnum, LED_PIN[0].Pinnum,0); //Off
- }
- }
- /*----------------------------------------------------------------------------
- * Thread 5 'Other'
- *---------------------------------------------------------------------------*/
- void Other (void const *argument) { // can be used to do other things
- for (;;) {
- osSignalWait(0x0100, osWaitForever); /* wait for an event flag 0x0100 */ //
- osDelay(100); /* delay 100ms text this osDelay time > that at signal_func ??? */
- }
- }
- osThreadDef(phaseA, osPriorityNormal, 1, 0); //设置优先级
- osThreadDef(phaseB, osPriorityNormal, 1, 0);
- osThreadDef(phaseC, osPriorityNormal, 1, 0);
- osThreadDef(phaseD, osPriorityNormal, 1, 0);
- osThreadDef(Other, osPriorityNormal, 1, 0);
- /*----------------------------------------------------------------------------
- * Main: Initialize and start RTX Kernel
- *---------------------------------------------------------------------------*/
- int main (void) {
- SystemCoreClockUpdate (); /* Update system core clock 100MHz */
- // SysTick_Config(SystemCoreClock/10); /* SysTick 中断 each 100 ms */
- // 不需要了, RTX 做系统初始化, SysTick被用于RTOS的调度计时,不得再它用
- LED_Initialize(); /* Initialize the LEDs */
- tid_phaseA = osThreadCreate(osThread(phaseA), NULL); //创建线程
- tid_phaseB = osThreadCreate(osThread(phaseB), NULL);
- tid_phaseC = osThreadCreate(osThread(phaseC), NULL);
- tid_phaseD = osThreadCreate(osThread(phaseD), NULL);
- tid_Other = osThreadCreate(osThread(Other), NULL);
- osSignalSet(tid_phaseA, 0x0001); /* set signal to phaseA thread 触发第一个线程 */
- osDelay(osWaitForever);
- while(1);
- }
复制代码
Program Size: Code=7968 RO-data=428 RW-data=84 ZI-data=3732
只是编译后的代码大了不少,从用来裸机的2K多增加到这个的近9K !
|