基于OK6410的嵌入式开发教程(上)

2018-09-26 15:19

图1

在这里,我们先来解释一下异常(Exception)的概念,异常指因

为内部或者外部的一些事件,使处理器不得不停下正在处理的事件,转而去处理这些发生的事件。

我们说ARM处理器共有7种工作模式,其中有5种异常模式,

而在这些异常模式下又有7种异常,它们分别是Reset(复位),Undefined instructions(未定义指令),SWI(软中断),Prefetch Abort(指令预取失败),Data Abort(数据预取失败),IRQ(外部中断),FIQ(快速中断)。

我们再来解释一下什么叫异常向量,当有异常发生时,ARM处理

器会自动跳转到对应该异常的一个固定的地址来执行异常处理程序,而这个固定地址就是异常向量。这个就对应图1后面的address两列,第一列是我们通常情况下使用的地址,后面是高位地址,我们可以通过cp15配置,使cpu选择高位地址还是低位地址,通常我们选用低

11

位的。

有了前面这些概念,我们现在再来看异常向量表就非常简单了:

图2

值得注意的是,我们在编写编译器脚本时里面有一句.=ALIGN(4),这就说明我们接下来的代码是4字节对齐的,所以我们可以看到每一行代码跳4个字节,另外由于中间确实了一部分,所以我们用一个无用的函数名not_used补全,这样就方便了许多。这样就结束了,不懂的可以自己在看一遍或查资料。

12

2.2 设置svc模式

我们前面说过普通的应用程序是在usr下运行的,而内核和

bootloader是在svc模式下运行,所以要运行bootloader,我们必须先将处理器设置为svc模式。我们之前也提过模式的设置是在CPSR的0-4位,这样我们就能找到设置的方向。

参照2.1里的图1和图3,我们可以清楚的看到svc模式是将CPSR0-4位设置为10011。另外还有一点也要说明,我们制作的是相对简单的bootloader,所以irq和fiq最好也设置为不可用,之前在讲CPSR时,也同样提到I位和F位的作用,在这里我们就可以设置了,我们的目的是将第6,7位设为1,第0-4位设为10011,而其他位不变。 编程如下:

2.3 关闭看门狗

首先我们先来介绍一下关门狗是什么,起到什么作用。Watchdog

mrs r0, cpsr bic r0, r0, 0x1f orr r0, r0, 0b11010011 msr r0, cpsr mov pc, lr

也就是我们所称的看门狗,一般是一个硬件模块,在长期无人值守的地方,电子器件可能会出现死机的情况,这是无人干预,就需要系统

13

自己带有一个自动重启的机制,它在硬件上实现了计时功能,启动计时后,需要用户(软件)来在其计时结束前重新开始计时,俗称“喂狗”,否则watchdog就会认为死机了,就会重启系统。说白了,它的作用就是在死机时使系统自动重启。

很多人看到这里,可能会想这看门狗好像很有用,那我们为什么

要关闭它?这个结论是我们之前分析U-BOOT时得到的,我们认为bootloader很简单,一般不会死机,而不关闭它反而在程序运行时要不断喂狗,这样很消耗资源,所以我们就把它直接关闭了。 这里我先给一张看门狗的原理图:

图1

这里的话,我就不对这个原理图多加赘述了。我们直接看如何关闭看门狗吧。

14

我们在这里就得看看门狗的控制寄存器(WTCON)了,我们要关注一下第0,2位,分别是控制了看门狗超时后重启和中断的功能,我们都将其设置为0就行。 编程如下:

15

#define WTCON 0x7e004000 ldr r0, =WTCON mov r1, 0x0 str r1, [r0] mov pc. Lr

U-BOOT编写

(基于OK6410)

Written by water

1

1.U-BOOT工作原理及设计蓝图

在所有章节开始必须首先介绍一下U-BOOT,所谓U-BOOT就是

bootloader的一种,可用于多种cpu(x86,ARM等),在不同系统像Linux,Vxworks,QNX等上运行调试。它能起到在上电后能对硬件,存储器,内核等完成初始化,并且引导内核和文件系统正常工作的作用。U-BOOT并不是单纯的裸机程序,在学完此课程后,可以完成大部分裸机程序的调试,移植与开发。最后得说明,我所用的开发板是OK6410,而我所做的学习笔记完全是建立在国嵌视频上的,我会在这个笔记中对视频的内容进行总结,顺序会有所调整。 1.1 U-BOOT工作原理

要讲解这个首先需要让大家看一张图(注:这张图是2440的机理图,放在这里是为了能方便讲解和理解):

这张图共分为两部分,左边部分是用于nor flash启动,而右边这

2

部分就是用于nand flash启动了,这个也是我们讲的重点。首先需要明确一点,上电后第一条程序,肯定是从0地址(0x00000000)开始执行的。左图显示0地址是在SROM中,这个我们不去管它,而右图显示第一条指令是从SRAM中执行的,而这个存储器只有4K,在这里我们就需要先明确一下“垫脚石(Stepping Stone)”这个概念了。

在2440选择从nand flash启动刚上电时,系统会自动将nand

flash中bootloader的前4K,放入到我们前面所述的0地址,也就是Boot internal SRAM中,此时系统会执行bootloader的前4K,同时系统还会将整个bootloader复制到我们的内存,前4k执行完后,就跳转至内存继续执行剩余的bootloader,而我们这里4K大小的SRAM就是垫脚石(stepping stone)。为什么需要它,这点很简单,两图对比,我们就能发现我们的nand flash没参与我们的统一编址,所以只能借stepping stone作为我们的桥梁。

介绍完2440,我们来看看6410。先给大家几张图:

图1

3

图2

图3

我们先看图2,从图2我们可以看出6410可以从哪些部分启动,

而那些部分又被存放在哪儿,我们可以注意到nand flash是被存放在IROM中的。接下来我们看图2,我们可以看到0地址处放的是一个最大可达到128M的存储器,描述和备注我就不赘述了,可以看出他

4

是个镜像存储器,接下来就是IROM和stepping stone,下面的存储器我们暂时不研究,不说了。

看完两张图,可能最有疑问的是最后一张图,我们说过上电执行

的第一条程序应该在0地址处,为什么那里不像2440一样放的是stepping stone,而是一个镜像存储器呢。其实聪明一些的话,应该已经注意到了,既然是镜像存储器,那当上电后,我们的IROM和stepping stone(两个最大都能达到64M),会被镜像入0地址处的存储器(最大可达128M)中,然后剩下的就和2440一样了。

在这里可能大家还会有疑问,2440是直接从stepping stone中直接

启动,6410前面突然多了个IROM,好象有些不适应。我们来看图1。

图1是6410bootloader的工作原理图,我们可以看到IROM里存

放的是BL0,这个BL0他会首先完成对硬件的初始化,同时会将BL1也就是BL的前8k(6410的steeping stone为8K)放入垫脚石中,然后就和2440一样,将BL放入内存继续执行。 至此,bootloader的工作原理讲解完毕。

现在必须要分析bootloader启动流程以完成我们自己的bootloader的架构设计。我们采用分析U-BOOT的方法,来建立属于我们自己的bootloader。由于分析的过程冗长耗时,在这里我就不分析了,但我会给出一张分析出的结果图:

5

2.U-BOOT编写

2.1 一些需要知道的知识概述

首先大家必须先要了解的是ARM处理器的七种工作模式:

图1

图2

6

两张图表明ARM处理器一共有7种工作模式,在这里我列一张表格来叙述其名称及作用: 用户模式 系统模式 管理模式 usr 用于普通状态下正常执行程序 sys 运行具有特权的操作系统任务 svc 对操作系统的保护模式 数据访问中断abt 当数据或指令预取终止时进入该模式,可用于模式 虚拟存储或存储保护 未定义指令终und 当未定义的指令执行时进入该模式,可用于支止模式 持硬件 外部中断模式 irq 用于通用的中断处理 快速中断模式 fiq 用于高速数据传输或信道处理 有几点需要说明普通的应用程序是在usr下运行,而我们的内核和bootloader是在svc下运行。

至此,7种工作模式交代完毕,而图1中0bxxxxx,这种东西是用于CPSR,即程序状态寄存器使用的。这也是我们接下来要说的,我们的处理器一共有37个寄存器,如图2,其中有31个通用寄存器和6个程序状态寄存器,r0-r7为不分组寄存器,r8-r14为分组寄存器,最后还有个r15,也就是我们所熟知的程序计数器(pc)。其实这当中还有些特殊的寄存器,比如r13(堆栈指针sp),r14(链接寄存器)。这个我们以后会说的。这部分内容相对简单,我也不想细说,如有疑问,请自己查资料。

接下来我们来谈谈CPSR(程序状态寄存器)和SPSR(程序状态

7

保存寄存器),我这里先给出一张表:

图3

然后我们再来谈谈CPSR和SPSR的作用和工作原理,看完图2,再查完一些资料,应该会对这个有一定的了解,先研究图2,图2显示我们所说的7种工作模式下都有CPSR,而SPSR只有5种工作模式下存在,这5种工作模式被我们称为异常模式,除usr以外的其他工作模式被称为特权模式,那我们就要问,为什么要这样设计?或者说,这样的设计能反映CPSR和SPSR怎样的工作原理?

这个问题很好回答,首先当我们在usr下运行时,我们的程序会

在CPSR下存有备份,现在可以把它理解成备份,这个时候出现中断,我们这个时候就切入了异常模式,我们会先将状态从CPSR转移至SPSR,等处理器处理完了此次中断,然后在将SPSR的数据返还至CPSR,使状态回复至之前的状态。

解释完了这些,我们现在来看看图3,它反映的是SPSR和CPSR

的寄存器数据格式。这里我们我们不需要全部记下来,但有5位需要注意,分别是N,Z,I,F,M。我这里只做简单叙述,详情请查阅ARM Architecture Reference Mannual。 N位占31位,当两数相减结果为负时置位。 Z位占30位,当两数相减结果为0时置位。 I位占7位,当被置位时,irq不可用。

8

F位占6位,当被置位时,fiq不可用。

M位占0-4位,这里是设置各种模式用的,图1的0bxxxxx就是用在这里的。

到这里我们就把所有的模式及寄存器讲完了,接下来我们就需要编程了,编程我是不会在学习笔记中赘述的,但我会截图说明,涉及到原理的,我可能会大篇叙述,如有疑问大家请自己查阅资料。

2.1 汇编框架的编写及异常向量表

既然是汇编文件,我们肯定需要一个.S的汇编文件,除此之外,

我们编写的bootloader可能文件有些繁杂,所以我们需要一个makefile文件,最后还需要一个.lds的汇编器脚本文件。

我们先编写汇编器脚本:

9

然后我们再来编写makefile文件:

最后,我们来编写start.S这个文件,要编写这个,我们必须要先

查阅一下ARM Architecture Reference Mannual。

10

2.4 关闭中断

关闭中断说白了就是关闭外部中断(irq)和快速中断(fiq),我

们先给一张图:

图1

我们从左到右可以在图中清楚的看到一个中断源会经过SRCPND,接下来要达到真正的中断还需要MODE和MASK的支持,我们需要做两件事才能真正地关闭中断,分别是设置CPSR和设置中断屏蔽寄存器。

第一件事我们在前面进入svc模式时已经做过,忘了的可以回头

看看,第二件就是我们现在将要做的。再给几张图(基于6410):

图2

16

图3

图4

图2我并没有截全,只截了重要的部分,我们可以看到一个description是Interrupt Enable Clear Register(中断使能寄存器)的寄存器,我们把那个寄存器找到,描述看图3,后面的备注告诉我们,这个寄存器只能用以设置中断使能,如果要关闭中断,需要一个叫INTENCLEAR 的寄存器,我们继续找这个寄存器,如图4,这个就是我们要找的寄存器。我们需要把VIC0,VIC1都设置为1。这样编程就简单了。 编程如下:

#define INTCLR_VIC0 0x71200014

17

#define INTCLR_VIC1 0x71300014 ldr r0, =INTCLR_VIC0 mvn r1, 0x0 str r1, [r0]

ldr r0, =INTCLR_VIC1 str r1, [r0] mov pc, lr

2.5 关闭mmu和cache

我们在编程前,必须要知道mmu是什么,起什么作用且为什么要关闭他。但是由于各种考虑,这些东西我觉得应该大家自己查阅资料,我这里只做简单介绍。

我们先给出百度对mmu给出的定义。

图1

说白了,mmu就是将虚拟地址处理成内存中的物理地址,它与虚拟内存是紧密相关的。另外cache,也就是高速缓冲存储器,或许根本不需要解释。我们这里只给ARM11核的几张图,大家意会一下,不懂可以去查资料。

18

图2

图2是计算机存储体系的一张非常有名的金字塔模型,同样适用于嵌入式系统,它反映了不同存储器与系统的关系紧密程度,存储器容量与速度大小与级别高低。

图3

19

图3在给出各种需要了解的重要概念的定义的同时也反映虚拟地址的作用,用它可以再继续深入到mmu的作用,方便易记。

图4

图4反映了MMU和cache在虚拟地址转物理地址时与cpu和主存之间的关系,上图是ARM11之前的系统,而下图则是ARM11之后(包括ARM11)的系统。

看完这些,自己再查阅一些资料以后,相信会对一些基础知识有所了解,我们在来说为什么要关闭mmu和cache,其实这两件东西都是好东西,我们智慧需要的时候会在打开,但现在由于系统还未配置好,

20

所以现在打开它,可能会造成内核取址(mmu)和内核数据往内存的下载(cache)上的错误,所以现在先关闭它。

我们现在来编程吧。我们需要ARM11的核手册,因为要配置mmu和cache需要设置cp15。我们先给几张图:

图5

21

图6

我们需要做两件事,第一件时使D-cache里的数据失效,见图5,已经给出了明确的说明,我们选择第3行。

第二件就是彻底使mmu和cache关闭,见图6,我们需要配置0位和2位,1位我们也顺便配置了,其实1位配不配置都无伤大雅。 编程如下:

mcr p15, 0, r0, c7, c7, 0 mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00000007 mcr p15, 0, r0, c1, c0, 0

22

mov pc, lr

2.6 点亮LED

先不说技术上的问题,我们先来谈谈技术上的问题。在我们编程时,我们可能会因为各种原因而犯错,但此时由于是bootloader初期,我们并未对我们串口等硬件初始化,我们的调试手段相当有限,而我们又不知道哪里犯了错,所以我们必须使用一个可视化的检查机制来告诉我们我们之前的程序是否出错,这样每添加一段程序,我们就可以使用此机制来验证,这样效率会提高很多。然后我们来谈一下技术上的事,毕竟LED大家都懂,不需要我来特地浪费篇幅解释。 我们要做点亮LED需要做两件事,不过在此之前,我们需要了解一些概念。我先给几张图:

23

图1

图2

24

图3

图1是6410底板LED的原理图,图2是核心板cpu上有关LED的GPIO引脚图,图3是图2所指区域的放大图。

我们先看图1,可以看出我们板上有4个用户使用的LED,他们所对应的GPIO引脚分别是NLED1,2,3,4。另外我们知道要使LED亮,我们需要在引脚上设置为输出低电平。图2位核心板cpu GPIO的示意图。在讲解这些之前,我们先了解一下什么叫GPIO。 GPIO(General-Perpose Input/Output Ports)也被称为通用输入输出端口,在嵌入式系统中,cpu通常需要控制一些简单的外部设备和电路,这些设备或电路通常只有开关两种状态,控制这些用USB或串口就显得复杂,所以嵌入式微处理器上通常提供了“通用可编程I/O端口”,也就是GPIO。

看完这些我们再来梳理一下思路,要使LED亮需要使线路输出低电平,也就是要控制LED的GPIO输出低电平,而图3告诉我们,

25

控制LED的GPIO分别是GPM0,1,2,3。

现在我们我们的思路就清楚了,然而我们还需要了解一个关于GPIO寄存器的知识,GPIO一般是由至少2个寄存器控制,6410应该是3个,我不确定,第3个我们暂时不需要了解。第一个是控制寄存器,控制引脚的输入输出,第二个是数据寄存器,控制高低电平。了解了这些我们就可以编程了。

图4

我们需要就GPM0,1,2,3都设置为输出,再把它们设为低电平。 编程如下:

#define GPMCON 0x7f008820 #define GPMDAT 0x7f008824 ldr r0, =GPMCON ldr r1, =0x1111

26

str r1, [r0] ldr r0, =GPMDAT mov r1, #0xf str r1, [r0]

2.7 时钟初始化

这个章节基础的知识很多,我们先解析一些概念。

我们先来了解一下时钟脉冲信号,时钟频率和时钟源这三个概念。 时钟脉冲信号按一定的电压幅度,一定的时间间隔连续发出的脉冲信号。时钟脉冲信号是时序逻辑的基础,它用于决定逻辑单元中的状态何时更新。数字芯片中众多的晶体管都工作在开关状态,它们的导通和关断动作无不是按照时钟信号的节奏进行的。说白了他们就是一个时钟,告诉系统何时该做什么事而何时又结束。

时钟脉冲频率就是单位时间产生的时钟脉冲个数。

时钟源这个可以分成两类,一种是晶振,另一种是PLL(锁相环)。第一种晶振,全称晶体振荡器,是将石英进行精密切磨然后通电产生,其振动频率与它们的材料,形状与切割方向密切相关。是一种简单的典型的系统时钟振荡器源。

PLL(锁相环)合成器是一种更加复杂的系统时钟源,它需要一个外部晶体并包含一个能对特定频率加倍或分频的PLL(锁相环)集成电路。

PLL比较晶振所需的晶体振动频率更低,但是他能合成符合各种

27

需求的振动频率。它的成本更低,精密程度不落于晶振,是一种优秀的系统时钟源。

现在,我们需要了解一些关于6410开发板的时钟体系,首先6410的晶振频率是12MHZ,然后我们需要知道它有哪些PLL,PLL又产生出了哪些时钟,这些时钟又有些什么用。

图1

从这张图上我们了解到6410有APLL,MPLL和EPLL三种PLL,而这三种PLL又分别产生ARMCLK,HCLK,PCLK和SCLK。这四种时钟有什么用呢?我们来看看。

图2

28

图3

图2大家了解一下就行,图3使我们各种时钟的作用。

图4

我们可以从图中看出,我们在初始化时钟时配置完后会出现Lock time,而在这之中SYSCLK的时钟频率会归零,之后会按照分频的频率运行,SYSCLK也就是我们这里的ACLK,毕竟系统时钟,会和内核联系在一起。现在我们来看看初始化时钟需要哪些步骤。 第一步是设置Lock Time 第二步是设置分频系数。

第三步是设置cpu到异步工作模式。 最后是设置PCLK和MCLK(见图5)

29

有人可能会对上面的步骤抱有异议,那我们现在来看看为什么要这样设置。第一步设置Lock Time,大家应该是不会说什么的,但是我们这里不设置,我们保持它的默认值不动。

第二步,我们之前说过PLL可以通过分频来满足一个系统中不同时钟体系的需求,很显然我们要用它,所以我们需要设置分频系数。 第三步,当ARMCLK(核的时钟)和HCLK(系统总线时钟)分频系数不同时,需要设置为异步模式。

第四步,分完頻,至少我们需要设置其中某个量为基准值。而图5也告诉我们APLL和MPLL似乎是基准值。 这样似乎就合理了,下面我们来编程吧。

图5

30

ldr r0, =0x7e001034

ldr r1, =( ( 15 / ( 1000000000 / 133000000 ) + 1 ) ) str r1, [r0]

ldr r0, =0x7e001038

ldr r1, =( ( 15 / ( 1000000000 / 133000000 ) + 1 ) ) @ ldr r2, [r0] str r1, [r0]

ldr r0, =0x7e00103c mov r1, #0x07 str r1, [r0]

ldr r0, =0x7e001040 mov r1, #0x02 str r1, [r0]

ldr r0, =0x7e001044

ldr r1, =( ( 120 / ( 1000000000 / 133000000 ) + 1 ) ) str r1, [r0]

ldr r0, =0x7e001048

46

ldr r1, =( ( 120 / ( 1000000000 / 133000000 ) + 1 ) ) str r1, [r0]

ldr r0, =0x7e00100c ldr r1, =0x00010012 str r1, [r0]

ldr r0, =0x7e00104c ldr r1, =0x0b45 str r1, [r0]

ldr r0, =0x7e001200 ldr r1, =0x150f8 str r1, [r0]

ldr r0, =0x7e001304 mov r1, #0x0 str r1, [r0]

ldr r0, =0x7e001008 ldr r1, =0x000c0000 str r1, [r0]

47

ldr r1, =0x00000000 str r1, [r0]

ldr r1, =0x00040000 str r1, [r0]

ldr r1, =0x000a0000 str r1, [r0]

ldr r1, =0x00080032 str r1, [r0]

ldr r0, =0x7e001004 mov r1, #0x0 str r1, [r0]

check_dmc1_ready:

ldr r0, =0x7e001000 ldr r1, [r0] mov r2, #0x3

48

and r1, r1, r2 cmp r1, #0x1

bne check_dmc1_ready nop mov pc, lr

最后我们还得了解一点,第一段汇编中的寄存器0x7e00f120,那个是什么寄存器呢?我们在芯片手册搜一下看看。

图11

49

图12

图11和图12我们可以看到这个寄存器各位的描述都不短,但我们现在只研究其中的第七位,我们应该设成0还是1。描述中Xm1是什么?其实他对应的是6410的Dynamic memory的数据线,也就是我们的DRAM的数据线,我们选0,是将他设为数据传输,选1是将他用于SROMC,明显应该选0。最后再附上6410的两块内存的图,以免有人看不清楚。

50


基于OK6410的嵌入式开发教程(上).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:CDMA认证摸底试题-2(答案)

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: