《ARM学习笔记》:更新中

汇编语言

map.lds文件

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
	. = 0;
	. = ALIGN(4);
	.text :
	{
		*(.text)
	}
    . = ALIGN(4);
    .data : 
	{ *(.data) }
    . = ALIGN(4);
    .bss :
    { *(.bss) }
}

mov与ldr伪指令

.global _start
_start:

mov r1,0x00000001 @不可以装载超过8bit数
ldr r2,=0x00000001	@可以装载32bit数

.end

指令机器码

@-----指令机器码-----
.if 0
mov r2,#0xA
.endif

cmp

@-----cmp-----
.if 0
mov r1,#5
mov r2,#5
cmp r1,r2	@cmp不需要加s就可以影响cpsr的标志位
@moveq r3,#0xA
@movne r3,#0xB
movge r3,#0xB	@大于等于
movle r3,#0xA	@小于等于
.endif

cmn

@—cmn—
mov r0,#0x1
mov r1,#0x2
cmn r0,r1
movgt r3,r0
movlt r3,r1
.endif

lsl lsr asr ror rrx

@---lsl lsr asr ror rrx---
mov r1,#0x7000000F
@lsls r1,#1
@asr r1,#1	@右移,高位用原数的31bit填充,
@ror r1,#1	@循环右移,高位用低位移出的填充
rrx r1,r1	@右移一位,高位用上次运行的cspsr的C位填充 对于减法无借位c位置1                                                                                                    

and orr eor bic

@--and orr eor bic---
mov r0,#0xFF
mov r1,#0xF0
@and r0,r1
@orr r1,r0
@eor r1,r0	@32位数的逻辑异或操作
bic r0,#0x0F @32位数的逻辑位清零指数

add adc

@---add adc---
.if 0
mov r0,#
.endif

合法立即数

@---合法立即数---

ldr伪指令

@---ldr伪指令---

tst teq

@---tst teq---

.if 0
@mov r0,#0xE0
@tst r0,#0x3	@z=0,该位置不为空,z=1,该位置为空

mov r1,#3
mov r2,#3
teq r1,r2	@ 两个不相等 z=0

寄存器和内存

单寄存器传送指令:

ldr/ldrh/ldrb 内存->寄存器
str/strh/strb 寄存器->内存(mov r1,#8)

1.不带偏移的赋值
str r1,[r2]			@不带偏移

不带索引

2.前索引赋值法:带偏移的赋值

先地址偏移,再赋值,类似++i;

str r1,[r2,#4]		@自动更新基值地址
str r1,[r2,#4]!	@自动更新基值地址

前索引

2.后索引赋值法:带偏移的赋值

先赋值,再更新(偏移)地址,类似i++;

str r1,[r2],#4		
测试代码
.text
.global _start
_start:

mov r0,#0	@把寄存器r0,设置为0x00000000
ldr r1,[r0]	@r0为一个地址,[]取地址里的内容ldr一次取四个字节32bit,取完的数址放在r1中
ldr r2,=buf @r2指向申请的16字节内存单元

@str r1,[r2] 		@不带偏移
@str r1,[r2,#4]		@前索引,先更新地址,再赋值
@str r1,[r2,#4]!	@前索引,自动更新基值地址
str r1,[r2],#4		@后索引,先赋值,再更新地址

ldr r3,=buf1
ldr r4,=buf2
ldr r5,=buf3

.data	@数据段
	buf:	@用buf指向数据段
		.space 16	@.space定义空闲段 malloc 16字节,用buf标识
	
	buf1:	@定义一个数组,用buf1指向
		.word 0xFF		@.word四个字节
		.word 0x12
		.word 0x34,0x56,0xFFFFFFFF
	buf2:
		.byte 'a'
		.byte 'b'
		.byte 'c'
		.byte 'd'
	buf3:
		.string "A hello word!"

.end

块寄存器传送指令

ldm 将多个内存单元的内容传送到多个寄存器中 内存->寄存器
stm 将多个寄存器的内容传送到多个内存单元中 寄存器->内存

不能单独使用,需要加上后缀,表明从小地址到大地址,还是从大地值往小地址方向传输
后缀:

ia:先赋值在增加地址 小地址->大地址 i++
ib:先增加地址再赋值 小地址->大地址 ++i
da:先赋值在减地址 大地址->小地址 i–
db:先减地址再赋值 大地值->小地址 --i

ldmia的特性

1.顺序赋值
ldmia顺序赋值
2.省略性写法
在这里插入图片描述

stmia的特性(从寄存器存贮到内存当中)

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

测试代码
.text
.global _start
_start:

ldr r0,=buf
@ldmia r0,{r1,r2,r3,r4,r5}	@先赋值再增加地址
ldmia r0!,{r1-r5}		@加了!会强制更新地址
@ldmib r0!,{r1-r3,r4-r5}	@先增加地址,再赋值

@ldmda r0,{r1-r5}
@ldmda r0!,{r1-r5}
@ldmdb r0!,{r1-r5}

ldr r6,=buf1
@stmia r6,{r1-r5}
@stmia r6!,{r1-r5}
@stmib r6,{r1-r5}	@先增加地址再赋值,不加!不更新地址
@stmib r6!,{r1-r5}   @先增加地址再赋值,加了!更新地址

@stmda r6,{r1-r5}
@stmda r6!,{r1-r5}	@先赋值,在增加地址
@stmdb r6,{r1-r5}	@先增加地址,再赋值,不更新地址
@stmdb r6!,{r1-r5}  @先增加地址,再赋值,自动跟新地址


.data
	buf:
		.word 0x1,0x2,0x3,0xFFFFFFFF,0xCC,0xAA
	bufend:
	buf1:
		.space 60
	buf1end:
.end
		

测试现象
!!!
在这里插入图片描述
!!!!!这里对于buf1,在测试ldmda/db/,strda/db寄存器写入内存的时候,我应该吧buf1end,给r6,而不是buf1,设置buf1给r6会导致“内存覆盖”!!

压栈和出栈 R13=SP存放栈指针

先进后出
入栈压栈示意图

压栈:

寄存器放到内存当中去
stmfd(与stmdb类似):倒着放进去,先减小地址再,赋值–i

出栈:

内存放到寄存器当中去
ldmfd(ldmia):正着出去,先赋值再增加地址i++

psr的操作指令

默认svc,特权模式
高权限可以切换低权限,地权限不能切换高权限

cpsr的第五位:

10000 User mode;
10001 FIQ mode;
10011 SVC mode;
10111 Abort mode;
11011 Undfined mode;
11111 System mode;
10110 Monitor mode;
10010 IRQ

测试代码 :svc模式转换成usr模式

.text
.global _start
_start:

@--- svc->usr ---
mrs r0,cpsr		@获取cpsr的第五位是啥
bic r0,#0x1F	@清空cpsr的第五位
orr r0,#0x10	@给cpsr增加新权限	
msr cpsr,r0		@把这个权限放到cpsr中	
.end

软交换指令(内存和地址互相交换指令)

swp(4个字节交换)
swpb(1个字节交换)

.text
.global _start
_start:

ldr r0,=buf
mov r1,#0xCC
swp r1,r1,[r0]

nop

.data
	buf:
		.word 0xFFFFFFFF

.end

软中断

中断:是一个过程,是指CPU在执行程序的过程中插入了另外一段程序的执行过程。
中断类型:软件中断–》是由程序员设计的中断,是可控的–>通过软件的方式发起的 中断
硬件中断–》是由硬件故障导致的中断,是不可控的
目标:设计一个软件中断,分析软件中断的处理流程
分析:1如何产生一个软件中断?
swi中断号(88)用户指令用户模式下使用
lr
2哪里能够接收软件中断?异常向量表
异常向量表–》是一个特殊位置上的一段代码
特殊位置:一般位于程序的最开始位置0x00000000,也可以修改
一段代码:由8条跳转指令组成的语句块,并且顺序是固定的
b reset复位异常
nop未定义异常
b swi_handler软中断异常
nop存取异常
nop 数据异常
nop 预留异常
nop 普通中断irq
nop 快速中断fiq
3软件中断的处理
swi_handler:保存现场处理中断恢复现场
保存现场:保存原工作模式下寄存器的值到栈里
stmfd sp!,{r0~r12,lr}
处理中断:1得到中断号
lr-4=得到了swi 88对应的地址
[lr-4]=swi 88的指令机器码格式
指令机器码的低24位存放的是中断号
2处理88号中断
比较swi 88的指令机器码的低24位是不是88?
如果是,处理88号中断;不是,程序顺序往下执行
恢复现场:将保存在栈里的内容恢复到原工作模式下
当swi 88执行时,ARM内核:
1拷贝cpsr到spsr
2设置cpsr=0x000000d3
3保存swi 88的下一条指令地址到lr
4设置PC=0x00000008
当处理完中断,恢复现场时,ARM内核:
1将spsr拷贝到cpsr
2将swi 88的下一条指令地址lr给pc
svc----->user---------->svc---------->usr
初始指令切换swi 88恢复现场
异常优先级:
异常指定了优先级和固定的服务顺序:
reset优先级最高
data abort
FIQ
IRQ
profectch abort
SWI
undefine优先级最低

测试代码
.text
.global _start
_start:


b reset			@复位异常
nop				@未定义异常
b swi_handler 	@软中断异常
nop				@读取异常
nop				@数据异常
nop				@预留异常
nop				@irq
nop				@fiq


reset:
@set svc mode stack
ldr sp,=stackend
@svc->user
mrs r0,cpsr
bic r0,#0x1F
orr	r0,#0x10
msr	cpsr,r0

@test
mov r0,#0xAA

swi 88
mov r1,#0xBB

swi_handler:
@save r0_r12 and lr
stmfd sp!,{r0-r12,lr}

@处理中断
@get swi number
sub r0,lr,#4
ldr r1,[r0]
bic r1,#0xFF000000

cmp r1,#88
bleq func

cmp r1,#99
bleq func1


@test
mov r0,#0xFF

ldmfd sp!,{r0-r12,pc}^


func:
mov r3,#8
nop
mov pc,lr	@存放程序调用的返回地址

func1:
nop


.data
	stack:
		.space 160
	stackend:

.end

流程分析表

请添加图片描述

C和汇编混合编程

汇编调用C

汇编调用C

测试代码

@--- start.h ---
.text
.global _start
_start:

ldr sp,=bufend
mov r10,sp

mov r0,#3
mov r1,#6

bl  func	@调用函数的时候可能会用到栈,因此先创建一个buf栈

mov r10,#0xAA
nop

.data
	buf:
		.space 160
	bufend:

.end



//main.c
int  func(int a,int b)
{
	return a+b;
}

C调用汇编函数

@---start.s---
.text 
.global _start
_start:

ldr sp,=bufend

b main

.global mycopy
mycopy:

loop:
	ldrb r2,[r0],#1		@r0的内容给r2
	strb r2,[r1],#1		@将r2的内容给r1
	
	cmp r2,#0
	bne loop
	mov r0,#5
	mov pc,lr

.data
	buf:
		.space 160
	bufend:

.end

//main.c
extern int mycopy(char *psrc,char *pdest);


int main()
{
	char *src="hello";
	char buf[10]={0};
	int ret=0;
	ret=mycopy(src,buf);
	return ret;
}


外设裸机编程

点灯

1.首先查外设的板子的手册找到LED,记住对应端口名称,这里找到LED2对应端口名称为GHG_COK

在这里插入图片描述
2.再去找核心板的手册,查找这个GHG_COK,可见其对应位置为GPX2_7
在这里插入图片描述
3.再去芯片手册查找这个GPX2,看看如何操作它
(1)
在这里插入图片描述
(2)
在这里插入图片描述

测试代码_汇编

@---start.s---

.text
.global _start
_start:

@初始化led2
ldr r0,=0x11000c40
ldr r1,[r0]
bic r1,#0xF0000000
orr r1,#0x10000000
str r1,[r0]

loop:

@打开led2
ldr r0,=0x11000c44
ldr r1,[r0]
orr r1,#0x80
str r1,[r0]

@delay
bl delay
	
@关闭led2
ldr r0,=0x11000c44
ldr r1,[r0]
bic r1,#0x80
str r1,[r0]

bl delay
b loop


delay:
ldr r0,=0x10000000
mov r1,#0
start:
	add r1,#1
	cmp r1,r0
	bne	start
	mov pc,lr

.end

测试代码_汇编和C混合编程

@--start.s---
.text 
.global _start
_start:

ldr sp,=bufend

b main

.data
	buf:
		.space 160
	bufend:

.end



//main.c

//把0x11000c40的地址中的内容给GPX2XXX
#define GPX2CON (*(volatile unsigned int *)0x11000c40)
#define GPX2DAT (*(volatile unsigned int *)0x11000c44)

void led2_init()
{
	//GPX2CON高四位清零 31 30 29 28,然后给高四位置为0x1
	GPX2CON=GPX2CON&~(0xF<<28)|(0x1<<28);
}

void led2_on()
{
	GPX2DAT=GPX2DAT|(0x1<<7);
	
}

void led2_0ff()
{
	GPX2DAT=GPX2DAT&~(0x1<<7);
}

void delay()
{
	int i;
	for(i=0;i<1000000;i++)
	{
		
	}
}

int main()
{
	led2_init();
	while(1)
	{
		led2_on();
		delay();
		led2_0ff();
		delay();
	}
	return 0;
}

uart

uart:Universal Asynchronous Receiver and Transmitter(通用异步收发器)
也称为串口,收发数据串行进行。
总线:uart spi iic 485 can总线
1 uart:通用异步收发器 用于设备接收或发送数据
2 关于uart概念:
通信方式:
单工通信:通信方向只能是一个方向
半双工通信:双向通信,但不能同时
全双工通信:双向通信,且能同时
uart可以实现全双工通信
bps:比特率 每秒钟传送bit位的个数
波特率:每秒钟传送码元的个数
码元:是一个数据单位,一个码元中携带的bit位可以是1bit、2bit、4bit。。
串口传送数据的码元=1bit
串口的波特率=比特率
115200 9600
串行通信(串口):一个只能发送一个bit位数据
并行通信(并口):一个可以发送多个Bit 位,传输效率较高
同步通信:是指数据传送是以数据块(一组字符)为单位,字符和字符之间、字符
内部的位与位之间是完全同步的(字符间同步、字符内部同步 有同步时钟)
异步通信:是指数据传送是以字符为单位,字符和字符之间是完全异步的,字符内部的位与位之间是完全同步的(字符间异步、字符内部同步)。
串口通信就是一种异步通信方式,也称为起止异步同步式
目标:通过串口实现字符回显

配置步骤

1.在外围板上找到串口位置,以及所对应的芯片信号接口
在这里插入图片描述

在这里插入图片描述

2.打开和新版原理图检索UART_AUDIO_TXD
在这里插入图片描述
3.查芯片手册

配置操作

1.配置管脚信息(初始化UART)
(1)GPA1_0、GPA1_1管脚为为uart模式
在这里插入图片描述
在这里插入图片描述
(2)配置UART收发数据的格式(奇偶位、停止位、数据位)
在这里插入图片描述
(3)配置UART收发数据的波特率 115200
整数位
小数位

波特率因子计算
公式:
DIV_VAL = (SCLK_UART/(bps *16)) - 1
说明:
bps:是所期望的波特率
SCLK_UART:提供给串口的时钟频率Hz
DIV_VAL:整数放在UBRDIVn,小数放在UFRACVALn,需要先将小数除以16(相当于乘以16)

问:这里使用的fs4412的串口,系统提供的时钟周期是100MHz,期望波特率为115200
解:
由,DIV_VAL = (SCLK_UART/(bps *16)) - 1
得,DIV_VAL = (100 000 000hz/(115200 *16)) - 1
= 53.25
UBRDIVn=53=0x25
UFRACVALn=0.25*16=4=0x4
(4)配置UART收发数据的方式(中断或者轮询)
在这里插入图片描述
在这里插入图片描述

2.配置数据的收发所需要配置的东西
(1)收发位置
接收寄存器
在这里插入图片描述
发送寄存器
在这里插入图片描述

(2)收发时间
在这里插入图片描述

在这里插入图片描述
发送buf不为空进行发送,接收buf为空进行接收

3.编写程序进行调试
(1)uart的初始化函数
(2)数据的发送函数
(3)数据的接收函数

中断

定时器

工作原理

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小囧豆豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值