第3章 ARM GPIO
程序员文章站
2022-04-01 18:32:53
...
这里我们使用的开发板是4412,开发板4412上有4个LED灯,我们这里可以利用其来做流水灯实验。下面是4个LED的原理图:
3.1利用汇编编写程序
以GPX2为例,通过修改GPX2CON、GPX2DAT 的值来控制LED的亮灭
1、GPX2CON
其地址为0x11000C40:
GPX2CON可以控制8个IO口,LED2是由GPX2_7控制的,所以我们只要设置GPX2CON[7]即可
可以看到该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
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 aaa@qq.com $^ -Tmap.lds -nostdlib $(LDFALGS)
$(CROSS_COMPILE)objdump -d aaa@qq.com > aaa@qq.com.s
$(CROSS_COMPILE)objcopy -O binary aaa@qq.com aaa@qq.com.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闪烁了。