LPC1788 uCOS-III移植

1. 准备

在正式移植之前,我们需要准备base工程,现在一般MCU原厂都有提供SDK,很少有让用户自己建立base工程的情况。LPC1788这款MCU相对较老,不容易找到SDK,但是网上有很多前辈出过LPC1788的开发板,我们可以基于其配套的例程作为base工程。

推荐例程:分享自己在nxp系列的悍马1788UART例程

  1. 删除无用的文件:
    删除无用文件
  2. 删除串口源码,因为用不到

删除UART源码

  1. 新建main.c,保留main函数,更改一个自己的工程名字,本文取名“OS_Demo”,保证工程编译通过,基础准备工作完成。
#include "LPC177x_8x.h"

int main(void)
{
    return 0;
}

2. 移植

2.1. 加入源码

工程项目中,新建一个uCOS-III文件夹,专门用于存放uCOS的源码。在uCOS-III路径下新建Config文件夹,用于保存OS配置相关的文件。

uCOS-III文件目录

uC-CPU-develop,uC-LIB-develop,uC-OS3-develop这种第三方源码我们希望原封不动放入,保持不删减、不修改,对OS相关配置只需要修改Config下的文件。
uCOS-III提供了配置文件的模板,分别存放在各目录下的"Cfg/Template"目录下,如下图统一把他们拷贝到Config目录下。
拷贝配置文件

2.2. 加入工程

  1. 新建工程目录树:

新建目录树

  1. 将"uC-OS3-develop/Source"目录下与内核相关的源码加入工程:

加入内核源码

  1. 加入与处理器平台移植相关的文件,比如任务切换底层汇编实现等源码都放在"uC-OS3-develop/Ports"目录下,可以看到支持的处理器平台众多,LPC1788是Cortex-M3内核,属于ARMv7-M架构。因此:
  • 加入"uC-OS3-develop/Ports/ARM-Cortex-M/ARMv7-M/ARM"下的文件;
  • 加入"uC-OS3-develop/Ports/ARM-Cortex-M/ARMv7-M/os_cpu_c.c"。
    加入Port源码
  1. 加入"uC-LIB-develop"目录下相关源码:
    加入Lib源码

  2. 加入"uC-CPU-develop"目录下相关源码:

加入CPU源码

6.加入"Config"目录下的源码:
加入Config源码

  1. 头文件包含路径加入到工程配置:

添加OS头文件路径

至此,所有文件加入完成,可以进行一次编译。

2.3. 编译问题

第一次编译会出现如下错误,提示CPU_CFG_NVIC_PRIO_BITS未定义。

首次编译错误

打开cpu_cfg.h定位到242行,使能CPU_CFG_NVIC_PRIO_BITS的定义:

#if 1
#define  CPU_CFG_NVIC_PRIO_BITS                            4u
#endif

再次编译可以通过。

2.4. 中断适配

大多数RTOS在Cortex-M3/M4上的移植都需要Systick、PendSV。

Systick是一个定时器,用于周期性地触发中断,以提供系统时钟。在RTOS中,Systick通常用于测量时间间隔,例如用于定时器、计数器等。在Systick中断中调度任务的执行,使得任务的执行时间能够得到控制。

PendSV异常用于任务切换,为了保证操作系统的实时性,除了使用 Systick 的时间片调度,还得加入PendSV异常加入抢占式调度。

  1. 新建lpc1788_it.c,这里面加入Systick中断处理函数。
#include "os.h"
void SysTick_Handler(void)
{
    OSIntEnter();     //用于统计中断的嵌套层数,对嵌套层数+1
    OSTimeTick();     //统计时间,遍历任务,对延时任务计时减1
    OSIntExit();      //对嵌套层数减1,在退出中断前启动任务调度
}
  1. startup_LPC177x_8x.s启动汇编中,将PendSV_Handler替换为OS提供的OS_CPU_PendSVHandler

替换PendSVHandler

至此基本移植工作结束,接下来建立两个任务验证任务是否能正常调度。

3. 验证

1.重写的main,创建一个启动任务:


#define STARTUP_TASK_STK_SIZE   100
#define STARTUP_TASK_PRIO       3
static OS_TCB       StartUp_TCB;
static CPU_STK      StartUp_Stk[STARTUP_TASK_STK_SIZE];

int main(void)
{
    OS_ERR err;

    /* 初始化"uC/OS-III"内核 */
    OSInit(&err);

    /*创建任务*/
    OSTaskCreate((OS_TCB     *)&StartUp_TCB,             // 任务控制块指针
                (CPU_CHAR   *)"StartUp",                 // 任务名称
                (OS_TASK_PTR )Task_Start,                // 任务代码指针
                (void       *)0,                         // 传递给任务的参数parg
                (OS_PRIO     )STARTUP_TASK_PRIO,         // 任务优先级
                (CPU_STK    *)&StartUp_Stk[0],           // 任务堆栈基地址
                (CPU_STK_SIZE)STARTUP_TASK_STK_SIZE/10,  // 堆栈剩余警戒线
                (CPU_STK_SIZE)STARTUP_TASK_STK_SIZE,     // 堆栈大小
                (OS_MSG_QTY  )0,                         // 可接收的最大消息队列数
                (OS_TICK     )0,                         // 时间片轮转时间
                (void       *)0,                         // 任务控制块扩展信息
                (OS_OPT      )(OS_OPT_TASK_STK_CHK |
                               OS_OPT_TASK_STK_CLR),     // 任务选项
                (OS_ERR     *)&err);                     // 返回值

    /* 启动多任务系统,控制权交给uC/OS-III */
    OSStart(&err);
}
  1. 在启动任务中,调用OS_CPU_SysTickInit函数初始化Systick中断,中断周期1ms,由于Systick定时器属于Cortex-M3内核提供,所以Systick初始化uCOS-III已经帮我们写好。然后初始化系统相关的基本外设。本文创建两个LED_Task任务,用于验证OS工作是否正常。
  • 创建LED任务:
#define TASK_LED1_STK_SIZE      256
#define TASK_LED2_STK_SIZE      256
#define TASK_LED1_PRIO          4
#define TASK_LED2_PRIO          5
static OS_TCB       LED1_TCB;
static CPU_STK      LED1_Stk[TASK_LED1_STK_SIZE];
static OS_TCB       LED2_TCB;
static CPU_STK      LED2_Stk[TASK_LED2_STK_SIZE];
void Task_Start(void *p_arg)
{
    OS_ERR err;
    (void)p_arg;

    OS_CPU_SysTickInit(SystemCoreClock/1000);            /* Initialize the SysTick. */
    led_init();

    //创建任务1
    OSTaskCreate((OS_TCB     *)&LED1_TCB,                // 任务控制块指针
                 (CPU_CHAR   *)"LED1",                   // 任务名称
                 (OS_TASK_PTR )led1_task,                // 任务代码指针
                 (void       *)0,                        // 传递给任务的参数parg
                 (OS_PRIO     )TASK_LED1_PRIO,           // 任务优先级
                 (CPU_STK    *)&LED1_Stk[0],             // 任务堆栈基地址
                 (CPU_STK_SIZE)TASK_LED1_STK_SIZE/10,    // 堆栈剩余警戒线
                 (CPU_STK_SIZE)TASK_LED1_STK_SIZE,       // 堆栈大小
                 (OS_MSG_QTY  )0,                        // 可接收的最大消息队列数
                 (OS_TICK     )0,                        // 时间片轮转时间
                 (void       *)0,                        // 任务控制块扩展信息
                 (OS_OPT      )(OS_OPT_TASK_STK_CHK |
                                OS_OPT_TASK_STK_CLR),    // 任务选项
                 (OS_ERR     *)&err);                    // 返回值

    //创建任务2
    OSTaskCreate((OS_TCB     *)&LED2_TCB,               // 任务控制块指针
                 (CPU_CHAR   *)"LED2",                  // 任务名称
                 (OS_TASK_PTR )led2_task,               // 任务代码指针
                 (void       *)0,                       // 传递给任务的参数parg
                 (OS_PRIO     )TASK_LED2_PRIO,          // 任务优先级
                 (CPU_STK    *)&LED2_Stk[0],            // 任务堆栈基地址
                 (CPU_STK_SIZE)TASK_LED2_STK_SIZE/10,   // 堆栈剩余警戒线
                 (CPU_STK_SIZE)TASK_LED2_STK_SIZE,      // 堆栈大小
                 (OS_MSG_QTY  )0,                       // 可接收的最大消息队列数
                 (OS_TICK     )0,                       // 时间片轮转时间
                 (void       *)0,                       // 任务控制块扩展信息
                 (OS_OPT      )(OS_OPT_TASK_STK_CHK |
                                OS_OPT_TASK_STK_CLR),   // 任务选项
                 (OS_ERR     *)&err);                   // 返回值

    //任务删除自己
    OSTaskDel(&StartUp_TCB, &err);
}
  • LED1 Task实现LED亮500ms,灭500ms。
void led1_task(void *p_arg)
{
    OS_ERR err;
    (void)p_arg;

    while (1)
    {
        LED1_ON;
        OSTimeDlyHMSM(0, 0,0,500,OS_OPT_TIME_HMSM_STRICT,&err);    //延时500ms

        LED1_OFF;
        OSTimeDlyHMSM(0, 0,0,500,OS_OPT_TIME_HMSM_STRICT,&err);    //延时500ms
    }
}
  • LED2 Task实现LED2亮200ms,灭200ms。
void led2_task(void *p_arg)
{
    OS_ERR err;
    (void)p_arg;

    while (1)
    {
        LED2_ON;
        OSTimeDlyHMSM(0, 0,0,100,OS_OPT_TIME_HMSM_STRICT,&err);    //延时100ms

        LED2_OFF;
        OSTimeDlyHMSM(0, 0,0,100,OS_OPT_TIME_HMSM_STRICT,&err);    //延时100ms
    }

}
  1. 如下图使用逻辑分析仪测量LED1、LED2的IO翻转时间为预期,证明系统时钟和Systick等配置正确无误。
    逻辑分析仪测量时间结果
    本文是一篇实战性较强的文章,重点指导大家如何在LPC1788如何移植uCOS-III,一些思路和方法,同样可以迁移到其他处理器平台,对于uCOS-III基础知识推荐这篇博客:实时操作系统UCOS学习笔记5—-UCOSIII移植
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值