01:2440----点灯大师

目录

一:点亮一个LED

1:原理图

2:寄存器

3:2440的框架和启动过程

A:框架

B:启动过程

4:代码

5:ARM知识补充

6:c语言和汇编的应用

A:代码

B:分析汇编语言

C:内存空间

7:内部机制

二:点亮2个灯

三:流水灯

四:按键控制LED

1:原理图

2:寄存器配置

3:代码


一:点亮一个LED

1:原理图

当LED输入低电平时出现电压差, LED被点亮 (n的意思是低电平有效)

LED1 LED2 LED4分别接在 GPF4,5,6的IO口上

2:寄存器

配置GPFCON寄存器的[9:8]位为0b01-----输出模式 ; GPFCON--设置串口的模式

GPFDAT寄存器: 当该端口配置为输入端口时,对应的位为引脚状态。当端口配置为输出端口时,引脚状态与对应的位相同。当端口配置为功能引脚时,将读取未定义值。

GPF4对应GPFDAT寄存器的第4位, GPF[4]----0低电平/1高电平

GPFDAT寄存器--设置串口具体输出的内容

3:2440的框架和启动过程

A:框架

注意: CPU----里面有许多寄存器(R0~R15) ; 在CPU里面的寄存器是可以直接访问的.

GPIO控制器----里面有各种引脚,当然也包括我们今天使用的GPF4引脚; GPIO控制器里面也有寄存器(GPFCON, GPCDAT),不过这里面的寄存器需要地址访问, 不能向CPU里面的寄存器直接访问. 在芯片手册中有寄存器的地址.

B:启动过程

大多数的ARM芯片都是从0地址启动的, 当然这也包括我们讲述的2440

NOR启动 : NOR Flash基地址为0 , 片内RAM的地址为0x4000 0000

CPU读取出NOR第一个指令(前4个字节),执行

CPU继续在读取出其他的指令在执行; 一边读取一边执行

Nand启动 : 片内4KARM基地址为0, NOR启动不可访问

2440硬件把Nand的前4K内容复制到片内RAM上, 然后CPU从0地址取出第条指令执行

4:代码

cpp 复制代码
/*
*点亮一个LED
*/
.text
.global _start

_start:
/* 配置GPFCON(0X56000050)寄存器的[9:8]位为01--输出模式*/
	ldr r1,=0X56000050
	ldr r0,=0x100
	str r0,[r1]
/*
*配置GPFDAT寄存器为低电平(0x56000054)--输出低电平
*/
	ldr r1,=0x56000054
	ldr r0,=0
	str r0,[r1]
	/*死循环*/
halt:
	b halt
	

我们采用的是交叉编译的方法---使用window书写汇编代码-----将汇编代码传给虚拟机-----在虚拟机下将传来的汇编代码编译为bin文件-----在将bin文件传给window-------window烧写bin文件给Linux开发板; 我们使用的是GPIO控制器里面的寄存器所以必须使用地址进行访问

5:ARM知识补充

程序计数器 R15: 寄存器 R15 保存程序计数器(PC),它总是用于特殊的用途。它经常可用于通用寄存器RO~R14 所使用的位置(即在指令编码中 R15 与 RO~R14 的地位一样,只是指令执行的结果不同),因此,可以认为它是一个通用寄存器。但是对于它的使用还有许多与指令相关的限制或特殊情况。这些将在具体的指令描述中介绍。通常,如果 R15 使用的方式超出了这些限制,那么指令将是不可预测的。

当指令对 R15 的读取没有超过任何对 R15 使用的限制时,读取的值是指令的地址加上 8个字节。由于 ARM 指令总是以字为单位,结果的 Bit[1:0]总是为 0。这种读取 PC 的方式主要用于对附近的指令和数据进行快速、与位置无关的寻址,包括程序中与位置无关的转移。

当使用 STR或 STM 指令保存 R15 时,出现了上述规则的一个例外。这些指令可将指令地址加 8字节保存(与其它指令读取 R15 一样)或将指令自身地址加 12 字节(将来还可能出现别的数据)。偏移量 8 还是 12(或是其它数值)取决于 ARM 的实现(也就是说,与芯片有关)。对于某个具体的芯片,它是个常量。这样使用 STR 和 STM 指令是不可移植的。

由于这个例外,最好避免使用 STR 和 STM 指令来保存 R15。如果很难做到,那么应当在程序中使用合适的指令序列来确定当前使用的芯片所使用的偏移量

在2440中R15(pc)的偏移量为8, 注意取决于他对数据的读取方式

x的地址=x的地址+8

当他在读取地址A指令的时候

已经在对地址A+4的指令进行译码

已经在读取地址A+8的指令

分析反汇编的代码:

cpp 复制代码
led_on.elf:     file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
   0:	e59f1014 	ldr	r1, [pc, #20]	; 1c <halt+0x4>
   4:	e3a00c01 	mov	r0, #256	; 0x100
   8:	e5810000 	str	r0, [r1]
   c:	e59f100c 	ldr	r1, [pc, #12]	; 20 <halt+0x8>
  10:	e3a00000 	mov	r0, #0
  14:	e5810000 	str	r0, [r1]

00000018 <halt>:
  18:	eafffffe 	b	18 <halt>
  1c:	56000050 	undefined instruction 0x56000050
  20:	56000054 	undefined instruction 0x56000054

由于我们使用的是伪指令; 他是不存在的指令,最会被拆分真正的几条ARM指令;

上面的汇编代码都是由伪指令拆分而来的

优点 : 他可以表示任意值;

ARM指令===>32位,但是如果使用MOV的话并不能表示32位, 因为MOV32位中的一些位是用来表示他自己的,剩下的不够32位, 剩下的不够32位也只能表示一些立即数

cpp 复制代码
ldr r1,=0X56000050=====>伪指令

6:c语言和汇编的应用

A:代码

我们需要写一个汇编代码, 给main函数设置内存, 调用main函数

cpp 复制代码
int main()
{
	unsigned int *pGPFCON = (unsigned int *)0x56000050;
	unsigned int *pGPFDAT = (unsigned int *)0x56000054;

	/* 配置GPF4为输出引脚 */
	*pGPFCON = 0x100;
	
	/* 设置GPF4输出0 */
	*pGPFDAT = 0;

	return 0;
}
cpp 复制代码
.text
.global _start

_start:

	/* 设置内存: sp 栈 */
	ldr sp, =4096  /* nand启动 */
//	ldr sp, =0x40000000+4096  /* nor启动 */

	/* 调用main */
	bl main

halt:
	b halt
	
cpp 复制代码
all:
	arm-linux-gcc -c -o led.o led.c
	arm-linux-gcc -c -o start.o start.S
	arm-linux-ld -Ttext 0 start.o led.o -o led.elf
	arm-linux-objcopy -O binary -S led.elf led.bin
	arm-linux-objdump -D led.elf > led.dis
clean:
	rm *.bin *.o *.elf *.dis
	

我们使用makefile来编译, 避免重复多次的编译

可以看到x.ids文件中的地址和给板子烧录的bin文件地址一致;

B:分析汇编语言

r0~r3寄存器负责----调用者和被调用者的传递参数的问题;

r4~r11寄存器在函数中,可能被使用, 所以在人口中保存他们, 在出口中恢复他们;

cpp 复制代码
led.elf:     file format elf32-littlearm

Disassembly of section .text:

00000000 <_start>:
   0:	e3a0da01 	mov	sp, #4096	; 0x1000
   4:	eb000000 	bl	c <main>

00000008 <halt>:
   8:	eafffffe 	b	8 <halt>

0000000c <main>:
   c:	e1a0c00d 	mov	ip, sp
  10:	e92dd800 	stmdb	sp!, {fp, ip, lr, pc}
  14:	e24cb004 	sub	fp, ip, #4	; 0x4
  18:	e24dd008 	sub	sp, sp, #8	; 0x8
  1c:	e3a03456 	mov	r3, #1442840576	; 0x56000000
  20:	e2833050 	add	r3, r3, #80	; 0x50
  24:	e50b3010 	str	r3, [fp, #-16]
  28:	e3a03456 	mov	r3, #1442840576	; 0x56000000
  2c:	e2833054 	add	r3, r3, #84	; 0x54
  30:	e50b3014 	str	r3, [fp, #-20]
  34:	e51b2010 	ldr	r2, [fp, #-16]
  38:	e3a03c01 	mov	r3, #256	; 0x100
  3c:	e5823000 	str	r3, [r2]
  40:	e51b2014 	ldr	r2, [fp, #-20]
  44:	e3a03000 	mov	r3, #0	; 0x0
  48:	e5823000 	str	r3, [r2]
  4c:	e3a03000 	mov	r3, #0	; 0x0
  50:	e1a00003 	mov	r0, r3
  54:	e24bd00c 	sub	sp, fp, #12	; 0xc
  58:	e89da800 	ldmia	sp, {fp, sp, pc}
Disassembly of section .comment:

00000000 <.comment>:
   0:	43434700 	cmpmi	r3, #0	; 0x0
   4:	4728203a 	undefined
   8:	2029554e 	eorcs	r5, r9, lr, asr #10
   c:	2e342e33 	mrccs	14, 1, r2, cr4, cr3, {1}
  10:	Address 0x10 is out of bounds.

C:内存空间

内存空间被分为三个部分:代码段(text segment,即程序代码)、数据段(data segment,即变量)和栈段(stack segment)。数据段从下往上增长,而栈从上向下增长图。在这两者之间是空闲的地址空间。栈的增长是随着程序的执行自动进行的,而数据的扩展则需要通过brk 系统调用来显式地完成,brk有一个参数来指定数据段的结束地址,它可比当前值大(表示扩展数据段 ),或是比当前值小(表示缩小数据段 )。当然,这个参数必须小于指针,否则栈和数据段将会重叠,这是不允许的。

7:内部机制

二:点亮2个灯

上面我们实现了被调用者给调用者传递参数;

我们这里学习---调用者给被调用者传递参数

cpp 复制代码
int len_on(int num)
{
	/*设置寄存器 点亮LED2*/
	unsigned int* GPFCON = 0x56000050;
	unsigned int* GPFDAT = 0x56000054;
	if (num == 4)
	{
		/*设置输出模式*/
		*GPFCON = 0x100;
	}
	if (num == 5)
	{
		/*设置输出模式*/
		*GPFCON = 0x400;
	}
	/*输出低电平*/
	
	*GPFDAT = 0;
	return 0;
}
void Delay(int n)
{
	while (n--);
}
cpp 复制代码
/*
*点亮一个LED
*/
.text
.global _start

_start:
 /*设置内存: sp栈*/
	 ldr sp,=4096 /*nand启动*/
	 ldr sp,=0x40000000+4096/*nor启动*/

	 mov r0 ,#4
	 bl len_on

	 ldr r0 ,=10000
	 bl Delay

	 mov r0 ,#5
	 bl len_on

halt:
	b halt
cpp 复制代码
led.elf:     file format elf32-littlearm


Disassembly of section .text:

00000000 <_start>:
   0:	e3a0da01 	mov	sp, #4096	; 0x1000
   4:	e59fd018 	ldr	sp, [pc, #24]	; 24 <halt+0x4>
   8:	e3a00004 	mov	r0, #4
   c:	eb000006 	bl	2c <len_on>
  10:	e59f0010 	ldr	r0, [pc, #16]	; 28 <halt+0x8>
  14:	eb000022 	bl	a4 <Delay>
  18:	e3a00005 	mov	r0, #5
  1c:	eb000002 	bl	2c <len_on>

00000020 <halt>:
  20:	eafffffe 	b	20 <halt>
  24:	40001000 	andmi	r1, r0, r0
  28:	00002710 	andeq	r2, r0, r0, lsl r7

0000002c <len_on>:
  2c:	e52db004 	push	{fp}		; (str fp, [sp, #-4]!)
  30:	e28db000 	add	fp, sp, #0
  34:	e24dd014 	sub	sp, sp, #20
  38:	e50b0010 	str	r0, [fp, #-16]
  3c:	e59f3058 	ldr	r3, [pc, #88]	; 9c <len_on+0x70>
  40:	e50b300c 	str	r3, [fp, #-12]
  44:	e59f3054 	ldr	r3, [pc, #84]	; a0 <len_on+0x74>
  48:	e50b3008 	str	r3, [fp, #-8]
  4c:	e51b3010 	ldr	r3, [fp, #-16]
  50:	e3530004 	cmp	r3, #4
  54:	1a000002 	bne	64 <len_on+0x38>
  58:	e51b300c 	ldr	r3, [fp, #-12]
  5c:	e3a02c01 	mov	r2, #256	; 0x100
  60:	e5832000 	str	r2, [r3]
  64:	e51b3010 	ldr	r3, [fp, #-16]
  68:	e3530005 	cmp	r3, #5
  6c:	1a000002 	bne	7c <len_on+0x50>
  70:	e51b300c 	ldr	r3, [fp, #-12]
  74:	e3a02b01 	mov	r2, #1024	; 0x400
  78:	e5832000 	str	r2, [r3]
  7c:	e51b3008 	ldr	r3, [fp, #-8]
  80:	e3a02000 	mov	r2, #0
  84:	e5832000 	str	r2, [r3]
  88:	e3a03000 	mov	r3, #0
  8c:	e1a00003 	mov	r0, r3
  90:	e28bd000 	add	sp, fp, #0
  94:	e8bd0800 	pop	{fp}
  98:	e12fff1e 	bx	lr
  9c:	56000050 	undefined instruction 0x56000050
  a0:	56000054 	undefined instruction 0x56000054

000000a4 <Delay>:
  a4:	e52db004 	push	{fp}		; (str fp, [sp, #-4]!)
  a8:	e28db000 	add	fp, sp, #0
  ac:	e24dd00c 	sub	sp, sp, #12
  b0:	e50b0008 	str	r0, [fp, #-8]
  b4:	e51b3008 	ldr	r3, [fp, #-8]
  b8:	e3530000 	cmp	r3, #0
  bc:	03a03000 	moveq	r3, #0
  c0:	13a03001 	movne	r3, #1
  c4:	e20330ff 	and	r3, r3, #255	; 0xff
  c8:	e51b2008 	ldr	r2, [fp, #-8]
  cc:	e2422001 	sub	r2, r2, #1
  d0:	e50b2008 	str	r2, [fp, #-8]
  d4:	e3530000 	cmp	r3, #0
  d8:	1afffff5 	bne	b4 <Delay+0x10>
  dc:	e28bd000 	add	sp, fp, #0
  e0:	e8bd0800 	pop	{fp}
  e4:	e12fff1e 	bx	lr

Disassembly of section .ARM.attributes:

00000000 <.ARM.attributes>:
   0:	00002541 	andeq	r2, r0, r1, asr #10
   4:	61656100 	cmnvs	r5, r0, lsl #2
   8:	01006962 	tsteq	r0, r2, ror #18
   c:	0000001b 	andeq	r0, r0, fp, lsl r0
  10:	00543405 	subseq	r3, r4, r5, lsl #8
  14:	01080206 	tsteq	r8, r6, lsl #4
  18:	04120109 	ldreq	r0, [r2], #-265	; 0x109
  1c:	01150114 	tsteq	r5, r4, lsl r1
  20:	01180317 	tsteq	r8, r7, lsl r3
  24:	Address 0x00000024 is out of bounds.


Disassembly of section .comment:

00000000 <.comment>:
   0:	3a434347 	bcc	10d0d24 <__bss_end__+0x10c8c3c>
   4:	74632820 	strbtvc	r2, [r3], #-2080	; 0x820
   8:	312d676e 	teqcc	sp, lr, ror #14
   c:	312e362e 	teqcc	lr, lr, lsr #12
  10:	2e342029 	cdpcs	0, 3, cr2, cr4, cr9, {1}
  14:	00332e34 	eorseq	r2, r3, r4, lsr lr

对于2440他的内部同样存在看门狗, 我们在程序中没有对看门狗进行操作; 所以他在一段时间就会复位.

三:流水灯

cpp 复制代码
void Delay(int n)
{
	while (n--);
}

int main(void)
{
	int i = 4;
	/*设置寄存器 点亮LED2*/
	volatile unsigned int* GPFCON = (volatile unsigned int*)0x56000050;
	volatile unsigned int* GPFDAT = (volatile unsigned int*)0x56000054;
	/*设置GPF4/5/6位位输出模式*/
	*GPFCON &= ~((3 << 8) | (3 << 10) | (3 << 12)); //3对应0b11 清位
	*GPFCON |= ((1 << 8) | (1 << 10) | (1 << 12)); //置1-设置位输出模式

	/*GPFDAT寄存器配置 */
	*GPFDAT &= ~((1 << 4) | (1 << 5) | (1 << 6)); //清位
	*GPFDAT |= ((1 << 4) | (1 << 5) | (1 << 6)); //把GPFDAT寄存器的4 5 6 位置1--灭灯
	/*led4 0x100 led5 0x400 */
	while (1)
	{	
		if (i == 7)i = 4;
		*GPFDAT &= ~(1 << i);
		Delay(10000);
		*GPFDAT |= (1 << i);
		Delay(10000);
		i++;		
	}
	return 0;
}
Matlab 复制代码
/*
*点亮一个LED
*/
.text
.global _start

_start:
/* 关闭看门狗 */
	ldr r0, =0x53000000
	ldr r1, =0
	str r1, [r0]

 /*设置内存: sp栈* 我们判断是nor启动还是nand启动/

	mov r1, #0
	ldr r0, [r1] /* 读出原来的值备份 */
	str r1, [r1] /* 0->[0] */ 
	ldr r2, [r1] /* r2=[0] */
	cmp r1, r2   /* r1==r2? 如果相等表示是NAND启动 */
	ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
	moveq sp, #4096  /* nand启动 */
	streq r0, [r1]   /* 恢复原来的值 */
	

	
	 bl main


halt:
	b halt
	

1:看门狗问题的解决

看门狗定时器控制(WTCON)寄存器WTCON寄存器允许用户启用/禁用看门狗定时器,选择来自4个不同源的时钟信号,启用/禁用中断,启用/禁用看门狗定时器输出。看门狗定时器用于S3C2440A上电后功能异常重启时恢复;如果不希望控制器重启,则关闭看门狗定时器

我们可以看到当 Reset enable/disable (重新启用/禁用) 设置WTCON寄存器位0时, 2400就会关闭我们的寄存器
2:如何区分是nar启动还是nand启动

nor启动 : 可以向内存一样读, 但是不能向内存一样写; (如果一定要写的话需要发送一定格式的数据才可以写)

nand : 可读取写

方法: 分辨是nor/nand启动

* 写0到0地址, 再读出来

* 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动 (nand-- 可读取写)

* 否则就是nor启动 (只能读)

四:按键控制LED

1:原理图

平时他为高电平, 当按键按下他为低电平;

2:寄存器配置

GPG3

GPF

按键位输入模式

3:代码

cpp 复制代码
void Delay(int n)
{
	while (n--);
}

#define GPFCON (*((volatile unsigned int*)0x56000050))
#define GPFDAT (*((volatile unsigned int*)0x56000054))
#define GPGCON  (*((volatile unsigned int*)0x56000060))
#define GPGDAT (*((volatile unsigned int*)0x56000064))
int main(void)
{
	
	/*volatile unsigned int* GPFCON = (volatile unsigned int*)0x56000050;
	volatile unsigned int* GPFDAT = (volatile unsigned int*)0x56000054;
	volatile unsigned int* GPGCON = (volatile unsigned int*)0x56000060;
	volatile unsigned int* GPGDAT = (volatile unsigned int*)0x56000064;*/
	/*设置GPF4/5/6位p 位输出模式*/
	GPFCON &= ~((3 << 8) | (3 << 10) | (3 << 12)); //3对应0b11 清位
	GPFCON |= ((1 << 8) | (1 << 10) | (1 << 12)); //置1-设置位输出模式

	
	/*设置GPF0和GPF2按键位输入模式*/
	GPFCON &= ~((3 << 0) | (3 << 4));
	/*GPG3位输入模式*/
	GPGCON &= ~(3 << 6);


	while (1)
	{	
		if (GPFDAT & (1 << 0)) /* s2 --> gpf6 */
		{
			/* 松开 */
			GPFDAT |= (1 << 6);
		}
		else
		{
			/* 按下 */
			GPFDAT &= ~(1 << 6);
		}

		if (GPFDAT & (1 << 2)) /* s3 --> gpf5 */
		{
			/* 松开 */
			GPFDAT |= (1 << 5);
		}
		else
		{
			/* 按下 */
			GPFDAT &= ~(1 << 5);
		}

		if ((GPGDAT & (1 << 3))==0) /* s4 --> gpf4 */
		{
			/* 按下 */
			GPFDAT &= ~(1 << 4);
		}
		else
		{
			/* 松开 */
			GPFDAT |= (1 << 4);	
		}
	}
	return 0;
}
cpp 复制代码
/*
*点亮一个LED
*/
.text
.global _start

_start:
/* 配置GPFCON(0X56000050)寄存器的[9:8]位为01--输出模式*/
	ldr r1,=0X56000050
	ldr r0,=0x100
	str r0,[r1]
/*
*配置GPFDAT寄存器为低电平(0x56000054)--输出低电平
*/
	ldr r1,=0x56000054
	ldr r0,=0
	str r0,[r1]
	/*死循环*/
halt:
	b halt
	
		
相关推荐
森旺电子1 小时前
51单片机仿真摇号抽奖机源程序 12864液晶显示
单片机·嵌入式硬件·51单片机
不过四级不改名6773 小时前
蓝桥杯嵌入式备赛教程(1、led,2、lcd,3、key)
stm32·嵌入式硬件·蓝桥杯
小A1593 小时前
STM32完全学习——SPI接口的FLASH(DMA模式)
stm32·嵌入式硬件·学习
Rorsion3 小时前
各种电机原理介绍
单片机·嵌入式硬件
善 .6 小时前
单片机的内存是指RAM还是ROM
单片机·嵌入式硬件
超级码农ProMax6 小时前
STM32——“SPI Flash”
stm32·单片机·嵌入式硬件
Asa3197 小时前
stm32点灯Hal库
stm32·单片机·嵌入式硬件
end_SJ8 小时前
初学stm32 --- 外部中断
stm32·单片机·嵌入式硬件
gantengsheng9 小时前
基于51单片机和OLED12864的小游戏《贪吃蛇》
单片机·嵌入式硬件·游戏·51单片机
嵌入式小强工作室10 小时前
stm32 查找进硬件错误方法
stm32·单片机·嵌入式硬件