第二章ARM编程的裸机开发
第二章ARM编程的裸机开发
嵌入式ARM编程的裸机开发是驱动开发的基础,学习裸机开发将有助于加深对嵌入式系统底层运行和底层控制的理解,将进一步提高后续驱动学习的效率。本章节将讲解裸机开发的思路,同时通过示例来说明如何进行裸机开发。
2.1 裸机开发思路讲解
对于接触过单片机开发的读者来说应该对裸机开发一点都不陌生,不过没有接触过单片机开发的读者也没关系,以下将详细讲解裸机开发的编程思路。
常规的裸机开发的思路顺序如下:
(1)分析电路原理图
(2)分析和理解硬件的控制原理
(3)阅读数据手册,分析和理解寄存器的操作控制流程
(4)编写程序实现硬件控制
2.1.1分析原理图
通过阅读嵌入式系统的硬件原理图,我们将可以获取到如下的信息:各个存储器、外围设备使用的硬件资源、各种接口和各款芯片的引脚的联接情况等。阅读电路原理图的思路主要是应该将CPU主控芯片作为中心点出发,逐步扩展阅读外围的存储器以及外围设备等,具体的步骤如下:
(1)阅读主控芯片的数据手册时可由获取到它的片选、中断和外设控制器资源等,可以根据不同种类资源进行标记,方便整理CPU与外设的控制关系。
(2)在步骤(1)中将获取到如下资源信息:符号(symbol)、网络标号(net)和相关的描述。
获取到符号(symbol)的信息,主要可以了解到芯片到外围引脚和信号引脚的信号信息。如下图2.1所示为原理图中符合展示。
图2.1 原理图中的符号
Figure 2.1 Symbol in schematic
在原理图中的网络(net),可以获取到芯片、接插件元件以及各个元器件之间的连接关系等,这样有助于我们对嵌入式系统整体关系,每个网络应该都有自己的合适名字,这些名字可以让开发工程师能够更好的理解原理图,如图2.1中的“ESP REST”这个标号可以获取到此引脚的连接关系以及表示芯片的复位引脚等信息。
如下图2.2所示为原理图中的描述部分,在这里我们可以了解到原理图相关信息,如完成日期、原理图大小以及文件的名字等。
图2.2 原理图中的描述部分
Figure 2.2 Description in schematic
2.1.2 阅读芯片数据手册
在刚接触嵌入式开发的时候,阅读芯片数据手册也是很有必要的,同时芯片数据手册也是我们学习的一个重要资料,但是常规的芯片数据手册往往是有达几百页,更有甚者达到上千页,若从手册最开始阅读到最后所花费的时间将非常久,这也是实际项目开发不允许的。因此正确的芯片数据手册阅读方法是很有必要被掌握的,主要思路是快速定位到有用的信息上面来,对于无关的信息自动省略。如下将以SP56818的数据手册为例来讲解阅读方法。
(1)芯片结构阅读
如下图2.3所示为SP56818这款芯片的结构示意图,这也是一般能够在数据手册的第一章中可以获取,可以让我们能够对主控芯片有一个整体的把控。
图2.3 芯片结构示意图
Figure 2.3 Schematic diagram of chip structure
(2) 目录的阅读
在阅读目录的过程中可以让我们了解到数据手册整体章节安排,一般情况下第一章是芯片的整体结构介绍,接下来的章节则是对芯片的各个模块进行详细的描述,在实际开发时可以直接跳转到我们需要的章节进行阅读。
图2.4 数据手册的目录
Figure 2.4 Directory of the Data Manual
(3)每个章节进行针对性的阅读
第二章为讲述芯片的内存寄存器资源以及其IO引脚的功能描述,对于开发时查看寄存器资源以及复用功能等,这一部分可以细看。
第三章至第四十六章主要讲述CPU的内部的外围设备以及总线控制器,当我们需要进行嵌入式驱动开发时,开发具体的接口驱动时,最为应该详细对相应的模块进行详细的查阅,在这些章节中将了解到芯片的控制时序、分析数据、控制、地址寄存器的访问方法以及具体设备操作流程。如图2.5所示为I2C总线控制的数据传输数据的时序图,利用它我们将可以知道I2C数据传输的时序以及我们也可以利用此来编写出用GPIO模拟的I2C接口。
图2.5 数据手册的I2C总线的数据传输时序图
Figure 2.5 Data transfer timing diagram of the I2C bus in the data manual
第四十七章则为芯片的物理特性以及电气特性等信息,同时也会提供芯片相关的尺寸和封装信息,从事硬件工程师需要利用这一章的信息来完成电路的设计,但驱动工程师一般只需泛读即可。
2.2 裸机开发示例讲解
本小节将通过一个示例来更加具体的讲解裸机开发的流程,让读者们有一个更加深刻的体会。本文所选用的主控芯片是三星系列的SP56818的芯片,所使用的系统版本是linux3.4.39,接下来将以输出引脚控制LED灯的亮灭为例进行讲解。
2.2.1 裸机开发项目需求
明确项目的需求将对整个项目开发的流程和思路至关重要,本次示例的开发需求是需要控制开发板上的引脚GPIOC17为低电平时,LED D8亮,反之控制GPIOC17为高电平时LED D8灭。如下图2.6所示为LED D8相关的电路原理图。
图2.6 LED D8电路原理图
Figure 2.6 LED D7 circuit schematic diagram
2.2.2 阅读数据手册
在原理图中获取到需要使用GPIOC17这个GPIO口,因此很有必要需要查阅相关的芯片的数据手册,如下图2.7所示为我们需要查找的SP56818芯片的数据手册,通过它能够查找到对应寄存器的使用方法和相关信息。
图2.7 SP56818数据手册
Figure 2.7 SP56818 Data manual
接下来我们需要查找GPIO寄存器的使用方法,我们首先定位与第十六章,如下图2.8所示。
图2.8 第十六章GPIO章节
Figure 2.8 Chapter XVI GPIO chapter
1、查找GPIO输出模式的配置和使用方法
由于从功能需求分析可知,此时需要将GPIOC17设置为输出模式,如下图2.9所示为GPIO输出功能设置的方法。具体步骤方法如下:
(1)配置GPIOC17为输出模式
通过查阅GPIOC17的复用功能情况可知,GPIO功能为该引脚的功能1,因此设置GPIOC复用功能寄存器的值为b’01(这个为二进制格式值)。
(2)使能当前输出引脚
GPIOC输出使能寄存器对应位设置为1。
(3)设置输出高/低电平
GPIOC输出寄存器对应的位的值为1/0。
图2.9 GPIO输出功能设置及使用
Figure 2.9 GPIO Output function settings and usage
图 2.10 GPIOC17复用功能情况
Figure 2.10 GPIOC17 multiplexing
2、查找对应的各个功能寄存器地址
如下图2.11所示为GPIOx的功能寄存器的地址,在后续的代码编程时需要使用到如下的地址进行映射。
图2.11 GPIOx常规的功能寄存器地址
Figure 2.11 GPIOx General function register address
3、查找与GPIOE13相关的更加详细的功能寄存器位址
(1)复用功能寄存器1
如图2.12所示为复用功能寄存器0的相关位址情况,可以看出GPIOx17是使用这个寄存器的的[3:2]这两位来选择对应的功能的,本示例将其设置为01。
由于本实例只用到复用功能寄存器1,对于其他的复用功能寄存器0用法跟它一致,此处不展开细讲。
图2.12 复用功能寄存器1
Figure 2.12 Multiplexing function register 1
(2)输出使能寄存器
如下图2.13所示为输出使能寄存器,将对应的位设置为1则是使能。
图2.13 输出使能寄存器
Figure 2.13 Output Enable Register
(3)输出电平寄存器
如下图2.14所示为输出电平寄存器地址及位址情况,可以设置对应位来控制不同位的输出情况,此处可以设置GPIOC的第17位的值来控制GPIOC17的状态输出。
图2.14 输出寄存器
Figure 2.14 Output registers
4.代码部分
下面则是本示例的代码部分,首先一开始先定义寄存器的地址,接着在函数入口开始处配置GPIOC口为输出模式,然后配置GPIOC17的复用功能0寄存器的值为00,进一步使能GPIOC17的输出使能。最后利用while循环来控制GPIOC17的输出状态值,来最终实现LED灯D8闪烁的效果。
//(1)定义寄存器 #define GPIOCOUT (*(volatile unsigned int *)0xC001C000) #define GPIOCOUTENB (*(volatile unsigned int *)0xC001C004) #define GPIOCOUTALTFN0 (*(volatile unsigned int *)0xC001C020) #define GPIOCOUTALTFN1 (*(volatile unsigned int *)0xC001C024) static void delay(void); //(2)c程序的入口,同时不使用标准的c库,因此入口函数名字为_start void _start(void) { //配置GPIOC17为输出模式 //思考,清零为什么用到位与,取反的操作 GPIOCOUTALTFN0&=~(3<<2); //GPIOE13的多功能配置[3:2]清零 GPIOCOUTALTFN0&|=(1<<2); //GPIOE13的多功能配置[3:2]设为01 //允许GPIOC17输出电平 GPIOCOUTENB|=1<<17; while(1) { //点亮 GPIOCOUT&=~(1<<17); //延时一会 delay(); //熄灭 GPIOCOUT|=1<<17; //延时一会 delay(); } } //(3)延时函数 void delay(void) { //思考为什么要加volatile volatile unsigned int i=0x2000000; while(i--); }
5.进行交叉编译并烧写运行
按照以下步骤来对C文件进行编译,得到bin文件,再在操作系统中运行。
(1)将led.c编译为目标文件led.o,同时不实用标准c库
arm-linux-gcc -o led.o -c led.c -nostdlib
(2)将led.o链接到内存地址0x40000000,输出新的执行程序led.elf
arm-linux-ld -Ttext 0x40000000 -o led.elf led.o
(3)将上述的执行程序再转换会bin文件才能够在开发板上运行
arm-linux-objcopy -O binary led.elf led.bin
(4)下载led.bin文件到板子上运行
下载程序 tftp 0x40000000 led.bin
知心程序 go 0x40000000
测试结果:LED灯D8开始闪烁
2.3 本章总结
本章主要讲解的裸机开发的思路进行了讲解,主要从如何阅读高效阅读原理图分析数据手册,进一步来编程完成相应实现的功能,最后结合一个示例来形象具体的说明如何利用上述的思路来真正的利用程序实现板子的裸机开发任务,让学习本章节的读者能够对裸机开发有更深的认识,为后续Linux驱动开发的学习做好准备。