ARM编程进阶之三 —— 裸机硬件的控制方法与例程

到目前为止,我们已经能够编写较复杂的ARM汇编程序了,遗憾的是这些程序是运行在ads自带的虚拟开发板ARMUL下的,(在axd界面下,单击options->configure target,可见到如下的目标板配置界面)

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

而我们最终的目的是要让程序运行在实际的硬件产品上,并能控制硬件。本文将初步介绍如何建立真实硬件的开发和调试环境,编写控制硬件的程序的方法。

第一部分内容:如何建立真实硬件的开发和调试环境

由于基于ARM的嵌入式开发板种类众多,硬件仿真器、调试代理软件也是种类繁多,使用方法各异,这就为学习编写ARM裸机程序控制硬件带来了较大的难度。为便于初学者快速入门,有必要选择一套成熟、易于学习和实践的软硬件环境。经过比较,本文(包括本系列所有与ARM硬件有关的文章)均采用友善之臂提供的带有nor flash 的qq2440(或者mini2440)嵌入式开发板(自带简易的jtag调试仿真器小板)和h-jtag调试器代理软件。以下关于开发和调试环境的搭建,均来源于友善之臂提供的qq2440用户手册(稍微修改),在此表示敬意。

1、 安装并设置H-JTAG

(1)安装H-JTAG

H-JTAG 安装文件位于 “Windows 平台工具\H-JTAG”目录,双击运行,按照其提示安装即可。

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

安装完毕,会在桌面生成H-JTAG 和H-Flasher 快捷方式,双击运行H-JTAG,程序将自动检测是否连接了JTAG 设备,因为之前我们还没有做任何设置,所以会跳出一个提示窗口:

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

点击确定,进入程序主界面,因为没有连接任何目标器件,因此显示如图所示:

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

(2)设置JTAG 端口

在H-JTAG 主界面的菜单里点Setting->Jtag Settings,作如下图所示设置,点OK 返回主界面。

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

(3)设置初始化脚本

把本文提供给你的qq2440_yangzhu.his 和H-Flasher_QQ2440_yangzhu.hfc 文件(点击下载)复制到H-JTAG 的安装目录,如图:

注:后续图及文档中的FriendlyARM2440.his都请认为是qq2440_yangzhu.his,H-Flasher_QQ2440.hfc都请认为是H-Flasher_QQ2440_yangzhu.hfc

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

在H-JTAG 的主界面,点Script->Init Script,跳出Init Script 窗口,点该窗口下面的Load 按钮,找到并选择打开刚刚复制的FriendlyARM2440.his 文件,如图:

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

这时,Init Script 窗口会被载入的脚本填充,如图,注意要点选“Enable Auto Init”,

点OK 退回H-JTAG 主界面:

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

(4)检测目标器件

使用开发板附带的JTAG 小板连接开发板的JTAG 接口,并接上打开电源。点主菜单Operations->Detect Target,或者点工具栏相应的图标也可以,这时就可以看到已经检测到目标器件了。

提示:如果没有设置初始化脚本,也可以检测到CPU,但无法进行下面的单步调试。

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

(5)设置自动下载程序到nor flash

单击Flasher,点选”Auto Download”

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

2、为H-JTAG 配置AXD DEBUGGER

(1)运行AXD Debugger

如图所示打开运行ADS1.2 软件的调试软件-AXD Debugger:

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

AXD Debugger 主界面:

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

(2)设置AXD Debugger

点菜单Options->Confiuguer Target…,出现如下窗口:

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

在该窗口中点击Add 按钮,跳出选择文件对话框,找到H-JTAG 安装目录,选择并打开里面的H-JTAG.dll 文件,如图。

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

此时会在Choose Target 窗口中多了一项H-JTAG,如图,点OK 返回AXD Debugger主界面。

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

(3) 使用H-JTAG 在ADS1.2 环境下进行仿真调试

关闭AXD Debugger。在CodeWarrior中点击菜单Project->Debug,它将自动启动AXD Debugger,AXD Debugger会启动目标映象,并通过Jtag 下载至目标板,这时在AXD Debugger 底部的状态栏会出现下载过程提示,下载完毕就可以进行单步或者全速调试了,调试过程中您可以看到CPU 各个寄存器的值,也可以设置断点等,详细的用法请参考ADS 附带的英文说明手册,这基本上和常见的VisualC++等集成开发环境是类似的。

第二部分内容:编写控制裸机硬件的方法与例程

1、程序控制硬件的编程原理

每一种硬件在其控制器芯片上都会有物理的寄存器(注意这里的寄存器不是指的CPU内部的寄存器R1等等,而是指的硬件芯片上的存储单元,在ARM体系下,这些存储单元与内存进行统一编址,可以被CPU通过访存指令,像访问内存一样去访问),这些寄存器通常分为3种类型:命令寄存器、状态寄存器、数据寄存器。程序控制硬件的办法通常是:程序通过str指令向命令寄存器写入合适的内容,就可以完成对硬件进行配置的操作或者要求硬件进行某种物理操作。到此为止,软件就完成了所有它该做的事情,之后硬件会自动完成相应操作,在硬件完成操作后,程序又可以通过ldr指令从数据寄存器中获得想要的数据,或者从状态寄存器中获知硬件的状态。可见,程序控制硬件,简单地说,其实就是程序对硬件的寄存器进行读写操作,命令硬件完成操作,获取硬件状态和数据,仅此而已。这里的关键是:某个硬件寄存器的内存地址是多少?为使硬件执行某个操作,应当向哪个寄存器写入什么值?这些都是程序员需要解决的问题,而这些问题的解决,关键在于程序员能:a)理解要控制的硬件的运作机制;b)能熟练查阅硬件的手册(手册中会指明寄存器的内存地址以及寄存器各种取值的含义);c)能看懂硬件的连线原理图

2、最简单的裸机硬件控制程序(控制led灯的亮灭,点击下载示例代码

如何才能点亮led灯呢?首先必须先看硬件连接图

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

显而易见,要点亮led1,则必须在nLED_1连接线上输出低电平。

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

要在nLED_1连接线上输出低电平,就必须让CPU的GPB5为低电平。

如何才能让CPU的GPB5为低电平?通过查阅S3C2440的硬件手册(点击下载)的第9章可知,需要将地址为0x56000010的这个寄存器的bit11和10设置为01,从而将GPB5这个管脚配置为输出,然后将地址为0x56000014的这个寄存器的bit5写为0,这样CPU的GPB5管脚就会输出低电平。

这就对应于示例代码中的如下关键代码:

#define    GPBCON        (*(volatile unsigned long *)0x56000010)
#define    GPBDAT        (*(volatile unsigned long *)0x56000014)
#define LEDS   (1<<5|1<<6|1<<7|1<<8)
GPBCON     = 0x00015400;
GPBDAT=(GPBDAT&(~LEDS)) | (1<<6|1<<7|1<<8);

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

示例代码中的readme.txt中,说道:

for running on real board, you need do following 3 things:
1. Change Target from ARM7TDMI to ARM920t
2. Change load address from 0x8000 to 0x30000000
3. Change image layout for placing init.o(INIT2440) at the first

做1的原因是开发板的CPU——S3C2440是ARM920t的内核,所以编译器编译时必须匹配

ARM编程进阶之三 —— 裸机硬件的控制方法与例程 

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

做2的原因是开发板的RAM位于0x30000000——0x34000000地址(共64M),程序必须被调试器加载到ARM才能运行。

特别说明:配置中的0x30000000被我称为“程序的期望加载地址”,简称“加载地址”。“运行地址”与“加载地址”是很重要的2个概念,请大家一定要弄清楚。“运行地址”是给编译器看的,通过看“运行地址”编译器就能计算出程序中各个标号、变量、函数等在内存中的绝对地址,从而完成涉及地址的指令的正确编译。而“加载地址”是给调试器或者操作系统看的,它的作用是让调试器或者操作系统将程序加载到正确(期望)的内存地址。通常情况下,代码的这2个地址是相等的,数据则不一定。

ARM编程进阶之三 —— 裸机硬件的控制方法与例程

做3的原因是init.o的INIT2440段的代码是首先运行的代码,因此必须放在整个二进制程序文件的最开头

ARM编程进阶之三 —— 裸机硬件的控制方法与例程