OS-ucos

    技术2022-05-20  36

    基于44b0xucos开发,网上开源很多,可以参考,但是最有益的是一切从头自己搭建,自己调试!环境基于ADS+wiggler调试。

    平台最主要的是清晰,一目了然,而且换不同cpu不同的os,代码结构不需要重大更改,比如ucos升级到新的版本,直接替换相应目录即可;比如如果应用比较大,需要更换操作系统,kernel的系统api做封装就不用担心了;比如cpu芯片更换或者升级,只需要更换相应的bsp;总之改动做到最小

    搭建的环境目录如下:

    ucos目录是官方os代码

    bsp/common目录是ucos的基于ARM7的官方SDK

    bsp/cpu目录是三星44b0x的官方SDK包,增加bsp.c文件,包含系统的外部中断处理函数,以及系统时钟的实现,延时函数,当然cpu的初始化函数port_init也在此实现

    kernel目录是提供os的系统API封装,printfudelay

    app目录是main函数入口,主要是多任务调度以及消息队列

    file://D:/ucos2    (7 folders, 2 files, 445.72 KB, 4.46 MB in total.)

    ucos.elf    234.92 KB

    ucos2.mcp    210.81 KB

    ├─app    (0 folders, 2 files, 15.86 KB, 15.86 KB in total.)

    main.c    7.64 KB

    main.c.bak    8.22 KB

    ├─bsp    (2 folders, 0 files, 0 bytes, 125.74 KB in total.)

    ├─cpu    (2 folders, 0 files, 0 bytes, 67.50 KB in total.)

    │├─src    (0 folders, 5 files, 37.76 KB, 37.76 KB in total.)

    ││44binit.s    13.63 KB

    ││44blib.c    8.38 KB

    ││bsp.c    4.87 KB

    ││bsp.c.bak    3.00 KB

    ││rtl8019.c    7.88 KB

    │└─inc    (0 folders, 11 files, 29.73 KB, 29.73 KB in total.)

    │44b.h    15.28 KB

    │44blib.h    1.54 KB

    │bsp.h    460 bytes

    │bsp.h.bak    486 bytes

    │def.h    434 bytes

    │includes.h    1.30 KB

    │Memcfg.s    2.49 KB

    │Option.h    837 bytes

    │Option.s    1.22 KB

    │rtl8019.h    5.02 KB

    │types.h    751 bytes

    └─common    (3 folders, 0 files, 0 bytes, 58.24 KB in total.)

    ├─src    (2 folders, 1 files, 11.97 KB, 31.38 KB in total.)

    │os_cpu_c.c    11.97 KB

    │├─arm    (0 folders, 1 files, 9.95 KB, 9.95 KB in total.)

    ││os_cpu_a.S    9.95 KB

    │└─gcc    (0 folders, 1 files, 9.46 KB, 9.46 KB in total.)

    │os_cpu_a.Sgcc    9.46 KB

    ├─arm    (0 folders, 1 files, 4.04 KB, 4.04 KB in total.)

    │ac_halt.c    4.04 KB

    └─inc    (0 folders, 5 files, 22.82 KB, 22.82 KB in total.)

    armdefs.h    2.21 KB

    def.h    2.59 KB

    halt.h    2.57 KB

    os_cfg.h    8.15 KB

    os_cpu.h    7.30 KB

    ├─kernel    (0 folders, 2 files, 502 bytes, 502 bytes in total.)

    xkernel.c    223 bytes

    xkernel.h    279 bytes

    ├─lwip-1.1.0    (2 folders, 4 files, 22.65 KB, 659.37 KB in total.)

    .......

    ├─shell    (0 folders, 3 files, 36.02 KB, 36.02 KB in total.)

    cmds.c    15.17 KB

    shell.c    18.83 KB

    shell.h    2.01 KB

    ├─ucos    (0 folders, 10 files, 294.83 KB, 294.83 KB in total.)

    os_core.c    43.44 KB

    os_flag.c    43.76 KB

    os_mbox.c    23.29 KB

    os_mem.c    13.79 KB

    os_mutex.c    27.26 KB

    os_q.c    33.79 KB

    os_sem.c    19.04 KB

    os_task.c    35.21 KB

    os_time.c    9.68 KB

    ucos_ii.h    45.59 KB

    └─

    1.        移植流程

    内部时钟:先搭好ucosbsp,不需要修改任何文件,新建main.c,新建两个task看能否在之间调度;显然是不可能的,所以动手写的第一行代码就是系统时钟函数,新建bsp.c,作为三星的SDK的补充来用,用timer0做系统tick,定时会产生内部中断,__vT0Interrupt是中断处理函数,这里挂ucosBSPAPI就行,当然别忘记了rINTMSK = ~(BIT_GLOBAL |BIT_TIMER0); 打开timer0中断,至于中断方式是向量中断还是非向量中断方式,在interrupt文章中记录,具体配置参考注释

    rTCFG0=(MCLK>>1)/1000000-1;

    /* 此为预分频值,MCLK为外部时钟频率 */

    rTCFG1=0x00000000;

    /* mux0 = 1/2 */

    /* 定时器0输入时钟频率=MCLK/(预分频值+1)/mux0,=MCLK/[(MCLK>>1)/1000000-1 + 1]/(1/2),计算得输入时钟频率为1M */

    /* 实际应用中是根据需要的输入时钟频率来赋值1000000 */      

    rTCNTB0= 1000000/OS_TICKS_PER_SEC;

    /* 每一秒有OS_TICKS_PER_SECtick产生,这个根据实际需要更改 */ 

    rTCMPB0= 0x0;

    rTCON=0x2;           

    rTCON=0x9;

    /* 内部中断控制寄存器配置 */         

    pISR_TIMER0= (unsigned)__vT0Interrupt;

     

    中断:有点小复杂,单独新开一篇中断的总结

    中断函数添加之后,调试用的printf,就是一个最基础的驱动了

    int printf(const char* pbFmt, ...)

    {

        va_list pArg;

        char abString[256];

        int rv;

     

        va_start(pArg, pbFmt);

        /* 根据最后一个固定参数取可变参数表的首地址,以后可以根据该首地址逐一取各变量地址;此时pArg指向第一个可变参数 */

        rv = vsprintf(abString, pbFmt, pArg);

        /* sprintf一样*/

        Uart_SendString(abString);

        /* 调底层驱动 */

        va_end(pArg);

     

        return rv;

    }

     

    串口接收就需要起一个dbg_entrytask,为了方便调试,这里是porting一个命令行过来,这几个函数是从RTEMS移植过来的,可以自行添加调试命令,Uart_GetString(line)函数也可以实现记忆小功能,包括上下左右等键

     

    void dbg_entry(void* Id)

    {

        unsigned char line[MAX_COUNT];

        unsigned char * dbg_prompt_s ;

     

        dbg_prompt_s = (unsigned char *) "dbg> " ;

        printf("OSPrioCur = %d/n", OSPrioCur);

     

        memset(line, 0, sizeof(line));

     

        shell_add_cmd(NULL, NULL, NULL, NULL); /* init the chain list*/

        /* 这个就是porting过来的一个命令行调试函数 */

        for (; ;)

        {

            printf((char *) dbg_prompt_s);

            if (Uart_GetString(line) < 0)    /* 底层驱动函数 */

                break;         

            if ((line[0] == 0) || (line[0] == '/n'))

                continue;

            process_line(line);

            /* 这个也是porting过来的解析命令行调试函数 */

        }

    }

     

    内存分配:挂接预先分配好的44b0x的堆的首地址和尾地址

    延时函数,因为OSTimeDly是基于tick数目来的,所以做一个封装,转换成ms可读性强的API

     

     

     

    void udelay(unsigned short ms)

    {

        unsigned short ucos_timeout;

     

        if(ms)

        {

            ucos_timeout = (ms * OS_TICKS_PER_SEC)/1000;

     

            if(ucos_timeout < 1)

                ucos_timeout = 1;

            else if(ucos_timeout > 65535)

                ucos_timeout = 65535;

        }

        else

        {

            ucos_timeout = 0;

        }

        OSTimeDly(ucos_timeout);

     

    }

    ucos的系统api,因为ucos相对简单,任务间通信不复杂的平台采用它最好不过了,任务状态也相对简单----休眠,等待,就绪,运行

    首先在os_cfg.h中使能需要用到的api,这里配置至少需要任务,事件,信号量,消息队列,系统心跳数以及时钟函数

    #define OS_MAX_TASKS             63    /* Max. number of tasks in your application ...                 */

    #define OS_FLAG_EN                1    /* Enable (1) or Disable (0) code generation for EVENT FLAGS    */

    #define OS_SEM_EN                 1    /* Enable (1) or Disable (0) code generation for SEMAPHORES     */

    #define OS_Q_EN                   1    /* Enable (1) or Disable (0) code generation for QUEUES         */

     

    #define OS_TICKS_PER_SEC        200    /* Set the number of ticks in one second                        */

    #define OS_TIME_GET_SET_EN        1    /*     Include code for OSTimeGet() and OSTimeSet()             */

     

    接下来就创建任务间需要传递的信号量和消息队列,当然任务也根据需要创建,注释代码如下

     

        OS_EVENT* RecTcpQFlag;   

        RecPackedFlag = OSSemCreate(0);

        /* 创建一个信号量 */

        RecUdpQFlag = OSQCreate(&RecUdpQ[0], Q_Max_Size);

        /* 创建一个消息队列,RecUdpQFlag是这个消息队列对应的事件 */

        OSTaskCreate(task1, (void *) &Id1, (OS_STK *) &Stack1[STACKSIZE - 1], 1);

        OSTaskCreate(task2, (void *) &Id2, (OS_STK *) &Stack2[STACKSIZE - 1], 2);

        /* 创建两个任务,STACKSIZE是他们的栈大小,这个时候任务处于休眠状态 */

        FRMWRK_vStartTicker(OS_TICKS_PER_SEC);  /* os_cfg.h */

        /* 起系统的tick */

        OSStart();

        /* 任务非休眠状态了,或等待或就绪或运行 */

     

    task1通过UdpTemp = OSQPend(RecUdpQFlag, 0, &eer);处于等待状态,在等待在RecUdpQFlag事件,task2处于运行状态,如果这个时候task2调用一个发送消息队列的函数OSQPost(RecUdpQFlag, (void *) send_buf);task可以顺利的接收数据到UdpTemp

    如果这个task2也等待一个事件,并且是在task1中发送的,那么这样就死锁了     

     

    void task1(void* Id)

    {

        void* UdpTemp;

        U8 eer;

        printf("OSPrioCur = %d/n", OSPrioCur);

     

     

        for (; ;)

        {

            UdpTemp = OSQPend(RecUdpQFlag, 0, &eer);

            /* 0代表tick数,最好的方法是利用(ms * OS_TICKS_PER_SEC)/1000转换一下,ms代表n毫秒,超时后如果还没有得到消息则恢复成就绪状态,并返回error,如果该值设置成零则表示任务将持续地等待消息 */

            if (eer == OS_NO_ERR)

            {

                printf("UdpTemp:0x%x", UdpTemp);

                eer = eer;

            }

        }

    }

     

    再结合外围接口的驱动代码和文件系统,这样整个平台就搭建起来了,就可以开工写应用代码了,用ucos做应用也只用到这些API了,也足够应付一个普通平台的设计了

     

    2.        ucos的移植分析

            EXPORT  OSStartHighRdy

            EXPORT  OSCtxSw

            EXPORT  OSIntCtxSw

            EXPORT  UCOS_IRQHandler   

     

    和底层相关的压栈,出栈,挂接中断函数后的处理

     

    3.        小结

    买公司性质的板子,服务配套好,读书时候的板子是周立功的,平台几乎都帮你搭好了,省了很多事;相反山寨一切都要自己弄,不过更能锻炼人,对整个平台细节都非常清晰

    ADS中为了编写烧入flash跑的bin,采用-info totals -ro-base 0 -first 44binit.o(init) 这样烧入不成功的,跳了好久呢,没搞清楚 rorw的不同

    MCLK要根据实际硬件来,要不然配置串口的时候波特率后,串口打的是乱码

    OS_Q_EN默认的没开启,需要使能,要不然编译出错

    uart_initportinit之前就做,因为我中断函数加了打印信息,中断初始化后中断马上来了,这个时候串口没来得及初始化

     

     


    最新回复(0)