arm第一个c语言程序,第一个ARM裸机程序

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

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

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

e6df3988f993fdf2d583482e13e59978.png

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

0a08fadc4e8699fb49971df69722df23.png

寄存器的描述

17123a0881d6a901864ff3afc2a0d9d6.png

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

0a163c7f742b4f1e487a4099254a464c.png

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

8f87a6395204d81e2ab33ebdf2d1db2a.png

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

f5bf9826e99693660b324014a14a249c.png

小结:

控制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。

44a6b84093af6e82b57ad75af8bd1604.png

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

bc81392fea15f0b288ba5e51217db47c.png

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

3.2.点亮或者熄灭LED灯

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

542fa2ef3f36730b460ad60dc7919264.png

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

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

4.一些汇编语言的知识

5.编写汇编代码

5.1.新建给汇编文件

bd63d2084d970a9fe08fd3985b066c1c.png

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地址

ff3b1d3e6293dbee1c4c1bca5cdbc1f5.png

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

59b6c8c616e78b7790942fdc6bedb206.png

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

e18f193b7198c96e3d407212fedb7eee.png

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

1acc8f534423b4f8f712447cd84fb79f.png

开始编译:

使用命令:

预编译: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

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

0cba2aea67df2f82477abb503daaf225.png

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

cec857fa56af92f098eb12c4e480ae90.png

使用NotePad打开,编辑如下:

1b85971346d8745ab8be385d80be3274.png

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

c749ac1734318e65275f395e6772763e.png

以后可以使用命令:

make clean     --清除编译文件

make               --编译

19c7ea8c2703512e6837b686bfd4a8ec.png

7.下载到开发版

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

e17d07b9c0861d60f5303537487d1426.png

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

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

a02a4f01972f7ad8ed8c4a01d7d6810e.png

选择eop烧写:

257e031dfd7236ebe687aecce81a7b58.png

选择2440:

2440:

80693a5a75e5771bd20b20ea43857238.png

烧写到Nand Flash:

687779e182ea679731a2d376c2c3b4af.png

从0地址开始烧写:

cdfa7f051b01539f8f910a64c3d57eb7.png

烧写完成,拔掉开发板的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虚拟机(因为已经上传过一次,所以此处应该为覆盖文件)

fcab715dc8d5a6557dad0080e8acec3e.png

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

机,使用make命令进行编译

4b0e2ad09f8bb0aad2b7048d3dbb1d3f.png

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

3c65cca6862523cc75a7ec8294bbb005.png

使用Notepad打开这个文件

88deebec9a0b39f99e263208d2ac3690.png

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

15d6cb3fceefc9d9ea8024866259f67b.png

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

8.3汇编代码指令解析:

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

28bb9d68e478d24422db807a7630340c.png

第二句指令:mov    r0, #256

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

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

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

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

5cdecbe5786e3fa7740271a79a8139f3.png

第五句指令:mov    r0, #0

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

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

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

小结:

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

8.4.编译器的功能

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

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

可以看出他们是一样的。

eb44cea8aaba42c50f916c14c64c3eaf.png

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

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

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

e6df3988f993fdf2d583482e13e59978.png

7ceeb73b3204602c02908ed5d0323144.png

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

即:网0x56000050地址写 0x400

f9355bea74f6f5b02bb261667603eedf.png

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进行编译:

04149dd116d85f852a9bea9169644b3d.png

4.传回windows 烧录到开发版

cbdd380a396371174f7b0a500292b1e2.png

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

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

342f185998e4c46cc44679f1712d7cef.png

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

MOV指令机器码如下所示:

84b63534b5608d9874e407779808a645.png

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

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

50a3a9b4bd3359ce66c37a7cd4425458.png

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

7cff53e101ed9df4784a07ad5190385f.png

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

79c67932f04aae613020cc64b2116e5a.png

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

具体看练习2的例子。

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

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

e6df3988f993fdf2d583482e13e59978.png

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

2494e8b24dc14887b28e87b1693f6a30.png

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

f9bb134ed4e241fde964b96c785ea626.png

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

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

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

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

5ffa1b9ce93c1ba4edd1163733fae41a.png

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

e4e56ce74ea59512aa4633087f4a107d.png

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

9.用c语言控制LED灯

9.1.编写思路

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

7dcd05543fba5486851deb88fa011323.png

在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系统进行编译

4ba26b23b8eb07da3c543c91601d9f41.png

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

ce065ce2393d570f41b5a07a0d898598.png

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

64d3f39d412c2dde7f1cc411babdba7e.png

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值