查看: 3457|回复: 3

[原创] IRDLPC1768 从裸机(bare metal)到RTOS B -- 2

[复制链接]
回帖奖励 99 NXP金币 回复本帖可获得 1 NXP金币奖励! 每人限 1 次
  • TA的每日心情
    擦汗
    2021-7-5 15:45
  • 签到天数: 664 天

    连续签到: 1 天

    [LV.9]以坛为家II

    42

    主题

    1594

    帖子

    19

    金牌会员

    Rank: 6Rank: 6

    积分
    5618
    最后登录
    2021-12-22
    发表于 2019-3-18 00:06:14 | 显示全部楼层 |阅读模式
    本帖最后由 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体系:
    0201.png
           CMSIS体系:
    0202.png


    3  例程   这里以RTX库,使用CMSIS-RTOSV1 实现4LED 序列明灭
    Keil uVision MDK- ARM 5.22,  生成新工程,选 lpc1768,Keil.LPC1700_DFP.2.5.0, 从运行时环境添加CMSIS CorestartupPINGPIORTOS Keil RTX库组件。
    0203.png

    创建4个线程tid_phaseA/B/C/D分别对应控制4个LED的亮灭,并依次以signal循环触发,一个后续线程tid_Other用于可能的后续工作。
    0204.png

           从调试堆栈stack, 可以看出总共有8个线程,4个线程tid_phaseA/B/C/D,一个后续线程tid_Other,一个main本身,一个计时器线程osTimerThread,一个后备工作线程os_idle_Demon。后两个个是缺省的,可配置

          RTX当然是可配置的,配置RTX_Conf_CM.C记录,可以看出RTX 配置很简单。

    0205.png

    程序代码如下,几乎是裸机版的翻版,但拥有8个线程,可以做为很复杂系统的基本框架了。

    1. /*----------------------------------------------------------------------------
    2. *      RL-ARM - RTX5 IRD-LPC1768 test
    3. *---------------------------------------------------------------------------*/
    4. #include "cmsis_os.h"                   // ARM::CMSIS:RTOS:Keil RTX5
    5. //#include "cmsis_os2.h"                  // ARM::CMSIS:RTOS2:Keil RTX5
    6. #include "LPC17xx.h"                  
    7. #include "PIN_LPC17xx.h"
    8. #include "GPIO_LPC17xx.h"

    9. #define LED_COUNT (4)
    10. const PIN LED_PIN[] = {     //后3个 相关USB,做led  就不要用usb
    11.   {0, 7},
    12.   {1, 18},
    13.   {2, 9},
    14.   {1, 22 },
    15. };
    16. int32_t LED_Initialize (void) {       // led初始化  GPIO
    17.   uint32_t n;
    18.   GPIO_PortClock     (1);          /* Enable GPIO clock */
    19.   for (n = 0; n < LED_COUNT; n++) {              /* Configure pins: Output Mode with Pull-down resistors */
    20.     PIN_Configure (LED_PIN[n].Portnum, LED_PIN[n].Pinnum, PIN_FUNC_0, PIN_PINMODE_PULLDOWN, PIN_PINMODE_NORMAL);
    21.     GPIO_SetDir   (LED_PIN[n].Portnum, LED_PIN[n].Pinnum, GPIO_DIR_OUTPUT);
    22.     GPIO_PinWrite (LED_PIN[n].Portnum, LED_PIN[n].Pinnum, 0);
    23.   }
    24.   GPIO_PinWrite (LED_PIN[1].Portnum, LED_PIN[1].Pinnum, 1);  // P1.18 led负端, 1灭
    25.         GPIO_PinWrite (LED_PIN[2].Portnum, LED_PIN[2].Pinnum, 1);  // P2.9 led负端, 1灭
    26.   return 0;
    27. }

    28. osThreadId tid_phaseA;                  /* Thread id of task: phase_a */
    29. osThreadId tid_phaseB;                  /* Thread id of task: phase_b */
    30. osThreadId tid_phaseC;                  /* Thread id of task: phase_c */
    31. osThreadId tid_phaseD;                  /* Thread id of task: phase_d */
    32. osThreadId tid_Other;                   /* Thread id of task: anyOther   */

    33. /*----------------------------------------------------------------------------
    34. *      Function 'signal_func' called from multiple threads
    35. *---------------------------------------------------------------------------*/
    36. void signal_func (osThreadId tid)  {      // 做公共处理、触发后续其他线程等
    37.   osSignalSet(tid_Other, 0x0100);           /* set signal to any other thread    */
    38.   osDelay(4500);                             /* delay 500ms                   */
    39.   osSignalSet(tid, 0x0001);                 /* set signal to thread next one of ABCD 'threads' */
    40.   osDelay(500);                             /* delay 500ms                   */
    41. }
    42. /*
    43. void signal_func (osThreadId tid)  {      // 只简单用于设置触发后续其他线程的标志
    44.   osSignalSet(tid, 0x0001);                 // set signal to thread  'thread'
    45. }
    46. */

    47. /*----------------------------------------------------------------------------
    48. *      Thread 1 'phaseA': Phase A output
    49. *---------------------------------------------------------------------------*/
    50. void phaseA (void const *argument) {
    51.   for (;;) {
    52.     osSignalWait(0x0001, osWaitForever);    /* wait for an event flag 0x0001  for start this thread */
    53.     GPIO_PinWrite(LED_PIN[3].Portnum, LED_PIN[3].Pinnum,1);        //On
    54.     signal_func(tid_phaseB);                /* call common signal function   to trigger next thread*/
    55.     GPIO_PinWrite (LED_PIN[3].Portnum, LED_PIN[3].Pinnum, 0);     //Off
    56.   }
    57. }                                                                           

    58. /*----------------------------------------------------------------------------
    59. *      Thread 2 'phaseB': Phase B output
    60. *---------------------------------------------------------------------------*/
    61. void phaseB (void const *argument) {
    62.   for (;;) {
    63.     osSignalWait(0x0001, osWaitForever);    /* wait for an event flag 0x0001 for start this thread*/
    64.     GPIO_PinWrite(LED_PIN[2].Portnum, LED_PIN[2].Pinnum,0);  //On
    65.     signal_func(tid_phaseC);                /* call common signal function   to trigger next thread*/
    66.     GPIO_PinWrite(LED_PIN[2].Portnum, LED_PIN[2].Pinnum,1);   //Off
    67.   }
    68. }

    69. /*----------------------------------------------------------------------------
    70. *      Thread 3 'phaseC': Phase C output
    71. *---------------------------------------------------------------------------*/
    72. void phaseC (void const *argument) {
    73.   for (;;) {
    74.     osSignalWait(0x0001, osWaitForever);    /* wait for an event flag 0x0001 */
    75.     GPIO_PinWrite(LED_PIN[1].Portnum, LED_PIN[1].Pinnum,0);  //On
    76.     signal_func(tid_phaseD);                /* call common signal function   */
    77.     GPIO_PinWrite(LED_PIN[1].Portnum, LED_PIN[1].Pinnum,1);   //Off
    78.   }
    79. }

    80. /*----------------------------------------------------------------------------
    81. *      Thread 4 'phaseD': Phase D output
    82. *---------------------------------------------------------------------------*/
    83. void phaseD (void  const *argument) {
    84.   for (;;) {
    85.     osSignalWait(0x0001, osWaitForever);    /* wait for an event flag 0x0001 */
    86.     GPIO_PinWrite(LED_PIN[0].Portnum, LED_PIN[0].Pinnum,1);  //On
    87.     signal_func(tid_phaseA);                /* call common signal function   */
    88.     GPIO_PinWrite(LED_PIN[0].Portnum, LED_PIN[0].Pinnum,0);   //Off
    89.   }
    90. }

    91. /*----------------------------------------------------------------------------
    92. *      Thread 5 'Other'
    93. *---------------------------------------------------------------------------*/
    94. void Other (void  const *argument) {          // can be used  to do other things
    95.   for (;;) {
    96.     osSignalWait(0x0100, osWaitForever);    /* wait for an event flag 0x0100 */  //
    97.     osDelay(100);                            /* delay 100ms    text  this osDelay time > that at signal_func ???             */
    98.   }
    99. }

    100. osThreadDef(phaseA, osPriorityNormal, 1, 0);   //设置优先级
    101. osThreadDef(phaseB, osPriorityNormal, 1, 0);
    102. osThreadDef(phaseC, osPriorityNormal, 1, 0);
    103. osThreadDef(phaseD, osPriorityNormal, 1, 0);
    104. osThreadDef(Other,  osPriorityNormal, 1, 0);

    105. /*----------------------------------------------------------------------------
    106. *      Main: Initialize and start RTX Kernel
    107. *---------------------------------------------------------------------------*/
    108. int main (void) {
    109.   SystemCoreClockUpdate ();                 /* Update system core clock    100MHz   */  
    110. // SysTick_Config(SystemCoreClock/10);      /* SysTick 中断 each 100 ms  */
    111. // 不需要了, RTX 做系统初始化,  SysTick被用于RTOS的调度计时,不得再它用
    112.   LED_Initialize();                         /* Initialize the LEDs           */

    113.   tid_phaseA = osThreadCreate(osThread(phaseA), NULL);      //创建线程
    114.   tid_phaseB = osThreadCreate(osThread(phaseB), NULL);
    115.   tid_phaseC = osThreadCreate(osThread(phaseC), NULL);
    116.   tid_phaseD = osThreadCreate(osThread(phaseD), NULL);
    117.   tid_Other  = osThreadCreate(osThread(Other),  NULL);

    118.   osSignalSet(tid_phaseA, 0x0001);          /* set signal to phaseA thread  触发第一个线程  */

    119.   osDelay(osWaitForever);
    120.   while(1);
    121. }
    复制代码

    Program Size: Code=7968 RO-data=428 RW-data=84 ZI-data=3732  

    只是编译后的代码大了不少,从用来裸机的2K多增加到这个的近9K !


    工程代码: irdlpc1768devRTXB.zip (29.99 KB, 下载次数: 4)
    该会员没有填写今日想说内容.
    回复

    使用道具 举报

  • TA的每日心情
    擦汗
    2021-7-5 15:45
  • 签到天数: 664 天

    连续签到: 1 天

    [LV.9]以坛为家II

    42

    主题

    1594

    帖子

    19

    金牌会员

    Rank: 6Rank: 6

    积分
    5618
    最后登录
    2021-12-22
     楼主| 发表于 2019-3-23 15:35:12 | 显示全部楼层

    论坛不活跃, 回帖奖励!!
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2025-7-11 08:53
  • 签到天数: 301 天

    连续签到: 2 天

    [LV.8]以坛为家I

    3868

    主题

    7472

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    39232
    最后登录
    2025-7-18
    发表于 2019-4-2 09:10:21 | 显示全部楼层

    回帖奖励 +1 NXP金币

    管管来晚了。。
    qiandao qiandao
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2021-7-5 15:45
  • 签到天数: 664 天

    连续签到: 1 天

    [LV.9]以坛为家II

    42

    主题

    1594

    帖子

    19

    金牌会员

    Rank: 6Rank: 6

    积分
    5618
    最后登录
    2021-12-22
     楼主| 发表于 2019-4-2 10:18:35 | 显示全部楼层

       你也缺金币?
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /3 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2025-7-20 20:02 , Processed in 0.118669 second(s), 23 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表