`
810364804
  • 浏览: 780041 次
文章分类
社区版块
存档分类
最新评论

ARM9硬件接口学习专题

 
阅读更多

ARM9硬件接口学习专题

FROM:http://blog.chinaunix.net/u2/60011/showart_471534.html

前言:

学习嵌入式,ARM接口知识还是很重要的。学好接口可以明白底层硬件是如何工作的以及程序员如何控制这些硬件工作。如果想将来从事嵌入式底层开发方面的工作的话,如bootloader(uboot)移植、ARM驱动开发等,这些知识也都是必备的。

本文对自己以前学习ARM接口知识时的笔记做了个整理,做成专题的形式。一方面复习巩固一下已学过的知识,另一方面和大家分享自己的经验,也希望能给初学者提供帮助!

学习ARM接口最好的办法莫过于自己动手写几行代码,控制硬件工作。这样才能对硬件有更深入的了解。如果只是看Datasheet而不写代码的话,效果甚微!

写接口代码并非很难,通常我们可以把它归结为两个步骤:一、看懂硬件datasheet 二、往寄存器赋值(控制硬件工作)。难点往往集中在第一步,看懂datasheet,这一点需要一定的硬件基础和英语基础。没有硬件基础的话就需要硬着头皮往下看,另外再找些相关资料和代码来互补学习。另外,英语也很重要,因为datasheet基本都是E文的。所以学嵌入式英语也一定要好啊!在大学里意识不到,等出来后真正应用到的时候就越来越发现英语的重要性了。现在得拼命地补啊! J

下面介绍一下我的开发环境:

1. 开发板:Armsys2410

板子是s3c2410 (arm920t core)的,做工不错,可惜价格贵了点,近一年前买时花了俺1700大洋,还没屏。本来以为还行,不过现在后悔了,提供的uboot只有bin文件,不送源码。上次uboot移植碰到点难题,打电话问他们要,讲了半天也没肯给。郁闷!花这么多钱还没技术支持! 好歹uboot还是开源的,一点开源精神都没有。还好自己后来移植成功了,uboot移植系列文章会在ARM接口专题结束后分享发出来。

2.开发工具:ads 1.2

ADS的使用就不介绍了,可以参考《ARM应用程序开发详解》,附录里有下。如果要用仿真器的话,必须用这个工具。还要注意的是,ADS里使用的汇编是ARM公司的标准汇编,和Linux 下的GNU汇编是不一样的,语法有少许差异,差异可以参考这篇文章:

http://blog.chinaunix.net/u2/60011/showart_470995.html

注意,这个专题里使用的是ARM标准汇编。

3.仿真器:Hjtag

没钱买仿真器的兄弟就用这个吧,绝对好东西啊。软仿真,不需要硬仿真器,完全免费。基本调试功能都有,支持ARM9,支持ADS。对于调试些小的接口程序足够了。强烈推荐!:)

使用参考:

http://blog.chinaunix.net/u2/60011/showart.php?id=471527

4.烧录工具:sjflash (for windows)+ jflash-s3c2410 (for linux)

工具及使用方法见附件:

参考资料:

1.S3c2410 datasheet

学习ARM硬件接口最好的资料就是该ARM的Datasheet,比任何资料都权威、详细。市面上大部分的ARM接口方面的书都是翻译的这上面的。

2.S3C2410完全开发流程

非常好的s3c2410 接口学习文档,强烈推荐。自己以前大部分的的接口代码都是参考的上面的。另外整个文档也写了非常好。

附件:

1.《ARM应用程序开发详解》

http://blogimg.chinaunix.net/blog/upfile2/080124235942.pdf

2.sjflash (for windows)+ jflash-s3c2410 (for linux)

ARM9硬件接口学习之一 WatchDog

Watchdog是整个ARM体系结构中相对比较简单的接口,控制代码只有几行,写起来比较容易。首先选择学习watchdog,可以快速入门,先对底层硬件工作原理有个初步的认识。(Watchdog即通常我们所说的“看门狗”)

Watchdog原理上就是一个定时器。定时器timer对时钟进行计数,当定时器溢出时,产生复位信号,使得整个系统复位。在程序或嵌入式系统中,需要定期的对看门狗timer进行复位重新计数,定时器不会溢出复位系统,从而保证系统的正常运行。当某种原因(例如干扰)引起程序跑飞或者进入死循环时,程序不能定期的复位看门狗timer, 计数溢出产生复位信号,导致系统复位。从上面的解释中可以看出,Watchdog的作用就是为了防止系统因意外“跑飞”,导致整个系统瘫痪时,恢复(reset)系统运行

下面认真分析一下Watchdog的Datasheet。这一步不可省,要想对硬件寄存器赋值,操作硬件工作,必须对硬件的datasheet及其工作原理相当熟悉,了解该硬件对应的每个寄存的作用以及每一位的含义,这样才能对其赋值。

这里简要的概括Watchdog的特性,详细的WatchDog及寄存器各位定义参考S3c2410 Datasheet的第18章WATCHDOG TIMER。

Watchdog Timer Block Diagram:

ARM9硬件接口学习专题(zhuan) - wuzhe888 - wuzhe888的个人主页

1.输入时钟为PCLK(该时钟频率等于系统的主频),它经过两级分频(Prescaler和frequency division factor),最后将分频后的时钟作为该定时器的输入时钟,当计数器期满后可以产生中断或者复位信号。

2.S3C2410的看门狗定时器有两个功能

1)作为常规定时器使用,并且可以产生中断

2)作为看门狗定时器使用,期满时,它可以产生128个时钟周期的复位信号

3.看门狗定时器计数值 <-----重要

1)输入到计数器的时钟周期

t_watchdog = 1/( PCLK / (Prescaler value + 1) / Division_factor )

预分频器Prescaler及分频因子Division factor的值由用户在WTCON(看门狗时钟控制寄存器)中设置。PCLK为系统运行频率,如200MHZ。

2)看门狗的定时周期

T = WTCNT * t_watchdog

WTCNT为看门狗数据寄存器,用来设置定时多少个时钟周期。乘以时钟周期就是定时的总长度了。

4.看门狗定时器寄存器

1)控制寄存器(WTCON) 设置预分频器及分频因子值等 <----重要

2)数据寄存器(WTDAT)若不设置WTCNT,会使用这里的初始值(0x8000)。

(注:这里我也不太确定,从datasheet上来看,好象是这个意思。但应该不影响使用。只要设置了WTCNT就行)

3)计数器寄存器(WTCNT) 用来设置定时多少个时钟周期(t_watchdog) <----重要

总的定时长度即T = WTCNT * t_watchdog

要控制Watchdog工作,我们只需要向这三个寄存器赋相应的值,Watchdog会按这些寄存器配置的值进行工作。

注:详细的WatchDog寄存器各位定义参考S3c2410 Datasheet的第18章WATCHDOG TIMER。

5.下面我们分析一下这些寄存器:

1)控制寄存器(WTCON)

ARM9硬件接口学习专题(zhuan) - wuzhe888 - wuzhe888的个人主页

这里比较重要的是Prescaler Value位和Clock Select位(即division factor),计算定时器时钟周期的公式里用到这两个值。

公式为:t_watchdog = 1/( PCLK / (Prescaler value + 1) / Division_factor ) 。另外,因为我们没有进行任何系统时钟频率设定,即没有使用PLL。系统默认工作频率PCLK为12MHZ(时钟设置,以后实验会介绍)。通过这三个值的设定,我们可以计算出Watchdog的时钟周期。接着再在WTCNT寄存器中设置定时的时钟周期数,根据公式T = WTCNT * t_watchdog就可以计算出整个定时时间。

2)计数器寄存器(WTCNT)

用来设置定时多少个时钟周期(t_watchdog)

3)数据寄存器(WTDAT)

通常采用Reset value或和WTCNT值一样即可。

实例分析

1.实验目的:使用watchdog实现系统每隔2.66S左右就复位一次。

这里有必要先介绍一下ADS下ARM接口程序的结构。通常我们的接口程序代码结构如下:

包含head.s和main.c两个文件

head.s是入口代码,执行系统最初简单的初始化。主要工作都放在main.c中的Main函数用C语言实现。以后我们的接口代码都按这样的结构来写。

head.s内容如下:

IMP<wbr>ORT Main</wbr>

AREA Init,CO<wbr>DE,READONLY</wbr>

ENTRY

_start

MOV sp, #0x33000000

B Main ;/*跳转到C语言程序*/

END

ARM标准汇编写法方法参考《ARM应用程序开发详解》第四章。这里有下载:

http://blogimg.chinaunix.net/blog/upfile2/080124235942.pdf

main.c内容如下:

#define rWATCNT (*(volatile unsigned short int *)(0x53000008))

#define rWATCON (*(volatile unsigned int *)(0x53000000))

#define rWATDAT (*(volatile unsigned short int *)(0x53000004))

int Main()

{

rWATCON = 0x8039;

rWATCNT = 0x0800;

rWATDAT = 0x0800;

while(1)

{

// rWATCNT=0x0800;

}

return 0;

}

rWATCON的值设置为0x8039,对应二进制码为0b1000 0000 0011 1001。对照WTCON寄存器每位的含义得出watchdog被配置为:Prescaler value为0x80 , 使能Watchdog Timer,clock divison factor为128,关闭中断,定时结束时产生reset信号。

根据时钟周期计算公式:t_watchdog = 1/( PCLK / (Prescaler value + 1) / Division_factor ),计算出Watchdog时钟周期t_watchdog=1/(3*2 ) S

rWATCNT值设置为0x0800,根据公式T = WTCNT * t_watchdog计算出定时长度为8/3≈2.66S

2.实验结果测试:

这里使用h-jtag仿真器进行测试。配置好hjtag和axd,将ads编译生成的watchdog.axf文件load到sdram 0x30000000处执行run。大概2.66s左右,系统会自动复位。由于我的nand flash内之前已经烧录了uboot,并且设置为从nandflash启动。所以系统复位后会从串口终端打印出uboot启动的信息。若想取消系统复位,可以将上面代码中rWATCNT=0x0800;这一行去掉。这行代码的作用是不停复位计数寄存器的值,俗称“喂狗”。这样计数寄存器里的值就永远不会计数到0从而产生系统复位。

3.实验总结:

通过这个实验,我们对底层硬件工作原理有了初步的了解。其实控制某个硬件接口工作,就是往其寄存器里赋值。硬件会按其寄存器里的配置进行工作。我们写接口代码是这样,写驱动其实也是这样,只是驱动在其上面做了好多层的封装,包括操作系统层的封装。搞懂硬件接口编程,对以后的驱动编程,也会有一定的帮助。

4.实验代码下载:

ARM9硬件接口学习之二 RTC

在一个嵌入式系统中,实时时钟单元可以其提供可靠的时钟,包括时分秒和年月日;即使在系统处于关机状态下它也能够正常工作(通常采用后备电池供电),它的外围也不需要太多的辅助电路,典型的就是只需要一个高精度的晶振。

S3c2410 RTC特性:

1.时钟数据采用BCD编码

2.能够对闰年的年月日进行自动处理

3.具有告警功能,当系统处于关机状态时,能产生告警中断;

4.具有独立的电源输入

5.提供毫秒级时钟中断,该中断可用于作为嵌入式操作系统的内核时钟

6.使用独立外部时钟晶震,频率为32.768khz。

ARM9硬件接口学习专题(zhuan) - wuzhe888 - wuzhe888的个人主页

RTC相比Watchdog,寄存器比较多。但这里我们只需要先通过实验实现RTC时间的读写功能,因此只要先了解时钟控制寄存器RTCOON、BCD时钟寄存器(SEC、MIN、HOUR等),其他和RTC告警功能等相关的寄存器可以暂时先不管。这样的话RTC接口代码的编写就变得很简单了。

下面分析一下这两个寄存器。

1.钟控制寄存器RTCOON

ARM9硬件接口学习专题(zhuan) - wuzhe888 - wuzhe888的个人主页

RTCEN bit can control all interfaces between the CPU and the RTC, so it should be set to 1 in an RTC control routine to enable da<wbr>ta read/write after a system reset.</wbr>

上面这句摘自datasheet。从这句可以知道,读写RTC寄存器前需要先将RTCEN位置1。另外还有一点需要注意的是CNTSEL位,RTC默认是使用BCD编码,这样对BCD时钟寄存器的读写就变得非常方便。

2.BCD时钟寄存器

包括BCDSEC、BCDMIN 、BCDHOUR等。里面存储时钟对应的BCD码。

实验为设置和读取RTC的时间。代码很简单,这里就不写了,看一下就知道。只要注意读写RTC时钟寄存器前要先打开RTCEN位就行。

代码下载:

ARM9硬件接口学习之三 GPIO

GPIO口一是个比较重要的概念,用户可以通过GPIO口和硬件进行数据交互(如UART),控制硬件工作(如LED、蜂鸣器等),读取硬件的工作状态信号(如中断信号)等。GPIO口的使用非常广泛。

S3C2410一共有GPA到GPH 8个GPIO口、117个pins。

The S3C2410A has 117 multi-functional input/output port pins. The ports are:

— Port A (GPA): 23-output port

— Port B (GPB): 11-input/output port

— Port C (GPC): 16-input/output port

— Port D (GPD): 16-input/output port

— Port E (GPE): 16-input/output port

— Port F (GPF): 8-input/output port

— Port G (GPG): 16-input/output port

— Port H (GPH): 11-input/output port

这些I/O Ports大部分是复用的,通常可以用作为输入口(input)、输出口(output)以及特殊功能口(如中断信号)。通过相应口的配置寄存器(GPxCON)可以选择配置为不同的功能。配置好GPIO口的功能后就可以在相应数据寄存器GPxDAT读/写数据,GPxUP用于确定是否使用内部上拉电阻。

(注:上拉电阻作用为当I/O PORTS被定义为input口时,为了避免信号干扰产生不正确的值,通常会使用上拉电阻。)

当引脚设为输入时,读此寄存器可知相应引脚的状态是高是低;当引脚设为输出时,写此寄存器相应位可令此引脚输出低电平或高电平。GpxUP:某位为0时,相应引脚无内部上拉;为1时,相应引脚使用内部上拉。

注:S3c2410各个GPIO口的地址和作用参考Datasheet第9章 I/O PORTS。

小键盘控制LED灯显示实验:

当K1-K4中某个按键按下时,LED1-LED4中相应LED点亮。

要完成这个实验,我们可以分为两步:

一.看懂发板电路图,确定使用到哪些CPU引脚

ARM9硬件接口学习专题(zhuan) - wuzhe888 - wuzhe888的个人主页

上面是我开发板armsys2410 LED部分对应的电路图。从图中可以看出,armsys2410 LED0-LED3分别对应I/O口GPF4-GPF7。

那如何控制LED灯亮灭呢?同样可以在电路图中看出。LED二极管左边为高电压(VDD33V),因此只要在LED右边对应的GPF口引脚输入低电平,导通电路,LED就可以亮了。即要使某个灯亮,就使对应引脚输出一个低电平(灭:高电平)。

ARM9硬件接口学习专题(zhuan) - wuzhe888 - wuzhe888的个人主页

这里是小键盘K1-K4部分对应的电路图。查看GPIO datasheet部分可以知道,EINT0对应GPF0,EINT2对应GPF2,EINT19对应GPG11。即K1对应GPF0、K2对应GPF2、K3对应GPG3 、K4对应GPG11。

二.配置对应的GPIO口

配置小键盘KEY1-KEY4对应的GPIO口为输入口,通过读取该口对应的状态信号,可以判断按键有没按下。配置LED0-LED3对应的GPIO口为输出口。通过输入数据控制LED灯闪灭。下面就是通过对寄存器赋相应的值控制LED显示了。

ARM9硬件接口学习之四 CLOCK

S3C2410 CPU默认的工作主频为12MHz,使用PLL电路可以产生更高的主频供CPU及外围器件使用。S3C2410有两个PLL:MPLL和UPLL,UPLL专用与USB设备。MPLL用于CPU及其他外围器件。

通过MPLL会产生三个部分的时钟频率:FCLK、HCLK、PLCK。FCLK用于CPU核,HCLK用于AHB总线的设备(比如SDRAM),PCLK用于APB总线的设备(比如UART)。从时钟结构图中可以查看到使用不同时钟频率的硬件。

Figure 7-1. Clock Generator Block Diagram

ARM9硬件接口学习专题(zhuan) - wuzhe888 - wuzhe888的个人主页

(注:这里要注意从图中看出,Uart使用的是PCLK,后面Uart实验会用到)

下面介绍MPLL的启动流程:

(注:下面内容部分直接摘录自《s3c2410完全开发流程》,Clock部分写了非常好)

S3c2410 datasheet 224页“Figure 7-4. Power-On Reset Sequence”展示了上电后MPLL启动的过程

ARM9硬件接口学习专题(zhuan) - wuzhe888 - wuzhe888的个人主页

请跟随FCLK的图像了解启动过程:

1、上电几毫秒后,晶振输出稳定,FCLK=晶振频率,nRESET信号恢复高电平后,

CPU开始执行指令。

2、我们可以在程序开头启动MPLL,在设置MPLL的几个寄存器后,需要等待一段时间(Lock Time),MPLL的输出才稳定。在这段时间(Lock Time)内,FCLK停振,CPU停止工作。Lock Time的长短由寄存器LOCKTIME设定。

3、Lock Time之后,MPLL输出正常,CPU工作在新的FCLK下。

设置S3c2410的时钟频率就是设置MPLL的几个寄存器:

1、LOCKTIME:设为0x00ffffff

前面说过,MPLL启动后需要等待一段时间(Lock Time),使得其输出稳定。位[23:12]用于UPLL,位[11:0]用于MPLL。使用确省值0x00ffffff即可。

2、CLKDIVN:用来设置FCLK:HCLK:PCLK的比例关系,默认为1:1:1

这里值设为0x03,即FCLK:HCLK:PCLK=1:2:4

CLKDIVN不同的设置及对应的时钟比例关系如下图:

ARM9硬件接口学习专题(zhuan) - wuzhe888 - wuzhe888的个人主页

3、MPLLCON:设为(0x5c << 12)|(0x04 << 4)|(0x00),即0x5c0040

对于MPLLCON寄存器,[19:12]为MDIV,[9:4]为PDIV,[1:0]为SDIV。有如下计算公式:

MPLL(FCLK) = (m * Fin)/(p * 2^s)

其中: m = MDIV + 8, p = PDIV + 2

Fin 即默认输入的时钟频率12MHz。MPLLCON设为0x5c0040,可以计算出FCLK=200MHz,再由CLKDIVN的设置可知:HCLK=100MHz,PCLK=50MHz。

通常我们将如上时钟初始化的过程写成clock_init函数供其他函数调用,代码如下:

void clock_init(void)

{

/*init clock*/

rLOCKTIME = 0xFFFFFF;

/*设置FCLK:HCLK:PCLK=1:2:4,这样假设处理器主频为200M,则HCLK为50M,PCLK为25M。ARM920T内核使用FCLK, 内存控制器,LCD控制器等使用HCLK,看门狗、串口等使用PCLK*/

rCLKDIVN = 0x3;

/* 设置时钟频率为200M*/

rMPLLCON = 0x5c0040;

}


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics