当前位置:   article > 正文

ARM体系结构_arm架构

arm架构

统一声明:
博客转载 声 明 : 本博客部分内容来源于网络、书籍、及各类手册。
        内容宗旨为方便查询、总结备份、开源分享。
        部分转载内容均有注明出处,如有侵权请联系博客告知并删除,谢谢!

百度云盘提取码:统一提取码: ziyu

ARM体系结构(推荐★★★★★) 最新更新+强化+个人网站:http://www.baiziqing.cn/index.php/archives/48/

在这里插入图片描述

百度云 ARM思维导图 下载链接: https://pan.baidu.com/s/1eKIJeJJ0HGsCU-S0-GnxSw

一、预备知识

在这里插入图片描述

1.1、计算机系统组成

计算机系统 = 硬件系统 + 软件系统

1.1.1、软件
看得见摸不着
(1)、系统软件
(2)、中间件
(3)、应用软件

1.1.2、硬件
看得见摸得着
(1)、电源
(2)、输入设备
常用的有键盘、鼠标、扫描仪等。
(3)、输出设备
常用的有显示器、打印机、绘图仪等。
(4)、存储器
请添加图片描述
(5)、总线
在这里插入图片描述
在这里插入图片描述
(6)、★CPU

  • 物理结构

  • ★CPU组成
    运算单元
    控制单元
    存储单元
    bus
    在这里插入图片描述

  • ★工作原理
    一条指令的执行,从硬盘到CPU的执行过程。
    在这里插入图片描述

  • ★CPU性能提升
    提升工艺
    cache
    多ALU和FPU
    ★最佳流水线
    在这里插入图片描述

  • 架构演进
    冯诺依曼结构:
    程序和数据都放在内存中,且不彼此分离 的结构称为冯诺依曼结构。譬如Intel的 CPU均采用冯诺依曼结构
    哈佛结构:
    程序和数据分开独立放在不同的内存块中, 彼此完全分离的结构称为哈佛结构。譬如 大部分的单(MCS51、 ARM9等)均采用哈佛结构
    优劣对比:
    冯诺依曼结构中程序和数据不区分的放在 一起,因此安全和稳定性是个问题,好处是处理起来简单。
    哈佛结构中程序(一般放在ROM、 flash中) 和数据(一般放在RAM中)独立分开存放, 因此好处是安全和稳定性高,缺点是软件处理复杂一些(需要统一规划链接地址等)

二、ARM公司简介

在这里插入图片描述

2.1、ARM公司

在这里插入图片描述

ARM官网:https://www.arm.com/

2.2、ARM全球分布

在这里插入图片描述

2.3、ARM公司合作伙伴

SILICON
DESIGN SUPPORT
SOFTWARE
TRAINING
CONSORTIA
在这里插入图片描述

2.4、ARM产品应用

ARM 微处理器及技术的应用几乎已经深入到各个领域:
(1) 、工业控制领域
(2) 、无线通讯领域
(2) 、网络应用
(3) 、消费类电子产品
(5)、成像和安全产品
在这里插入图片描述

2.5、ARM处理器的最新发展

在这里插入图片描述
在这里插入图片描述

三、系统设计

在这里插入图片描述

3.1、嵌入式和单片机的区别

嵌入式系统,是以应用为中心,以计算机技术为基础软硬件可裁剪,适用于对功可靠性成本,体积、功耗有严格要求的专用计算机系统。
单片机更多是指单片机的功能单一,它是完成运算、逻辑控制、通信等功能的单一模块。即便它性能再强大,功能依然是单一的。
3.1.1、软件
从软件组成上区别,通用单片机并不能直接运行,因为里面没有应用程序,而嵌入式系统一定要有控制软件,实现控制的方法可以用硬件电路,也可用软件程序。
3.1.2、硬件
从硬件组成上区别,单片机是由一块集成电路芯片组成,具体包含微控制器电路,输入输出接口控件。而嵌入式,随着电子技术发展,如今既可以用单片机实现,也可以用其他可编程的电子器件实现。
3.1.3、生态环境

可参考嵌入式与单片机之间的关系是什么?链接:https://www.zhihu.com/question/315310041

3.2、★SOC

SoC:System on Chip的缩写,称为系统级芯片,也有称片上系统,意指它是一个产品,是一个有专用目标的集成电路,其中包含完整系统并有嵌入软件的全部内容。

SOC参考链接:https://baike.baidu.com/item/soc/1053305?fr=aladdin

3.3、AMBA

AMBA (芯片通信标准)

  • AHB (高速总线)
  • ASB (系统总线)
  • APB (外部总线)

四、ARM编程模型

在这里插入图片描述

4.1、数据和指令类型

4.1.1、ARM采用的是32位架构(最新ARMV8 64位架构)
4.1.2、ARM约定:

  • Byte: 8 bits
  • Halfword: 16 bits(2 byte)
  • Word: 32 bits(4 byte)
  • Doubleword 64-bits (8byte) ( Cortex-A处理器)

4.1.3、大部分 ARM core提供

  • ARM指令集(
  • Thumb指令集(16bit64/32-bit))
  • Cortex-A处理器
  • 16位和32位 Thumb-2指令集
  • 16位和32位 ThumbEE指令集

4.1.4、Jazelle cores支持 Java byteco

4.2、★工作模式

4.2.1、ARM有8个基本工作模式

  • User非特权模式,大部分仼务执行在这种模式,不能直接切换到其他模式 (用户空间、用户态)
  • Systen:使用和User模式相同寄存器集的特权模式,可以直接切换到其他模式等特权 (除了特权,和User一模一样)
  • FIQ:当一个高优先级(fast)中断产生时将会进入这种模式
  • IRQ:当一个低优先级( normal)中断产生时将会进入这种模式
  • Supervisor(SⅤC):当复位或软中断指令执行时将会进入这种模式 (内核空间、内核态)
  • Abort:当存取异常时将会进入这种模式
  • Undef:当执行未定义指令时会进入这种模式
  • Monitor:是为了安全而扩展出的用于执行安全监控代码的模式;也是一种特权模式 (Cortex-A特有模式)

4.3、★寄存器

寄存器本质就是CPU的存储介质。
在这里插入图片描述
4.3.1、User 通用寄存器
(r0~r15) 通用寄存器

4.3.2、三个特殊的寄存器
(sp) stack point “栈指针” //存储的是栈顶地址
(lr) Link Register "连接寄存器 " //存储的是返回地址
(pc) program counter “程序计数器” //存储的是程序下一条地址

4.3.3、PSR
Runtime Status Register “运行状态寄存器”
(cpsr) 程序状态寄存器
(spsr) 对程序状态寄存器备份

程序状态寄存器 (cpsr)
在这里插入图片描述

4.4、异常

对应本文 6.1 章节详解:

转载 ARM的异常处理 可参考链接:https://blog.csdn.net/lushoumin/article/details/81089103

4.5、大小端

4.5.1、大端模式(big endian)
高字节对应高地址(大端模式)

4.5.2、小端模式(little endian)
高字节对应低地址(小端模式)

五、ARM汇编指令集

在这里插入图片描述
在这里插入图片描述

5.1、为什么要学习汇编

  • 理解机器执行过程(比如向量表,异常跳转等)
  • 性能要求较高的时候,使用汇编,或者混合编程
  • 逆向工程:查找异常,外挂,破解等
  • 总结:
    汇编学习后,可以向上理解软件,向下感知硬件,对理解系统有很大好处

5.2、模拟器编译环境搭建

5.2.1、Linux环境下载与安装
如果没有Linux环境先安装虚拟机和Ubuntu

转载 最新超详细VMware虚拟机下载与安装 链接:https://blog.csdn.net/qq_40950957/article/details/80467513

转载 VMware虚拟机安装Linux系统(详解版) 链接:http://c.biancheng.net/view/714.html

Ubuntu系统 下载 链接:https://cn.ubuntu.com/download

5.2.2、qemu安装:

Linux下在线安装qemu命令:sudo apt-get install qemu qemu-system
在这里插入图片描述

查看安装的版本:qemu-system-arm -version
显示版本QEMU emulator version 2.5.0 (Debian 1:2.5+dfsg-5ubuntu10.15), Copyright © 2003-2008 Fabrice Bellard
在这里插入图片描述

转载:Ubuntu下用qemu搭建arm+linux运行环境参考链接:https://www.cnblogs.com/hxwater/p/7529108.html

转载:使用qemu模拟器搭建arm运行环境参考链接:https://www.jianshu.com/p/0ca97e5b6c08

Ubuntu低版本可能不能上网下载,可以推荐我配置好的 Ubuntu 64_20.04 可上网高版本,直接解压打开使用VM打开即可。
在这里插入图片描述

Ubuntu 64_20.04 阿里云链接:https://www.aliyundrive.com/s/DWUDBP5KZsU
Ubuntu 64_20.04 百度云链接:https://pan.baidu.com/s/1OEYYSHQ1eTVJE-iHjWb7WQ

5.3、编译

预编译
编译
编译汇编
链接
生成二进制

转载 Linux预处理、编译、汇编、链接和运行的过程 参考链接:https://www.cnblogs.com/narjaja/p/9117224.html

5.3.1、环境搭建及第一个汇编
(1)、查看编译环境
查看命令:arm-linux-gcc -v
在这里插入图片描述
这里显示有内容,如果有可以跳过第二步,没有请进行第二步

(2)、安装交叉编译器_保存并压缩
如果没有就将压缩包放在用户目录(家目录)下

gcc-4.6.4.tar.xz文件 百度云下载链接:https://pan.baidu.com/s/1E05MEdMvoUXh7S6ii7Nb-g

解压命令:tar -xvf gcc-4.6.4.tar.xz
在这里插入图片描述
(3)、安装32位支持库
ARM是32的,Ubuntu高版本64位的,需要安装32位支持库
Ubuntu18.04 及以上版本 64 位系统 安装32位支持库
通过下面两条命令进行安装即可:
sudo apt-get install lib32ncurses5
在这里插入图片描述
sudo apt-get install lib32z1
在这里插入图片描述
(4)、pwd把绝对路径显示出来
cd
cd gcc-4.6.4/
cd bin/
pwd
/home/hqyj/gcc-4.6.4/bin

(5)、添加环境变量
vim ~/b.bashrc
export LC_ALL=C
如果没有就根据刚刚复制的决定路径添加到当前120行,有就不用重复添加,添加后保存并退出:wq
export PATH=$PATH:/home/hqyj/gcc-4.6.4/bin
在这里插入图片描述
(6)、关闭所有的终端,再重新打开新终端就可以了。

(7)、新建文件.S结尾
在这里插入图片描述
(8)、写入内容并提出

.global _start
  _start:
     mov r0, #3
   	 mov r1, #5
     nop
     nop
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这里插入图片描述
(9)、编译文件
arm-linux-gcc start.s -o start.o -c -g       //编译
arm-linux-ld start.o -o start.elf -Ttext=0x0     //链接
在这里插入图片描述
(10)、启动文件(模拟了一个设备)
在这个环境下虚拟模拟一个板子(开发板)
qemu-system-arm -machine vexpress-a9 -m 256M -serial stdio -kernel start.elf -S -s
qemu模拟的设备 不要选择vexpress-a9, 选择 xilinx-zynq-a9 可以正常运行。
qemu-system-arm -machine xilinx-zynq-a9 -m 256M -serial stdio -kernel start.elf -S -s
在这里插入图片描述
(11)、调试
重新打开一个终端(快捷键:Ctrl+alt+t):
arm-none-linux-gnueabi-gdb start.elf

target remote 127.0.0.1:1234
在这里插入图片描述
调试命令:s / l /p /q /quit

如果(显示以下)错误及解决
在这里插入图片描述
在这里插入图片描述

5.4、★ARM汇编指令

转载 ARM汇编指令集汇总 参考链接:https://blog.csdn.net/qq_40531974/article/details/83897559

可参考 《ARM汇编指令(中文版)》百度云下载链接:https://pan.baidu.com/s/14CiX5VA6sUI4HUFsWkp-Pw
在这里插入图片描述

本节部分内容转载 朱有鹏老师《1.2.ARM裸机第二部分-ARM体系结构与汇编指令》推荐链接:https://edu.51cto.com/course/3635.html

5.4.1、数据处理指令
(1)、传输数据指令:MOV
【MOV指令】:它的传送指令只能是把一个寄存器的值(要能用立即数表示)赋给另一个寄存器,或者将一个常量赋给寄存器,将后边的量赋给前边的量。

MOV R1,R0   					;将寄存器R0的值传送到寄存器R1
MOV PC,R14   					;将寄存器R14的值传送到PC,常用于子程序返回
MOV R1,R0,LSL#3 				;将寄存器R0的值左移3位后传送到R1(即乘8)
MOVS PC, R14	  				;将寄存器R14的值传送到PC中,返回到调用代码并恢复标志位

MVN R0,#0   					;将立即数0取反传送到寄存器R0中,完成后R0=-1(有符号位取反)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

(2)、算术运算指令:ADD、SUB、ADC、SBC、MUL

1、【加法指令】:ADD
ADD  R0,R1,R2           		; R0 = R1 + R2
ADD  R0,R1,#256            	; R0 = R1 + 256
ADD  R0,R2,R3,LSL#1      		; R0 = R2 + (R3 << 1)

2、【带进位的加法指令】:ADC
ADDS  R0,R4,R8          		; 加低端的字,R0=R4+R8
ADCS  R1,R5,R9            	; 加第二个字,带进位,R1=R5+R9
ADCS  R2,R6,R10       		; 加第三个字,带进位,R2=R6+R10
ADC  R3,R7,R11       			; 加第四个字,带进位,R3=R7+R11

3、【减法指令】:SUB
SUB  R0,R1,R2           		; R0 = R1 - R2
SUB  R0,R1,#256            	; R0 = R1 - 256
SUB  R0,R2,R3,LSL#1      	; R0 = R2 - (R3 << 1)

4、【带借位减法指令】:SBC
SUBS  R0,R1,R2           		;R0 = R1 - R2 - !C,并根据结果设置CPSR的进位标志位
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

(3)、位操作指令: AND、ORR、EOR、BIC

1、【逻辑与指令】:AND
AND  R0,R0,#3           		; 该指令保持R0的01位,其余位清零。

2、【逻辑或指令】:ORR
ORR  R0,R0,#3           		; 该指令设置R0的01位,其余位保持不变。

3、【逻辑异或指令】:EOR
EOR  R0,R0,#3           		; 该指令反转R0的01位,其余位保持不变。

4、【位清零指令】:BIC
BIC  R0,R0,#%1011         	; 该指令清除 R0 中的位 01、和 3,其余的位保持不变。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

(4)、跳转指令: B、BL

1、【B指令】
B  Label  						;程序无条件跳转到标号Label处执行
CMP R1,#0  					;当CPSR寄存器中的Z条件码置位时,程序跳转到标号Label处执行
BEQ Label  

2、【BL指令】
BL Label  						;当程序无条件跳转到标号Label处执行时,同时将当前的PC值保存到R14中
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

(5)、比较指令: CMP

(1)【直接比较指令】:CMP
CMP R1,R0   					;将寄存器R1的值与寄存器R0的值相减,并根据结果设置CPSR的标志位
CMP R1,#100  					;将寄存器R1的值与立即数100相减,并根据结果设置CPSR的标志位

2、【负数比较指令】:CMN
CMN R1,R0   					;将寄存器R1的值与寄存器R0的值相加,并根据结果设置CPSR的标志位
CMN R1,#100  					;将寄存器R1的值与立即数100相加,并根据结果设置CPSR的标志位
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

(6)、汇编加载/存储指令: LDR、STR

1、【LDR指令】
LDR  R0,[R1]             		;将存储器地址为R1的字数据读入寄存器R0。
LDR  R0,[R1,R2]        		;将存储器地址为R1+R2的字数据读入寄存器R0。
LDR  R0,[R1,#8]        		;将存储器地址为R1+8的字数据读入寄存器R0。
LDR  R0,[R1,R2] !       		;将存储器地址为R1+R2的字数据读入寄存器R0,并将新地址R1+R2写入R1。
LDR  R0,[R1,#8] !       		;将存储器地址为R1+8的字数据读入寄存器R0,并将新地址R1+8写入R1。
LDR  R0,[R1],R2        		;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2写入R1。
LDR  R0,[R1,R2,LSL#2]! 		;将存储器地址为R1+R2×4的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。
LDR  R0,[R1],R2,LSL#2  		;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。

2、【STR指令】
STR R0,[R1],#8 				;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。
STR R0,[R1,#8] 				;将R0中的字数据写入以R1+8为地址的存储器中。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

(7)、多寄存器语句传输指令: LDM、STM

LDM(或STM){条件}{类型} 基址寄存器{},寄存器列表{}

IA 每次传送后地址加1;
IB 每次传送前地址加1;
DA 每次传送后地址减1;
DB 每次传送前地址减1;
FD 满递减堆栈;
ED 空递减堆栈;
FA 满递增堆栈;
EA 空递增堆栈;

{}为可选后缀,若选用该后缀,则当数据传送完毕之后,将最后的地址写入基址寄存器,否则基址寄存器的内容不改变。
基址寄存器不允许为R15,寄存器列表可以为R0~R15的任意组合。
{}为可选后缀,当指令为LDM且寄存器列表中包含R15,选用该后缀时表示:除了正常的数据传送之外,还将SPSR复制到CPSR。
同时,该后缀还表示传入或传出的是用户模式下的寄存器,而不是当前模式下的寄存器。

STMFD  R13!{R0,R4-R12,LR}  	;将寄存器列表中的寄存器(R0,R4到R12,LR)存入堆栈。
LDMFD  R13!{R0,R4-R12,PC}  	;将堆栈内容恢复到寄存器(R0,R4到R12,LR)。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

条件码
在这里插入图片描述
汇编实现从1加到100
for(int i=0; i<100; i++);

.global _start
_start
	mov r0, #1	/*i: r0*/
	mov r1, #0	@ sum: r1

loop:
	add r1, r1, r0
	add r0, r0, #1
	cmp r0, #101
	blt loop

	nop
	nop
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述
5.4.2、立即数

  • 合法立即数与非法立即数
  • ARM指令都是32位,除了指令标记和操作标 记外,本身只能附带很少位数的立即数。 因此立即数有合法和非法之分。
  • 合法立即数:经过任意位数的移位后非零 部分可以用8位表示的即为合法立即数

合法立即数: 0x000000ff 0x00ff0000 0xf000000f
非法立即数: 0x000001ff
在这里插入图片描述
高4位:循环右移2位/次
低8位:基数

判断是否是立即数的三步骤
在这里插入图片描述
5.4.3、寻址方式
(1)、立即寻址
mov r0, #3

(2)、寄存器寻址
mov r0, r1

(3)、寄存器偏移寻址
mov r0, r1, lsl #3   //左移
mov r0, r1, lsr #3   //左移

(4)、寄存器间接寻址
在这里插入图片描述
str r0, [r1]    //存   竟有的几个从左向右
ldr r0, [r1]    //读   从右向左

(5)、寄存器基址变址寻址
ldr r0, [r1, #4]    r0 = *(0x100+4)
ldr r0, [r1,#4]!    r0 = *(0x100+4) then r1=r1+4
ldr r0, [r1, ], #4    r0 = *(0x100) then r1=r1+4

(6)、多寄存器寻址
stmfd sp!, {r0-r12}   压栈
ldmfd sp!, {r0-r12}   出栈

5.4.4、跳转(分支)指令

  • b & bl & bx
    • b 直接跳转(就没打开算返回)
    • bl branch and link,跳转前把返回地址放入lr中,以便返回,以便用于函数调用
    • bx跳转同时切换到ARM模式,一般用于异常处理的跳转。

5.4.5、软中断指令

  • swi(software interrupt)
  • 软中断指令用来实现操作系统中系统调用

5.5、协处理器指令

5.5.1、协处理器cp15操作指令

  • mcr & mrc
    mrc用于读取CP15中的寄存器
    mcr用于写入CP15中的寄存器

5.5.2、什么是协处理器

  • SoC内部另一处理核心,协助主CPU实现某 些功能,被主CPU调用执行一定任务。
  • ARM设计上支持多达16个协处理器,但是一 般SoC只实现其中的CP15.(cp: coprocessor)
  • 协处理器和MMU、 cache、 TLB等处理有关, 功能上和操作系统的虚拟地址映射、 cache 管理等有关。

5.5.3、MRC & MCR的使用方法

  • mcr{} p15, <opcode_1>, , , , {<opcode_2>}
    opcode_1:   对于cp15永远为0
    Rd:       ARM 的普通寄存器
    Crn:       cp15 的寄存器,合法值是c0~c15
    Crm:       cp15 的寄存器,一般均设为c0
    opcode_2:    一般省略或为0

转载 ARM协处理器CP15寄存器详解 参考链接:https://www.cnblogs.com/lifexy/p/7203786.html

5.6、ARM汇编伪指令

5.6.1、伪指令的意义

  • 伪指令不是指令,伪指令和指令的根本区 别是经过编译后会不会生成机器码。
  • 伪指令的意义在于指导编译过程
  • 伪指令是和具体的编译器相关的,我们使 用gnu工具链,因此学习gnu环境下的汇编 伪指令。
buf: .word 0x10 0x20  ---> int buf[2]={0x10 0x20};
buf1: .byte 0x20 0x33 0x55  ---> char buf1[]={0x20,0x33,0x55};
data: .space 128*2     ---> malloc(128*2);

ldr r0, =0x100 ---> r0 = 0x100 
                    mov r0, #0x100 
ldr r0, =0xffff 

ldr r0, =buf   	@伪指令  把buf的值赋给r0
ldr r1, [r0]   	@指令    以r0值作为地址,取内容给r1 
ldr r1, [r0,#4]

ldr r0, buf   	@指令: 以buf的值作为地址,取地址里面的内容,给r0
               	@ *buf 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

5.6.2、常用gnu伪指令

  • .global _start       @ 给_start外部链 接属性
  • .section .text       @ 指定当前段为代 码段
  • .ascii .byte .short .long .word   @ 类型C语言定义变量
  • .quad .float .string     @ 定义数据
  • .align 4          @ 以16字节对齐
  • .balignl 16 0xabcdefgh   @ 16字节对齐
  • .equ            @ 类似C语言宏定义

IRQ_STACK_START:
  .word 0x0badc0de
等价于 unsigned int IRQ_STACK_START = 0x0badc0de;

.align 4 @ 16字节对齐
.align 2 @ 4字节对齐

.balignl 16, 0xdeadbeef     @ 对齐 + 填充
b表示位填充;align表示要对齐;l表示long,以4字节为单位填充;16表示16字节对齐;0xdeadbeef是用来填充的原料。

0x00000008:     .balignl 16, 0xdeadbeef
0x0000000c     0xdeadbeef
0x00000010:     下一条指令

5.6.3、最重要的几个伪指令

  • ldr 大范围的地址加载指令
  • adr 小范围的地址加载指令
  • adrl 中等范围的地址加载指令
  • nop 空操作

ARM中有一个ldr指令,还有一个ldr伪指令
一般都使用ldr伪指令而不用ldr指令

ldr指令: ldr r0, #0xff
伪指令: ldr r0, =0xfffl   @涉及到合法/非法立即数,涉及到ARM文字池

5.6.4、adr与ldr

  • adr编译时会被1条sub或add指令替代,而 ldr编译时会被一条mov指令替代或者文字 池方式处理;
  • adr总是以PC为基准来表示地址,因此指令 本身和运行地址有关,可以用来检测程序 当前的运行地址在哪里
  • ldr加载的地址和链接时给定的地址有关, 由链接脚本决定。

adr和ldr的差别:
ldr加载的地址在链接时确定而adr加载的地址在运行时确定
所以我们可以通过adr和ldr加载的地址比较来判断当前程序是否在链接时指定的地址运行。

5.7、寄存器访问指令

5.7.1、为什么需要多寄存器访问指令

  • ldr/str每周期只能访问4字节内存,如果 需要批量读取、写入内存时太慢,解决方 案是stm/ldm。
  • ldm (load register mutiple)
  • stm(store register mutiple)

5.7.2、举例(uboot start.S 537行)

  • stmia sp, {r0 - r12}
  • 将r0存入sp指向的内存处(假设为 0x30001000);然后地址+4(即指向 0x30001004),将r1存入该地址;然后地址再+4(指向0x30001008),将r2存入该 地址······直到r12内容放入(0x3001030), 指令完成。
  • 一个访存周期同时完成13个寄存器的读写。

5.7.3、8种后缀

  • ia(increase after)先传输,再地址+4
  • ib(increase before)先地址+4,再传输
  • da(decrease after)先传输,再地址-4
  • db(decrease before)先地址-4,再传输
  • fd(full decrease)满递减堆栈
  • ed(empty decrease)空递减堆栈
  • fa(·······) 满递增堆栈
  • ea(·······)空递增堆栈

5.7.4、四种栈

  • 空栈栈指针指向空,每次存入时可以 直接存入然后栈指针移动一格;而取出时 需要先移动一格才能取出
  • 满栈栈指针指向栈中最后一格数据,每 次存入时需要先移动栈指针一格再存入; 取出时可以直接取出,然后再移动栈指针
  • 增栈:栈指针移动时向地址增加的方向移动的栈
  • 减栈:栈指针移动时向地址减小的方向移动的栈

5.7.5、!的作用

  • ldmia r0, {r2 - r3}
  • ldmia r0!, {r2 - r3}
  • 感叹号的作用:就是r0的值在ldm过程中发生 的增加或者减少最后写回到r0去,也就是 说ldm时会改变r0的值。

5.7.6、^的作用

  • ldmfd sp!, {r0 - r6, pc}
  • ldmfd sp!, {r0 - r6, pc}^
  • ^的作用:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用于从异常模式 返回。

5.7.7、总结

  • 批量读取或写入内存时要用ldm/stm指令
  • 谨记:操作栈时使用相同的后缀就不会出错,不管是满栈还是空栈、增栈还是减栈

汇编实现两个数交换

buf: .word 0x1, 0x2, 0x3, 0x4

.global	_start
_start
	ldr	r0,	=buf
	ldr	r1,	[r0, #8]
	ldr	r2,	[r0, #12]
	str r1, [r0, #12]
	str r2, [r0, #8]
	
	nop
	nop
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在这里插入图片描述
高版本 str 不起作用,低版本可以,解决方法:
qemu-system-arm -machine vexpress-a9 -m 256M -serial stdio -kernel start.elf -S -s
qemu模拟的设备 不要选择vexpress-a9, 选择 xilinx-zynq-a9 可以正常运行。
qemu-system-arm -machine xilinx-zynq-a9 -m 256M -serial stdio -kernel start.elf -S -s

六、异常处理

在这里插入图片描述

6.1、异常

  • 正常工作之外的流程都叫异常
  • 异常会打断正在执行的工作,并且一般我 们希望异常处理完成后继续回来执行原来 的工作
  • 中断是异常的一种

6.1.1、模式和异常的关系
在这里插入图片描述

6.1.2、从user到异常再回到user的流程
在这里插入图片描述

6.1.3、异常向量表

  • 所有的CPU都有异常向量表,这是CPU设计 时就设定好的,是硬件决定的。
  • 当异常发生时, CPU会自动动作(PC跳转到 异常向量处处理异常,有时伴有一些辅助 动作)
  • 异常向量表是硬件向软件提供的处理异常 的支持。
    在这里插入图片描述

6.1.4、ARM的异常处理机制

  • 当异常产生时, ARM core:
    – 拷贝 CPSR 到 SPSR_
    – 设置适当的 CPSR 位:
      • 改变处理器状态进入 ARM 态
      • 改变处理器模式进入相应的异常模式
      • 设置中断禁止位禁止相应中断 (如果需要)
    – 保存返回地址到 LR_
    – 设置 PC 为相应的异常向量

  • 返回时, 异常处理需要:
    – 从 SPSR_恢复CPSR
    – 从LR_恢复PC
    – Note:这些操作只能在 ARM 态执行.
    在这里插入图片描述

6.1.5、异常优先级

  • 异常在当前指令执行完成之后才被响应
  • 多个异常可以在同一时间产生
  • 异常指定了优先级和固定的服务顺序
    Reset
    Data abort
    FIQ
    IRQ
    Prefetch abort
    SWI
    Undefined instruction

6.1.6、异常处理流程
在这里插入图片描述
异常处理函数 - 加法

.globl _start
_start: 
	b	reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq
_undefined_instruction: .word _undefined_instruction
_software_interrupt:	.word swi_handler
_prefetch_abort:	.word _prefetch_abort
_data_abort:		.word _data_abort
_not_used:		.word _not_used
_irq:			.word _irq
_fiq:			.word _fiq

reset:
	/* 设置cpu模式为SVC */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f  
	orr	r0, r0, #0xd3
	msr	cpsr,r0
	/* 设置异常向量表的起始地址 */
	ldr	r0, =0x0
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR

	/*  设置栈空间 */
	ldr sp, stacktop
	sub r1, sp, #64
	/* 设置cpu模式为user */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f  
	orr	r0, r0, #0xd0
	msr	cpsr,r0

	mov sp ,r1 

	/*  跳转到应用main  */
	bl	_main
	mov r4,r0 

_main:
	stmfd sp!,{lr}
	mov r5,#3
	mov r6,#4
	bl myadd
	nop
	ldmfd sp!,{pc}
loop:
	b loop

myadd:
	stmfd sp!,{lr}
	swi 0
	nop
	nop
	ldmfd sp!,{pc}

swi_handler:
	stmfd sp!,{lr}
	bl sysadd
	ldmfd sp!,{pc}^

sysadd:
	stmfd sp!,{lr}
	add r0, r5, r6
	ldmfd sp!,{pc}

stack: .space 64*2
stacktop: .word stack+64*2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

6.2、中断

  • ARM有两级外部中断FIQ、IRQ
  • 可是大多数的基于ARM的系统有>2个的中断源!
  • Note:通常中断处理程序总是应该包含清除中断源的代码。
    在这里插入图片描述

6.2.1、FIQ VS IRQ

  • FIQ和IRQ提供了非常基本的优先级级别

  • FIQS有高于IRQs的优先级,表现在下面2个方面
    当多个中断产生时,CPU优先处理FIQ
    处理FIQ时禁止IRQs

  • FIQS的设计使中断响应尽可能的快
    FIQ向量位于异常向量表的最末
    FIQ模式有5个额外的私有寄存器(r8r12)

七、C / 汇编的混合编程

在这里插入图片描述

  • 汇编的优势:执行效率高、能够直接控制处理器
  • 汇编调用C函数:将C函数地址赋值给PC指针即可(ldr PC, =main)
  • 调用汇编函数:将汇编函数声明为全局函数,在C程序中直接调用
  • C内嵌汇编:内嵌汇编代码可由编译器的优化器来传递

7.1、C / 汇编第一个程序

C语言main函数
在这里插入图片描述

extern int myadd(int a, int b)	/*声明myadd*/

void main(void)
int a=3
int b= 4
int d= 0
c= myadd (a, b)
c= 0xffffff
while(1);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

自己写一个Makefile
在这里插入图片描述
在这里插入图片描述

all:
	arm-linux-gcc test.S -o test.o -c -g
	arm-linux-gcc main.c -o main.o -c -g
	arm-linux-ld  test.o main.o -o start.elf -Ttest=0x0
clean:
	rm *.o *.elf
zs:
	@echo "zs ...."
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

编译
在这里插入图片描述
声明头文件
在这里插入图片描述

.globl _start
.globl myadd
_start: 
	b	reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq
_undefined_instruction: .word _undefined_instruction
_software_interrupt:	.word swi_handler
_prefetch_abort:	.word _prefetch_abort
_data_abort:		.word _data_abort
_not_used:		.word _not_used
_irq:			.word _irq
_fiq:			.word _fiq

reset:
	/* 设置cpu模式为SVC */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f  
	orr	r0, r0, #0xd3
	msr	cpsr,r0
	/* 设置异常向量表的起始地址 */
	ldr	r0, =0x0
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR

	/*  设置栈空间 */
	ldr sp, stacktop
	sub r1, sp, #64
	/* 设置cpu模式为user */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f  
	orr	r0, r0, #0xd0
	msr	cpsr,r0

	mov sp ,r1 

	/*  跳转到应用main  */
	bl	_main
	mov r4,r0 

_main:
	stmfd sp!,{lr}
	mov r5,#3
	mov r6,#4
	bl myadd
	nop
	ldmfd sp!,{pc}
loop:
	b loop

myadd:
	stmfd sp!,{lr}
	swi 0
	nop
	nop
	ldmfd sp!,{pc}

swi_handler:
	stmfd sp!,{lr}
	bl sysadd
	ldmfd sp!,{pc}^

sysadd:
	stmfd sp!,{lr}
	//add r0, r5, r6
	add r0, r0, r1		//C语言函数传参
	ldmfd sp!,{pc}

stack: .space 64*2
stacktop: .word stack+64*2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

编译
在这里插入图片描述
C语言函数传参
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.2、C / 汇编第二个程序

main.c

extern int myadd(int a, int b);

void main(void)
{
    int a = 3;
    int b = 4;
    int c = 0;
    c = myadd(a,b);
    c = 0xffffff;
    while(1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

test.S

.globl _start
.globl myadd
_start: 
	b	reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq
_undefined_instruction: .word _undefined_instruction
_software_interrupt:	.word swi_handler
_prefetch_abort:	.word _prefetch_abort
_data_abort:		.word _data_abort
_not_used:		.word _not_used
_irq:			.word _irq
_fiq:			.word _fiq

reset:
	/* 设置cpu模式为SVC */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f  
	orr	r0, r0, #0xd3
	msr	cpsr,r0
	/* 设置异常向量表的起始地址 */
	ldr	r0, =0x0
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR

	/*  设置栈空间 */
	ldr sp, stacktop
	sub r1, sp, #64
	/* 设置cpu模式为user */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f  
	orr	r0, r0, #0xd0
	msr	cpsr,r0

	mov sp ,r1 

	/*  跳转到应用main  */
	bl	main
	mov r4,r0 

myadd:
	stmfd sp!,{lr}
	swi 0
	nop
	nop
	mov r0, #100
	ldmfd sp!,{pc}

swi_handler:
	stmfd sp!,{lr}
	bl sysadd
	ldmfd sp!,{pc}^

sysadd:
	stmfd sp!,{lr}
	add r0, r0, r1
	ldmfd sp!,{pc}

stack: .space 64*2
stacktop: .word stack+64*2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

Makefile

all:
	arm-linux-gcc test.S -o test.o -c -g
	arm-linux-gcc main.c -o main.o -c -g
	arm-linux-ld  test.o main.o -o start.elf -Ttest.lds
clean:
	rm *.o *.elf
zs:
	@echo "zs ...."
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

test.lds

ENTRY(_start)
SECTIONS{
	. = 0x0;
	.text : {
		test.o(.text)
		*(.text)
	}
	.data : {
		*(.data)
	}
	.bss : {
		*(.bss)	
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

gdbS

#!/bin/bash
qemu-system-arm -machine vexpress-a9 -m 256M -serial stdio -kernel start.elf -S -s
  • 1
  • 2

编译运行参考:5.3.3、(9)~(11)

实列 mysyscall 百度云源码链接:https://pan.baidu.com/s/1tT8KosRvv0Ccf-4aoOhMkQ

八、嵌入式硬件平台接口开发

在这里插入图片描述

8.1、GPIO

GPIO(general purpose i/o ports)意思为通用输入/输出端口,通俗的说就是一些引脚。
我们可以通过它们输出高低电平 或 读入引脚的状态。

1、通用IO脚 仅仅只能高低电平输入输出
2、专用IO脚 只提供给某一个设备使用的
3、复用IO脚 提供给多个管脚使用 / 通用IO使用
在这里插入图片描述
8.1.1、操作GPIO

百度云 4412用户手册和原理图 资料下载:https://pan.baidu.com/s/1LBZkJJD5kphBNdc30wBN7A

(1)、查看原理图:
在这里插入图片描述
在这里插入图片描述
(2)、芯片手册操作步骤:
(1)、通过目录找到控制器
(2)、简单流量描述
(3)、找全对应的寄存器
(4)、依据功能进行筛选
   1)全局控制配置寄存器
   2)和功能相关的要
   3)辅助寄存器(查看状态的、查看设备型号、设置延时、设置读写异常状态的)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
8.1.2、汇编实现点灯
在这里插入图片描述
①、test.S

.globl _start
_start: 
	b	reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq
_undefined_instruction: .word _undefined_instruction
_software_interrupt:	.word _software_interrupt 
_prefetch_abort:	.word _prefetch_abort
_data_abort:		.word _data_abort
_not_used:		.word _not_used
_irq:			.word _irq
_fiq:			.word _fiq

reset:
	/* 设置cpu模式为SVC */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f  
	orr	r0, r0, #0xd3
	msr	cpsr,r0

	/* 设置异常向量表的起始地址 */
	ldr	r0, =0x41000000
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR

	/*  设置栈空间 */
	ldr sp, stacktop
	sub r1, sp, #64
	/* 设置cpu模式为user */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f  
	orr	r0, r0, #0xd0
	msr	cpsr,r0

	mov sp ,r1 

	/*  跳转到应用main  */
	bl	_main

_main:
    ldr r0, =0x11000C40			@设置输出,找到他的地址,对应数据手册
	ldr r1,[r0]					@把内容取出来
	bic r1, r1, #0xf0000000		@将【28:31】置1,先清0,对应寄存器工具28,29,30,31,1,就是0xf 
								@bic 位清0 //这里连续的一个需要先清0
	orr r1, r1, #0x10000000		@orr或,0x1
	str r1, [r0]				@写回去
loop:
    ldr r0, =0x11000C44			@设置高电平,找到他的地址,对应数据手册,亮
	ldr r1,[r0]					@把内容取出来
	orr r1, r1, #0x80			@对应数据手册【7:0】,这里需要将第8接口置高置低,对应第八位置1就是0x80,可以参考寄存器工具
	str r1, [r0]

ldr r5, =0xffffff				@延时
delay:
	sub r5,r5,#1
	cmp r5,#0
	bne delay

    ldr r0, =0x11000C44
	ldr r1,[r0]
	bic r1, r1, #0x80			@灭
	str r1, [r0]
	
ldr r5, =0xffffff				@延时
delay1:
	sub r5,r5,#1
	cmp r5,#0
	bne delay1
	b loop

stack: .space 64*2
stacktop: .word stack+64*2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

在这里插入图片描述
②、Makefile

all:
	arm-linux-gcc test.S -o test.o -c -g
	arm-linux-ld test.o -o start.elf -Ttest.lds
	arm-linux-objcopy  start.elf myled.bin -O binary
clean:
	rm *.o *.elf
zs:
	@echo "zs ...."
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

③、test.lds

ENTRY(_start)
SECTIONS{
	. = 0x41000000;
	.text : {
		test.o(.text)
		*(.text)
	}
	.data : {
		*(.data)
	}
	.bss : {0
		*(.bss)	
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

百度云 纯汇编点灯 源码下载链接:https://pan.baidu.com/s/17TGapa3tiaHGoc4ikJJWPg

8.1.3、C/汇编实现点灯
①、main.c

#define GPX2XON *(volatile unsigned int*)0x11000c40;
#define GPX2DAT *(volatile unsigned int*)0x11000c44;

#define GPF3CON   *(volatile unsigned int *)0x114001E0
#define GPF3DAT   *(volatile unsigned int *)0x114001E4

void mydelay(int n)
{
    int i,j;
    for(i=0;i<n;i++){
        for(j=0;j<2000;j++){
            
        }
    }
}

void main(void)
{
	/*volatile 防止优化*/
	//unsigned int* gpx2con = (volatile unsigned int*)0x11000c40;
	//*gpx2con = 0x1;
	//unsigned int a = *gpx2con;
#if 0	
	@【28:31】因为是4为所以为0xf28对应的位置,|1
	@eg:对i的第2位清0 i=i & ~(1<<2) 
	GPX2XON = (GPX2XON & (~(0xf<<28))) | (0x1<<28);
	while(1)
	{
		GPX2DAT = GPX2DAT | (0x1<<5);
		 mydelay(500);
		GPX2DAT = GPX2DAT & ~(0x1<<5);
		 mydelay(500);
	}
#endif

#if 1
	GPF3CON = GPF3CON & ~(0xf<<20);
    GPF3CON = GPF3CON | (0x1<<20);

    while(1){
        
        GPF3DAT = GPF3DAT | (0x1<<5);
        mydelay(500);

        GPF3DAT = GPF3DAT & ~(0x1<<5);
        mydelay(500);

    }
#endif
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

②、Makefile

all:
	arm-linux-gcc test.S -o test.o -c -g
	arm-linux-gcc main.c -o main.o -c -g
	arm-linux-ld main.o test.o -o start.elf -Ttest.lds
	arm-linux-objcopy  start.elf myled.bin -O binary
clean:
	rm *.o *.elf
zs:
	@echo "zs ...."

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

③、test.lds

ENTRY(_start)
SECTIONS{
	. = 0x41000000;
	.text : {
		test.o(.text)
		*(.text)
	}
	.data : {
		*(.data)
	}
	.bss : {
		*(.bss)	
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

④、test.S

.globl _start
_start: 
	b	reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq
_undefined_instruction: .word _undefined_instruction
_software_interrupt:	.word _software_interrupt 
_prefetch_abort:	.word _prefetch_abort
_data_abort:		.word _data_abort
_not_used:		.word _not_used
_irq:			.word _irq
_fiq:			.word _fiq

reset:
	/* 设置cpu模式为SVC */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f  
	orr	r0, r0, #0xd3
	msr	cpsr,r0

	/* 设置异常向量表的起始地址 */
	ldr	r0, =0x41000000
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR

	/*  设置栈空间 */
	ldr sp, stacktop
	sub r1, sp, #64
	/* 设置cpu模式为user */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f  
	orr	r0, r0, #0xd0
	msr	cpsr,r0

	mov sp ,r1 

	/*  跳转到应用main  */
	bl	main

stack: .space 64*2
stacktop: .word stack+64*2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

百度云盘 C/汇编点灯 源码下载链接:https://pan.baidu.com/s/1_8JC6-QR7zwLjKzpMxEhVA

8.1.4、编译运行
(1)、Linux下编译运行
在这里插入图片描述
在这里插入图片描述
(2)、开发板串口线插上串口,设备显示有串口标志
选择开发板启动模式,(我这里选择的是TF/SD模式下启动)
在这里插入图片描述

如果未显示端口或者读取不出端口号,可能未安装驱动更新
百度云 驱动 下载链接:https://pan.baidu.com/s/1embWLV-Cc6IVv3J3j4Yq-A

(3)、打开超级终端

百度云 超级终端 下载链接:https://pan.baidu.com/s/12uwJeO1fOHJXsegW4K98uQ

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
启动开发板:打开开发板电源开关,迅速按电脑ENTER键,否则会进入Linux系统
在这里插入图片描述
加载地址:loadb 41000000
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
请添加图片描述

转载 【嵌入式Linux+ARM】GPIO操作 可参考链接:https://blog.csdn.net/scottly1/article/details/38960309

8.2、串口

8.2.1、串口基本概念:

串口通信指串口按位(bit)发送和接收字节,串口通信的概念非常简单,串口按位(bit)发送和接收字节。
尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线 接收数据。它很简单并且能够实现远距离通信。

在通信领域内,有两种数据通信方式:并行通信和串行通信在这里插入图片描述
8.2.2、原理图:
在这里插入图片描述
在这里插入图片描述

8.3.3、串口的实现:
①、main.c

#define GPA1CON     *(volatile unsigned int *)0x13800020
#define ULCON       *(volatile unsigned int *)0x13820000
#define UCON        *(volatile unsigned int *)0x13820004
#define UTXH        *(volatile unsigned int *)0x13820020
#define URXH        *(volatile unsigned int *)0x13820024
#define UBRDIV      *(volatile unsigned int *)0x13820028
#define UFRACVAH    *(volatile unsigned int *)0x1382002C
 
void mydelay(int n)
{
    int i,j;
    for(i=0;i<n;i++){
        for(j=0;j<2000;j++){
             
        }
    }
}
 
void uart_init()
{
    GPA1CON = GPA1CON & ~0xff | 0x22;
    ULCON = 0x3;
    UCON = UCON & ~0x1f | 0x1<<2 | 0x1<<0;
    UBRDIV = 53;
    UFRACVAH = 4;
}
 
void uart_send(char c)
{
    mydelay(500);
    UTXH = c;
}
 
void uart_str(char* str)
{
    while(*str != '\0')
        uart_send(*str++);
}
 
void main(void)
{
     
        char c = 'S';
        char* str = "hello word";
        uart_init();
        uart_send(c);   
         
        uart_str(str);
         
        while(1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

其余3个文件同上

百度云盘 串口 源码下载连接:https://pan.baidu.com/s/1zMv4WqAeGwQ0avIf8I4brw

8.3.4、编译/运行:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
启动开发板:打开开发板电源开关,迅速按电脑ENTER键,防止初始化进入Linux系统
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

转载 Tiny4412之串口(Uart)驱动编写 可参考链接:https://www.cnblogs.com/wenqiang/p/4984756.html

8.3、RTC

RTC(Real-Time Clock) 实时时钟。RTC是集成电路,通常称为时钟芯片。
在一个嵌入式系统中,通常采用RTC来提供可靠的系统时间,包括时分秒和年月日等,而且要求在系统处于关机状态下它也能正常工作(通常采用后备电池供电)。它的外围也不需要太多的辅助电路,典型的就是只需要一个高精度的32.768kHz 晶体和电阻电容等。

(1)、参考RTC数据手册
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
(2)、代码实现
①、main.c

/**************************uart**************************/
#define GPA1CON   	*(volatile unsigned int *)0x13800020
#define ULCON		*(volatile unsigned int *)0x13820000
#define UCON		*(volatile unsigned int *)0x13820004
#define UTXH		*(volatile unsigned int *)0x13820020
#define URXH		*(volatile unsigned int *)0x13820024
#define UBRDIV		*(volatile unsigned int *)0x13820028
#define UFRACVAH	*(volatile unsigned int *)0x1382002C
#define  UTRSTATn	*(volatile unsigned int *)0x13820010

/**************************rtc***************************/
#define RTCCON		*(volatile unsigned int *)0x10070040
#define BCDSEC		*(volatile unsigned int *)0x10070070
#define BCDMIN		*(volatile unsigned int *)0x10070074
#define BCDHOUR		*(volatile unsigned int *)0x10070078
//#define BCDDAYWEEK	*(volatile unsigned int *)0x1007007c
//#define BCDDAY		*(volatile unsigned int *)0x10070080
#define BCDDAYWEEK	*(volatile unsigned int *)0x10070080
#define BCDDAY		*(volatile unsigned int *)0x1007007c
#define BCDMON		*(volatile unsigned int *)0x10070084
#define BCDYEAR		*(volatile unsigned int *)0x10070088

void mydelay(int n)
{
    int i,j;
    for(i=0;i<n;i++)
	{
        for(j=0;j<2000;j++)
		{
            
        }
    }
}

void dalay(void)
{
	int i = 0xffffff/5;
	while(i--);
}

void uart_init()
{
	GPA1CON = GPA1CON & ~0xff | 0x22;
	ULCON = 0x3;
	UCON = UCON & ~0x1f | 0x1<<2 | 0x1<<0;
	UBRDIV = 53;
	UFRACVAH = 4;
}

void uart_send(char c)
{
	while(!(UTRSTATn&(0x1<<2)));
	UTXH = c;
}

void uart_str(char* str)
{
	while(*str != '\0')
		uart_send(*str++);
}

char uart_recv(void)
{
	while(!(UTRSTATn&0x1));
	return URXH;
}

void rtc_init()
{	
	//RTCCON 	=	RTCCON | 0x1;		//开启
	RTCCON 	= 	0x1;					//开启

	BCDSEC	=	0x10;	
	BCDMIN	=	0x24;
	BCDHOUR	=	0x20;
	BCDDAYWEEK	=	0X04;
	BCDDAY	=	0X19;	
	BCDMON	=	0X08;	
	BCDYEAR	=	0X21;
	
	//RTCCON 	=	RTCCON & ~(0x1);	//关闭	
	RTCCON 	&=	~0x1;					//关闭	
}

void rtc_chage(int data, int n)
{
	int i = 0;
	for(i=n; i>0; i--)
	{
		char c = ((data>>(4*(i-1)))&0xf)+'0';
		uart_send(c);
	}
}

int rtc_show()				
{
	int year= BCDYEAR;		//年
	int mon	= BCDMON;		//月
	int day	= BCDDAY;		//日
	int week= BCDDAYWEEK;	//周
	int hour= BCDHOUR;		//时
	int min	= BCDMIN;		//分
	int sec	= BCDSEC;		//秒
	
	uart_send('2');
	rtc_chage(year, 3);
	uart_send('/');
	rtc_chage(mon, 2);
	uart_send('/');
	rtc_chage(day, 2);
	uart_send(' ');
	rtc_chage(week, 2);
	uart_send(' ');
	rtc_chage(hour, 2);
	uart_send(':');
	rtc_chage(min, 2);
	uart_send(':');
	rtc_chage(sec, 2);
	uart_send('\n');
	uart_send('\r');
}

void main(void)
{
	uart_init();
	rtc_init();
	
	while(1)
	{
		rtc_show(1);
		dalay();
	}
	
	while(1);
}
其他3个文件夹相同,不需修
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136

百度云 RTC 源代码下载:https://pan.baidu.com/s/12-qB_uCJ9waJX6iXuOhdnQ

(3)、编译运行
编译同上

(4)、运行结果
在这里插入图片描述

转载 Exynos4412裸机开发 —— RTC 实时时钟单元 可参考链接:https://blog.csdn.net/zqixiao_09/article/details/50739834

8.4、ADC

ADC,nalog-to-Digital Converter的缩写,指模数转换器。即将模拟信号转变为数字信号。

(1)、原理图
在这里插入图片描述
在这里插入图片描述
(2)、数据手册
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(3)、代码实现
①、main.c

/**************************uart**************************/
#define GPA1CON   	*(volatile unsigned int *)0x13800020
#define ULCON		*(volatile unsigned int *)0x13820000
#define UCON		*(volatile unsigned int *)0x13820004
#define UTXH		*(volatile unsigned int *)0x13820020
#define URXH		*(volatile unsigned int *)0x13820024
#define UBRDIV		*(volatile unsigned int *)0x13820028
#define UFRACVAH	*(volatile unsigned int *)0x1382002C
#define  UTRSTATn	*(volatile unsigned int *)0x13820010

/**************************ADC***************************/
#define	ADCCFG		*(volatile unsigned int *)0x10010118
#define	ADCCON		*(volatile unsigned int *)0x126C0000
#define	ADCDAT		*(volatile unsigned int *)0x126C000C
#define	ADCMUX		*(volatile unsigned int *)0x126C001c

void mydelay(int n)
{
    int i,j;
    for(i=0;i<n;i++)
	{
        for(j=0;j<2000;j++)
		{
        }
    }
}

void delay(void)
{
	int i = 0xffffff/5;
	while(i--);
}

void uart_init()
{
	GPA1CON = GPA1CON & ~0xff | 0x22;
	ULCON = 0x3;
	UCON = UCON & ~0x1f | 0x1<<2 | 0x1<<0;
	UBRDIV = 53;
	UFRACVAH = 4;
}

void uart_send(char c)
{
	while(!(UTRSTATn&(0x1<<2)));
	UTXH = c;
}

void uart_str(char* str)
{
	while(*str != '\0')
		uart_send(*str++);
}

char uart_recv(void)
{
	while(!(UTRSTATn&0x1));
	return URXH;
}

void adc_init(void)
{
	ADCCFG = ADCCFG & (~(0x1<<16));			//选择通用ADC
	ADCCON =  (0x1<<16) | (0x1<<14) | (0xff<<6);		//简易写法
	ADCMUX = 0x3;						//选择通道3
	ADCCON = ADCCON | (0x1<<1);				//启动,读转换
}

int adc_read(void)
{
	//ADCCON = ADCCON | (0x1<<0);			//启动,读转换
	//while(!(ADCCON & (0x1<<15)));
	return ADCDAT & 0xfff;				//确保读取的是否12位
}

void main(void)
{
	uart_init();
	adc_init();
	
	while(1)
	{
		int data = adc_read();
		data = data*18/4096;			//小数点保留3位
		uart_send(data/10+'0');
		uart_send('.');
		uart_send(data%10+'0');
		uart_str("v\n\r");
		//delay();
		mydelay(1000);
	}
	
	while(1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

其余3个文件同上

(4)、编译运行
在这里插入图片描述

百度云 ADC 源码下载链接:https://pan.baidu.com/s/1Z8E-e2uwLeTYNKMm-NmoNQ

转载 基于ARM4412的ADC原理及实现 可参考链接:
https://blog.csdn.net/jianwen_hi/article/details/53290461

8.5、PWM

PWM的概念
PWM( Pulse width Modulation):脉冲宽度调制
占空比:就是输出的PWM中,高电平保持的时间与该PWM的时钟周期的时间比
在这里插入图片描述

(1)、原理图
在这里插入图片描述
在这里插入图片描述
(2)、数据手册
在这里插入图片描述
在这里插入图片描述
(3)、代码实现
①、main.c

/**************************uart**************************/
#define GPA1CON   	*(volatile unsigned int *)0x13800020
#define ULCON		*(volatile unsigned int *)0x13820000
#define UCON		*(volatile unsigned int *)0x13820004
#define UTXH		*(volatile unsigned int *)0x13820020
#define URXH		*(volatile unsigned int *)0x13820024
#define UBRDIV		*(volatile unsigned int *)0x13820028
#define UFRACVAH	*(volatile unsigned int *)0x1382002C
#define  UTRSTATn	*(volatile unsigned int *)0x13820010

/***********************BEEP**************************/
#define GPD0CON   	*(volatile unsigned int *)0x114000A0

/***********************PWM**************************/
#define	TCFG0 	  	*(volatile unsigned int *)0x139D0000
#define	TCFG1 	  	*(volatile unsigned int *)0x139D0004
#define	TCON 	  	*(volatile unsigned int *)0x139D0008
#define	TCNTB0 	  	*(volatile unsigned int *)0x139D000C
#define	TCMPB0 	  	*(volatile unsigned int *)0x139D0010

void mydelay(int n)
{
    int i,j;
    for(i=0;i<n;i++)
	{
        for(j=0;j<2000;j++)
		{
        }
    }
}

void delay(void)
{
	int i = 0xffffff/5;
	while(i--);
}

void uart_init()
{
	GPA1CON = GPA1CON & ~0xff | 0x2;
	ULCON = 0x3;
	UCON = UCON & ~0x1f | 0x1<<2 | 0x1<<0;
	UBRDIV = 53;
	UFRACVAH = 4;
}

void uart_send(char c)
{
	while(!(UTRSTATn&(0x1<<2)));
	UTXH = c;
}

void uart_str(char* str)
{
	while(*str != '\0')
		uart_send(*str++);
}

char uart_recv(void)
{
	while(!(UTRSTATn&0x1));
	return URXH;
}

void pwm_init(void)
{
	GPD0CON = GPD0CON &(~0xf) | 0x22;
	TCFG0 = TCFG0 | 0xff;
	TCFG1 = TCFG1 & (~0xf) | 0x2;
	TCNTB0 = 300;
	TCMPB0 = 150;
	TCON = TCON  & (~0xf) | (0x1<<3) | (0x1<<1);
	TCON = TCON  & (~(0x1<<1));
}

void pwm_on(void)
{
	TCON = TCON | 0x1;
}

void pwm_off(void)
{
	TCON = TCON & (~(0x1));
}

void main(void)
{
	uart_init();
	pwm_init();
	
	while(1)
	{
		char c = uart_recv();
		if('1' == c)
		{
			pwm_on();
		}
		else
		{
			pwm_off();
		}
	}
	
	while(1);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106

其余3个文件同上

(4)、编译运行
在这里插入图片描述

百度云 PWM_BEEP 源码下载链接:https://pan.baidu.com/s/1_AtuOAHLnPssRBmct7JI2A

百度云 4412 PWM 可参考链接:https://www.cnblogs.com/ch122633/p/9506835.html

8.6、中断

中断的基本概念:
在程序运行中,出现了某种紧急事件,CP必须中止现行程序,转去处理此紧急事件(执行中断服务程序),并在处理完毕后再返回运行程序的过程。

(1)、原理图
在这里插入图片描述
在这里插入图片描述
(2)、外部中断
在这里插入图片描述
(3、源码实现
①、main.c

#include <stdio.h>
#define GPA1CON  *(volatile unsigned int*)0x11400020
#define ULCON    *(volatile unsigned int*)0x13820000
#define UCON     *(volatile unsigned int*)0x13820004
#define UTXH 	 *(volatile unsigned int*)0x13820020
#define URXH 	 *(volatile unsigned int*)0x13820024
#define UBRDIV 	 *(volatile unsigned int*)0x13820028
#define UFRACVAL *(volatile unsigned int*)0x1382002C
#define UTRSTAT  *(volatile unsigned int*)0x13820010

#define GPX1CON  	   *(volatile unsigned int*)0x11000C20
#define EXT_INT41_CON  *(volatile unsigned int*)0x11000E04
#define EXT_INT41_MASK *(volatile unsigned int*)0x11000F04
#define EXT_INT41_PEND *(volatile unsigned int*)0x11000F44

#define ICDDCR 		   *(volatile unsigned int*)0x10490000
#define ICDISER1_CPU0  *(volatile unsigned int*)0x10490104
#define ICDIPR14_CPU0  *(volatile unsigned int*)0x10490438
#define ICDIPTR14_CPU0 *(volatile unsigned int*)0x10490838
#define ICCICR_CPU     *(volatile unsigned int*)0x10480000
#define ICCPMR_CPU 	   *(volatile unsigned int*)0x10480004
#define ICCIAR_CPU 	   *(volatile unsigned int*)0x1048000C
#define ICDICPR1_CPU0  *(volatile unsigned int*)0x10490284
#define ICCEOIR_CPU    *(volatile unsigned int*)0x10490010

void uart_init(){
	GPA1CON = GPA1CON & (~0xff) | 0x22;
	ULCON = 0x3;
	UCON = UCON & (~0x1f) | (0x1<<2) | (0x1<<0);
	UBRDIV = 53;
	UFRACVAL = 4;
}

void uart_send(char c){
	while(!(UTRSTAT & (0x1<<2)));
	UTXH = c;
}

void uart_send_str(char *buf){
	while(*buf){
		uart_send(*buf++);
	}
}

char uart_recv(){
	while(!(UTRSTAT & 0x1));
	return URXH;
}

void doirq(){
	int num = ICCIAR_CPU&0x3ff;
	uart_send_str("S\n\r");
	EXT_INT41_PEND = EXT_INT41_PEND | (0x1<<1);
	ICDICPR1_CPU0 |= (0x1<<25);
	ICCEOIR_CPU = num;
}

void key_init(){
	GPX1CON |= (0xf<<4);
	EXT_INT41_CON = EXT_INT41_CON & (~(0x7<<4)) | (0x2<<4);
	EXT_INT41_MASK = EXT_INT41_MASK & (~(0x1<<1)); 
}

void gic_init(){
	ICDDCR = 0x1;
	ICDISER1_CPU0 |= (0x1<<25);
	ICDIPR14_CPU0 &= ~(0xff<<8);
	ICDIPTR14_CPU0 = ICDIPTR14_CPU0 & (~(0xff<<8)) | (0x1<<8);
	ICCICR_CPU = 0x1;
	ICCPMR_CPU = 0xff;
}

void main(void){
	gic_init();
	uart_init();
	key_init();
	while(1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

②、test.S

.globl _start
_start: 
	b	reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq
_undefined_instruction: .word _undefined_instruction
_software_interrupt:	.word _software_interrupt 
_prefetch_abort:	.word _prefetch_abort
_data_abort:		.word _data_abort
_not_used:		.word _not_used
_irq:			.word irq_handler
_fiq:			.word _fiq

reset:
	/* 设置cpu模式为SVC */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f  
	orr	r0, r0, #0xd3
	msr	cpsr,r0

	/* 设置异常向量表的起始地址 */
	ldr	r0, =0x41000000
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR

	/*  设置栈空间 */
	ldr sp, stacktop
	sub r1, sp, #64
	
	/* 设置cpu模式为irq */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f  
	orr	r0, r0, #0x12
	msr	cpsr,r0
	
	/*  设置栈空间 */
	mov sp, r1
	sub r1, sp, #64
	
	/* 设置cpu模式为user */
	mrs	r0, cpsr
	bic	r0, r0, #0xff
	orr	r0, r0, #0x10
	msr	cpsr,r0

	/*  设置栈空间 */
	mov sp ,r1 

	/*  跳转到应用main  */
	bl	main

irq_handler:
	sub lr, lr, #4
	stmfd sp!,{r0-r12,lr}
	bl doirq
	ldmfd sp!,{r0-r12,pc}^

stack: .space 64*3
stacktop: .word stack+64*3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

其余2个文件同上,未修改

(4)、编译运行
在这里插入图片描述

百度云 4412 按键和中断 可参考链接:https://www.cnblogs.com/ch122633/p/9512041.html

8.7、看门狗

看门狗
特点:不断的接受信号或重新设置计数值,保持计数值不为0,一旦一段时间接受不到信号或者计数值为0,看门狗将发出复位信号或者产生中断。

看门狗工作原理
在系统运行以后也就启动了看门狗的计数器,看门狗就开始自动计数,如果到了一定的时间还不去清看门狗,那么看门狗计数器就会溢出从而引起看门狗中断,造成系统复位。
在这里插入图片描述

转载 ARM笔记(看门狗)可参考链接:https://blog.csdn.net/wantingfy/article/details/78713418?spm=1001.2014.3001.5501

转载 14. 从0学ARM-exynos4412-看门狗裸机程序编写 可参考链接:https://www.cnblogs.com/yikoulinux/p/14395171.html

8.8、SPI

SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线。

SPI有三个寄存器分别为:
控制寄存器SPCR,状态寄存器SPSR,数据寄存器SPDR。

SPI总线系统可直接与各个厂家生产的多种标准外围器件直接接口,该接口一般使用4条线:
串行时钟线(SCLK)、主机输入/从机输出数据线MISO、主机输出/从机输入数据线MOSI和

特点:
信号线少,协议简单,相对数据速率高

缺点:
没有指定的流控制,没有应答机制确认是否接收到数据。
在这里插入图片描述

转载 arm底层通讯协议之SPI通讯 可参考链接:http://news.eeworld.com.cn/mcu/ic467470.html

转载 4412 SPI驱动 可参考链接:https://www.cnblogs.com/ch122633/p/9528760.html

8.9、I2C

I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备,是微电子通信控制领域挂规范采用的一种总线标准。

特点是:半双工,仅需要两根线(所以又被称为i2-wire总线)

百度云 i2c 可参考文档链接:https://pan.baidu.com/s/11WcpO6L13KPwqQCM5QhdhQ

结构框图
在这里插入图片描述
数据传输格式
在这里插入图片描述
数据传输
在这里插入图片描述

转载 Exynos4412 IIC总线驱动开发(二)— IIC 驱动开发 可参考链接:https://blog.csdn.net/zqixiao_09/article/details/50917655

转载 4412 i2c驱动 可参考链接:https://www.cnblogs.com/ch122633/p/9518648.html

跳转:上一篇、QT GUI编程!

跳转:上一篇、QT GUI编程!

跳转:下一篇、Linux系统移植!

跳转:下一篇、Linux系统移植!

跳转:开头

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/984640
推荐阅读
相关标签
  

闽ICP备14008679号