Hello China操作系统STM32移植指南(一)
Hello China操作系统移植指南
首先说明一下,为了适应更多的文化背景,对Hello China操作系统的名字做了修改,修改为“Hello X”,或者连接在一起,写为“HelloX“。其中X是不固定的,可以根据具体应用的国家,甚至城市,进行定制化。比如在中国,我仍然会叫做”Hello China“,但是如果有人在美国使用了,则可以叫做”Hello USA“,在香港使用了,可以叫做”Hello Hongkong“,等等。但这些都是HelloX的分支,必须明确说明。这样做,主要目的是为了Hello China操作系统的全球化推广,后续将作为开源项目托管到github上,届时任何国家和地区的人都可以参与开发。
经过一段时间的尝试,现在已经成功把Hello China V1.76版的内核代码,移植到STM32芯片上。本文就对这个移植过程进行详细说明。在正式介绍移植步骤之前,先说明一下移植原理。
STM32移植原理
STM32芯片是ST公司开发的基于Cortex-M3 CPU(ARM系列)的SoC,在各行各业中得到了广泛的应用。往STM32上移植,本质上就是向Cortex-M3 CPU上的移植,移植后的内核,可以很容易的再移植到其它基于Cortex-M3 CPU的芯片上。关于STM32更多的背景及特性等,这里就不多说了。为了更好的理解下面的内容,建议读者先了解一下Cortex-M3 CPU的一些基础机制,这样阅读起来更加容易。
如果您一直在x86系列CPU上做开发,那么当接触到ARM系列CPU的时候,您会发现非常容易。ARM CPU非常简洁,逻辑比x86简单得多。根据我个人的经验,x86CPU的保护模式编程,比实模式编程要简单,而ARM CPU的编程,则比x86的保护模式更简单。虽然模型简单了,但由于指令系统不同,对中断/异常的处理机制也不一样,导致移植过程并不是很容易。主要集中在下列几个方面:
中断机制的移植
首先是中断处理机制的移植,这是任何操作系统向不同CPU上的移植,都需要涉及到的问题。x86 CPU采用中断描述表的方式,每个中断占用一个表项,表里面记录了处理中断的中断向量(函数)。一旦中断发生,CPU会调用对应中断号的中断向量。Cortex-M3CPU的中断机制与此基本类似,但是其中断向量表却简单得多,不像x86一样,内嵌了很多的控制信息。而且Cortex-M3的中断向量表的位置是固定的,就是在物理内存的起始处。Hello China在设计的时候,为了兼容多种CPU,其中断机制采用了“统一入口”的形式。即针对CPU的所有中断向量表,都填写一个唯一的入口-GeneralIntHandler,这个入口由Hello China操作系统实现。然后GeneralIntHandler再根据实际的中断向量号,调用具体的中断处理程序。这样的机制,可以适应大多数CPU的需求。因为有的CPU只有一个中断向量表,不像x86那样,每个中断都有向量表对应。因此在把中断处理机制移植到Cortex-M3的时候,必须要把每个中断向量跟GeneralIntHandler链接起来。下面是Cortex-M3的典型中断向量表结构:
__Vectors
DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; ExternalInterrupts
DCD WWDG_IRQHandler ; Window Watchdog
DCD PVD_IRQHandler ; PVD through EXTI Line detect
DCD TAMPER_IRQHandler ; Tamper
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
………
向量表的第一个表项,是CPU的初始堆栈指针(即MSP寄存器的值),后续每个表项,都对应特定异常或中断的处理程序。最简单的修改方式,就是把上述每个中断向量(除初始堆栈指针外),替换为GeneralIntHandler函数。但这样做有一个问题,那就是无法确定中断向量号。因为GeneralIntHandlr是用C语言实现的,接受中断向量作为参数。因此又用汇编语言实现了一个Int_Entry_Wrapper的函数,作为中间的一层封装。这个函数代码如下:
Int_Entry_Wrapper PROC
EXPORT Int_Entry_Wrapper
IMPORT GeneralIntHandler ;Implementedin C file.
MRS R0,IPSR
AND R0,R0,#0xFF
MRS R1,MSP
LDR R2,=GeneralIntHandler
BX R2
ENDP
函数的逻辑很简单,首先读取IPSR寄存器,从中得到中断向量号,然后再以中断向量号和堆栈指针为参数,调用GeneralIntHandler。使用Int_Entry_Wrapper替换Cortex-M3的中断向量表中的所有项,即可实现中断的连接。
用户如果要编写设备驱动程序,需要在中断向量表中增加中断向量的时候,就无需修改启动文件(比如MDK生成的是startup_stm32f10x_xx.S文件)了,直接调用Hello China提供的中断接口函数-ConnectInterrupt和DisconnectInterrupt即可,屏蔽了底层的中断实现。
用户在实现中断函数的时候,也无需在进入中断和离开中断时调用操作系统的“夹层函数”(诸如InterruptEnter/InterruptLeave等函数),因为夹层功能都已经在GeneralIntHandler中实现了。这种编程模型,符合操作系统对硬件的抽象理念,同时扩展性也非常强。
需要说明的是,在基于MDK开发环境的STM32移植中,我们并没有直接使用上述方法,而是做了适当变通,使得这个过程更加简单。因为直接用Int_Entry_Wrapper替换中断向量表,毕竟有很多的编辑工作,需要一条一条的修改,容易出错。在MDK生成的启动文件中,对所有的中断处理函数都做了一个缺省实现,如下:
………
USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
EXTI15_10_IRQHandler
RTCAlarm_IRQHandler
USBWakeUp_IRQHandler
B .
ENDP
这是一个死循环,因此,只要用下面的代码,替换这个死循环实现即可:
………
RTCAlarm_IRQHandler
USBWakeUp_IRQHandler
IMPORTInt_Entry_Wrapper
LDRR0,=Int_Entry_Wrapper
BX R0
ENDP
在具体移植过程中,只要找到startup_stm32f10x_XX.S文件,用上述代码替换缺省实现的死循环,即可完成中断链接。需要说明的是,Int_Entry_Wrapper是在另外一个汇编文件-osadapt.S中实现的。Osadapt.S是Hello China为了向STM32移植而增加的一个文件,里面还实现了其它底层功能,后面会详细讲解。
任务切换机制的移植
任务切换是操作系统移植的最重要的地方,毕竟不同的CPU,其底层机制是不同的。操作系统的任何功能模块都可以用C语言编写,但任务切换确是例外,目前尚未看到能够用C语言直接实现任务切换的CPU。
Hello China在实现的时候,任务切换机制做了优化,所需的汇编代码非常少。但是为了迎合不同的任务切换场景,分成了两种任务切换时机:
1. 中断内的任务切换,这种情形下,CPU的硬件中断处理机制已经帮助操作系统把任务(或者线程)的堆栈框架搭建好,操作系统无需自行建立堆栈框架,只需选择合适的线程,恢复堆栈框架即可;
2. 非中断内的任务切换(也叫做进程内的任务切换),这种情况下,一般是由系统调用引发。用户程序调用系统功能,比如调用CreateKernelThread创建一个核心线程,操作系统内核会在线程创建完成之后,重新检查系统中的所有内核线程,看是否有比当前核心线程优先级更高的线程。如果有,则内核会保存当前线程的堆栈框架,然后选择优先级最高的处于就绪状态的线程,恢复其堆栈框架,让其运行。与中断过程不同,这里需要操作系统内核自己建立待切换出线程的堆栈框架,工作要稍微多一些。
对应上述两种情况,Hello China操作系统实现了两个函数:ShceduleFromInt和ScheduleFromProc,分别用以实现。这两个函数分别调用更加底层的用汇编语言实现的两个函数:__SwitchTo和__SaveAndSwitch,来实现核心线程的切换。这种切换机制,因为不用引发额外的系统中断,所以我们称为inline schedule,对应config.h文件中的__CFG_SYS_IS。大多数CPU都是采用这种切换方式,高效简洁,易于理解。
但到了Cortex-M3的CPU,由于其底层机制的制约,无法使用上述inlineschedule机制。Cortex-M3的推荐做法是,专门预留了一个系统中断(异常),叫做PendSV,用于线程切换。任何希望切换线程的代码,需要引发这个异常,所有具体的切换工作,在这个异常处理程序中进行。这样就不分中断切换和进程切换了,所有切换统一进行。为了区分上面的inline schedule方式,我们把这种情形称为uniform schedule(简称unischedule,统一切换)。Unischedule的好处是不分切换所处的状态,相对简洁,但是不好之处在于,兼容性比较差,很少有CPU只支持这种方式,因此其通用性欠佳。
Hello China支持上述两种方式。在移植的时候,如果在config.h文件中定义了宏__CFG_SYS_IS(Inline Schedule),则采用第一种方式切换,如果把这个宏定义注释掉,则采用第二种方式切换。在Cortex-M3上,我们注释掉该宏定义,采用第二种方式切换。要引发任务切换,操作系统内核代码必须引发一个PendSV中断,下面是引发该中断的汇编代码:
ScheduleFromInt
ScheduleFromProc
PROC
EXPORTScheduleFromInt
EXPORTScheduleFromProc
PUSH {R4,R5}
LDRR4,=NVIC_INT_CTRL
LDRR5,=NVIC_PENDSVSET
STR R5,[R4]
POP {R4,R5}
BX LR
NOP
ENDP
要切换线程的时候,只要调用ScheduleFromInt或者ScheduleFromProc两个函数即可,这两个函数的实现是一样的,就是引发一个PendSV异常。
异常引发之后,在所有系统中断都处理完毕后,PendSV异常处理程序会被调用。由于我们在系统初始化的时候,设置PendSV异常的优先级是最低的,因此如果有任何中断处理程序在运行,PendSV异常处理程序就不会被调用。下面是PendSV处理程序的代码:
PendSV_Handler PROC
EXPORT PendSV_Handler
IMPORT UniSchedule ;UniSchedule is implemented in C file.
PUSH {R4-R11} ;Save un-saved registers.
MOV R4,LR
MRS R0,MSP
MRSR1,MSP
LDR R2,=UniSchedule
BLX R2 ;Now R0 contains the new thread toswitch to.
MOV LR,R4
MSR MSP,R0
POP {R4-R11}
BX LR
ENDP
这个程序也非常简单,先保存R4到R11寄存器(其它的寄存器,在进入异常处理程序的时候,已经由CPU保存),然后以当前堆栈指针为参数,调用UniSchedule函数。这个函数返回一个优先级最高的可调度线程的堆栈指针,然后PendSV直接恢复返回的线程的堆栈,即可切换到目标线程继续运行。
UniSchedule是用C语言实现的一个函数,位于ktmgr.c文件中。这个函数从系统中选择一个优先级最高的,且状态是ready的线程,返回其上下文指针(即堆栈指针)。需要注意的是,如果系统中没有比当前核心线程优先级更高的线程,则该函数直接返回当前核心线程的上下文,这样的结果就是,当前核心线程继续执行。
驱动程序的移植
个人认为,驱动程序移植是操作系统移植过程中工作量最大,也是最难的工作。毕竟不同的CPU,其外设管理方式不同,访问方式也不同。比如x86 CPU,是通过读写端口(in/out指令)来实现外设访问的。但是ARM系列CPU,则是通过内存映射方式,直接读写外设控制寄存器的。在实现驱动程序的时候,一般是针对某种CPU写的,在程序中内嵌了很多访问外设的代码。在移植的时候,这些代码都要一行一行的变换。如果说操作系统内核的移植是“黑盒移植”,无需审视每一行代码,只要移植几个接口函数即可,那么驱动程序的移植,则必须是“白盒移植”,需要认真检查每一行代码,确保每一行代码都能够在目标设备上工作。
在Hello China V1.76的移植中,只移植了一个USART设备的驱动程序。在基于x86的PC上,这就是串口驱动程序。移植的时候,所花的时间,比内核部分的移植还要大。从代码量上统计,也跟内核部分差不多,可见驱动程序移植只繁琐。虽然工作量大,但其难度却不是很大,不像内核一样,存在很多陷阱。
在移植过程中,还有其它一些注意的地方,在此就不做更多说明了。如有需要,在本文后面会提到。
Hello China操作系统STM32移植指南(一)的更多相关文章
- Hello China操作系统STM32移植指南(二)
移植步骤详解 下面就以MDK 4.72为开发环境,详细说明Hello China内核向STM32的移植过程.MDK 4.72评估版只支持32K代码的编译,这对Hello China的内核来说,裁剪掉一 ...
- Hello China操作系统STM32移植指南(三)
移植到STM32的源代码,可从下列链接下载: http://download.csdn.net/detail/hellochina15/7049909 包含两个包:一个是移植前的Hello China ...
- MiniGUI ial 移植指南
MiniGUI ial 移植指南 2.1 ial的定义 ial是一个通用的抽象输入接口,可以输入统一的数据的结构,也就是说在MiniGUI的核心代码里输入的数据的格式是固定的,不管输入设备是鼠标 还是 ...
- 【转】hurry_liu 大神STM32移植contiki入门之一:系统介绍和开发环境搭建
前言: 由于项目的原因,需要在LPC1788(STM32 cortex-M3)上面跑contiki. 之前没有涉及到contiki,不知其为何物.不过这个不是难事,做IT的,每每遇到新事物,都不会处理 ...
- LwIP应用开发笔记之一:LwIP无操作系统基本移植
现在,TCP/IP协议的应用无处不在.随着物联网的火爆,嵌入式领域使用TCP/IP协议进行通讯也越来越广泛.在我们的相关产品中,也都有应用,所以我们结合应用实际对相关应用作相应的总结. 1.技术准备 ...
- STM32移植RT-Thread后的串口在调试助手上出现:(mq != RT_NULL) assert failed at rt_mq_recv:2085和串口只发送数据不能接收数据问题
STM32移植RT-Thread后的串口在调试助手上出现:(mq != RT_NULL) assert failed at rt_mq_recv:2085的问题讨论:http://www.rt-thr ...
- Hello China操作系统在Virtual PC上的安装和使用
http://blog.csdn.net/hellochina15/article/details/7253350 本文介绍如何在Windows 7操作系统和Virtual PC 2007虚拟机上安装 ...
- STM32移植USB驱动总结
https://blog.csdn.net/stm32_newlearner/article/details/88095944 stm32 移植usb驱动开发 单片机 STM32单片机和51单片机 ...
- STM32 移植 RT-Thread 标准版的 FinSH 组件
一.移植准备 开发版STM32F10xC8T6 准备好移植RT-Thread的移植工程 没动手移植过RT-Thread的小伙伴,可以看RT-Thread移植到stm32 我这里是将控制台信息打印到串口 ...
随机推荐
- 微软URLRewriter.dll的url重写在.net简单使用
最近在做一个cms的网站 打算做成伪静态,从博客园上差了很多人的资料,终于实验成功了,原理就不讲了,附上在本地的配置,IIS的配置遇到后在发布. 文章最后附上源码 步骤如下 1.新建网站,添加URLR ...
- log4j 详解
转载自:http://www.blogjava.net/hwpok/archive/2008/08/23/223891.html >>>>1. 概述<<< ...
- T-SQL 基于关系分割字符串
今天晚上学习了下 SQL 基于关系的运算,同时也捉摸着写了个例子,虽然我知道性能不是很好,还有待优化.直接上源代码吧,思路表达出来有点困难,直接贴上代码,如果有人不懂得可以MM 我. declare ...
- CocoaPods 安装和使用
CocoaPods的安装 >1. 打开终端, 输入 gem sources -remove https://rubygems.org/ >2. 再输入 gem sources -a htt ...
- CKEditor + CKFinder 实现编辑上传图片配置
下载最新版 ckfinder 本人下载的php版本 https://cksource.com/ckfinder/download 下载最新版ckeditor http://ckeditor.com/ ...
- QT国际化
写的比较简洁 语言文件: ts:编辑翻译用的,是xml,可以用linguist(qt语言专家)或者Editplus进行翻译 qm:这种文件是ts的release版,无法编辑,发布的时候用这个 操作: ...
- css兼容问题与实践归纳总结
css兼容问题与实践归纳总结 一.IE6/7 原生块元素与display:inline-block; <div style="display:inline-block;"&g ...
- C++可变参数的另一种实现
大家熟知的C库函数printf函数就是一个可变参数函数,它是怎么实现的呢?不过他实现是有条件的,必须函数参数的入栈顺序为从右向左的顺序,也即函数的形参,在函数调用之前,必须是最右边的参数先入栈,并且参 ...
- Delphi的WebBrowser改造,对网页中Alter等对话框的改造方法(通过COM来改造)
刚有一段时间没做博客了,今天刚好有人问了这个问题,而自己以前也弄过,于是这里有了一篇新的博文. 关于改造WebBrowser控件的一些技巧,大家可以参考MSDN或者蒋晟写的一个东西,里面有讲的很详细的 ...
- 灰度直方图及处理“cvQueryHistValue_1D”: 找不到标识符”的问题(上)
// HIstogram.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include "opencv2/opencv.hpp ...