ARM裸机:一步步点亮LED(汇编)

news/2024/7/24 5:50:32 标签: arm开发, 汇编, 单片机

硬件工作原理及原理图查阅

  • 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可以帮助理解程序

学习记录,侵权联系删除。
来源:朱老师物联网大课堂


http://www.niftyadmin.cn/n/5546900.html

相关文章

Spring Boot集成grpc快速入门demo

1.什么是GRPC&#xff1f; gRPC 是一个高性能、开源、通用的RPC框架&#xff0c;由Google推出&#xff0c;基于HTTP2协议标准设计开发&#xff0c;默认采用Protocol Buffers数据序列化协议&#xff0c;支持多种开发语言。gRPC提供了一种简单的方法来精确的定义服务&#xff0c…

【 正己化人】 把自己做好,能解决所有问题

阳明先生说&#xff1a;与朋友一起辩论学问&#xff0c;纵然有人言辞观点浅近粗疏&#xff0c;或者是炫耀才华、显扬自己&#xff0c;也都不过是毛病发作。只要去对症下药就好&#xff0c;千万不能怀有轻视别人的心理&#xff0c;因为那不是君子与人为善的心。 人会爱发脾气、…

Unity3d C#实现基于UGUI ScrollRect的轮播图效果功能(含源码)

前言 轮播功能是一种常见的页面组件&#xff0c;用于在页面中显示多张图片/素材并自动或手动进行切换&#xff0c;以提高页面的美观度和用户体验。主要的功能是&#xff1a;自动/手动切换;平滑的切换效果;导航指示器等。可惜Unity的UGUI系统里没有现成的实现该功能&#xff0c…

搜维尔科技:OptiTrack在NAB2024展示了一系列业界领先的媒体技术

广泛的显示和动作捕捉跟踪技术组合涵盖无与伦比的室内和室外 LED 解决方案、前沿技术演示以及最新的软件和硬件产品 可视化技术领域的全球领导者 Planar及其附属公司 3D 跟踪系统的全球领导者OptiTrack宣布&#xff0c;两家公司将在 2024 年全国广播协会 (NAB) 展会上展示其最全…

CentOS 7:停止更新后如何下载软件?

引言 CentOS 7 是一个广受欢迎的 Linux 发行版&#xff0c;它为企业和开发者提供了一个稳定、安全、且免费的操作系统环境。然而&#xff0c;随着时间的推移&#xff0c;CentOS 7 的官方支持已经进入了维护阶段&#xff0c;这意味着它将不再收到常规的更新和新功能&#xff0c;…

微软清华提出全新预训练范式,指令预训练让8B模型实力暴涨!实力碾压70B模型

现在的大模型训练通常会包括两个阶段&#xff1a; 一是无监督的预训练&#xff0c;即通过因果语言建模预测下一个token生成的概率。该方法无需标注数据&#xff0c;这意味着可以利用大规模的数据学习到语言的通用特征和模式。 二是指令微调&#xff0c;即通过自然语言指令构建…

uniapp——银行卡号脱敏

样式 代码 {{bankNumber.replace(/(\d{4})(?\d)/g, "●●●● ").replace(/(\d{2})(?\d{2}$)/, " $1")}} 将银行卡号按照每四位一组的方式进行处理&#xff0c;前面的变成 剩下的正常显示

fatal error: napi.h: No such file or directory

使用Cmake-js构建基于node-addon-api的C扩展 基于node-addon官方的eample改造测试&#xff1a;https://github.com/nodejs/node-addon-examples Cmake-js的github给了一个例子&#xff0c;但是是基于NAN的&#xff0c;而不是node-addon-api&#xff1a;https://github.com/cma…