晚上一点多从办公室回到家,我看了几眼博文,本来没有写作的意愿,当看到一个“新浪网友”鼓励博主写作Blog的评论后,而且开博以来得到了广泛的支持,我于是不由得奋笔疾书有感而发,重新加工整理了此文发表,看来明天又要睡到上午十点了。
“有时努力并非一定有回报”,这就是人们常说的简单而又深奥的道理。因此学习不仅仅需要勤奋和毅力,但更重要的是需要思想和方法,这是初学者在学习中必须注意的,但也是非常容易忽视的。下面举例说明博主在学习ARM和写作三年的过程中的收获与方法。
一、由厚到薄的学习方法
人们常说读书要“由厚到薄”,那么到底怎样才能做得到?即就是“图解法”――将文字用直观、简单易懂的图和表来描述的方法。
我们不妨以ARM为例,通过下面展现的几张图来看看是否可以起到画龙点睛的作用。如图1所示为LPC2000系列ARM中断系统简图,标志位I和F是中断禁止标志位,用来使能或禁能ARM的2种外部中断源,其实所有LPC2000的外设都会与这2条中断线相连。当I置位时,IRQ中断被禁止;当F置位时,FIQ中断被禁止。
注意:当I或者F控制位为0时,则允许IRQ或者FIQ中断(使能),即CPU内核可以“响应中断”了。也就是说,如果I = F = 1,此时即便外设产生了中断,ARM内核也不响应。那么到底需要满足什么条件CPU才能“响应外设中断”?到底需要满足什么条件CPU外设才能“产生中断”?
图1 LPC2000系列ARM中断系统简图
LPC2000系列ARM的中断系统可以分为3个层次。最外层是数量众多的外设部件,它们可以产生中断信号。处于最里层的是ARM内核,它通过IRQ和FIQ两根信号线接收外部的中断请求信号,并根据CPSR寄存器的I和F位来决定是否响应中断请求。处于中间层的是向量中断控制器(VIC),它起着承前启后的作用,它管理着外层各个外设部件的中断信号,将这些中断信号分配到ARM内核的两根中断请求信号线上,从而自然得出如图2所示的CPSR与IRQ、FIQ中断的关系图。
图2 CPSR与IRQ、FIQ中断的关系
我们以定时器外设为例来看看VIC中断向量是如何“响应(管理)外设中断”的,如图3所示,定时器0和定时器1分别处于VIC的通道4和通道5,中断使能寄存器VICIntEnable用来控制VIC通道的中断使能。
图3 定时器与VIC的关系
- 当VICIntEnable[4] = 1时,通道4中断使能,即:定时器0中断使能;
- 当VICIntEnable[5] = 1时,通道5中断使能,即,定时器1中断使能。
中断选择寄存器VICIntSelect用来分配VIC通道的中断。当某一位为“1”时,对应的通道中断分配为FIQ;当某一位为“0”时,对应的通道中断分配为IRQ。VICIntSelect[4]和VICIntSelect[5]分别用来控制通道4和通道5,即:
- 当VICIntSelect[4] = 1时,定时器0中断分配为FIQ中断;
- 当VICIntSelect[4] = 0时,定时器0中断分配为IRQ中断;
- 当VICIntSelect[5] = 1时,定时器1中断分配为FIQ中断;
- 当VICIntSelect[5] = 0时,定时器1中断分配为IRQ中断。
当分配为IRQ时,还需要设置对应的通道控制寄存器和地址寄存器。
图4 匹配中断示意图
LPC2000系列ARM定时器计数溢出时不会产生中断,但是匹配时可以产生中断。每个定时器都具有4个匹配寄存器(MR0~MR3),可以用来存放匹配值,当定时器的当前计数值TC等于匹配值MR时,就可以产生中断。寄存器TnMCR控制匹配中断的使能,以定时器0为例,定时器匹配控制寄存器TnMCR用来使能定时器的匹配中断,如图4所示。
- 当T0TC = T0MR0时,发生匹配事件0,若T0MCR[0] = 1,则T0IR[0]置位;
- 当T0TC = T0MR1时,发生匹配事件1,若T0MCR[3] = 1,则T0IR[1]置位;
- 当T0TC = T0MR2时,发生匹配事件2,若T0MCR[6] = 1,则T0IR[2]置位;
- 当T0TC = T0MR3时,发生匹配事件3,若T0MCR[7] = 1,则T0IR[3]置位。
由此可见,以中断为主线贯穿整个CPU渗透到各个功能部件环环相扣,起到了极其重要的纽带作用,由内核到外设以中断为主干、以外设为枝叶,从而构成“中断关联多叉树”,这是学习和设计ARM嵌入式应用系统的关键所在。
从单片机、X86到ARM半导体,原厂提供的很多功能部件内部结构图实在没有几个搞应用的人能够真正明白,事实上无论是8位单片机还是32位ARM,控制外设就是对外设寄存器的编程,寄存器的每一位不是0便是1,据此画出“寄存器逻辑开关关联图”也就不难了,然后采取填鸭式的方法“按图索意”编程即可。
显而易见,只要抓住了“中断关联多叉树”和“寄存器逻辑开关关联图”,一切问题将迎刃而解。
二、嵌入式实时操作系统
《嵌入式实时操作系统μC/OS-II(第二版)》是一本好书,但对于初学者来说有一定的难度,如果作为教材的话就有点显得不合适了,那么怎样才能做到用最少的代价达到最佳的效果呢?
我们不妨猜想一下,μC/OS-II作者在最初的开发阶段写的第一段代码一定是很简单的,可能只有任务的调度,暂且命名为“μC/OS-II最小内核”,即0.1版本。
当考虑到不常用的任务在运行完毕之后,如果不删除的话势必要占用一定的RAM开销,于是又不得不添加一个新的函数,即OSTaskDel()删除任务函数,暂且命名为0.2版本,称之为“μC/OS-II微小内核”。
当任务间需要传递信息时,当然还有很多的需求,比如任务间同步,ISR与任务间同步,资源同步,于是写出了0.3版本的代码,即增加信号量的创建、发送信号量、接收信号量等函数,直至发布1.0版本。
基于上述思想,我们对μC/OS-II 2.52版本进行了恰当的裁减,分别由小到大将μC/OS-II 2.52版本裁减为4个只具备基本功能的微小内核。
通过查看裁减出来的4个微小内核的源程序,可以看到其中代码量最大的一个微小内核SOURCE4也不过1100行(指剔除文件头和函数头后的数目),而且仅移植代码和配置代码就占用了1/4,对于初学者来说,最多只需要阅读μC/OS-II微小内核中的800行源程序即可。然而最基本的微小内核SOURCE1的核心代码只有418行(指剔除文件头和函数头后的数目),仅包含5个最基本的服务函数的“最小内核”。
μC/OS-II微小内核虽然代码很少,但已经具备了RTOS的基本特性,而且这是μC/OS-II最核心的代码,因此通过分析这些代码,对于理解和使用RTOS已经足够了。μC/OS-II微小内核麻雀虽小,但五脏俱全,由此可见对于初学者来说,通过解剖μC/OS-II最小内核是学习嵌入式实时操作系统最好的入门方法。
三、基于μC/OS-II的程序设计
用简单易懂的语言、图、表以及简单的程序来说明复杂的理论知识,这是博主长期以来的愿望和风格,但常常却有人认为如此简单太没有水平了,殊不知这恰恰是最难的。
程序设计基础有关的内容只用了3个器件,那就是一个LED发光二极管、一个蜂鸣器和一个按键,为增加程序的可读性,还在按键上并接了一个用硬件的去抖动的小电容。
为阐述互斥信号量、信号量、事件标志组、消息邮箱、消息队列与动态内存管理,选用了28个简单得不能再简单的例子,详细介绍了标志与、标志或、资源同步、ISR与任务间同步、任务间同步、在中断中获取信号量、任务间数据通信、数据通信、多任务接收数据等系统函数的使用。
四、注重细节直至精通
比如说,半字与字的定义,刚开始我也没有在意,当要给学生讲课时,我感到表述起来有点困难了。通过学习和讨论之后,定义如下:
字:以能被4整除的地址开始连续的4个字节构成一个字,字的数据类型为4个连续的字节。
半字:从偶数地址开始连续的2个字节构成一个半字,半字的数据类型为2个连续的字节。
ARM的指令的长度刚好是1个字,Thumb指令的长度刚好是一个半字。
五、学习经验总结
作为CEO最重要的才能不是精通技术,但要做到样样“通”才能立于不败之地,所以从某种意义上来说,我并非技术专家。但由于我有深厚的技术功底和见多识广的学习机会,再加上我非常愿意学习新技术,且不耻下问地向公司的技术专家请教。有时为了一个技术问题,我时常在几年之内一直“耿耿于怀”直到最后得出最好的方法为止,在我来看这就是勤奋。
其实上述方法的总结也并非一朝一夕可以得到的,通过勤奋学习和钻研最终上升到方法的境界,“深入浅出、图文并茂、前后对照、来龙去脉”十六字心得就是我学习新知识的法宝。