[RK3399][Uboot]使用内存映射方式直接控制IO

本文详细介绍了如何在RK3399平台上通过内存映射方式直接操作GPIO寄存器,涉及GPIO的IOMUX、PullUp/PullDown、DDR及DR设置,以GPIO4_A1和GPIO4_D6为例,演示了如何设置GPIO的输入输出及电平。代码示例展示了在Uboot中初始化和验证GPIO状态的流程。
摘要由CSDN通过智能技术生成

系列文章目录

第一章 [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的地址映射

GRF

RK3399地址映射
GPIO DR DDR

上两图中我标出了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

GPIO Registor Description
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 0110】
从寄存器的详细描述中可以看出,高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 1010】,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进行设置初始化。希望您看到此文有所收获,也希望如果您看到有错误的地方,能不吝赐教。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值