1.原理图



2.芯片手册

3.几条汇编代码

1.ldr:读内存
ldr R0, [R1]
假设R1的值是x,读取地址x上的数据(4字节),保存到R0中
ldr R0, =0x12345678 (4字节)
R0 = 0x12345678 此语句是伪指令,它会被分为几条真正的ARM指令
2.str:写内存命令
str R0, [R1]
假设R1的值是x,把R0的值写到地址x(4字节)
3.b:跳转
4.mov:
mov R0, R1 把R1的值赋给R0,R0 = R1
mov R0, #0x100 R0 = 0x100 (#0x100立即数)
注意:
mov R0, =0x12345678 不可,mov只能表示简单值(被称为立即数)
而ldr R0, =任意值
5.
add r0, r1, #4 r0 = r1 + 4
sub r0, r1, #4 r0 = r1 - 4
sub r0, r1, r2 r0 = r1 - r2
6.bl:跳转
bl xxx
跳转到xxx,把返回地址保存在lr寄存器(下一条指令的地址)
7.ldm:读内存,写入多个寄存器
stm:把多个寄存器的值写入内存
ldm: ia:内存过后增加 ib:内存预先增加
stm: da:内存过后减少 db:内存预先减少 stmdb sp!, {fp, ip, lr, pc} 假设:sp = 4096
R11 R12 R14 R15 高编号在高地址
解析:
!:被修改的sp不为原始值,为加减后的值

    ldmia sp, {fp, sp, pc}        假设:sp = 4080
解析:
无!,修改后的地址值不存入sp中

4.汇编代码

/*
* 点亮led
*/ .text
.global _start
_start: /* 配置gpf4为输出引脚
* 把0x100写到地址0x56000050
*/
ldr r1, = 0x0x56000050
ldr r0, = 0x100
str r0, [r1] /* 设置gpf4输出高电平
* 把0x10写到地址0x56000054
*/
ldr r1, = 0x56000054
ldr r0, = 0x10
str r0, [r1] /*死循环*/
halt:
b halt

解析:

1:.text部分是处理器开始执行代码的地方,指定了后续编译出来的内容放在代码段【可执行】,是arm-gcc编译器的关键字
2:.global关键字用来让一个符号对链接器可见,可以供其他链接对象模块使用;告诉编译器后续跟的是一个全局可见的名字【可能是变量,也可能是函数名】
3:.global _start让_start符号成为可见符号,这样链接器就知道跳转到程序的什么地方并开始执行
4:_start是默认起始地址,也是编译,链接后程序的起始地址,由于程序是通过加载器来加载的,必须要找到_start名字的函数,因此_start必须定义成全局的,以便存在于编译后的全局符合表中,供其它程序【如加载器】寻找到

Makefile

all:
arm-linux-gcc -c -o led_on.o led_on.S
arm-linux-ld -Ttext 0 led_on.o -o led_on.elf
arm_linux-objcopy -O binary -S led_on.elf led_on.bin clean:
rm *.bin *.o *.elf

5.寄存器

cup:可直接访问的寄存器

r0-r3:参数结果寄存器。可以用来传参数
r4-r11:可以参与程序的操作。如果使用到了它们,则需要在函数的入口保存它们,在函数的出口恢复它们
sp:栈指针
lr:用来保存返回地址
pc:程序计数器。当把一个地址写到pc时,cpu会跳到地址去执行。pc的值为当前地址 + 8

其他寄存器需要通过地址来访问

6.ARM三级流水线

PC=当前指令+8

流水线结构:

当前执行地址A的地址
已经在对地址A + 4的指令进行译码了
已经在读取地址A + 8的指令(pc的值)

7.2440启动流程

NOR启动

nor启动的时候,nor flash自身地址为0,片内4KRAM为0x4000 0000
程序直接在nor上读取,运行
nor能够像内存一样的读取,但不能直接写

NAND启动

片内4KRAM的地址为0x0000 0000,nor flash不可见
自动复制Nand前4K的程序到片内RAM中运行

堆栈设置:(栈区用来保存寄存器和局部变量)

NOR启动

 sp = 0x40000000 + 4096

NAND启动

sp = 4096

自动判断NOR启动还是NAND启动

先读出0地址的值,在写0到0地址后,读出0地址中的值。
如果读出来的值和写入的值不一样。表示0地址上的值被修改了,它对应ram地址为0x0000 0000,为NAND启动。否则为NOR启动
最后要把0地址的值复原

8.C语言代码

start.S

.text
.global _start
_start: /* 设置内存: sp栈 */
ldr sp, =4096 /* NAND启动 */
//ldr sp, =0x40000000 + 4096 /* NOR启动 */ /* 调用main函数 */
bl main halt:
b halt

led.c

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

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 *.o *.elf *.bin *.dis

解析:

arm-linux-objdump -D led.elf > led.dis

这句意义是生成反汇编文件,用于查看和分析

9.ATPCS规则

这里直接引用别人的文章

https://www.cnblogs.com/zongzi10010/p/10023531.html

10.栈区存储示意图

栈顶存储寄存器和局部变量

栈底存储代码段(未重定义前,代码都存储在RAM中只能接受4k的代码)

11.延时点亮LED

start.S

.text
.global _start
_start: ldr r0, = 4096 /* NAND 启动 */ mov r0, #4
bl led_on ldr r0, =10000
bl delay mov r0, #5
bl led_on halt:
b halt

解析:

这里使用到了r0-r3可以传递参数的功能

led.c

void delay(int i)
{
while(i--);
} int led_on(int which)
{
/* 配置地址 */ unsigned int *pGPFCON = (unsigned int *)0x56000050;
unsigned int *pGPFDAT = (unsigned int *)0x56000054; if(which == 4)
{ /* 配置GPF4为输出 */ *pGPFCON = 0x100;
}
else if(which == 5)
{ /* 配置GPF5为输出 */ *pGPFCON = 0x500;
}
/* 配置GPF4和GPF5输出为0 */ *pGPFDAT = 0; return 0; }

12.看门狗

汇编代码

/*    关闭看门狗    */
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]

13.自动判断NOR启动还是NAND启动

汇编代码

/* 设置内存: sp 栈 */
/* 分辨是nor/nand启动
* 写0到0地址, 再读出来
* 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动
* 否则就是nor启动
*/
mov r1, #0
ldr r0, [r1] /* 读出原来的值备份 */
str r1, [r1] /* 0->[0] */
ldr r2, [r1] /* r2=[0] */
cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */
ldr sp, =0x40000000 + 4096 /* 先假设是nor启动 */
moveq sp, #4096 /* nand启动 */
streq r0, [r1] /* 恢复原来的值 */

解析:

moveq:cmp r1, r2相等时执行
streq:cmp r1, r2相等时执行

14.按键控制LED

原理图

led.c

#include "s3c2440_soc.h"

void delay(volatile int d)
{
while(i--);
} int main(void)
{
int val1, val2; /* 设置GPFCON让GPF4/5/6配置为输出引脚 */
GPFCON &= ~((3<<8) | (3<<10) | (3<<12)); // 清零
GPFCON |= ((1<<8) | (1<<10) | (1<<12)); /* 配置3个按键引脚为输入引脚:
* GPF0(S2),GPF2(S3),GPG3(S4)
*/
GPFCON &= ~((3<<0) | (3<<4)); /* gpf0,2 */
GPGCON &= ~((3<<6)); /* gpg3 */ /* 循环点亮 */
while (1)
{
val1 = GPFDAT;
val2 = GPGDAT; if (val1 & (1<<0)) /* s2 --> gpf6 */
{
/* 松开 */
GPFDAT |= (1<<6);
}
else
{
/* 按下 */
GPFDAT &= ~(1<<6);
} if (val1 & (1<<2)) /* s3 --> gpf5 */
{
/* 松开 */
GPFDAT |= (1<<5);
}
else
{
/* 按下 */
GPFDAT &= ~(1<<5);
} if (val2 & (1<<3)) /* s4 --> gpf4 */
{
/* 松开 */
GPFDAT |= (1<<4);
}
else
{
/* 按下 */
GPFDAT &= ~(1<<4);
} } return 0;
}

ARM指令集百度云文档:

https://pan.baidu.com/s/1E2JhzBlJHgLbZ7hqZWmXIw

最新文章

  1. [Bootstrap-插件使用]Jcrop+fileinput组合实现头像上传功能
  2. sql server2008 代码折叠
  3. 用于制作app store的截图的工具:Brief Wrapper —— 最便捷的应用商店屏幕快照
  4. 【BZOJ 3048】【USACO2013 Jan】Cow Lineup 滑块思想
  5. Java多线程与并发库高级应用-传统线程机制回顾
  6. Opencv step by step - 绘图
  7. Hbase之获取数据
  8. php定时执行PHP脚本一些方法总结
  9. hadoop集群配置实例
  10. maven学习系列第二课,关于springmvc的pop.xml的依赖的添加
  11. 能取悦生理期的女性吗?Le Parcel提供女性卫生用品按月订购服务,不是按包出售而是可以按片自由搭配 | 36氪
  12. js jquery 实现html页面之间参数传递(单一参数、对象参数传递)
  13. 为什么要使用addEventListener而不是on监听事件
  14. Spring Security入门(1-12)Spring Security 的过滤器机制
  15. Ubuntu使用命令行打印文件
  16. gdb调试5--工程项目的断点调试
  17. 类的内置方法__attr__介绍
  18. 获取AFP共享的文件夹及其权限
  19. Nginx安装、配置虚拟主机、反向代理、负载均衡
  20. 16个PHP设计模式详解

热门文章

  1. 14、Spring Boot 2.x 集成 Druid 数据源
  2. 第八章 用SQL语句操作数
  3. json读写
  4. BZOJ 3679 数字之积 数位DP
  5. Bluetooth M590 mouse problem Ubuntu
  6. mysql 进程
  7. Java操作文件那点事
  8. CentOS 7.5 ——如何开放80、8080、3306等端口
  9. Echarts-树状图(源码 含flare.json)
  10. 如何查看linux内核中驱动的初始化顺序?