本帖最后由 okwh 于 2019-3-23 15:35 编辑
【IRD-LPC1768-DEV】 从裸机 (bare metal)到RTOS C -- 3 上篇用CMSIS-RTOSV1 + Keil RTX 初步试验了RTX, 本篇分析下目前CMSIS-RTOS的目前状况。
1 CMSIS-RTOS CMSIS标准的软件架构主要有四层:用户应用层(中间件)、操作系统层、CMSIS层和硬件寄存器层。CMSIS层分3个部分:核内外设访问层(CPAL),由ARM负责实现对寄存器名称、地址定义、NVIC中断接口等定义。片上外设访问层(DPAL)和外设访问函数(AFP),由芯片厂商负责实现。目前已增加了DSP、SVD、NN、DAP等有用组件。
目前版本为CMSIS Version 5.4.0, CMSIS-RTOS2 Version 2.1.3, Keil RTX version 5(RTX5)(RTX5为RTX version 4 and CMSIS-RTOS V1 提供兼容支持)。CMSIS软件包在 http://github.com/ARM-software/CMSIS_5开源,ARM.CMSIS.5.4.0.pack和不需要硬件支持基于模拟器的学习包Hitex.CMSIS_RTOS_Turorial.xxxx.pack以及各芯片的Pack可随时在uVision下载使用。 CMSIS-RTOS2 API 就是个通用API, 符合CMSIS-RTOS标准的任何RTOS核都可被使用。 KeilRTX version 5是keil的一个RTOS核实现,随uVision环境自动提供。另外似乎为uVision支持的其他芯片也可类似使用Keil RTX,比如RTX51 Tiny就是一个更简单的RTX。 换句话说,使用CMSIS-RTOS2API写程序,基本上可以不管实际的OS是什么了(除了做大同小异的配置)。RTX5 C 源代码质量合乎MISRA C:2012汽车级软件编码标准规范 (MISRA (The Motor Industry Software Reliability Association 汽车工业软件可靠性联会),保障了软件易读、可靠安全、可移植、易于维护,还可用uVision的Tools菜单以PC_Lint静态代码分析工具对嵌入软件质量的进行分析和工程规范评价。编译器调试器提供DAP提供了Event Recorder等很好的软件调试途径。 Keil RTX5目前已支持在ARM、IAR、GNU编译工具链,目前支持的芯片主要包括Cortex-M0/M0+/M23,Cortex-M3/M4/M7/M33,Cortex-A5/A7/A9。 未来CMSIS 软件包本身还将提供了ARM::CMSIS-RTOS_Validation用于RTOS规范测试ttps://github.com/xpacks/arm-cmsis-rtos-validator。C++版的CMSIS也在开发中。 只是这两者进展极其缓慢。
2 嵌入程序的ABC 裸机程序大致这个样子+中断: (相当于轮询判断处理+中断处理) void main(void) { 初始化 while(1) { DoThing1( ); //+标志判断 DoThing2( ); DoThing3 ( ); //…………. } } 有OS的大致这个样子+中断:(OS用一个计数器调度所有线程函数什么时候或轮询或优先执行,协调各种任务间的关系、协调各任务的紧急程度,协助合理完成其他事务如资源分配、数据传递等) Thing1( ); Thing2( ); Thing3( ); void main(void) { 初始化 os_init(); os_create_task(Thing1); os_create_task(Thing2); os_create_task(Thing3); ……. os_start(); }
3 CMSIS-RTOSV2 + Keil RTX5 CMSIS-RTOSV2 + Keil RTX5比以前有了不错的改进,更规范清晰,主要是去除了宏定义,API分下表十类, 可以清晰按图索骥。http://www.keil.com/pack/doc/CMSIS/RTOS2/html/rtos_api2.html 基础知识准备:状态机。 CMSIS-RTOS V2 + Keil RTX5 功能分类 | Kernel Information and Control | | | | | | | | | | | | | | | | | | | | | | | | cmsis_os2.h头文件中实现所有CMSIS-RTOS C API v2函数 RTX_Config.h RTX_Config.c 是RTX 配置
os_systick.c 是OS Tick API
rtx_os.h 是RTX 用的各种数据结构定义 |
重要事项: 1) 中断服务中,最好1ms(默认调度时间基准单位)内完成,仅仅用于发送信号、设置标志,真正处理留在高优先级别线程进行; 2) RTX5 任务调度基于三类:基于时间轮询的SysTick_Handler(对应rtx_system.c的osRtxTick_Handler,调度普通线程), 锁定停用低级线程、处理优先执行的SVC_Handler(调度OS系统级线程),配合中断处理的最高优先的PendSV_Handler(对应rtx_system.c 的osRtxPendSV_Handler调度中断服务线程); 3) RTX5不含其他设备部分,那些可用CMSIS的设备驱动RTE (Run-TimeEnvironment) Device、DFP (Device Family Pack)或自行基于厂家的Chip Driver、HAL(hardware abstraction layer)、BSP(Board Support Package)建立。 4) 包含RTX5源代码的工程需要设置C/C++编译选项 C99 Mode启用。
工程建立过程: 选芯片,在运行时环境选添加CMSIS Core、startup、PIN、GPIO、RTOS Keil RTX5库组件等。
配置RTX: RTX_Config.h RTX_Config.c
主程序结构大致如下: //#include "RTE_Components.h" ///#include CMSIS_device_header #include "cmsis_os2.h"
/*---- Application1/2/3/4/5… thread --------*/ void app1 (void *argument) { for(;;) {} } void app2 (void *argument) { for(;;) {} }
int main (void) { SystemCoreClockUpdate(); //System Initialization //... 其他初始化 osKernelInitialize(); // Initialize CMSIS-RTOS osThreadNew(app1, NULL, NULL); // Create application1 thread osThreadNew(app2, NULL, NULL); // Create application2 thread osKernelStart(); // Start thread execution for(;;) {} }
4 例程 将前文的CMSIS-RTOS1 API+Keil RTX 4.81改为CMSIS-RTOS2 API+Keil RTX5
- /*----------------------------------------------------------------------------
- * RL-ARM - RTX5 IRD-LPC1768 test
- *---------------------------------------------------------------------------*/
- #include <b><font color="#ff0000">"cmsis_os2.h" </font></b> // 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 <b>LED_Initialize</b> (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;
- }
- <b>osThreadId_t</b> tid_phaseA; /* Thread id of task: phase_a */
- osThreadId_t tid_phaseB; /* Thread id of task: phase_b */
- osThreadId_t tid_phaseC; /* Thread id of task: phase_c */
- osThreadId_t tid_phaseD; /* Thread id of task: phase_d */
- osThreadId_t tid_Other; /* Thread id of task: anyOther */
- /*--- Function 'signal_func' called from multiple threads ----*/
- void signal_func (osThreadId_t tid) { // 做公共处理、触发后续其他线程等
- <b> osThreadFlagsSet</b>(tid_Other, 0x0100); /* set signal to any other thread */
- osDelay(500); /* delay 500ms */
- osThreadFlagsSet(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 *argument) {
- for (;;) {
- <b> osThreadFlagsWait</b>(0x0001,osFlagsWaitAny, 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 *argument) {
- for (;;) {
- osThreadFlagsWait(0x0001,osFlagsWaitAny, 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 *argument) {
- for (;;) {
- osThreadFlagsWait(0x0001,osFlagsWaitAny, 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 *argument) {
- for (;;) {
- osThreadFlagsWait(0x0001,osFlagsWaitAny, 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 *argument) { // can be used to do other things
- for (;;) {
- osThreadFlagsWait(0x0100,osFlagsWaitAny, osWaitForever); /* wait for an event flag 0x0100 */ //
- osDelay(100); /* delay 100ms text this osDelay time > that at signal_func ??? */
- }
- }
- /*----------------------------------------------------------------------------
- * Main: Initialize and start RTX Kernel
- *---------------------------------------------------------------------------*/
- int <b>main </b>(void) {
- SystemCoreClockUpdate (); /* Update system core clock 100MHz */
- // SysTick_Config(SystemCoreClock/10); /* SysTick 中断 each 100 ms */
- // 不需要了, RTX 初始化SysTick,用于RTOS的调度计时,不得再它用
- LED_Initialize(); /* Initialize the LEDs */
- <b> osKernelInitialize</b>();
- tid_phaseA = <b>osThreadNew</b>(phaseA, NULL, NULL); //创建线程
- tid_phaseB = osThreadNew(phaseB, NULL, NULL);
- tid_phaseC = osThreadNew(phaseC, NULL, NULL);
- tid_phaseD = osThreadNew(phaseD, NULL, NULL);
- tid_Other = osThreadNew(Other, NULL, NULL);
- <b> osThreadFlagsSet</b>(tid_phaseA, 0x0001); /* set signal to phaseA thread 触发第一个线程 */
- <b> osKernelStart</b>();
- //osDelay(osWaitForever);
- while(1);
- }
复制代码
编译后,Program Size: Code=13412 RO-data=464RW-data=5140 ZI-data=612
工程文件:
RTX5.zip
(30.05 KB, 下载次数: 2)
|