硬件工作原理及原理图查阅
-
LED物理特性介绍
LED本身有2个接线点,一个是LED的正极,一个是LED的负极。LED这个硬件的功能就是点亮或者不亮,物理上想要点亮一颗LED只需要给他的正负极上加正电压即可,要熄灭一颗LED只需要去掉电压即可。 -
查阅原理图了解板载LED硬件接法
查阅原理图,发现开发板上一共有5颗LED。其中一颗D26的接法是:正极接5V,负极接地。因此这颗LED只要上电就会常亮。因此我们分析这颗LED是电源指示灯。
剩下4颗LED的接法是:正极接3.3V,负极接了SoC上的一个引脚(GPIO),具体详细接法是:
D22:GPJ0_3
D23:GPJ0_4
D24:GPJ0_5
D25:PWMTOUT1(GPD0_1)
- 分析如何点亮及熄灭LED(GPIO)
分析:LED点亮的要求是:正极和负极之间有正向电压差。
思考:在开发板上如何为LED制造这个电压差让它点亮呢?
解答:因为正极已经定了(3.3V),而负极接在了SoC的引脚上,可以通过SoC中编程来控制负极的电压值,因此我们可以通过程序控制负极输出低电平(0V),这样在正负极上就有了压差,LED即可点亮。
数据手册查阅及相关寄存器浏览
-
GPIO概念的引入
GPIO:general purpose input output 通用输入输出
GPIO就是芯片的引脚(芯片上的引脚有些不是GPIO,只有一部分是),作为GPIO的这类引脚,他的功能和特点是可以被编程控制它的工作模式,也可以编程控制他的电压高低等。
通过之前的分析我们知道,我们设计电路时就把LED接在了一个GPIO上,这样我们就可以通过编程控制GPIO的模式和输入输出值来操控LED亮还是灭;如果你当时设计电路时把LED接在非GPIO上那就不可能了。 -
阅读数据手册中有关部分
当我们想要通过编程操控GPIO来操作LED时,我们首先需要通读一下S5PV210的数据手册中有关于GPIO的部分,这部分在数据手册的Section2.2中。
《S5PV210_UM_REV1.1.pdf》
-
GPIO相关的寄存器介绍
回忆下之前说过的,软件操作硬件的接口是:寄存器。
我们当前要操作的硬件是LED,但是LED实际是通过GPIO来间接控制的,所以当前我们实际要操作的设备其实是SoC的GPIO。要操作这些GPIO,必须通过设置他们的寄存器。
查阅数据手册可知,GPJ0相关的寄存器有以下:
GPJ0CON, (GPJ0 control)GPJ0控制寄存器,用来配置各引脚的工作模式
GPJ0DAT, (GPJ0 data)当引脚配置为input/output模式时,寄存器的相应位和引脚的电平高低相对应。
GPJ0PUD, (pull up down)控制引脚内部弱上拉、下拉
GPJ0DRV, (driver)配置GPIO引脚的驱动能力
GPJ0CONPDN,(记得是低功耗模式下的控制寄存器)
GPJ0PUDPDN (记得是低功耗模式下的上下拉寄存器)
注:在驱动LED点亮时,应该将GPIO配置为output模式。实际上真正操控LED的硬件,主要的有:GPJ0CON, GPJ0DAT 这么2个。
如何点亮LED,编程的步骤是:
1、操控GPJ0CON寄存器中,选中output模式
2、操控GPJ0DAT寄存器,相应的位设置为0
开始手写汇编点亮LED
D22:GPJ0_3
D23:GPJ0_4
D24:GPJ0_5
GPJ0CON(0xE0200240)寄存器和GPJ0DAT(0xE0200244)寄存器
第一步:设置引脚模式为输出模式(向GPxCON寄存器写入0001)
第二步:写入控制的数据(向GPxDAT寄存器写入0输出低电平,LED亮;1输出高电平,LED灭)
文件目录:
led.S:
_start:
//把0x1111 1111 写入 GPJ0CON(0xE0200240)
//这里 ldr 是Load Register(加载寄存器)的缩写,
//用于从给定的地址加载数据到处理器寄存器r0。
//=0x11111111是一个立即数常量,
//表示存储在内存中的十六进制数值 11111111
ldr r0, =0x11111111 //ldr伪指令,编译器判断立即数是否合法
//将立即数 0xE0200240加载到寄存器r1中
ldr r1, =0xE0200240
//str是Store Register(存储寄存器)的缩写,
//它将寄存器r0中的值存储到[r1]所指向的位置。
//这里的[r1]是对另一个内存地址的操作符,
//意味着将r0的内容放到r1当前内容指明的那个内存位置
//将r0的值存储到r1指向的内存地址处
str r0, [r1] //寄存器间接寻址去
; //LED灭:将0xff放到GPJ0DAT(0xE0200244)寄存器
; ldr r0, =0xff
; ldr r1, =0xE0200244
; str r0, [r1]
//LED亮:将 0x0 放到GPJ0DAT(0xE0200244)寄存器
ldr r0, =0x0
ldr r1, =0xE0200244
str r0, [r1]
//结束死循环
falt:
b falt //直到CPU断电关机
编译结果:
LED常亮
使用位运算实现复杂点亮要求
-
如何只点亮中间1颗(两边是熄灭的)LED
//LED亮:将 0xf7 放到GPJ0DAT(0xE0200244)寄存器 亮1颗
中间一颗:0xEF
最后一颗:0xdf -
常用位运算:与、或、非、移位
位与(&) 位或(|) 位非(取反 ~) 移位(左移<< 右移>>) -
使用位运算实现功能
1<<3 等于 0b1000
1<<5 等于 0b100000
(1<<3)|(1<<5) 等于 0b101000 -
扩展一下:如何只熄灭中间1颗而点亮旁边2颗
ldr r0, =((0<<3) | (1<<4) | (0<<5))
汇编编写延时函数并实现LED闪烁效果
延时就是编写一些没有目的的代码,占用CPU的时间。
//延时函数
delay:
ldr r2, =0x900000
ldr r3, =0x0
delay_loop:
cmp r3, r2
//比较r3 r2 会影响Z标志位 如果r2==r3 则Z=1 下一句当中的ne就会成立
sub r2, r2, #1 //r2=r2-1
bne delay_loop //如果r2==r3 就不会执行这句
mov pc, lr //函数调用返回
#define GPJ0CON 0xE0200240
#define GPJ0DAT 0xE0200244
//3、添加链接属性添加
.globl _start //将-start 修改为外部链接属性,其他文件就能找到_start
_start:
//把0x1111 1111 写入 GPJ0CON(0xE0200240)
//这里 ldr 是Load Register(加载寄存器)的缩写,
//用于从给定的地址加载数据到处理器寄存器r0。
//=0x11111111是一个立即数常量,
//表示存储在内存中的十六进制数值 11111111
ldr r0, =0x11111111 //ldr伪指令,编译器判断立即数是否合法
//将立即数 0xE0200240加载到寄存器r1中
ldr r1, =GPJ0CON
//str是Store Register(存储寄存器)的缩写,
//它将寄存器r0中的值存储到[r1]所指向的位置。
//这里的[r1]是对另一个内存地址的操作符,
//意味着将r0的内容放到r1当前内容指明的那个内存位置
//将r0的值存储到r1指向的内存地址处
str r0, [r1] //寄存器间接寻址
delay_loop_ok:
//熄灭中间1颗而点亮旁边2颗
ldr r0, =((0<<3) | (1<<4) | (0<<5))
ldr r1, =GPJ0DAT
str r0, [r1]
//延时一下:
bl delay
ldr r0, =((1<<3) | (0<<4) | (1<<5))
ldr r1, =GPJ0DAT
str r0, [r1]
//延时一下:
bl delay
; bne delay_loop_ok
b delay_loop_ok //死循环
再难一点的流水灯效果
12321的点亮LED
#define GPJ0CON 0xE0200240
#define GPJ0DAT 0xE0200244
//3、添加链接属性添加
.globl _start //将-start 修改为外部链接属性,其他文件就能找到_start
_start:
//把0x1111 1111 写入 GPJ0CON(0xE0200240)
//这里 ldr 是Load Register(加载寄存器)的缩写,
//用于从给定的地址加载数据到处理器寄存器r0。
//=0x11111111是一个立即数常量,
//表示存储在内存中的十六进制数值 11111111
ldr r0, =0x11111111 //ldr伪指令,编译器判断立即数是否合法
//将立即数 0xE0200240加载到寄存器r1中
ldr r1, =GPJ0CON
//str是Store Register(存储寄存器)的缩写,
//它将寄存器r0中的值存储到[r1]所指向的位置。
//这里的[r1]是对另一个内存地址的操作符,
//意味着将r0的内容放到r1当前内容指明的那个内存位置
//将r0的值存储到r1指向的内存地址处
str r0, [r1] //寄存器间接寻址
delay_loop_ok:
ldr r0, =((0<<3) | (1<<4) | (1<<5))
ldr r1, =GPJ0DAT
str r0, [r1]
//延时一下:
bl delay
ldr r0, =((1<<3) | (0<<4) | (1<<5))
ldr r1, =GPJ0DAT
str r0, [r1]
//延时一下:
bl delay
ldr r0, =((1<<3) | (1<<4) | (0<<5))
ldr r1, =GPJ0DAT
str r0, [r1]
//延时一下:
bl delay
ldr r0, =((1<<3) | (0<<4) | (1<<5))
ldr r1, =GPJ0DAT
str r0, [r1]
//延时一下:
bl delay
; bne delay_loop_ok
b delay_loop_ok //死循环
//结束死循环 2、高级点的死循环
b . //直到CPU断电关机 .:当前指令的地址
//延时函数
delay:
ldr r2, =0x900000
ldr r3, =0x0
delay_loop:
cmp r3, r2
//比较r3 r2 会影响Z标志位 如果r2==r3 则Z=1 下一句当中的ne就会成立
sub r2, r2, #1 //r2=r2-1
bne delay_loop //如果r2==r3 就不会执行这句
mov pc, lr //函数调用返回
位取反操作:
//1、使用宏定义
#define GPJ0CON 0xE0200240
#define GPJ0DAT 0xE0200244
//3、添加链接属性添加
.globl _start //将-start 修改为外部链接属性,其他文件就能找到_start
_start:
//把0x1111 1111 写入 GPJ0CON(0xE0200240)
//这里 ldr 是Load Register(加载寄存器)的缩写,
//用于从给定的地址加载数据到处理器寄存器r0。
//=0x11111111是一个立即数常量,
//表示存储在内存中的十六进制数值 11111111
ldr r0, =0x11111111 //ldr伪指令,编译器判断立即数是否合法
//将立即数 0xE0200240加载到寄存器r1中
ldr r1, =GPJ0CON
//str是Store Register(存储寄存器)的缩写,
//它将寄存器r0中的值存储到[r1]所指向的位置。
//这里的[r1]是对另一个内存地址的操作符,
//意味着将r0的内容放到r1当前内容指明的那个内存位置
//将r0的值存储到r1指向的内存地址处
str r0, [r1] //寄存器间接寻址
delay_loop_ok:
ldr r0, =~(1<<3)
ldr r1, =GPJ0DAT
str r0, [r1]
//延时一下:
bl delay
ldr r0, =~(1<<4)
ldr r1, =GPJ0DAT
str r0, [r1]
//延时一下:
bl delay
ldr r0, =~(1<<5)
ldr r1, =GPJ0DAT
str r0, [r1]
//延时一下:
bl delay
; bne delay_loop_ok
b delay_loop_ok //死循环
//结束死循环 2、高级点的死循环
b . //直到CPU断电关机 .:当前指令的地址
//延时函数
delay:
ldr r2, =0x900000
ldr r3, =0x0
delay_loop:
cmp r3, r2
//比较r3 r2 会影响Z标志位 如果r2==r3 则Z=1 下一句当中的ne就会成立
sub r2, r2, #1 //r2=r2-1
bne delay_loop //如果r2==r3 就不会执行这句
mov pc, lr //函数调用返回
反汇编工具objdump的使用简介
反汇编的原理&为什么要反汇编
arm-linux-objdump -D led.elf > led_elf.dis
objdump是gcc工具链中的反汇编工具,作用是由编译链接好的elf格式的可执行程序反过来得到汇编源代码
-D表示反汇编 > 左边的是elf的可执行程序(反汇编时的原材料),>右边的是反汇编生成的反汇编程序
- 反汇编的原因
1、逆向破解
2、调试,理解程序链接脚本、链接地址
3、理解C语言和汇编语言的关系
(汇编 assembly 反汇编 Disassembly)
标号的实质是地址
指令被转换为机器码
地址池实现非法立即数
bin文件内部是机器码,机器码会有指定的指令地址,使用ld链接在一起
总结
1、知道LED点亮原理
2、查看原理图知道接线方式
3、查看数据手册知道寄存器地址
4、开始编程
5、编译可执行文件
6、下载到设备上
7、添加延时达到流水灯
8、位操作增加可读性,同时比较简略
9、反汇编工具objdump可以帮助理解程序
学习记录,侵权联系删除。
来源:朱老师物联网大课堂