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)及申引的更多相关文章

  1. 【arm学习】我的第一个裸板程序

    初学ARM感觉写个裸板程序还真的不容易,可能是没有用到ADS,keil之类的开发平台的缘故吧.编译,链接过程在linux平台上完成,这样学起来更有实感,还能顺便熟悉linux环境,以及命令,何乐而不为 ...

  2. ARM裸板开发:04_MMU 链接地址与运行地址不一致时,(SDRAM)初始化程序地址无关码问题的分析

    ARM裸板开发过程,程序的链接地址设置为为0x30000000,而前期的启动代码以及相关硬件的初始化代码需要在内部iRAM(steppingstone,起始地址0x0)的4K中运行.链接地址与运行地址 ...

  3. 关于在arm裸板编程时使用printf问题的解决方法

    在ARM裸板驱动编程中,是不允许程序直接调用C库程序的.为什么呢?因为此时kernel还没有被加载,所以在封装在kernel层的C库的API是用不了的,那怎么办? 在开发过程中,printf的功能我不 ...

  4. 6410裸板程序,led、蜂鸣器、按键…

    //***************************************************************** //作者:昊天 // //功能:在ok6410板子上跑裸板程序, ...

  5. arm裸板驱动总结(makefile+lds链接脚本+裸板调试)

    在裸板2440中,当我们使用nand启动时,2440会自动将前4k字节复制到内部sram中,如下图所示: 然而此时的SDRAM.nandflash的控制时序等都还没初始化,所以我们就只能使用前0~40 ...

  6. ARM裸板开发:07_IIC 通过IIC总线接口读写时钟芯片时间参数实现的总结

    问题一:程序直接在iRAM内部可正常执行,而程序搬移(Nand ->SDRAM)之后,就不能正常运行了 #define NAND_SECTOR_SIZE 2048 /* 读函数 */ void ...

  7. ARM学习篇一 点亮LED

    要点亮LED,先决条件是什么,当然得有相应的硬件设施.板子的整个电路图比较大,我就直接取相关部分. 给发光二级管加上3.3v电压后,通过1k电阻,直接与S3C2440连接.至于为什么要加电阻,大家应该 ...

  8. 对arm裸板调试的理解

    由于arm芯片一般都包含的由jtag调试这项功能,cpu向外部发出信号时,一般都要同jtag发送出去,它就像一个路口的交警一样,能够控制车辆的运行,当然在arm中指的是cpu发出的数据和地址,我们在调 ...

  9. ARM裸板调试思路总结、笔记

    1. 点灯 2. 串口打印 3. JTAG调试器3.1 命令行调试 3.2 源码级别的调试前提a. 程序必须已经重定位好,位于它的链接地址a.1 如果程序的链接地址是SDRAM, 使用openocd初 ...

随机推荐

  1. 9、Spring Boot 2.x 集成 Thymeleaf

    1.9 Spring Boot 2.x 集成 Thymeleaf 完整源码: Spring-Boot-Demos 1.9.1 在pom中引入依赖 <dependency> <grou ...

  2. spring boot项目接入xxl-job

    关于分布式任务调度平台XXL-JOB,作者 许雪里 在其发布的中文教程中已经介绍的很清楚了,这里就不做过多的介绍了.按照文档搭建xxl-job,做此纪录. 1.源码下载地址 GitHub:https: ...

  3. The 2019 ICPC China Nanchang National Invitational and International Silk-Road Programming Contest B、H

    比赛链接https://www.jisuanke.com/contest/3098?view=challenges B题 拉格朗日插值 题意  T组输入.一个n次多项式 f(x) ,每项的系数不知道, ...

  4. (转)Git操作

    本地修改了许多文件,其中有些是新增的,因为开发需要这些都不要了,想要丢弃掉,可以使用如下命令: git checkout . #本地所有修改的.没有的提交的,都返回到原来的状态 git stash # ...

  5. MySQL中使用LIMIT分页

    需求:客户端通过传递pageNum(页码)和pageSize(每页显示的条数)两个参数去分页查询数据库表中的数据. 我们知道MySQL提供了分页函数limit m,n,但是该函数的用法和需求不一样,所 ...

  6. Ryu控制器编程开发——packet_in和packet_out简易交换机实现

    Ryu控制器二次开发,实现一个简单的只能够简单地广播数据包的交换机. from ryu.base import app_manager from ryu.controller import ofp_e ...

  7. fastjson序列化和反序列化报com.alibaba.fastjson.JSONException: autoType is not support异常问题,解决方案整合

    1.问题起因 2017年3月15日,fastjson官方发布安全升级公告,该公告介绍fastjson在1.2.24及之前的版本存在代码执行漏洞,当恶意攻击者提交一个精心构造的序列化数据到服务端时,由于 ...

  8. PostgreSQL判断一个表是否存在

    postgresql判断一个表是否存在: 方法一: select count(*) from pg_class where relname = 'tablename'; 方法二: select cou ...

  9. vagrant系列三:vagrant搭建的php7环境

    原文:https://blog.csdn.net/hel12he/article/details/51107236 前面已经把vagrant的基础知识已经基本过了一遍 了,相信只要按着教程来,你已经搭 ...

  10. Flutter移动电商实战 --(28)列表页_商品列表后台接口调试

    主要调试商品列表页的接口 这个接口是最难的因为有大类.小类还有上拉加载 先配置接口 config/service_url.dart //const serviceUrl='http://test.ba ...