做嵌入式底层的东西,初始化硬件设备应该是必须的。一个个内核调用函数足以让人头晕!
最近在看Linux嵌入式(s3c2410)方面的内容,遇到了这样一个函数:set_gpio_ctrl(); 它从字面上看来是对gpio口的设置扩展。GPIO是什么呢?它是通用 IO端口引脚,在s3c2410用户手册中有A,B,C,D,E,F,G,H7组共117个引脚。这些引脚除了可作为INPUT/OUTPUT外,大部份 还可以用作其它用,如外部中断,UART等。它们用的控制寄存器主要有GPACON-GPHCON,GPADAT-GPHDAT,GPBUP- GPHUP,还有一些不累赘了。
我在linux内核原代码的/include/asm-arm/arch-s3c2410/s3c2410.h 这头文件个找到set_gpio_ctrl()函数的定义,体质来讲是个宏定义。以下是我记录下的一段相关代码,我将依照这段代码来分析。想必刚开始看这 一段东西,一般人都会头痛,我也是。但这是必须通过的一道门槛,所以我们应该学会抓住重点分析。
#define GPCON(x) __REG2(0x56000000, (x) * 0x10) //(此为GPACON---GPHCON 的一种表示)
#define GPDAT(x) __REG2(0x56000004, (x) * 0x10) //(此为GPADAT--GPHDAT 的一种表示)
#define GPUP(x) __REG2(0x56000008, (x) * 0x10)
#define GPIO_OFS_SHIFT 0
#define GPIO_PORT_SHIFTT 8
#define GPIO_PULLUP_SHIFT 16
#define GPIO_MODE_SHIFT 24
#define GPIO_OFS_MASK 0x000000ff
#define GPIO_PORT_MASK 0x0000ff00
#define GPIO_PULLUP_MASK 0x00ff0000
#define GPIO_MODE_MASK 0xff000000
#define GPIO_MODE_IN (0 << GPIO_MODE_SHIFT)
#define GPIO_MODE_OUT (1 << GPIO_MODE_SHIFT)
#define GPIO_MODE_ALT0 (2 << GPIO_MODE_SHIFT)
#define GPIO_MODE_ALT1 (3 << GPIO_MODE_SHIFT)
#define GPIO_PULLUP_EN (0 << GPIO_PULLUP_SHIFT)
#define GPIO_PULLUP_DIS (1 << GPIO_PULLUP_SHIFT)
#define PORTA_OFS 0
#define PORTB_OFS 1
#define PORTC_OFS 2
#define PORTD_OFS 3
#define PORTE_OFS 4
#define PORTF_OFS 5
#define PORTG_OFS 6
#define PORTH_OFS 7
#define MAKE_GPIO_NUM(p, o) ((p << GPIO_PORT_SHIFTT) | (o << GPIO_OFS_SHIFT))
#define GRAB_MODE(x) (((x) & GPIO_MODE_MASK) >> GPIO_MODE_SHIFT)
#define GRAB_PULLUP(x) (((x) & GPIO_PULLUP_MASK) >> GPIO_PULLUP_SHIFT)
#define GRAB_PORT(x) (((x) & GPIO_PORT_MASK) >> GPIO_PORT_SHIFTT)
#define GRAB_OFS(x) (((x) & GPIO_OFS_MASK) >> GPIO_OFS_SHIFT)
#set_gpio_ctrl(GPIO_MODE_OUT|GPIO_F6) 设置相应GPIO位是输入还是输出
#define set_gpio_ctrl(x) /
({ GPCON(GRAB_PORT((x))) &= ~(0x3 << (GRAB_OFS((x))*2)); /
GPCON(GRAB_PORT(x)) |= (GRAB_MODE(x) << (GRAB_OFS((x))*2)); /
GPUP(GRAB_PORT((x))) &= ~(1 << GRAB_OFS((x))); /
GPUP(GRAB_PORT((x))) |= (GRAB_PULLUP((x)) << GRAB_OFS((x))); })
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
#define set_gpioA_mode(x) /
({ GPCON(GRAB_PORT((x))) &= ~(0x1 << GRAB_OFS((x))); /
GPCON(GRAB_PORT((x))) |= (GRAB_MODE((x)) << GRAB_OFS((x))); })
#define read_gpio_bit(x) ((GPDAT(GRAB_PORT((x))) & (1<<GRAB_OFS((x)))) >> GRAB_OFS((x)))
#define read_gpio_reg(x) (GPDAT(GRAB_PORT((x)))
#define write_gpio_bit(x, v) /
({ GPDAT(GRAB_PORT((x))) &= ~(0x1 << GRAB_OFS((x))); /
GPDAT(GRAB_PORT((x))) |= ((v) << GRAB_OFS((x))); })
#define write_gpio_reg(x, v) (GPDAT(GRAB_PORT((x))) = (v))
#define GPIO_A0 MAKE_GPIO_NUM(PORTA_OFS, 0)
#define GPIO_A1 MAKE_GPIO_NUM(PORTA_OFS, 1)
::::::::::::::::::::
#define GPIO_A21 MAKE_GPIO_NUM(PORTA_OFS, 21)
#define GPIO_A22 MAKE_GPIO_NUM(PORTA_OFS, 22)
#define GPIO_H0 MAKE_GPIO_NUM(PORTH_OFS, 0)
#define GPIO_H6 MAKE_GPIO_NUM(PORTH_OFS, 6)
::::::::::::::::::::
#define GPIO_H10 MAKE_GPIO_NUM(PORTH_OFS, 10)
#define GPIO_MODE_nXBACK GPIO_MODE_ALT0
:::::::::::::::::::::;;;
#define GPIO_MODE_LCD_PWRDN GPIO_MODE_ALT1
第一个要明白的是GPCON(x)。GPACON-GPHCON为端口控制寄存器。其起始地趾为0x56000000
#define GPCON(x) __REG2(0x56000000, (x) * 0x10) x的取值为0-7对应GPACON---GPHCON 的地址。
#define GPDAT(x) __REG2(0x56000004, (x) * 0x10) #(此为GPADAT--GPHDAT 的一种表示)
#define GPUP(x) __REG2(0x56000008, (x) * 0x10) 也是一样的道理。
第二个是 GPIO_OFS_SHIFT GPIO_PORT_SHIFT GPIO_PULLUP_SHIFT GPIO_MODE_SHIFT 。s3c2410.h中是这样定义的:
#define GPIO_OFS_SHIFT 0 #define GPIO_OFS_MASK 0x000000ff
#define GPIO_PORT_SHIFTT 8 相对应 #define GPIO_PORT_MASK 0x0000ff00
#define GPIO_PULLUP_SHIFT 16 #define GPIO_PULLUP_MASK 0x00ff0000
#define GPIO_MODE_SHIFT 24 #define GPIO_MODE_MASK 0xff000000
他 们的作用是什么呢,为什么要这样定义呢?我们都知道对硬件的初始化和操作有很大一部分是对其相对应的寄存器的操作。说白一点也就是对相应位置 0或1。s3c2410的寄存器是32位的,再来看一下set_gpio_ctrl(GPIO_MODE_OUT | GPIO_H6)中的参数:GPIO_MODE_OUT | GPIO_H6。它表示将端口组H的6端口设置成输出模式(MODE)。看一下参数命令的结构,这很重要,是突破点:这命令化成数值是32位的,高位为 GPIO_MODE_OUT, 低位为GPIO_H6。那么GPIO_MODE_OUT和GPIO_H6又是什么呢,再看几个定义:
#define GPIO_MODE_IN (0 << GPIO_MODE_SHIFT)
#define GPIO_MODE_OUT (1 << GPIO_MODE_SHIFT)
#define GPIO_MODE_ALT0 (2 << GPIO_MODE_SHIFT)
#define GPIO_MODE_ALT1 (3 << GPIO_MODE_SHIFT)
#define GPIO_PULLUP_EN (0 << GPIO_PULLUP_SHIFT)
#define GPIO_PULLUP_DIS (1 << GPIO_PULLUP_SHIFT)
###MODE_IN,MODE_OUT,MODE_ 是表示端口的作用是输入输出还是另作它用。
#为什么要用0,1来左移呢?s3c2410数据手册告诉我们00表示输入01表示输出
#GPIO_MODE_SHIFT=24即把00或01搬到第24位去: 1 0000 0000 0000 0000 0000 0000
##或 0 0000 0000 0000 0000 0000 0000(这是用32位中的位24-31来表示端口的作用)
##那么命令中的GPIO_H6:即哪个端口组的哪个端口又是如何表示的?看看下面的
#define GPIO_OFS_SHIFT 0
#define GPIO_PORT_SHIFTT 8
#define GPIO_PULLUP_SHIFT 16
#define PORTA_OFS 0 ##和#define GPIO_MODE_SHIFT 24 看看,我们大致可以猜到:(这是这是用32位中的位16-23来表示要设置某个端口的上拉功能)
#define PORTB_OFS 1 ##(这是用32位中的位8-15来表示要设置哪一个端口组)
#define PORTC_OFS 2 ###(这是用32位中的位0-7来表示要设置一个端口组的哪一个端口)
#define PORTD_OFS 3 #那么类似GPIO_H6是如何构造的?看下面的宏定义,我们展开:
#define PORTE_OFS 4 #GPIO_H6=MAKE_GPIO_NUM(7, 6)
#define PORTF_OFS 5 #= ( (7<<8) | (6<<0))= ( 111 0000 0000 | 110)=111 0000 110
#define PORTG_OFS 6#看出程序把端口组A-H用0-7表示放在8-15位
#define PORTH_OFS 7##端口组中具体端口则放在0-7位
#MAKE_GPIO_NUM(p, o)这个宏就是构造这样的
#define MAKE_GPIO_NUM(p, o) ((p << GPIO_PORT_SHIFTT) | (o << GPIO_OFS_SHIFT))
#define GPIO_H6 MAKE_GPIO_NUM(PORTH_OFS, 6)
前面分析了参数命令的构造,那么如何解释命令呢?
#define GRAB_MODE(x) (((x) & GPIO_MODE_MASK) >> GPIO_MODE_SHIFT)
#define GRAB_PULLUP(x) (((x) & GPIO_PULLUP_MASK) >> GPIO_PULLUP_SHIFT)
#define GRAB_PORT(x) (((x) & GPIO_PORT_MASK) >> GPIO_PORT_SHIFTT)
#define GRAB_OFS(x) (((x) & GPIO_OFS_MASK) >> GPIO_OFS_SHIFT)
grab顾名思义,是抓取的意思。GRAB_MODE(x)是把命令的端口模式取出,得到的是一8位的值。GRAB_PORT(x)得到端口组,GRAB_OFS(x)得到端口号。
最重要的已经分析完了,在回过头来看一下set_gpio_ctrl()
#define set_gpio_ctrl(x) /
({ GPCON(GRAB_PORT((x))) &= ~(0x3 << (GRAB_OFS((x))*2)); /
GPCON(GRAB_PORT(x)) |= (GRAB_MODE(x) << (GRAB_OFS((x))*2)); /
GPUP(GRAB_PORT((x))) &= ~(1 << GRAB_OFS((x))); /
GPUP(GRAB_PORT((x))) |= (GRAB_PULLUP((x)) << GRAB_OFS((x))); })
GPCON(GRAB_PORT((x))) 表示一个端口组的控制寄存器.
在控制寄存器.里是用2位来表示去功能:0-1表示0端口,2-3表示1端口;;;;;
所以GRAB_OFS((x))*2 表示这个端口号是从寄存器的第几位开始.
GPCON(GRAB_PORT((x))) &= ~(0x3 << (GRAB_OFS((x))*2)); 表示把对应位清0.
以下类似,不一一表书.