赞
踩
统一声明:
博客转载 声 明 : 本博客部分内容来源于网络、书籍、及各类手册。
内容宗旨为方便查询、总结备份、开源分享。
部分转载内容均有注明出处,如有侵权请联系博客告知并删除,谢谢!
百度云盘提取码:统一提取码:ziyu
ARM体系结构(推荐★★★★★) 最新更新+强化+个人网站:http://www.baiziqing.cn/index.php/archives/48/
百度云 ARM思维导图 下载链接: https://pan.baidu.com/s/1eKIJeJJ0HGsCU-S0-GnxSw
计算机系统 = 硬件系统 + 软件系统
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官网:https://www.arm.com/
SILICON
DESIGN SUPPORT
SOFTWARE
TRAINING
CONSORTIA
ARM 微处理器及技术的应用几乎已经深入到各个领域:
(1) 、工业控制领域
(2) 、无线通讯领域
(2) 、网络应用
(3) 、消费类电子产品
(5)、成像和安全产品
嵌入式系统,是以应用为中心,以计算机技术为基础软硬件可裁剪,适用于对功可靠性成本,体积、功耗有严格要求的专用计算机系统。
单片机更多是指单片机的功能单一,它是完成运算、逻辑控制、通信等功能的单一模块。即便它性能再强大,功能依然是单一的。
3.1.1、软件
从软件组成上区别,通用单片机并不能直接运行,因为里面没有应用程序,而嵌入式系统一定要有控制软件,实现控制的方法可以用硬件电路,也可用软件程序。
3.1.2、硬件
从硬件组成上区别,单片机是由一块集成电路芯片组成,具体包含微控制器电路,输入输出接口控件。而嵌入式,随着电子技术发展,如今既可以用单片机实现,也可以用其他可编程的电子器件实现。
3.1.3、生态环境
可参考嵌入式与单片机之间的关系是什么?链接:https://www.zhihu.com/question/315310041
SoC:System on Chip的缩写,称为系统级芯片,也有称片上系统,意指它是一个产品,是一个有专用目标的集成电路,其中包含完整系统并有嵌入软件的全部内容。
AMBA (芯片通信标准)
4.1.1、ARM采用的是32位架构(最新ARMV8 64位架构)
4.1.2、ARM约定:
4.1.3、大部分 ARM core提供
4.1.4、Jazelle cores支持 Java byteco
4.2.1、ARM有8个基本工作模式
非特权模式
,大部分仼务执行在这种模式,不能直接切换到其他模式 (用户空间、用户态)(fast)中断
产生时将会进入这种模式( normal)中断
产生时将会进入这种模式复位或软中断
指令执行时将会进入这种模式 (内核空间、内核态)寄存器本质就是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)
对应本文 6.1 章节详解:
转载 ARM的异常处理 可参考链接:https://blog.csdn.net/lushoumin/article/details/81089103
4.5.1、大端模式(big endian)
高字节对应高地址(大端模式)
4.5.2、小端模式(little endian)
高字节对应低地址(小端模式)
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
预编译
编译
编译汇编
链接
生成二进制
转载 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
(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
如果(显示以下)错误及解决
:
转载 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(有符号位取反)
(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的进位标志位
(3)、位操作指令: AND、ORR、EOR、BIC
1、【逻辑与指令】:AND
AND R0,R0,#3 ; 该指令保持R0的0、1位,其余位清零。
2、【逻辑或指令】:ORR
ORR R0,R0,#3 ; 该指令设置R0的0、1位,其余位保持不变。
3、【逻辑异或指令】:EOR
EOR R0,R0,#3 ; 该指令反转R0的0、1位,其余位保持不变。
4、【位清零指令】:BIC
BIC R0,R0,#%1011 ; 该指令清除 R0 中的位 0、1、和 3,其余的位保持不变。
(4)、跳转指令: B、BL
1、【B指令】
B Label ;程序无条件跳转到标号Label处执行
CMP R1,#0 ;当CPSR寄存器中的Z条件码置位时,程序跳转到标号Label处执行
BEQ Label
2、【BL指令】
BL Label ;当程序无条件跳转到标号Label处执行时,同时将当前的PC值保存到R14中
(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的标志位
(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为地址的存储器中。
(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加到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
5.4.2、立即数
合法立即数: 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、跳转(分支)指令
5.4.5、软中断指令
5.5.1、协处理器cp15操作指令
5.5.2、什么是协处理器
5.5.3、MRC & MCR的使用方法
转载 ARM协处理器CP15寄存器详解 参考链接:https://www.cnblogs.com/lifexy/p/7203786.html
5.6.1、伪指令的意义
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
5.6.2、常用gnu伪指令
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、最重要的几个伪指令
ARM中有一个ldr指令,还有一个ldr伪指令
一般都使用ldr伪指令而不用ldr指令
ldr指令: ldr r0, #0xff
伪指令: ldr r0, =0xfffl @涉及到合法/非法立即数,涉及到ARM文字池
5.6.4、adr与ldr
adr和ldr的差别:
ldr加载的地址在链接时确定
,而adr加载的地址在运行时确定
;
所以我们可以通过adr和ldr加载的地址比较来判断当前程序是否在链接时指定的地址运行。
5.7.1、为什么需要多寄存器访问指令
5.7.2、举例(uboot start.S 537行)
5.7.3、8种后缀
5.7.4、四种栈
栈指针指向空
,每次存入时可以 直接存入然后栈指针移动一格;而取出时 需要先移动一格才能取出栈指针指向栈中最后一格数据
,每 次存入时需要先移动栈指针一格再存入; 取出时可以直接取出,然后再移动栈指针增加的方向移动的栈
减小的方向移动的栈
5.7.5、!的作用
5.7.6、^的作用
5.7.7、总结
汇编实现两个数交换
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
高版本 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.1、模式和异常的关系
6.1.2、从user到异常再回到user的流程
6.1.3、异常向量表
6.1.4、ARM的异常处理机制
当异常产生时, ARM core:
– 拷贝 CPSR 到 SPSR_
– 设置适当的 CPSR 位:
• 改变处理器状态进入 ARM 态
• 改变处理器模式进入相应的异常模式
• 设置中断禁止位禁止相应中断 (如果需要)
– 保存返回地址到 LR_
– 设置 PC 为相应的异常向量
返回时, 异常处理需要:
– 从 SPSR_恢复CPSR
– 从LR_恢复PC
– Note:这些操作只能在 ARM 态执行.
6.1.5、异常优先级
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
6.2.1、FIQ VS IRQ
FIQ和IRQ提供了非常基本的优先级级别
FIQS有高于IRQs的优先级,表现在下面2个方面
当多个中断产生时,CPU优先处理FIQ
处理FIQ时禁止IRQs
FIQS的设计使中断响应尽可能的快
FIQ向量位于异常向量表的最末
FIQ模式有5个额外的私有寄存器(r8r12)
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);
自己写一个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 ...."
编译
声明头文件
.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
编译
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);
}
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
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 ...."
test.lds
ENTRY(_start)
SECTIONS{
. = 0x0;
.text : {
test.o(.text)
*(.text)
}
.data : {
*(.data)
}
.bss : {
*(.bss)
}
}
gdbS
#!/bin/bash
qemu-system-arm -machine vexpress-a9 -m 256M -serial stdio -kernel start.elf -S -s
编译运行参考:5.3.3、(9)~(11)
实列 mysyscall 百度云源码链接:https://pan.baidu.com/s/1tT8KosRvv0Ccf-4aoOhMkQ
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
②、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 ...."
③、test.lds
ENTRY(_start)
SECTIONS{
. = 0x41000000;
.text : {
test.o(.text)
*(.text)
}
.data : {
*(.data)
}
.bss : {0
*(.bss)
}
}
百度云 纯汇编点灯 源码下载链接: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为所以为0xf,28对应的位置,|置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
}
②、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 ...."
③、test.lds
ENTRY(_start)
SECTIONS{
. = 0x41000000;
.text : {
test.o(.text)
*(.text)
}
.data : {
*(.data)
}
.bss : {
*(.bss)
}
}
④、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
百度云盘 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.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);
}
其余3个文件同上
百度云盘 串口 源码下载连接:https://pan.baidu.com/s/1zMv4WqAeGwQ0avIf8I4brw
8.3.4、编译/运行:
启动开发板:打开开发板电源开关,迅速按电脑ENTER键,防止初始化进入Linux系统
转载 Tiny4412之串口(Uart)驱动编写 可参考链接:https://www.cnblogs.com/wenqiang/p/4984756.html
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个文件夹相同,不需修
百度云 RTC 源代码下载:https://pan.baidu.com/s/12-qB_uCJ9waJX6iXuOhdnQ
(3)、编译运行
编译同上
(4)、运行结果
转载 Exynos4412裸机开发 —— RTC 实时时钟单元 可参考链接:https://blog.csdn.net/zqixiao_09/article/details/50739834
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);
}
其余3个文件同上
(4)、编译运行
百度云 ADC 源码下载链接:https://pan.baidu.com/s/1Z8E-e2uwLeTYNKMm-NmoNQ
转载 基于ARM4412的ADC原理及实现 可参考链接:
https://blog.csdn.net/jianwen_hi/article/details/53290461
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);
}
其余3个文件同上
(4)、编译运行
百度云 PWM_BEEP 源码下载链接:https://pan.baidu.com/s/1_AtuOAHLnPssRBmct7JI2A
百度云 4412 PWM 可参考链接:https://www.cnblogs.com/ch122633/p/9506835.html
中断的基本概念:
在程序运行中,出现了某种紧急事件,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);
}
②、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
其余2个文件同上,未修改
(4)、编译运行
百度云 4412 按键和中断 可参考链接:https://www.cnblogs.com/ch122633/p/9512041.html
看门狗
特点:不断的接受信号或重新设置计数值,保持计数值不为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
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
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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。