1.第一个ARM裸机程序

目录

1.查看原理图和数据手册,设置IO口功能

2.S3C2440框架和启动过程

3.编写程序点亮LED灯思路

4.一些汇编语言的知识

5.编写汇编代码

6.编译文件

7.下载到开发版

8.查看伪指令解析后的汇编指令

练习1:修改led1.S驱动LED2

练习2:修改bin文件点亮LED3

9.用c语言控制LED灯



1.查看原理图和数据手册,设置IO口功能

提示:此处使用的芯片是三星的S3C2440A

1.1.LED1,连接到GPF4的IO口,从原理图看出是低电平点亮LED灯。

1.第一个ARM裸机程序_第1张图片

1.2查看数据手册,配置寄存器

1.第一个ARM裸机程序_第2张图片

  • 寄存器的描述

1.第一个ARM裸机程序_第3张图片

  • 查看这些寄存器的具体功能,直接定位到GPF这一组寄存器的端口

1.第一个ARM裸机程序_第4张图片

  • 看GPF具体位的定义是什么?

1.第一个ARM裸机程序_第5张图片

  • 配置好输出模式之后,设置GPF4的输出电平,写GPFDAT寄存器,具体描述如下:

1.第一个ARM裸机程序_第6张图片

小结:

控制GPF4需要设置两个寄存器,GPFCON,GPFDAT

1.设置GPF[9:8] = 0b01            --GPF4配置为输出

2.设置GPFDAT[4]= 0或者1;  --GPF4输出低/高电平


2.S3C2440框架和启动过程

2.1.基本框架

S3C2440是一个SOC,在一块芯片上面集成了有CPU、GPIO控制器、Nand控制器、Nor控制器、以及4K的SRAM。

1.第一个ARM裸机程序_第7张图片

2.2.启动过程(大多数ARM芯片从0地址启动)

1.NoR启动,NoR Flash 基地址为0(片内RAM地址为:0x40000000),CPU读出NOR读出上的第一个指令(前四个字节)执行,CPU继续读出其他指令执行。

2.Nand启动,片内4K SRAM 基地之为0(Nor Falsh 不可访问),硬件2240把NAND前4K内容复制到片内内存SRAM中,然后CPU从0地址取出第一条指令(前四个字节)执行。


3.编写程序点亮LED灯思路

3.1.设置GPFCON寄存器的 【9:8】为 0 1 ,也就是网GOFCON这个地址(0x56000050)写值

使用寄存器助手看一下,设置的值为多少?设置的值为:0x100

1.第一个ARM裸机程序_第8张图片

即把值0x100写到地址:0x56000050 上

3.2.点亮或者熄灭LED灯

熄灭:设置GPFDAT寄存器的第4位为1,也就是网GOFDAT这个地址(0x56000054)写值,写的值为:0x10

1.第一个ARM裸机程序_第9张图片

点亮:设置GPFDAT寄存器的第4位为0,也就是网GOFDAT这个地址(0x56000054)写值,写的值为:0

注意:此方法会破坏寄存器原本的位,即改变这个寄存器所有的位,因为此时控制一个LED所以不管别的位。


4.一些汇编语言的知识

  • 点我查看

5.编写汇编代码

5.1.新建给汇编文件

1.第一个ARM裸机程序_第10张图片

5.1.使用source insight 打开文件,编辑文件,代码如下:

    /*
	*点亮LED,GPF4
    */


.text
.global  _start
_start:
/*配置GPF4为输出引脚*/
/*0x100写到地址0x56000050*/

//使用伪指令,把GPFCON的地址赋值给R1
	ldr r1,=0x56000050
//把0x100赋值给R0
	mov r0,#0x100  /*ldr r0,=0x100*/
//把r0的值写到地址r1去
	str r0,[r1]
	
/*配置GPF4输出低电平,点亮LED*/
/*把0写到地址0x56000054*/	
 
//使用伪指令,把GPFDAT的地址赋值给R1
	ldr r1,=0x56000054
//把0赋值给R0
	mov r0,#0  /*ldr r0,=0*/
//把r0的值写到地址r1去
	str r0,[r1]
	
/*死循环*/
halt:
	b halt

6.编译文件

6.1.打开Linux的虚拟机,查看虚拟机的IP地址

1.第一个ARM裸机程序_第11张图片

6.2.在window系统使用远程登陆工具(filezilla Pro),登陆Linux账户,进行文件的传输

1.第一个ARM裸机程序_第12张图片

6.3.双击led1.S,文件自动进行传输;

1.第一个ARM裸机程序_第13张图片

6.4.进入Linux系统,进入work目录

1.第一个ARM裸机程序_第14张图片

开始编译:

使用命令:

预编译:arm-linux-gcc -c -o led1.o led1.S

链接   :arm-linux-ld -Ttext 0 led1.o -o led1.elf 

得到bin文件:arm-linux-objcopy -O binary -S led1.elf led1.bin

一次输入上面的命令得到,下面的编译文件:

1.第一个ARM裸机程序_第15张图片

提示:手动输入比较容易出错,所以在这里制作一个Makefile文件

1.第一个ARM裸机程序_第16张图片

使用NotePad打开,编辑如下:

1.第一个ARM裸机程序_第17张图片

上传Makefile到Linux系统上(使用filezilla Pro):

1.第一个ARM裸机程序_第18张图片

以后可以使用命令:

  • make clean     --清除编译文件
  • make               --编译

1.第一个ARM裸机程序_第19张图片


7.下载到开发版

7.1.把Linux系统编译好的led1.bin文件传回windows系统

1.第一个ARM裸机程序_第20张图片

7.2.使用oflash烧写bin文件到开发版

进入window命令行,跳转到bin文件所在目录,执行oflash led1.bin

1.第一个ARM裸机程序_第21张图片

选择eop烧写:

1.第一个ARM裸机程序_第22张图片

选择2440:

烧写到Nand Flash:

从0地址开始烧写:

烧写完成,拔掉开发板的eop烧写器,使开发板从Nand Flash启动,重启开发版。重启之后就可以在开开发版中看到连接到GPF4的LED被点亮了。



8.查看伪指令解析后的汇编指令

8.1.修改Makefile 把 .elf文件反汇编,查看真正的汇编指令

使用指令:arm-linux-objdump -D led1.elf > led1.dis

修改后入下:

all:
	arm-linux-gcc -c -o led1.o led1.S
	arm-linux-ld -Ttext 0 led1.o -o led1.elf
	arm-linux-objcopy -O binary -S led1.elf led1.bin	
	arm-linux-objdump -D led1.elf > led1.dis

clean:
	rm *.bin *.o *.elf

使用远程登陆工具(filezilla Pro)上传Makefile文件到Linux虚拟机(因为已经上传过一次,所以此处应该为覆盖文件)

1.第一个ARM裸机程序_第23张图片

8.2.回到Linux虚拟机,使用make命令进行编译

1.第一个ARM裸机程序_第24张图片

生成的 led1.dis文件传回到windows

1.第一个ARM裸机程序_第25张图片

使用Notepad打开这个文件

1.第一个ARM裸机程序_第26张图片

想要看懂汇编语言语句,需要对ARM寄存器有所了解:

1.第一个ARM裸机程序_第27张图片

注意:ARM的指令的运行采用的是流水线型,当指令地址A的指令时,CPU已经对地址A+4的指令进行译码,同时已经在读取地址A+8的指令。所以pc的值是当前的地址+8;

8.3汇编代码指令解析:

第一句指令:ldr    r1, [pc, #20]

1.第一个ARM裸机程序_第28张图片

第二句指令:mov    r0, #256

解析:把0x100的值赋值给寄存器r0

第三句指令:str    r0, [r1]

解析:把ro的值(0x100)写到r1对应的内存,即把值0x100写入到内存:【0x56000050】

第四句指令:ldr    r1, [pc, #12]

1.第一个ARM裸机程序_第29张图片

第五句指令:mov    r0, #0

解析:把0赋值给r0寄存器

第六五句指令:str    r0, [r1]

解析:把ro的值(0x0)写到r1对应的内存,即把值0x0写入到内存:【0x56000054】

小结:

在CPU的角度来看,GPFCON、GPFDAT他们没有本质的区别,都当作内存来用。

8.4.编译器的功能

编译过程,我们的编译器会把汇编码转换成机器码,而机器码就是bin文件(二进制文件)的内容

把Linux编译好的 .bin文件从Linux拉出来,用Hex工具打开和.dis文件的机器码进行比较

可以看出他们是一样的。

1.第一个ARM裸机程序_第30张图片


练习1:修改led1.S驱动LED2

修改led1.S文件驱动连接GPF5的LED灯

1.由原理图可知,需要操作的的是GPF5

1.第一个ARM裸机程序_第31张图片

1.第一个ARM裸机程序_第32张图片

通过查看寄存器GPFCON的描述可知,需要设置GPFCON[11:10] = 0b01

即:网0x56000050地址写 0x400

1.第一个ARM裸机程序_第33张图片

2.修改led1.S文件

代码如下:

.text
.global  _start
_start:
/*配置GPF5为输出引脚*/
/*0x400写到地址0x56000050*/

//使用伪指令,把GPFCON的地址赋值给R1
	ldr r1,=0x56000050
//把0x100赋值给R0
	mov r0,#0x400  /*ldr r0,=0x400*/
//把r0的值写到地址r1去
	str r0,[r1]
	
/*配置GPF5输出低电平,点亮LED1*/
/*把0写到地址0x56000054*/	
 
//使用伪指令,把GPFDAT的地址赋值给R1
	ldr r1,=0x56000054
//把0赋值给R0
	mov r0,#0  /*ldr r0,=0*/
//把r0的值写到地址r1去
	str r0,[r1]
	
/*死循环*/
halt:
	b halt

3.上传到Linux进行编译:

1.第一个ARM裸机程序_第34张图片

4.传回windows 烧录到开发版

1.第一个ARM裸机程序_第35张图片

5.使用oflash 烧录到Nand Flash中,重启开发版,LED2被点亮。

附加:查看反编译的机器码是什么,下个练习用到

1.第一个ARM裸机程序_第36张图片

既然机器码是真正写到硬件的二进制代码,理论上我们通过修改二进制代码就可以修改他们的功能了。所以我们需要去查看RAM架构手册中对于 mov指令的描述,它每一个位的含义是什么?

MOV指令机器码如下所示:

1.第一个ARM裸机程序_第37张图片

当使用命令:mov r0 ,#1024  --把0x400赋值给r0的时候(也就是点亮LED2的时候)的机器码是:0xe3a00b01

把该值赋值到寄存器查看助手,查看一下哪一个位被设置了

1.第一个ARM裸机程序_第38张图片

如果想通过修改机器码去修改程序的执行结果,就是要修改mov指令机器码的立即数【11:0】的值

1.第一个ARM裸机程序_第39张图片

计算一下0x400的立即数是什么是否和上面的解释的一样

1.第一个ARM裸机程序_第40张图片

所以我们就可以通过改变机器码里边的立即数来改变最终程序的执行结果了。这样子的话,只要我们知道了我们需要设置的立即数,我们就可以推算出机器码,然后把这个机器码写入到硬件里面,程序就可以按我们的想法执行了。

具体看练习2的例子。


练习2:修改bin文件点亮LED3

1.由原理图可知,需要操作的的是GPF6

1.第一个ARM裸机程序_第41张图片

如果是使用汇编语言的话,那么和练习1的句子应该只有一句不同,只需要将上面的GPFCON[13:12]=0b01

1.第一个ARM裸机程序_第42张图片

使用寄存器助手看一下需要设置的值,设置为0x1000

1.第一个ARM裸机程序_第43张图片

可以看出这个程序的立即数是:0x1000,那么这个立即数 对应的机器码是什么?

0x1000可以转化成二进制可以表示为: 19个0加上1000000000000,用机器码表示成:1右移20位得到的数

那么机器码的rotate就是:20/2=10;  immed_8=1;

那么整个机器码我们可以表示为:e3a00a01

1.第一个ARM裸机程序_第44张图片

直接复制练习1的例子中的bin文件,把e3a00b01 改成  e3a00a01 

1.第一个ARM裸机程序_第45张图片

烧录修改后的bin文件到开发版中,烧录重启后,可以看到开发版的LED3亮灯。


9.用c语言控制LED灯

9.1.编写思路

通过查数据手册,知道了寄存器GPFCON和GPFDAT的数值,它们是32位的地址,其实本质来说也是内存,不过我们修改了这个内存的值就会使程序的运行结果发生变化,如果能根据数据手册的要求去改变这个寄存器所在地址的数值的话就能控制对应IO了。

1.第一个ARM裸机程序_第46张图片

在C语言中可以定义一个指针变量来存放这两个32位的地址,然后在程序中改变这个32位地址的(改变寄存器)值。

9.2.编写c代码

新建一个.c文件,很简单几条语句

int main()
{
	unsigned int *pGPFCON=(unsigned int *)0x56000050;
	unsigned int *pGPFDAT=(unsigned int *)0x56000054;
	/*配置GPF4的引脚为输出引脚*/

	*pGPFCON=0x100;

	/*配置GPF4的引脚输出低电平(点亮LED)*/
	*pGPFDAT=0;

	return ;
	
}

问题:

1.编写的main函数谁来调用

2.main函数定义的变量保存在内存中,内存地址是多少

答:写一个汇编代码,给main函数设置使用的内存,调用main函数。

9.3.编写汇编代码

新建一个启动文件,命名为start.S,具体内容如下:

.text
.global _start

_start:

/*c语言中局部变量保存在栈中,栈对应的是一块内存*/
/*设置内存 :sp 栈*/
	ldr sp ,=4096; //Nand Flash 启动

/*对于2440来说,当设置为Nand启动,从0开始的4k空间
  对应的是片内内存,把栈设置在内存的顶部*/

   // ldr sp ,=0x40000000+4096; //Nand Flash 启动
/*对于2440来说,当设置为Nor启动,片内4k内存的地址是0x40000000
  对应的是片内内存,把栈设置在内存的顶部*/
	
/*调用main函数,跳转到main函数*/
	bl main
halt:
	b halt

9.4.编写一个Makefile,方便对于汇编文件的编译,内容如下:

all:
	arm-linux-gcc -c -o led.o led.c
	arm-linux-gcc -c -o start.o start.S
	arm-linux-ld -Ttext 0 start.o led.o -o led.elf
	arm-linux-objcopy -O binary -S led.elf led.bin	
	arm-linux-objdump -D led.elf > led.dis

clean:
	rm *.bin *.o *.elf *.dis

9.5.上传到Linux系统进行编译

1.第一个ARM裸机程序_第47张图片

在Linux系统进入源文件所在目录进行编译

1.第一个ARM裸机程序_第48张图片

9.6.把Linux系统编译好的文件传回window,然后烧写led.bin文件到开发版,可以看到GPF4对应的led灯亮了,这样这个c语言代码就便宜完成了。

1.第一个ARM裸机程序_第49张图片


 

你可能感兴趣的:(Linux裸机开发学习笔记)