《嵌入式 - ARM》第3章 ARM GPIO

这里我们使用的开发板是4412,开发板4412上有4个LED灯,我们这里可以利用其来做流水灯实验。下面是4个LED的原理图:

在这里插入图片描述

图1

3.1利用汇编编写程序

以GPX2为例,通过修改GPX2CON、GPX2DAT 的值来控制LED的亮灭

1、GPX2CON
其地址为0x11000C40:
GPX2CON可以控制8个IO口,LED2是由GPX2_7控制的,所以我们只要设置GPX2CON[7]即可

表1

在这里插入图片描述

可以看到该4个bit 为0x1 时IO口为输出功能,则我们可以这样设置:

LDR R0,=0x11000C40  
LDR R1,[R0]  
BIC R1,R1,#0xf0000000  
ORR R1,R1,#0x10000000  
STR R1,[R0]  

现将[31:28]位清零再置一,则该端口被设置为输出引脚,而至于输出高电平还是低电平,则由GPX2DAT来控制。
2、GPX2DAT
其地址为0x110000C44

表2

在这里插入图片描述

GPX2DAT低8位有效,每1个bit控制一个端口输出电平的高低,该位置1,则输出高电平,置0,则输出低电平,为点亮LED,我们可以这样设置:

LDR R0,=0x11000C44  
LDR R1,[R0]  
ORR R1,R1,#0x80  
STR R1,[R0]  

第[7]位置1即可,此时LED被点亮;
同样,该位置0,则LED熄灭:

LDR R1,[R0]  
BIC R1,R1,#0x01  
STR R1,[R0]  

下面是一个完整的汇编程序,实现LED灯的闪烁(这里以LED3为例):

.globl _start  
.arm  
_start:  
    LDR R0,=0x11000C20  
    LDR R1,[R0]  
    BIC R1,R1,#0x0000000f  
    ORR R1,R1,#0x00000001  
    STR R1,[R0]  
loop:  
    LDR R0,=0x11000C24  
    LDR R1,[R0]  
    ORR R1,R1,#0x01  
    STR R1,[R0]  
    BL delay  
    LDR R1,[R0]  
    BIC R1,R1,#0x01  
    STR R1,[R0]  
    BL delay  
    B loop  
delay:  
    LDR R2,=0xfffffff  
loop1:  
    SUB R2,R2,#0x1  
    CMP R2,#0x0  
    BNE loop1  
    MOV PC,LR  
    .end  

其实这里我们可以看到,汇编程序的缺点,就是非常繁琐,而且辨识度差,这段代码,我们看其中一段,根本看不出其实现了什么功能,ARM 裸机程序,我们同样可以用C来编写。

3.2用C 实现流水灯

ARM裸机开发中最重要的就是寄存器的控制,我们如何配置寄存器呢?这里以GPX2为例,我们在头文件里定义下面这个结构体:

/* GPX2 */  
typedef struct {  
    unsigned int CON;  
    unsigned int DAT;  
    unsigned int PUD;  
    unsigned int DRV;  
}gpx2;  
#define GPX2 (* (volatile gpx2 *)0x11000C40 )  

这里将GPX2所用到的寄存器放到一个结构体内,我们看这句

#define GPX2 (* (volatile gpx2 *)0x11000C40 )

该宏定义是什么意思呢?将0x11000C40 强转成 gpx2 * 类型的地址,并取出该地址里面的值。 则我们可以直接向GPX2.CON里写入数据,便可控制该寄存器

GPX2.CON = GPX2.CON & (~(0xf0000000)) | (0x10000000)  

等价于

LDR R0,=0x11000C40  
LDR R1,[R0]  
BIC R1,R1,#0xf0000000  
ORR R1,R1,#0x10000000  
STR R1,[R0]  

可以看出大大加快了我们的开发效率。
下面是开发实例:
led.c

#include "exynos_4412.h"  
#include "led.h"  
void led_init(void)  
{  
    GPX2.CON = GPX2.CON & (~(0xf0000000)) | 0x10000000;  
    GPX1.CON = GPX1.CON & (~(0x0000000f)) | 0x00000001;  
    GPF3.CON = GPF3.CON & (~(0x000f0000)) | 0x00010000;  
    GPF3.CON = GPF3.CON & (~(0x00f00000)) | 0x00100000;  
}  
void led_on(int n)  
{  
    switch(n)  
    {  
        case 0:  
            GPX2.DAT = GPX2.DAT|0x80;  
            break;  
        case 1:  
            GPX1.DAT = GPX1.DAT|0x01;  
            break;  
        case 2:  
            GPF3.DAT = GPF3.DAT|0x10;  
            break;  
        case 3:  
            GPF3.DAT = GPF3.DAT|0x20;  
            break;  
    }  
}  
void led_off(int n)  
{  
    switch(n)  
    {  
        case 0:  
            GPX2.DAT = GPX2.DAT&(~(0x80));  
            break;  
        case 1:  
            GPX1.DAT = GPX1.DAT&(~(0x01));  
            break;  
        case 2:  
            GPF3.DAT = GPF3.DAT&(~(0x10));  
            break;  
        case 3:  
            GPF3.DAT = GPF3.DAT&(~(0x20));  
            break;  
    }  
}  
void  delay_ms(unsigned int num)  
{  
    int i,j;  
    for(i=num; i>0;i--)  
    for(j=1000;j>0;j--)  
        ;  
}  

main.c

#include "exynos_4412.h"  
#include "led.h"  
  
int main (void)  
{  
    int i = 0;  
    led_init ();  
while (1) 
{  
        led_on(i%4);  
        led_off((i-1+4)%4);  
        i++;  
        delay_ms(500);  
    }  
   return 0;  
}  

exynos_4412.h

#ifndef __EXYNOS_4412_H_
#define _EXYNOS_4412_H_

// Exynos-4412 Controller
struct exynos4412_gpx1 {
	unsigned int CON;
	unsigned int DAT;
	unsigned int PUD;
	unsigned int DRV;
};
#define GPX1_ADDR ((volatile struct exynos4412_gpx1 *)0x11000C20)
#define GPX1 (*GPX1_ADDR)

//方式一
struct exynos4412_gpx2 {
	unsigned int CON;
	unsigned int DAT;
	unsigned int PUD;
	unsigned int DRV;
};
#define GPX2_ADDR ((volatile struct exynos4412_gpx2 *)0x11000C40)
#define GPX2 (*GPX2_ADDR)

//方式二
typedef struct {  
    unsigned int CON;  
    unsigned int DAT;  
    unsigned int PUD;  
    unsigned int DRV;  
}gpx2;  
#define GPX2 (* (volatile gpx2 *)0x11000C40 )  

struct exynos4412_gpf3 {
	unsigned int CON;
	unsigned int DAT;
	unsigned int PUD;
	unsigned int DRV;
};
#define GPF3_ADDR ((volatile struct exynos4412_gpf3 *)0x114001E0)
#define GPF3 (*GPF3_ADDR)

#endif

led.h

#ifndef __LED_H_
#define _LED_H_

void led_init(void);
void led_on(int n);
void led_off(int n);
void delay_ms(unsigned int num) ;

#endif

Start.S

	.arm
	.globl	_start
_start:
	B		reset
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP

reset:
	LDR		SP, =stack_top

	BL		main
	
__die:
	B		__die

	/** void __delay(void)*/
	.global __delay
__delay:
	PUSH	{R2, LR}

	MOV		R2, #0x20000000
loop1:
	SUBS	R2, R2, #0x1
	BNE		loop1

	POP		{R2, LR}
	MOV		PC, LR

	.data
	.space 8192
stack_top:

	.end

Makefile

TARGET	:= my_a9
all: $(TARGET)

CROSS_COMPILE := /opt/gcc-4.6.4/bin/arm-none-linux-gnueabi-
CC		:= $(CROSS_COMPILE)gcc

CFLAGS	+= -march=armv7-a -mtune=cortex-a9
ASFLAGS	+= -march=armv7-a -mtune=cortex-a9
LDFALGS	+= -march=armv7-a -mtune=cortex-a9

OBJS	:= $(patsubst %.c, %.o, $(wildcard *.c))
OBJS	+= $(patsubst %.S, %.o, $(wildcard *.S))

$(TARGET): $(OBJS)
	$(CC) -o $@ $^ -Tmap.lds -nostdlib $(LDFALGS)
	$(CROSS_COMPILE)objdump -d $@ > $@.s
	$(CROSS_COMPILE)objcopy -O binary $@ $@.bin

%.o: %.S
	$(CC) -c $^ -nostdinc $(CFLAGS)

%.o: %.c
	$(CC) -c $^ -nostdinc $(ASFLAGS)


install: $(TARGET)
	#cp $(TARGET).bin /tftpboot

clean:
	rm -f $(TARGET)
	rm -f $(OBJS)
	rm -f $(TARGET).s
	rm -f $(TARGET).bin
将生成的A9.bin 文件烧入开发板 0x40008000处,使用命令 go 0x40008000,则可看到开发板上的LED闪烁了。

本章参考代码

点击进入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Bruceoxl

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

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

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

打赏作者

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

抵扣说明:

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

余额充值