c/ os 是一种多任务实时操作系统。内核源代码公开、短小精干、可裁剪、执行时间可确定, 可移植性较强, 非常适用于一些中小型嵌入式系统开发。uc/os 可以移植到8~ 64 位的不同类型、不同规模的嵌入式系统, 并能在大部分的8 位、16 位、32 位, 甚至64 位的微处理器和dsp上运行[ 1] 。
mcf52235是飞思卡尔公司co ldf ire 系列32 位单片机解决方案的嵌入式微控制器, 采用的是v2 版本的risc 内核。mcf52235 内部有32 kb sram 和256 kb flash, 并且集成了标准的coldfire外围设备, 包括三个适合中长距离通信的sci, 一个i2 c 和一个用于系统内部和外围设备通信的q spi。在60hz的核心频率下, mcf52235 的处理能力为56 mips, 具备较高的性能价格比[ 24] 。mcf52235 对于移植c/ os 来说有足够的
ram 和flash, 且有较快的处理速度和较低的成本,所以对于嵌入式应用系统的开发来说, 嵌入c/ os到mcf52235 微控制器是一个不错的选择。uc/ os 的体系结构要实现c/ os 向mcf52235 的移植, 需要做两方面的工作: 一是重新定义内核的大小和功能; 二是为内核编写与硬件相关的代码。c/ os 的文件结构如图1 所示。可以看到, c/ os 与cpu 类型无关的c 代码文件cos . c 包括很多文件, 它们是c/ os 的内核和很多功能函数, 其中前三个文件是实时内核、任务管理和时钟节拍, 这三个文件是一定要用的。后面6 个功能函数用于任务间的通信, 应用程序中可能只用到其中的几个, 不用的可以不包含进去, 以免编译时生成没用的代码。这部分代码与cpu 类型无关, 在移植时, 这些文件不要改动。配置文件os_cfg. h 需要根据应用要求来进行,主要作用是确定c/ os 提供的系统功能函数, 应用程序用哪些和不用哪些, 这个文件移植时需要修改。与cpu 类型有关的代码文件主要有三个: os _cpu. h, os_cpu_a. asm 和os_cpu_c. c。文件定义用于特定cpu 的数据类型来定义相关的宏。os _cpu_a . asm 是用汇编语言写的与硬件有关的代码,os_cpu_c. c 是用c 语言写的与硬件有关的代码。由于移植使用c 交叉编译工具, 在c 代码中可以插入汇编语句, 在移植中可将这两个文件合并成一个文件[ 5] 。
产生时钟节拍的定时中断来自微控制器内部, 但并非来自v2 内核内部, 可以用实时时钟产生定时中断,也可以用片内的外设模块定时器单元来产生定时中断,这部分代码显然与硬件相关, 移植时要自己写[ 6] 。
2 移植过程
所谓移植, 就是使一个实时内核能在某个微处理器或微控制器上运行。为了方便移植, 大部分的c/ os代码是用c 语言写的, 但仍需要用c 和汇编语言写一些与处理器相关的代码, 这是因为c/ osii 在读写处理器寄存器时只能通过汇编语言来实现 。移植过程主要包括移植前的准备、bsp ( 板级支持包) 的编写和与处理器相关代码的修改和编写。c/os 核心代码、与cpu 相关的接口程序、bsp 和用户应用程序之间的关系如图2 所示。
移植前的准备
进入c/ os 官方网站下载c/ os 源代码。打开codew arrior 6. 4 建立mcf52235 的工程文件, 然后把c/ os 的源代码文件加入到工程里面[ 8] 。其中有几个地方需要改动:
( 1) 下载的源代码中os_cfg _r. h 改为o s_cfg. h;os_dbg_r. c改为os_dbg. c。
( 2) 由于会引起重复定义错误, 需要把源代码中重复包含的文件注释掉。
( 3) 需要在int ernal_flash 模式下编译, 而不能在ram 模式下, 否则会产生溢出错误。
编写bsp
板级支持包( bsp) 是介于底层硬件和操作系统之间的软件层次, 负责进行系统启动后最初的硬件和软件
初始化, 并对底层硬件进行封装, 使得操作系统不再面对具体的硬件[ 9] 。在此建立两个bsp 文件: bsp. asm 和bsp. c。其中, bsp. asm 中包含了汇编语言写的中断接口程序。bsp. c 中包含了硬件和软件的初始化程序和产生时钟节拍的中断服务程序。
与处理器相关代码的修改和编写
有三个与处理器相关的文件, 即os_cpu . h, os_cpu _ a. asm 和os _ cpu _ c. c 需要修改。由于mcf52235 有emac 模块, 所以还需要编写os_cpu _i. asm 文件, 用来在任务切换和中断时以及中断返回
时保存和恢复相关寄存器。
1 、os_cpu. h 的移植
os_cpu. h 包含了一些与处理器和编译器相关的宏定义和数据类型定义。由于使用codew arrior 编译
器, shor t 类型是16 位的, int 类型是32 位的。mcf52235 的堆栈是32 位宽的, 因此os_stk 定义为
32 位, 所有任务的堆栈必须声明使用os_st k 这种数据类型。数据类型定义如下:
ty pedef unsigned char boolean;
ty pedef unsigned char int 8u;
ty pedef signed char int8s;
ty pedef unsigned sho rt int16u;
ty pedef signed shor t int16s;
ty pedef unsigned int int32u;
ty pedef signed int int32s;
typedef floatfp32;
typedef double fp64;
typedef unsigned int os_stk;
typedef unsigned shor t os_cpu_sr;
( 1) 临界区域处理。像所有的实时性内核一样, 在进入代码临界区时要关中断, 完成时要开中断。c/
os 定义了两个宏来关闭和使能中断: os_ent er_critical( ) 和os_exit _crit ical( ) 。c/ os定义了三种方法来关闭和使能中断, 大多数情况下选择第三种方法。
# define os_critical_meth od # 3
# define os_enter_critical( ) { cpu_sr = os _cpu_
sr_save( ) ; } / / 关中断
# def ineos _ exit _ critical( ) { os _ cpu _ sr_ resto re
( cpu_sr) ; } / / 开中断
( 2) 任务层上下文切换。当c/ os 调用os _task_sw( ) 时发生任务层的上下文切换。因为上下
文切换是根据处理器的不同而不同的, 所以需要执行一个汇编的函数。在这种情况下, 用tra p 指令来产
生一个异常, 用t rap 指令的优点是能使它像发生了一次中断一样。这里用# 14 t rap, 因为大多数情况
下, # 15 trap 被调试和监控程序保留了。# 14trap 定位于vbr+ 0x00b8, 然后跳转到相应的地址。
在这个向量处放置osctx sw( ) 的地址。这个函数声明在os_cpu_a. asm 里。vbr 代表向量基址寄存器,
包含异常向量表的基址, 程序开始时被初始化为0x00000000, 但是在运行时可以改变。
# define os_task_sw( ) asm( t rap # 14; )
( 3) 堆栈的增长方向。mcf52235 的堆栈增长方向是从高地址向低地址, 因此os _st k_growth 置
为1。
# define os_stk_growth 1
2 、os_cpu _c. c 的移植
os_cpu_c. c 里面包含10 个比较简单的c 语言函数, 一般来说c/ os 只需要ost askstkinit ( ) 。其他函数是用来让用户在自己的程序里扩展操作系统功能的。如果需要使用这些函数, 需要在os_cfg. h 里设置os_cpu _hooks_en 为1。堆栈的初始化: ostaskstkinit ( ) 虽然是用c 语言编
写的, 但它是一个与cpu 硬件相关的函数。这个函数功能是初始化任务的堆栈, 由建立任务函数ostask
create( ) 或扩展地建立任务函数ostaskcreateexit ( ) 调用。任务堆栈初始化的实质就是模拟一次中断, 使堆栈看起来就像刚发生过中断一样。任务堆栈中保存了任务代码的起始地址和一些cpu 寄存器的值, 一旦条件满足, 就可以执行该任务。初始化后的任务堆栈结构如图3所示。
3 、os_cpu_a. asm 的移植
这个文件包含5 个相当简单的汇编函数, 因为一般不能用c 语言来保存和恢复寄存器。
( 1) os_cpu_sr_save( )
这个函数是通过保存中断屏蔽寄存器, 然后关闭中断来实现os_critical_met hod # 3 的。当函数返回时, d0 包含了状态寄存器的内容, 里面包含当前的中断关闭状态。这个返回值被调用函数保存到变量
cpu_sr 中。
( 2) os_cpu_sr_restore( )
这个函数用来实现恢复中断屏蔽到调用os _enter_critical( ) 之前的状态。也就是说调用os_
enter_critical( ) 之前中断是关闭的, 那么在os_exit_critical( ) 之后, 中断是关闭的。
( 3) osstarthighrdy( )
这个函数被osstar t ( ) 调用来运行优先级最高的任务。osstar t ( ) 设置ostcbhighrdy 指向优先级最高任务的os _t cb。一旦从ostaskswhoo k( ) 返回,就把osrunning 设为os_t ru e, 它表明现在rt os
将要运行。从最高优先级任务的os_t cb 中恢复堆栈指针, 然后从任务堆栈里取出cpu 寄存器。最后执行
一个ret 指令, 这个指令可以从堆栈中弹出sr 和pc,现在的任务代码就开始执行。
( 4) osctx sw( )
当一个任务不再运行时就会发生一个任务级的任务切换, 比如任务调用一个延迟10 个时钟节拍的函数。
这时, c/ os 需要找出下一个最重要的任务准备去运行。osctx sw ( ) 的功能是保存需要挂起的任务的cpu 寄存器和堆栈, 恢复需要运行任务的cpu 寄存器和堆栈。任务级上下文切换如图4 所示。
( 5) osintctx sw( )
当中断服务函数完成时, 调用osintex it ( ) 函数去决定是否有一个更重要的任务比被中断的任务更需要执行。这种情况下, osintex it( ) 决定运行哪个任务, 然后调用osintctx sw ( ) 。这种情况下, 中断服务程序已经保存了被中断任务的cpu 寄存器, 而需要做的只是去恢复新任务的cpu 寄存器。
4 、os_cpu _i. asm 的编写
如果用到增强的乘法累加单元( emac) 模块, 在上下文切换和中断时就应该保存和恢复emac 寄存器。保存和恢复emac 寄存器通过两个宏来实现[ 10] 。代码如下:
。 macro os_em ac_save
move. l macsr, d7
clr. l d0
move. l d0, m acsr
move. l acc0, d0
move. l acc1, d1
move. l acc2, d2
move. l acc3, d3
move. l accext01, d4
move. l accext23, d5
move. l mask, d6
lea 32( a7) , a7
movem. l d0d7, ( a7)
。 endm
。 macro os_em ac_rest ore
movem. l ( a7) , d0d7
move. l # 0, macsr
move. l d0, acc0
move. l d1, acc1
move. l d2, acc2
move. l d3, acc3
move. l d4, accext01
move. l d5, accext23
move. l d6, mask
move. l d7, macsr
lea 32( a7) , a7
。 endm
时钟节拍的产生
最后还需要编写利用片内定时器产生时钟节拍的中断服务程序。c/ os要求微控制器提供一个简单的时钟, 用于任务的延时等功能。在此利用可编程中断定时器来产生时钟节拍中断。在定时器中断服务程序中调用ostimetick( ) 就产生了系统所需要的时钟节拍。c/os中产生中断后的中断处理程序如下所示:
_bsp_tickisr:
move. w # 0x2700, sr
lea 60( a7) , a7
movem. l d0d7/ a0 a6, ( a7)
os_emac_save
moveq. l # 0, d0
move. b ( _osi ntnesting) , d0
addq. l # 1, d0
move. b d0, ( _osi ntnesting)
cmpi. l # 1, d0
bne _bsp_tickisr_1
move. l ( _ostcbcur) , a1
move. l a7, ( a1)
_bsp_tickisr_1:
jsr _bsp_t ickisr_h andler
jsr _osint ex it
os_emac_restore
movem. l ( a7) , d0d7/ a0a6
lea 60( a7) , a7
rte
任务的创建和移植代码的测试
源程序移植完, 用户就可以试着制作自己的项目。编写任务代码, 与以前在前后台系统中基本相同, 不同
的是要把每个任务独立编写成一个文件, 最后由主程序统一调度。为了测试是否移植成功, 用staskcreateext ( ) 创建了两个任务。一个任务使板上led 每一秒闪动一次, 另一个任务是用片内a/ d 采样板上的加速度传感器信号, 并在数码管上显示出当前加速度数值。最后调用osstar t( ) 启动多任务调度。
1 、定义每个任务的堆栈大小
os_stk
taskstartstk[ t askstart st k_size] ;
os_stk adt askstk[ t askstk_size] ;
然后在main( ) 函数里系统初始化:
osinit( ) ;
2、 创建任务
ost askcreateex t( taskstart, ( void * ) 0, ( os _ st k * )
& t askstar tst k[ t askstartst k_size1] , t ask _start _
prio, task_start_prio, ( os_st k* ) & t askstart st k[ 0] ,
task_st art_st k_size, ( vo id * ) 0, os_task_opt _st k_
ch k| os_task_opt_stk_clr) ;
ostaskcreateext ( adt ask, ( vo id * ) 0, ( os _ stk * )
& adtaskstk[ taskst k_size1] , adtask_prio, adt ask
_ prio, ( os _ st k * ) & adtaskstk [ 0] , task _ st k _size,
( vo id * ) 0, os_task_opt_stk_chk) ;
3 、系统启动运行
osstart( ) ;
4 、测试结果
测试代码经过编译下载到实验板上运行后, 通过实验板上显示的信息, 表示两个任务在交替运行, 说明移植工作是成功的, 如图5 所示。
相约慕展丨矽力杰携车规级MCU及BMS AFE方案重磅亮相
rk3399和rk1808哪个好
如何为开关电源设计选择合适的电感
LM555组成的宽动态脉宽调制器
组件好还是逆变器好?国产逆变器的崛起
uCOSii的移植过程详解
防水测试仪使用时要注意什么
东大金智科技40GBASE-SR4 150m FTL410QE3C特征英文版
AIOT 去“碎片化” 应用解析
电动洁面仪开发方案,用合封单片机让体积更小
LG InnoTek正在增加支出,以提高Apple相机模块的产量
意法半导体推出可支持先进网络技术的汽车IC
特斯拉计划“有选择地”解决工厂问题,以解决产能瓶颈问题
rs232介绍_rs232串口通信程序
视频小程序开发者大赛提交通道关闭在即,切莫错失良机
随着工业机器人的需求的不断增加,工业机器人生产更加的标准化
采集针锋利度测试仪
可编程安规综合测试仪Supernova Elite的功能特点
小鹏汽车NGP功能将于春节前通过OTA推送给用户
在MWC 2023,华为创新解决方案点亮F5.5G时代的每一处光