μC/OS-II 移植笔记 1(FreeScale 68HCS12 核单片机)

发布者:知识智慧最新更新时间:2016-05-06 来源: eefocus关键字:μCOS-II  移植笔记  68HCS12  核单片机 手机看文章 扫描二维码
随时随地手机看文章
μC/OS-II  移植笔记 1(移植到FreeScale 68HCS12 核单片机,Small Memory Model)


最近闲暇下来,花了些时间研究了如何将 μC/OS-II 移植到 FreeScale 68HCS12 内核的单片机。其实这个工作前年做过一次,当时是在网上找的相近的移植代码(68HC11核,Bank Memory Model,METROWERKS 编译器)自己做了些修改,内核已经跑起来了,但是在跑串口测试程序(ESBB书上的那个串口模块)时,程序运行一段时间就会跑飞。当时调试了许久也没有找到问题。这次就是接着上次的工作继续深入的往下做,消除错误。其实前年调试时就已经隐约的想到了错误可能的地方,只是当年对 68HCS12 内核还有 CodeWarrior IDE 附带的 C 编译器、汇编器了解的有限,尤其是对编译器的 C Calling Convention、还有 C 编译器对代码中内嵌汇编的处理几乎完全不知,这种水平下调试不出错误也是理所当然的。
这次由于有了以前的基础,这次着眼点直接就放在了编译器对我写的移植代码的处理上,在汇编语言的层面上对移植代码进行了剖析,很快(其实也不算快了,花了我整整 2 天时间)就找到了问题所在,并给出了一个初步的解决方案。现在的移植代码谈不上完美,但至少是正确的。
1. μC/OS-II 版本的选择
这次的移植代码主要针对 μC/OS-II V2.52,2.52 之后的版本对移植代码的要求大体相同,因此这个移植代码稍加修改就应该能在新版本上运行,并且我相信修改的难度应该很小。 μC/OS-III还没有研究过,因此移植代码是否适合 μC/OS-III 我就不得而知了。
之所以选择μC/OS-II V2.52这个版本主要基于两个方面的考虑。首先,Jean J.Labrosse写的那本大名鼎鼎的《MicroC/OS-II The Real-Time Kernel Second Edition》就是根据这个版本写成的,移植过程中遇到问题至少可以翻翻书。另外,这个版本确实也称得上经典,每一行代码都经过经得起推敲。因此,我选择了这个版本。
2. 移植代码详解
μC/OS-II 移植主要需要重写 3 个代码文件:
OS_CPU.H
OS_CPU_C.C
OS_CPU_A.S
下面就对移植代码进行详细的说明。开发环境采用:CodeWarrior Development Studio V5.9.0
2.1 OS_CPU.H
OS_CPU.H 中的代码主要有两部分,第一部分 typedef 了一系列的基本数据类型和几个宏定义。具体代码如下:

 
  1. /* 
  2. ****************************************************************************** 
  3. *                              DATA TYPES 
  4. ****************************************************************************** 
  5. */  
  6.   
  7. typedef unsigned char  BOOLEAN;  
  8. typedef unsigned char  INT8U;       /* Unsigned  8 bit quantity */  
  9. typedef signed   char  INT8S;       /* Signed    8 bit quantity */  
  10. typedef unsigned int   INT16U;      /* Unsigned 16 bit quantity */  
  11. typedef signed   int   INT16S;      /* Signed   16 bit quantity */  
  12. typedef unsigned long  INT32U;      /* Unsigned 32 bit quantity */  
  13. typedef signed   long  INT32S;      /* Signed   32 bit quantity */  
  14. typedef float          FP32;        /* Single precision floating point */  
  15. typedef double         FP64;        /* Double precision floating point */  
  16.   
  17.   
  18. #define BYTE    INT8S        /* Define data types for backward compatibility..*/  
  19. typedef char    SBYTE;  
  20. #define UBYTE   INT8U        /* ... to uC/OS V1.xx */  
  21. #define WORD    INT16S  
  22. #define UWORD   INT16U  
  23. #define LONG    INT32S  
  24. #define ULONG   INT32U  
  25.   
  26. typedef unsigned char  OS_STK;      /* Each stack entry is 8-bit wide */  
  27. typedef unsigned char  OS_CPU_SR;   /* Define size of CPU status register (CCR = 8 bits) */  
  28.   
  29. /* 
  30. ****************************************************************************** 
  31. *                               CONSTANTS 
  32. ****************************************************************************** 
  33. */  
  34.   
  35. #ifndef  FALSE  
  36. #define  FALSE    0  
  37. #endif  
  38.   
  39. #ifndef  TRUE  
  40. #define  TRUE     1  
  41. #endif  


基本数据类型的长度查编译器手册都有详细的说明。
有点难度的是堆栈和状态寄存器,需要查68HCS12内核手册,虽然68HCS12内核是16位的内核,但是堆栈是以字节为单位的,所以有如下代码:
typedef unsigned char  OS_STK;

68HCS12内核的状态寄存器称为 CCR,也是8位了,因此:
typedef unsigned char  OS_CPU_SR;

OS_CPU.H 中还包含对临界区的处理:


 
  1. #define  OS_CRITICAL_METHOD    3  
  2.   
  3. #if      OS_CRITICAL_METHOD == 3  
  4. OS_CPU_SR OSCPUSaveSR(void);             
  5. void OSCPURestoreSR(OS_CPU_SR os_cpu_sr);          
  6. #endif  
  7.   
  8. #if      OS_CRITICAL_METHOD == 1  
  9. #define  OS_ENTER_CRITICAL() __asm sei;  
  10. #define  OS_EXIT_CRITICAL()  __asm cli;   
  11. #endif  
  12.   
  13. #if      OS_CRITICAL_METHOD == 2   
  14. #define  OS_ENTER_CRITICAL() __asm  pshc; __asm  sei;  
  15. #define  OS_EXIT_CRITICAL()   __asm  pulc;  
  16. #endif  
  17.   
  18. #if      OS_CRITICAL_METHOD == 3   
  19. #define  OS_ENTER_CRITICAL()  ( cpu_sr = OSCPUSaveSR() )    /* Disable interrupts                     */  
  20. #define  OS_EXIT_CRITICAL()   ( OSCPURestoreSR(cpu_sr) )    /* Enable  interrupts                     */  


上述代码中虽然列出了三种对临界区的不同处理方法,但实际只有第三种是正确的。
第一种方法直接开关中断,这种方法的问题在于退出临界区后中断就被强制性的打开了,及时在进入临界区前中断是关着的。在任务级代码中这样做没有太大的问题,因为在执行任务代码是中断基本都是打开的,中断几乎只有在临界区中才是关闭的。但是在中断处理函数中情况就不是这样的了,68HCS12 内核在进入中断处理函数后中断是关闭的,中断处理函数中要调用 OSIntExit(),OSIntExit()中是有临界区的,一退出临界区就会导致中断打开,CPU 处理新的中断,也就是形成所谓的中断嵌套。而中断嵌套是我们不希望发生的,因为允许中断嵌套并不能显著提升系统的性能,还会导致各个任务的堆栈使用量加大,,对于内存紧张的单片机来说,绝对弊大于利。因此,在我的移植代码中不允许中断嵌套,也就否掉了第一种临界区的处理方式。
第二种方法看似很好,进入临界区时先将 CCR 的值存入堆栈然后关闭中断,退出临界区时直接将堆栈的内容恢复到 CCR。看似很完美的解决方法,实际上却行不通。C 编译器要利用堆栈指针的地址来寻址局部变量,而汇编语句 pshc 却改变了堆栈指针的指向,导致对局部变量的访问产生了错位。
要想说明这个问题还要从 C 编译器对局部变量的处理方式说起,我使用的 C 编译器将局部变量放到堆栈上,利用堆栈指针(SP)间接寻址局部变量。如果堆栈指针指向的地址变了,访问局部变量时就要出问题。下面用个例子来说明:

  volatile char a = 1;
  volatile char b = 2;

  __asm  pshc;   __asm  sei;
 
  a = 3;
  b = 4;
 
  __asm  pulc;

将其转化为汇编代码后如下:

PSHD     
  volatile char a = 1;
LDAB  #1
STAB  1,SP
  volatile char b = 2;
LSLB  
STAB  0,SP

  __asm  pshc;   __asm  sei;
PSHC  
SEI   
  a = 3;
INCB  
STAB  1,SP
  b = 4;
INCB  
STAB  0,SP
   __asm  pulc;
PULC

PSHD 指令调整堆栈指针,在堆栈上空出 2 个字节存放 a 和 b 的值。a 的地址为[SP+1],b 的地址为[SP],然后给 a 和 b 赋初值。PSHC 首先调整堆栈指针(SP=SP-1),然后将 CCR 寄存器的值存入堆栈,这里需要注意的是68HCS12核的堆栈是倒生堆栈,实栈顶(也就是说SP指向的是有数据的地方,而不是个空位),而老的68HC11内核是虚栈顶的(SP指向的是个空位)。这时 [SP] 中存的是 CCR 的值,  [SP+1]指向 b,[SP+2]指向a。因此, STAB, 0,SP 将原本存的 CCR 的值改成了 4, b 的值却没有任何改变,说明这款编译器无法感知堆栈的变动生成正确的代码。因此第二种方法会导致错误的结果,不能采用。
这里多说两句,熟悉 x86 体系的读者可以将 68HCS12 核与 x86 内核做个对比。这两个核都是冯诺依曼结构体系,复杂指令集,从某种意义上可以说这两种内核具有某种神似。在 x86 内核上大多数编译器也是将局部变量存放到堆栈上,但是不同的是访问局部变量时用的是 BSP 寄存器而不直接使用 ESP 寄存器,因此在 x86 内核上用第二种方法处理临界区是没有问题的,并且可以认为是一种相当好的方式。从这也可以看出来寄存器多一些确实是有好处的。

第三种方式是实现两个函数 OSCPUSaveSR 和 OSCPURestoreSR。虽然这样会多些函数调用产生的额外开销,却没有了前两种方法的问题。这两个函数具体的实现可以放到OS_CPU_A.S 中,也可以在 OS_CPU_C.C。

如果用 C 语言实现,可以如下:

 
  1. OS_CPU_SR OSCPUSaveSR( void )   
  2. {  
  3. __asm   
  4.  {  
  5.           tfr ccr, b      // copy the value of CCR to the register B  
  6.           sei             // Disable interrupts  
  7.  }  
  8. }  
  9.   
  10. void OSCPURestoreSR(OS_CPU_SR os_cpu_sr)   
  11. {  
  12. __asm   
  13.  {  
  14.    tfr  b, ccr  //B contains the CCR value to restore, move to CCR  
  15.  }  
  16. }  


想要理解上面代码,除了要知道汇编指令 tfr 和 sei 的含义。还要知道所谓的 C 语言调用约定,对于当前代码来说需要知道 C 语言编译器使用何种方式传递函数的参数和返回值。 这些知识在 编译器附带的文档《S12(X)Build Tools Reference Manual》可以查到。我这里只介绍直接相关的几条,其余的请自己查阅手册。

我所采用的编译器对参数固定的 C 函数采用 PASCAL 调用约定,参数采用堆栈传递,传递顺序为从左到右依次入栈,如果最后一个参数小于 4 个字节则最后一个参数采用寄存器传递。1 字节的参数存放到寄存器 B 中,OSCPURestoreSR 就是这种情况。函数的返回值如果也是 1 个字节,那么也通过 寄存器 B 传出来,OSCPUSaveSR 就是这种情况。关于这两个函数我就说这么多了。

如果想直接写汇编代码的话也很简单,下面是例子:

xdef   OSCPUSaveSR
xdef   OSCPURestoreSR 
OSCPUSaveSR:
    tfr  ccr,b   ; It's assumed that 8-bit return value is in register B
    sei          ; Disable interrupts
    rts          ; Return to caller with D containing the previous CCR

OSCPURestoreSR:
    tfr  b, ccr  ; B contains the CCR value to restore, move to CCR
    rts

和上面 C 函数的代码几乎一样,没什么可介绍的了。

另外多说一句,大家可能觉得上面的代码可以直接写成内联汇编语句,就不用函数调用了。比如下面的代码:

#define  OS_ENTER_CRITICAL()  asm ("tpa;  staa cpu_sr;  sei") 
#define  OS_EXIT_CRITICAL()   asm ("ldaa cpu_sr;  tap")

上面的代码看似很好,进入临界区时将 CCR 的值放到 寄存器 a 中,然后存到 cpu_sr 中,再关中断。退出临界区是恢复 CCR。问题在于 CodeWarrior Development Studio 中带的 C 编译器太弱智了,无法感知 寄存器 a 被改变了,更无法添加相应的处理代码。自己保存寄存器 a 的内容又很麻烦,不能直接放到堆栈中,否则会影响局部变量的访问,只能存在 C 编译器可以感知的地方。比如用下面的方法:
char tmp_a;
asm ("staa tmp_a, tpa;  staa cpu_sr;  sei; ldaa tmp_a")
asm ("staa tmp_a, tpa;  ldaa cpu_sr;  tap; ldaa tmp_a")

这样的开销也不小,不如直接写两个函数方便。

OS_CPU.H 还有几行代码:

 
  1. #define  OS_TASK_SW() __asm swi;  
  2.   
  3. #define  OS_STK_GROWTH    /* Define stack growth: 1 = Down, 0 = Up  */  
  4.   
  5. #pragma CODE_SEG NON_BANKED  
  6. void          OSStartHighRdy          (void);  
  7. void          OSIntCtxSw              (void);  
  8. void          OSCtxSw                 (void);  
  9. #pragma CODE_SEG DEFAULT  


都比较简单,OS_TASK_SW()将操作系统使用的中断指定为 SWI 中断,也就是 software interrupt。
堆栈生长方向为向下生长。

 

至此,OS_CPU.H 就写完了。

关键字:μCOS-II  移植笔记  68HCS12  核单片机 引用地址:μC/OS-II 移植笔记 1(FreeScale 68HCS12 核单片机)

上一篇:μC/OS-II 移植笔记 2(FreeScale 68HCS12 核单片机)
下一篇:基于SPCE061A和PTR8000的模拟SPI总线通信技术

推荐阅读最新更新时间:2024-03-16 14:53

基于MCU与USB设备控制器IP的设计方案
  1 引言   在传统的计算机系统上常采用串口(如RS232)和并口连接外围设备,但串口和并口都存在着通信速度 慢,接口独占不利于扩展等无法克服的缺点,而通用串行总线(Universal Serial Bus,即USB)因具有传输 速度快、支持热插拔、扩展方便、抗干扰强、成本低、数据传输质量高、节省系统资源等优点而得到了广 泛的应用,当前它已成为计算机最常用的接口之一。   现在USB控制器主要有两种:带USB接口的单片机(MCU) 和纯粹的USB接口芯片。纯粹的USB接口芯片仅处 理USB通信,必须有个外部微处理器来进行协议处理和数据交换。典型产品有Philips公司的PDIUSBD11(I2C 接口)、PDIUSBD12
[单片机]
基于<font color='red'>MCU</font>与USB设备控制器IP<font color='red'>核</font>的设计方案
STM32 文件系统 fatfs 移植笔记详解
1、内存和Flash介绍 stm32 的 flash 地址起始于 0x0800 0000,结束地址是 0x0800 0000 加上芯片实际的 flash 大小,不同的芯片 flash 大小不同。 RAM 起始地址是 0x2000 0000,结束地址是 0x2000 0000 加上芯片的 RAM 大小,不同的芯片RAM也不同。 Flash 中的内容一般用来存储代码和一些定义为 const 的数据,断电不丢失,RAM 可以理解为内存,用来存储代码运行时的数据,变量等等,掉电数据丢失。 stm32 将外设等都映射为地址的形式,对地址的操作就是对外设的操作。 stm32 的外设地址从 0x4000 0000 开始,可以
[单片机]
STM32 文件系统 fatfs <font color='red'>移植</font><font color='red'>笔记</font>详解
大容量Flash型AT91系列ARM微控制器
摘要:主要介绍美国Atmel公司最新推出的基于ARM7TDMI核的AT91FR40162微控制器的体系结构及功能特性。AT91FR40162是对AT91R40008增加了16Mbit的Flash存储器后形成的最终产品。本文对AT91FR40162新增的Flash存储器以及AT91 Flash Uploader软件作重点介绍。 关键词:AT91FR40162 AT91R40008 SRAM Flash 微控制器 引 言   AT91FR40162是美国Atmel公司生产的AT91系列微控制器中的一员,具有ARM7TDMI核、大容量Flash存储器以及片内SRAM和外围。这种微控制器的特点是高性能--32位RISC体系结构、高密度--1
[嵌入式]
AT91RM9200Linux移植笔记(二)-移植u-boot-1.1.6
u-boot的下载地址为http://sourceforge.net/project/showfiles.php?group_id=65938 ,最新的为u-boot-1.1.6,这个版本已经可以很好的支持AT91RM9200 添加PATH环境变量或者修改u-boot的Makefile将之前编译好的工具链路径添加进来 因为我们的开发板配置和at91rm9200dk很类似,因而可以直接使用at91rm9200dk_config的配置,如果相差比较大的话可以添加自己的开发板配置,还要修改诸如flash等的驱动,具体方法可参考u-boot的文档,一种简便的做法是在u-boot已经支持的开发板中参考选择一种较接近板的进行修改。 $ tar
[单片机]
实时操作系统C/OS-II在ARM7上的移植
  引言   目前,嵌入式系统在家电、移动电话、PDA等各种领域的应用日益广泛,程序设计也越来越复杂,这就需要采用一个通用的嵌入式操作系统来对其进行管理和控制。移植了操作系统的嵌入式系统开发,可大大减轻程序员的负担,操作系统提供了多任务的管理功能,只需专注于每个任务的管理。对于不同的应用,可以按照相同的步骤完成系统设计。如果更换硬件平台,则只需要对操作系统进行少量的移植工作,与硬件无关的应用代码完全无需修改,同时,可增强代码的可读性、可维护性和可扩展性。   μC/OS-II是一种专门为微处理器设计的抢占式实时多任务操作系统,具有源代码公开、可移植性和可靠性高等特点。由于μC/OS-II是为嵌入式应用编写的通用软件,故在具体应用
[单片机]
实时操作系统C/OS-II在ARM7上的<font color='red'>移植</font>
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved