第8课.第一个ARM裸板程序(点亮led)及申引
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
第8课.第一个ARM裸板程序(点亮led)及申引的更多相关文章
- 【arm学习】我的第一个裸板程序
初学ARM感觉写个裸板程序还真的不容易,可能是没有用到ADS,keil之类的开发平台的缘故吧.编译,链接过程在linux平台上完成,这样学起来更有实感,还能顺便熟悉linux环境,以及命令,何乐而不为 ...
- ARM裸板开发:04_MMU 链接地址与运行地址不一致时,(SDRAM)初始化程序地址无关码问题的分析
ARM裸板开发过程,程序的链接地址设置为为0x30000000,而前期的启动代码以及相关硬件的初始化代码需要在内部iRAM(steppingstone,起始地址0x0)的4K中运行.链接地址与运行地址 ...
- 关于在arm裸板编程时使用printf问题的解决方法
在ARM裸板驱动编程中,是不允许程序直接调用C库程序的.为什么呢?因为此时kernel还没有被加载,所以在封装在kernel层的C库的API是用不了的,那怎么办? 在开发过程中,printf的功能我不 ...
- 6410裸板程序,led、蜂鸣器、按键…
//***************************************************************** //作者:昊天 // //功能:在ok6410板子上跑裸板程序, ...
- arm裸板驱动总结(makefile+lds链接脚本+裸板调试)
在裸板2440中,当我们使用nand启动时,2440会自动将前4k字节复制到内部sram中,如下图所示: 然而此时的SDRAM.nandflash的控制时序等都还没初始化,所以我们就只能使用前0~40 ...
- ARM裸板开发:07_IIC 通过IIC总线接口读写时钟芯片时间参数实现的总结
问题一:程序直接在iRAM内部可正常执行,而程序搬移(Nand ->SDRAM)之后,就不能正常运行了 #define NAND_SECTOR_SIZE 2048 /* 读函数 */ void ...
- ARM学习篇一 点亮LED
要点亮LED,先决条件是什么,当然得有相应的硬件设施.板子的整个电路图比较大,我就直接取相关部分. 给发光二级管加上3.3v电压后,通过1k电阻,直接与S3C2440连接.至于为什么要加电阻,大家应该 ...
- 对arm裸板调试的理解
由于arm芯片一般都包含的由jtag调试这项功能,cpu向外部发出信号时,一般都要同jtag发送出去,它就像一个路口的交警一样,能够控制车辆的运行,当然在arm中指的是cpu发出的数据和地址,我们在调 ...
- ARM裸板调试思路总结、笔记
1. 点灯 2. 串口打印 3. JTAG调试器3.1 命令行调试 3.2 源码级别的调试前提a. 程序必须已经重定位好,位于它的链接地址a.1 如果程序的链接地址是SDRAM, 使用openocd初 ...
随机推荐
- redis使用例子
package test.iafclub.redis; import java.util.ArrayList; import java.util.HashMap; import java.util.I ...
- python执行playbook
from collections import namedtuple from ansible.parsing.dataloader import DataLoader from ansible.va ...
- 2019-2020 ICPC, NERC, Southern and Volga Russian Regional Contest (Online Mirror, ICPC Rules, Teams Preferred)【A题 类型好题】
A. Berstagram Polycarp recently signed up to a new social network Berstagram. He immediately publish ...
- Catalan Number-卡特兰数入门
卡特兰数 首先,我们设f(n)=序列个数为n的出栈序列种数.同时,我们假定,从开始到栈第一次出到空为止,这段过程中第一个出栈的序数是k.特别地,如果栈直到整个过程结束时才空,则k=n. 令h(0)=1 ...
- resize([[data],fn]) 当调整浏览器窗口的大小时,发生 resize 事件。
resize([[data],fn]) 概述 当调整浏览器窗口的大小时,发生 resize 事件. 参数 fnFunctionV1.0 在每一个匹配元素的resize事件中绑定的处理函数.直线电机 ...
- vue使用Echarts图表
vue使用Echarts图表 童话_xxv 关注 0.5 2018.12.11 09:09* 字数 325 阅读 1456评论 2喜欢 13 在开发后台系统时,使用图表进行数据可视化,这样会使数据更 ...
- Oracle 后台进程(二)DBWR进程
一.DBWR进程介绍 DBWR进程执行将数据块缓冲区写入数据文件的工作,是负责缓冲存储管理的一个Oracle后台进程.在修改DB Cache中的某个缓冲区时,会将它标志为“DIRTY”,DBWR的主要 ...
- Java Collection Framework 备忘点
最顶端是两个接口,集合和映射—— Collection<T> / Map<K, V> List 列表 保持插入顺序 ArrayList 擅长随机读 LinkedList ...
- MySQL数据分析-(5)数据库设计之ER模型
大家好,我是jacky,很高兴跟大家分享本课时的内容,从本节课开始,就开始了我们第二章的学习,第一章我们抛出了若干问题,从第二章开始往后,都是解决问题的一个过程: 第一章的案例中,我们拿手机销售公司举 ...
- ORM SQLAlchemy 简介
对象关系映射(Object Relational Mapping,简称ORM使用DB-API访问数据库,需要懂 SQL 语言,能够写 SQL 语句,如果不想懂 SQL,又想使用关系型数据库,可以使用 ...