GPIO
GPIO。英文全称General-Purpose IO ports,是常见IO口。
在嵌入式系统中经常有数量众多,可是结构却比較简单的外部设备/电路,对这些设备/电路有的须要CPU为之提供控制手段。有的则须要被CPU用作输入信号。并且,很多这种设备/电路仅仅要求一位,即仅仅要有开/关两种状态就够了,比方灯亮与灭。对这些设备/电路的控制,使用传统的串行口或并行口都不合适。所以在微控制器芯片上一般都会提供一个“通用可编程IO接口”。即GPIO。
接口至少有两个寄存器。即“通用IO控制寄存器”与“通用IO数据寄存器”。
数据寄存器的各位都直接引到芯片外部。而对数据寄存器中每一位的作用。即每一位的信号流通方向时输入还是输出,则能够通过控制寄存器中相应位独立的加以设置。这样,有无GPIO接口也就成为微控制器差别于微处理器的一个特征。
所以,在了解共性的基础上去了解个性。
假设系统结构支持独立的IO地址空间,而且是port映射。就必须使用汇编语言完毕实际对设备的控制,由于C语言并没有提供真正的“port”的概念。假设是内存映射,那就方便的多了。
举个样例,比方像寄存器A(地址假定为0x48000000)写入数据0x01,那么就能够这样设置了。
|
当中volatilekeyword是嵌入式系统开发的一个重要特点。这个就不再这里总结了。
上述表达式拆开来分析,首先(volatile unsigned long *)0x48000000的意思是把0x48000000强制转换成volatile unsigned long类型的指针。暂记为p。那么就是#define A *p,即A为P指针指向位置的内容了。这里就是通过内存寻址訪问到寄存器A,能够读/写操作。
#define CTL_REG_WRITE(addr, val) (*(volatile unsigned long *)(addr)=(var))
|
|
当然。只做一个最简单的led灯实验,倒是省去了非常多步骤。
当某位设为0,对应引脚为输出引脚,由于Port A控制的23个pin仅仅能进行输出。所以也就没有输入的控制。此时往GPADAT对应的位中写0/1,能够让引脚输出低电平/高电平;当某位设为1。则对应引脚为地址线,或者用于地址控制,此时GPADAT没实用了。一般而言,GPACON通常全设为1,以便訪问外部存储器件。PORT B-H在寄存器操作方面全然同样。
GPxCON中每两位控制一根引脚:00表示输入,01表示输出。10表示特殊功能,11保留。GPxDAT用于读/写引脚:当引脚设为输入时,读此寄存器可知对应引脚状态是高/低;当引脚设为输出时,写此寄存器对应位能够使对应引脚输出低电平或高电平。GPxUP:某位设为0,对应引脚无内部上拉。为1,对应引脚使用内部上拉。
关于特殊功能,那就得结合特殊外设来进行设置了。
当IO引脚输出为低电平的时候,LED灯被点亮。仅仅须要关注三个寄存器GPFCON、GPFDAT、GPFUP。
因为硬件电路的关系,设置上拉电阻与否并不影响LED灯的点亮,所以GPFUP能够不必考虑。
剩下的就是GPFCON和GPFDAT。
版本号1是採用ARM汇编语言完毕,版本号2採用C语言完毕。版本号1练习了宏定义函数,子程序等。相对而言比較简单。
版本号2重点练习了软件架构。尽管短小。可是仍然模仿了vivi的软件架构。仅仅是没有必要写复杂的Makefile。所以仅仅写了比較简单的Makefile。在编写过程中,发现自己对ld。objcopy,和一些细节没有非常好的把握,经过查看资料,已经基本掌握。兴许工作须要就这些工具进行深入的学习,目标是可以熟练掌握。
.equ WTCON, 0x53000000
.equ GPFCON, 0x56000050
.equ oGPFDAT, 0x04
@ you should initial IO pins about leds in order to use this macro
.macro LED_ON led_value //注意 在这里定义了一段宏,相当于函数,在以下能够直接通过宏名+參数值来调用该宏
ldr r1, =/led_value
str r1, [r0, #oGPFDAT] //将r1中的值赋给0x5300000054寄存器中,也就是GPFDAT寄存器
bl delay //调用延时子程序
.endm
.global _start @ specified by GNU ld. Here is
@ ultimately 0x00000000,and you
@ can fine this in Makefile.
@ disable watch dog timer
@ otherwise mcu will reset at fixed interval, and
@ you will find led on and off in abnormal way.
mov r0, #WTCON
mov r1, #0x00
str r1, [r0]
@ please read datasheet //開始初始化pin相应的Port控制寄存器和上拉寄存器
ldr r0, =GPFCON
ldr r1, =0x5500 //为什么要将GPFCON寄存器设置为0x5500就要一位位的来分析GPFCON寄存器,由于4个LED灯与IO口的相应关系为:GPF[7:4]----LED[4:1]。我们知道在Port F中每两位来控制一个引脚,并且要将引脚设置为输出才干控制LED,所以GPF7也就是[15:14]=01,GPF6也就是[13:12]=01,同理GPF5也就是[11:10]=01,GPF4也就是[9:8]=01,所以GPFCON要被设置为0x5500
str r1, [r0]
@ led on when low voltage
1: //循环的依次点亮4个LED灯,也就是依次将以下的值设置给GPFDAT寄存器,通过以下的0xd0,0x70,0xe0,0xb0能够知道在GPFDAT寄存器中的[7:4]来控制LED的亮灭。
LED_ON 0xd0
LED_ON 0x70
LED_ON 0xe0
LED_ON 0xb0
b 1b
stop:
b stop
@ SUB Routine
@
delay:
mov r2, #0x10000
2:
subs r2, r2, #0x1
bne 2b
在这里,由于仅仅是涉及到.s的编译不採用隐含规则,所以没有把Rules.make单独拿出。其实能够单独写为Rules.make,然后在Makefile后增加include Rules.make就能够了。
由于子程序调用时,要进行入栈出栈处理。又由于从nand flash启动。而nand flash在S3C2410下的特点规定堆栈不能超过4K。
#define bGPIO(p, o) CTL_REG_READ(GPIO_CTL_BASE + (p) + (o))
#define oGPIO_CON 0x0
#define oGPIO_DAT 0x4
#define oGPIO_UP 0x8
#define oGPIO_F 0x50 //GPFCON寄存器相相应与GPADAT的偏移
#define GPFCON bGPIO(oGPIO_F, oGPIO_CON)
#define GPFDAT bGPIO(oGPIO_F, oGPIO_DAT)
#define GPFUP bGPIO(oGPIO_F, oGPIO_UP)
{
unsigned char i;
unsigned long led[4] = {0xd0, 0x70, 0xe0, 0xb0};
for (i=0; i<4; i++) {
GPFDAT = led[i];
delay(DELAYTIME);
}
}
}
void delay(unsigned long n)
{
while (n--) {
;
}
}