系列文章目录
第一章 [RK3399][Uboot]使用内存映射方式直接控制IO
文章目录
前言
嵌入式工作这么多年,经常会用到GPIO,通常是使用内核的API或Sysfs来操作GPIO,直接操作GPIO寄存器的情况或GPIO内存映射的情况比较少,本文就针对RK3399平台的GPIO,以操作GPIO地址映射的方式来控制一下GPIO。会涉及到GPIO的IOMUX,Pull Up/Pull Down, 设置输入输出,给输出设置高电平等方面。本文重点会跟踪GPIO4_A1和GPIO4_D6这两个GPIO。
目标
1.查看并设置GPIO4_A1的IOMUX和Pull Up/Pull Down状态,并设置其默认Pull Down.
2.查看并设置GPIO4_D6输入输出状态,并设置其输出高电平。
一、GPIO的地址映射
上两图中我标出了GPIO使用的几个基地址。
涉及到GPIO的IOMUX和Pull Up/Pull Down的有GRF和PMUGRF。
涉及到具体GPIO输入输出的有GPIO0/GPIO1/GPIO2/GPIO3/GPIO4这几个基地址。
GPIO4_A1
要查看IOMUX和Pull Up/Pull Down,所以要用到它的GRF基地址,从第二个图中可知,GRF基地址为:0xFF770000
IOMUX
用GRF_GPIO4A_IOMUX地址:GRF基地址+偏移地址=0xFF770000 + 0x0E020 = 0xFF77E020
Pull Up/Pull Down
用GRF_GPIO4A_P地址:GRF基地址 + 偏移地址 = 0xFF7700 + 0x0E060 = 0xFF77E060
GPIO4_D6
要查看并设置输入输出,所以要用到GPIO4的基地址,从第二个图中可知,GPIO4的基地址为:0xFF790000
DDR
输入输出方向设置寄存器,使用GPIO_SWPORTA_DDR地址:GPIO4基地址+偏移地址 = 0xFF790000 + 0x0004 = 0xFF790004
DR
数据寄存器,使用GPIO_SWPORTA_DR地址:GPIO4基地址+偏移地址 = 0xFF790000 + 0x0000 = 0xFF790000
二、设置GPIO
1.GPIO的IOMUX及PULL
IOMUX 默认值为:0x00000000
二进制表示为:【0000 0000 0000 0000 0000 0000 0000 0000】
从寄存器的详细描述中可以看出,高16位用来标记低16位是否可写,1代表可写,从左往右,GPIO4_A7到GPIO4_A0共占用16位,每个IO占用2位。同时可以看到GPIO4_A1在CPU Reset时,默认的IOMUX为GPIO功能。
PU/PD默认值为: 0x0000aa96
二进制制表示:【0000 0000 0000 0000 1010 1010 1001 01
10】
从寄存器的详细描述中可以看出,高16位用来标记低16位是否可写,1代表可写,从左往右,GPIO4_A7到GPIO4_A0共占用16位,每个IO占用2位。同时可以看到GPIO4_A1在CPU Reset时,默认的PU/PD 为01,即Pull-Up。
2.设置GPIO的PULL
IOMUX设置与PU/PD设置类似,这里以PU/PD设置举例。
设置GPIO4_A1由默认的Pull-Up为Pull-Down,高16位设置为1,IO对应位由01变成10即 【1111 1111 1111 1111 1010 1010 1001 10
10】,16进制表示为0xffffaa9a
。
代码设置如下:
/* GPIO4_A1 PU/PD 设置为Pull-Down */
u32 value;
value = 0xffffaa9a;
iowrite32(value,(void *)(0xFF77E060));
value = ioread32((void *)(0xFF77E060));
printf("GPIO4_Ax PU/PD Setting value:[0xFF77E060] 0x%08x\n",value);
上面的value为我们要设置GPIO4_A1为Pull-Down的值0xffffaa9a
,使用iowrite32这个函数,把它写到GPIO4的PULL地址0xFF77E060
,然后再通过ioread32这个函数打印出来验证我们是否写成功。
3.GPIO的DDR及DR
DDR: 0x00000000
二进制表示为:【00000000 00000000 00000000 00000000】
DDR寄存器32位,每8位一组,位置从左往右,代表GPIOD到GPIOA。
比如一个DR值为: 0x11223344,其中0x11代表GPIOD中的8个IO,从左往右为GPIOx_D7到GPIOx_D0,0x44代表GPIOA中的8个IO,从左往右为GPIOx_A7到GPIOx_A0。
再看GPIOA中的8个IO用0x44是怎么表示的,它换成二进制为0100 0100,从左往右依次代表GPIOx_A7 到 GPIOx_A0,即GPIOx_A6和GPIOx_A2为1,其它为0。DDR相应位设置1为输出模式,0为输入模式。
DR值: 0x00000000
二进制表示为:【00000000 00000000 00000000 00000000】
DR寄存器有32位,每8位一组,位置从左往右,代表GPIOD到GPIOA。
比如一个DR值为: 0x11223344,其中0x11代表GPIOD中的8个IO,从左往右为GPIOx_D7到GPIOx_D0,0x44代表GPIOA中的8个IO,从左往右为GPIOx_A7到GPIOx_A0。
再看GPIOA中的8个IO用0x44是怎么表示的,它换成二进制为0100 0100,从左往右依次代表GPIOx_A7 到 GPIOx_A0,即GPIOx_A6和GPIOx_A2为1,其它为0。当DDR相应位设置为1即输出模式时,往相应位写1,代表拉高该IO。
4. 设置GPIO的DDR及DR
GPIO4_D6输出设置,值为 0100 0000 即0x40,写到DDR寄存器的值为0x40000000,同样DR寄存器的值为0x40000000。
代码如下:
/* GPIO4_D6 DDR 设置输出 */
/* GPIO4_DX: 01000000 40 */
/* GPIO4_CX: 00000000 00 */
/* GPIO4_BX: 00000000 00 */
/* GPIO4_AX: 00000000 00 */
value = 0x40000000;
iowrite32(value, (void *)(0xFF790004));
value = ioread32((void *)(0xFF790004));
printf("GPIO4_xx DDR Setting value:[0xFF790004] 0x%08x\n",value);
/* GPIO4_D6 DR 输出高 */
value = 0x40000000;
iowrite32(value, (void *)(0xFF790000)); //DR High
0x40000000表示GPIO4_D6设置为输出及输出高,所以把这个值写到DDR寄存器地址0xFF790004
和DR寄存器地址0xFF790000
即可
5.完整代码验证
我们在Uboot中设置IO及其它输入输出。
代码如下:
diff --git a/common/board_f.c b/common/board_f.c
index 8bceb0c..ab54ec4 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -259,7 +259,7 @@ static int setup_mon_len(void)
return 0;
}
+static int io_test(void)
+{
+ u32 value;
+
+ /* GPIO4_Ax IOMUX */
+ value = ioread32((void *)(0xFF77E020));
+ printf("GPIO4_Ax IOMUX Reset value:[0xFF77E020] 0x%08x\n",value);
+
+ /* GPIO4_Ax PU/PD */
+ value = ioread32((void *)(0xFF77E060));
+ printf("GPIO4_Ax PU/PD Reset value:[0xFF77E060] 0x%08x\n",value);
+
+ /* GPIO4_A1 PU/PD 设置为Pull-Down */
+ value = 0xffffaa9a;
+ iowrite32(value,(void *)(0xFF77E060));
+ value = ioread32((void *)(0xFF77E060));
+ printf("GPIO4_Ax PU/PD Setting value:[0xFF77E060] 0x%08x\n",value);
+
+ /* GPIO4_xx DDR/DR */
+ value = ioread32((void *)(0xFF790000));
+ printf("GPIO4_xx DR Reset value:[0xFF790000] 0x%08x\n",value);
+ value = ioread32((void *)(0xFF790004));
+ printf("GPIO4_xx DDR Reset value:[0xFF790004] 0x%08x\n",value);
+
+ /* GPIO4_D6 DDR 设置输出 */
+ /* GPIO4_DX: 01000000 40 */
+ /* GPIO4_CX: 00000000 00 */
+ /* GPIO4_BX: 00000000 00 */
+ /* GPIO4_AX: 00000000 00 */
+ value = 0x40000000;
+ iowrite32(value, (void *)(0xFF790004));
+ value = ioread32((void *)(0xFF790004));
+ printf("GPIO4_xx DDR Setting value:[0xFF790004] 0x%08x\n",value);
+
+ /* GPIO4_D6 DR 循环输出高,低 */
+ while (1)
+ {
+ value = 0x40000000;
+ iowrite32(value, (void *)(0xFF790000)); //DR High
+ udelay(500*1000);
+ value = 0x00000000;
+ iowrite32(value, (void *)(0xFF790000)); //DR Low
+ udelay(500*1000);
+ value = 0x40000000;
+ iowrite32(value, (void *)(0xFF790000)); //DR High
+ udelay(500*1000);
+ value = 0x00000000;
+ iowrite32(value, (void *)(0xFF790000)); //DR Low
+ udelay(500*1000);
+ }
+
+ return 0;
+}
+
__weak int arch_fpga_init(void)
{
return 0;
@@ -968,7 +1022,8 @@ __weak int arch_cpu_init_dm(void)
static const init_fnc_t init_sequence_f[] = {
setup_mon_len,
+ io_test,
#ifdef CONFIG_OF_CONTROL
fdtdec_setup,
#endif
输入如下结果:
总结
以上就是今天要讲的内容,本文使用操作GPIO地址映射的方式来直接控制IO,设置了GPIO4_A1为下拉,GPIO4_D6默认输出高。在uboot阶段对IO进行设置初始化。希望您看到此文有所收获,也希望如果您看到有错误的地方,能不吝赐教。