1、在rk3588的底板原理图上找到LED灯是哪个GPIO控制的(GPIO1_B3)
2、配置GPIO(复用关系、方向、高低电平)
复用关系
在RK3588 TRM手册中找的GPIO1B的地址,其中偏移地址0x0028对应低32位,0x002C对应高32位,查找到前缀BUS_IOC的基地址为0xFD5F8000。要操作的地址为0xFD5F8000+0x0028
在低32位找到GPIO1_B3,说明如果将GPIO1B_IOMUX_SEL_L的12到15位都设为0,就能把GPIO1_B3配置为GPIO功能。
方向
查找到GPIO1的基地址为0xFEC20000,控制GPIO方向(DDR)的偏移地址如下图,低32位偏移地址为0x0008,高32位偏移地址为0x000C。
每个GPIO分为A、B、C、D四组,每组分为0~7号的IO口, 因此低32位DDR_L负责管理A、B两组GPIO的方向(0~15位控制A0~7、B0~7方向,16~31位负责控制0~15位的写数据使能)。所以,GPIO1_B3就是在GPIO1的基地址上对DDR_L的第10个bit进行控制,写1为输出,写0为输入。
高低电平
控制高低电平的数据位偏移地址如图所示,因此要操作的寄存器地址是GPIO基地址0xFEC20000+0x0000。如果给寄存器写入0800 0800,就可以使GPIO1_B3输出高电平,点亮LED。
3.根据以上信息写驱动文件(伪代码)
#define GPIO_DR 0xFEC20000
struct device_test{
det_t dev_num;
int major;
int minor;
struct cdev cdev_test;
struct class *class;
struct device *device;
char kbuf[32];
unsigned int *vir_gpio_dr;
};
struct device_test dev1;
#include <linux/io.h>
static int modulecdev_init(void)
{
dev1.vir_gpio_dr=ioremap(GPIO_DR, 4); //将实际地址转换为虚拟地址
if(IS_ERR(dev1.vir_gpio_dr)){
iounmap(GPIO_DR);
}
}
static ssize_t cdev_test_write(struct file *file, const char __user *buf, size_t size, loff_t *off)
{
if(test_dev->kuf[0]==1){
*(test_dev->vir_gpio_dr)=0x08000800;//点亮
}else if(test_dev->kuf[0]==0){
*(test_dev->vir_gpio_dr)=0x08000000;//熄灭
}
}
static void modulecdev_exit(void)
{
unregister_chrdev_region(dev.dev_num, 1);
...
iounmap(dev1.vir_gpio_dr);
}
主文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int fd;
char buf[32] = {0};
fd = open("/dev/test", 0_RDWR);
if(fd < 0)
{
perror("open error\n");
return fd;
}
buf[0] = atoi(argv[1]);
write(fd, buf, sizeof(buf));
close(fd);
return 0;
}