[MCU] 【正点原子RV1126 AI Linux开发板】 GPIO驱动程序测试

TL-LED   2024-3-29 15:07 楼主

编写字符驱动程序,测试下GPIO口的输出。

 

一、硬件部分

 

测试使用端口GPIO0_A4,开发板端口电路图如下:

001.png

 

二、硬件驱动分析

 

开发上LED端口对应的是GPIO3_D4端口,驱动程序也是配置这个端口测试的,下里面来分析下使用GPIO0_A4端口怎么配置。

开发板主芯片资料有些地方没有看太明白,根据芯片手册和开发板教程来理解测试。

配置端口需要配置以下方面:

 

PMUGRF_GPIO0A_IOMUX_H和PMUGRF_GPIO0A_DS_H寄存器来配置GPIO0_A4~GPIO0_A7端口的复用功能和驱动能力,下面是的偏移地址。

002.png

 

2.1、端口引脚复用配置

寄存器PMUGRF_GPIO0A_IOMUX_H的配置gpio0_A4选择GPIO功能,write_enable相应的为置1后,可以写入寄存器。

003.png

 

2.2、端口驱动能力配置

寄存器MUGRF_GPIO0A_DS_H的配置gpio0_A4_ds选择端口的驱动能力,这部分手册上也没有找到介绍,这里选择默认0。

write_enable相应的为置1后,可以写入寄存器。

004.png

 

2.3、GPIO输入输出设置

设置寄存器GPIO0_SWPORT_DDR_L

006.png

 

2.4、GPIO数据寄存器

端口的输入输出电平在寄存器GPIO0_SWPORT_DR_L中设置或读取

005.png

 

关于GPIO方向和输入\输出的电平设置可以参考开发板驱动手册中有管的讲解。

008.png

 

三、程序

 

3.1、驱动程序dev_led_n.c

//dev_led.c

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h> 
#include <linux/device.h> 
#include <linux/errno.h>

#include <asm/io.h>

#define DEVICE_NUMBER 	1 					//定义次设备号的个数
#define DEVICE_NAME		"dev_led_n" 		//设备名字  

struct chr_dev_led
{
	dev_t devid;
	struct cdev cdev;			//定义一个 cdev 结构体
	struct class *class; 		//定义类
	struct device *device; 		//设备 
	int major;					//主设备号
	int minor;					//次设备号
};

struct chr_dev_led new_chr_dev_led;		//设备


//#define DEV_LED_MAJOR		200		/* 主设备号 */


#define LEDOFF 	0				/* 关灯 */
#define LEDON 	1				/* 开灯 */

#define GRF_BASE						(0XFE000000)
#define GRF_GPIO3D_IOMUX_H				(GRF_BASE + 0X1006C)
#define GRF_GPIO3D_DS_H					(GRF_BASE + 0X100EC)

#define GPIO3_BASE						(0XFF640000)
#define GPIO3_SWPORT_DR_H				(GPIO3_BASE + 0X0004)
#define GPIO3_SWPORT_DDR_H				(GPIO3_BASE + 0X000C)

#define PMUGRF_BASE						(0XFE020000)
#define PMUGRF_GPIO0A_IOMUX_L			(PMUGRF_BASE + 0X0000)
#define PMUGRF_GPIO0A_IOMUX_H			(PMUGRF_BASE + 0X0004)
#define PMUGRF_GPIO0A_DS_L				(PMUGRF_BASE + 0X0020)
#define PMUGRF_GPIO0A_DS_H				(PMUGRF_BASE + 0X0024)

#define GPIO0_BASE						(0XFF460000)
#define GPIO0_SWPORT_DR_L				(GPIO0_BASE + 0X0000)
#define GPIO0_SWPORT_DR_H				(GPIO0_BASE + 0X0004)
#define GPIO0_SWPORT_DDR_L				(GPIO0_BASE + 0X0008)
#define GPIO0_SWPORT_DDR_H				(GPIO0_BASE + 0X000C)

/* 映射后的寄存器虚拟地址指针 */
static void __iomem *GRF_GPIO3D_IOMUX_H_PI;
static void __iomem *GRF_GPIO3D_DS_H_PI;
static void __iomem *GPIO3_SWPORT_DR_H_PI;
static void __iomem *GPIO3_SWPORT_DDR_H_PI;

static void __iomem *PMUGRF_GPIO0A_IOMUX_L_PI;
static void __iomem *PMUGRF_GPIO0A_IOMUX_H_PI;
static void __iomem *PMUGRF_GPIO0A_DS_L_PI;
static void __iomem *PMUGRF_GPIO0A_DS_H_PI;
static void __iomem *GPIO0_SWPORT_DR_L_PI;
static void __iomem *GPIO0_SWPORT_DR_H_PI;
static void __iomem *GPIO0_SWPORT_DDR_L_PI;
static void __iomem *GPIO0_SWPORT_DDR_H_PI;

void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == LEDON) {
		val = readl(GPIO3_SWPORT_DR_H_PI);
		val &= ~(0X1 << 12); /* bit12 清零*/
		val |= ((0X1 << 28) | (0X1 << 12));	/* bit28 置1,允许写bit12,
											bit12,低电平	*/ 
		writel(val, GPIO3_SWPORT_DR_H_PI);
		//--------------------------------------------
		val = readl(GPIO0_SWPORT_DR_L_PI);
		val &= ~(0X1 << 4);  
		val |= ((0X1 << 20) | (0X1 << 4));
		writel(val, GPIO0_SWPORT_DR_L_PI);

	}else if(sta == LEDOFF) { 
		val = readl(GPIO3_SWPORT_DR_H_PI);
		val &= ~(0X1 << 12); /* bit12 清零*/
		val |= ((0X1 << 28) | (0X0 << 12));	/* bit28 置1,允许写bit12,
	 					   				   bit12,低电平	*/
		writel(val, GPIO3_SWPORT_DR_H_PI);
		//--------------------------------------------
		val = readl(GPIO0_SWPORT_DR_L_PI);
		val &= ~(0X1 << 4); 
		val |= ((0X1 << 20) | (0X0 << 4));	
		writel(val, GPIO0_SWPORT_DR_L_PI);
	}	
}

/*
 * @description		: 物理地址映射
 * @return  			: 无
 */
void led_remap(void)
{
  	GRF_GPIO3D_IOMUX_H_PI = ioremap(GRF_GPIO3D_IOMUX_H, 4);
	GRF_GPIO3D_DS_H_PI = ioremap(GRF_GPIO3D_DS_H, 4);
	GPIO3_SWPORT_DR_H_PI = ioremap(GPIO3_SWPORT_DR_H, 4);
	GPIO3_SWPORT_DDR_H_PI = ioremap(GPIO3_SWPORT_DDR_H, 4);
	//GPIO0_A
	PMUGRF_GPIO0A_IOMUX_H_PI = ioremap(PMUGRF_GPIO0A_IOMUX_H, 4);
	PMUGRF_GPIO0A_IOMUX_L_PI = ioremap(PMUGRF_GPIO0A_IOMUX_L, 4);

	PMUGRF_GPIO0A_DS_L_PI = ioremap(PMUGRF_GPIO0A_DS_L, 4);
	PMUGRF_GPIO0A_DS_H_PI = ioremap(PMUGRF_GPIO0A_DS_H, 4);

	GPIO0_SWPORT_DR_L_PI = ioremap(GPIO0_SWPORT_DR_L, 4);
	GPIO0_SWPORT_DR_H_PI = ioremap(GPIO0_SWPORT_DR_H, 4);
	GPIO0_SWPORT_DDR_L_PI = ioremap(GPIO0_SWPORT_DDR_L, 4);
	GPIO0_SWPORT_DDR_H_PI = ioremap(GPIO0_SWPORT_DDR_H, 4);
}

/*
 * @description		: 取消映射
 * @return 			: 无
 */
void led_unmap(void)
{
	/* 取消映射 */
	iounmap(GRF_GPIO3D_IOMUX_H_PI);
	iounmap(GRF_GPIO3D_DS_H_PI);
	iounmap(GPIO3_SWPORT_DR_H_PI);
	iounmap(GPIO3_SWPORT_DDR_H_PI);

	iounmap(PMUGRF_GPIO0A_IOMUX_H_PI);
	iounmap(PMUGRF_GPIO0A_IOMUX_L_PI);
	iounmap(PMUGRF_GPIO0A_DS_H_PI);
	iounmap(PMUGRF_GPIO0A_DS_L_PI);
	iounmap(GPIO0_SWPORT_DR_L_PI);
	iounmap(GPIO0_SWPORT_DR_H_PI);
	iounmap(GPIO0_SWPORT_DDR_L_PI);
	iounmap(GPIO0_SWPORT_DDR_H_PI);
}


static int dev_led_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &new_chr_dev_led; 		//设置私有数据 
	return 0;
}

static ssize_t dev_led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

static ssize_t dev_led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char databuf[1];
	unsigned char ledstat;

	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}

	ledstat = databuf[0];		/* 获取状态值 */

	if(ledstat == LEDON) {	
		led_switch(LEDON);		/* 打开LED灯 */
	} else if(ledstat == LEDOFF) {
		led_switch(LEDOFF);		/* 关闭LED灯 */
	}
	return 0;
}


static int dev_led_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/*
 * 设备操作函数结构体
 */
static struct file_operations dev_led_fops = {
	.owner = THIS_MODULE,	
	.open = dev_led_open,
	.read = dev_led_read,
	.write = dev_led_write,
	.release = dev_led_release,
};


static int __init dev_led_init(void)
{
	int retvalue = 0;
	u32 val = 0;

	/* 初始化LED */
	/* 1、寄存器地址映射 */
	led_remap();

	/* 2、设置GPIO3_D4为GPIO功能。*/
	val = readl(GRF_GPIO3D_IOMUX_H_PI);
	val &= ~(0X7 << 0);	/* bit2:0,清零 */
	val |= ((0X7 << 16) | (0X0 << 0));	/* bit18:16 置1,允许写bit2:0,
	 					   				   bit2:0:0,用作GPIO3_D4	*/
	writel(val, GRF_GPIO3D_IOMUX_H_PI);

	/* 3、设置GPIO3_D4驱动能力为level0 */
	val = readl(GRF_GPIO3D_DS_H_PI);
	val &= ~(0XF << 0);	/* bit3:0清零*/
	val |= ((0XF << 16) | (0X0 << 0));	/* bit19:16 置1,允许写bit3:0,
	 					   				   bit2:0:0,用作GPIO3_D4	*/
	writel(val, GRF_GPIO3D_DS_H_PI);

	/* 4、设置GPIO3_D4为输出 */
	val = readl(GPIO3_SWPORT_DDR_H_PI);
	val &= ~(0X1 << 12); /* bit12 清零*/
	val |= ((0X1 << 28) | (0X1 << 12));	/* bit28 置1,允许写bit12,
	 					   				   bit12,高电平	*/
	writel(val, GPIO3_SWPORT_DDR_H_PI);

	/* 5、设置GPIO3_D4为低电平,关闭LED灯。*/
	val = readl(GPIO3_SWPORT_DR_H_PI);
	val &= ~(0X1 << 12); /* bit12 清零*/
	val |= ((0X1 << 28) | (0X1 << 12));	/* bit28 置1,允许写bit12,
	 					   				   bit12,低电平	*/
	writel(val, GPIO3_SWPORT_DR_H_PI);



	//-----------------------------------------------------------------------
	//GPIO0_A--GPIO设置为GPIO功能
	val = readl(PMUGRF_GPIO0A_IOMUX_H_PI);
	val &= ~(0X7 << 0);						/* bit2:0,清零 */
	val |= ((0X7 << 16) | (0X0 << 0));		/* bit18:16 置1,允许写bit2:0,	
	 					   				   	   bit2:0:0,用作GPIO0_A0	*/
	writel(val, PMUGRF_GPIO0A_IOMUX_H_PI);

	//val = readl(PMUGRF_GPIO0A_IOMUX_L_PI);
	//printk("PMUGRF_GPIO0A_IOMUX_L_PI=0x%x\r\n",val);

	//设置GPIO0_A0驱动能力为level0 
	val = readl(PMUGRF_GPIO0A_DS_H_PI);
	val &= ~(0XF << 0);	/* bit3:0清零*/
	val |= ((0XF << 16) | (0X0 << 0));	  					   				    
	writel(val, PMUGRF_GPIO0A_DS_H_PI);

	//设置GPIO0_A0为输出 
	val = readl(GPIO0_SWPORT_DDR_L_PI);
	val &= ~(0X1 << 4); /* bit0 清零*/
	val |= ((0X1 << 20) | (0X1 << 4));	 
	 					   				 
	writel(val, GPIO0_SWPORT_DDR_L_PI);

	//val = readl(GPIO0_SWPORT_DDR_L_PI);
	//printk("GPIO0_SWPORT_DDR_L_PI=0x%x\r\n",val);
	//设置GPIO0_A0为低电平
	val = readl(GPIO0_SWPORT_DR_L_PI);
	val &= ~(0X1 << 4); 				 
	val |= ((0X1 << 20) | (0X1 << 4));	 
	 					   				   
	writel(val, GPIO0_SWPORT_DR_L_PI);
	//printk("GPIO0_SWPORT_DR_L_PI=0x%x\r\n",val);

	//-----------------------------------------------------------------------
	//注册字符设备驱动
	//创建设备号
	if (new_chr_dev_led.major)
	{
		new_chr_dev_led.devid = MKDEV(new_chr_dev_led.major, 0);
		retvalue = register_chrdev_region(new_chr_dev_led.devid, DEVICE_NUMBER, DEVICE_NAME);
		if(retvalue < 0) 
		{
			//printk("register chrdev failed!\r\n");
			pr_err("cannot register %s char driver [ret=%d]\n",DEVICE_NAME, DEVICE_NUMBER);
			goto fail_map;
		}
	}
	else
	{
		retvalue = alloc_chrdev_region(&new_chr_dev_led.devid, 0, DEVICE_NUMBER, DEVICE_NAME); 
		if(retvalue < 0) 
		{
			//printk("register chrdev failed!\r\n");
			pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", DEVICE_NAME, retvalue);
			goto fail_map;
		}
		new_chr_dev_led.major = MAJOR(new_chr_dev_led.devid);
		new_chr_dev_led.minor = MINOR(new_chr_dev_led.devid);
	}
	printk(" major=%d,minor=%d\r\n",new_chr_dev_led.major,new_chr_dev_led.minor);

	//初始化cdev
	new_chr_dev_led.cdev.owner = THIS_MODULE;
	cdev_init(&new_chr_dev_led.cdev, &dev_led_fops);
	
	//添加cdev
	retvalue = cdev_add(&new_chr_dev_led.cdev, new_chr_dev_led.devid, DEVICE_NUMBER);
	if(retvalue < 0)
	{
		goto del_unregister;
	}

	//创建类 
	new_chr_dev_led.class = class_create(THIS_MODULE, DEVICE_NAME);
	if(IS_ERR(new_chr_dev_led.class)) 
	{
		goto del_cdev;
	}

	//创建设备 
	new_chr_dev_led.device = device_create(new_chr_dev_led.class, NULL,new_chr_dev_led.devid, NULL, DEVICE_NAME);
	if (IS_ERR(new_chr_dev_led.device)) 
	{
		goto destroy_class;
	}

	return 0;
		
destroy_class:
	class_destroy(new_chr_dev_led.class);
del_cdev:
	cdev_del(&new_chr_dev_led.cdev);
del_unregister:
	unregister_chrdev_region(new_chr_dev_led.devid, DEVICE_NUMBER);
fail_map:
	led_unmap();
	return -EIO;
}

static void __exit dev_led_exit(void)
{
	u32 val=0;
	
	//--------------------------------------------------------------------------
	val = readl(GPIO3_SWPORT_DR_H_PI);
		val &= ~(0X1 << 12); /* bit12 清零*/
		val |= ((0X1 << 28) | (0X0 << 12));	/* bit28 置1,允许写bit12,
	 					   				   bit12,低电平	*/
	writel(val, GPIO3_SWPORT_DR_H_PI);

	val = readl(GPIO0_SWPORT_DR_L_PI);
		val &= ~(0X1 << 4); /* bit12 清零*/
		val |= ((0X1 << 20) | (0X0 << 4));	/* bit28 置1,允许写bit12,
	 					   				   bit12,低电平	*/
	writel(val, GPIO0_SWPORT_DR_L_PI);
	//--------------------------------------------------------------------------
	
	//取消映射
	led_unmap();   
	//注销字符设备驱动
	cdev_del(&new_chr_dev_led.cdev);//删除cdev
	unregister_chrdev_region(new_chr_dev_led.devid, DEVICE_NUMBER); //注销设备号 
	device_destroy(new_chr_dev_led.class, new_chr_dev_led.devid);
	class_destroy(new_chr_dev_led.class);
}

/* 
 * 将上面两个函数指定为驱动的入口和出口函数 
 */
module_init(dev_led_init);
module_exit(dev_led_exit);

/* 
 * LICENSE和作者信息
 */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");

 

3.2、应用程序

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

#define LEDOFF 	0
#define LEDON 	1

int main(int argc, char *argv[])
{
	int fd, retvalue;
	char *filename;
	unsigned char ledbuf[1];

	if(argc != 3){
		printf("Error Usage!\r\n");
		return -1;
	}

	filename = argv[1];

	/* 打开驱动文件 */
	fd  = open(filename, O_RDWR);
	if(fd < 0){
		printf("Can't open file %s\r\n", filename);
		return -1;
	}

	ledbuf[0]=atoi(argv[2]);

	retvalue = write(fd, ledbuf, sizeof(ledbuf));
	if(retvalue < 0){
		printf("write file led failed!\r\n");
		close(fd);
		return -1;
	}

	retvalue = close(fd);
	if(retvalue < 0){
		printf("Can't close file %s\r\n", filename);
		return -1;
	}

	return 0;
}



 

四、运行

 

4.1、复制文件到开发板

root@ubuntu:/opt/atk-rv1126_app/led_test_n# scp dev_led_n.ko root@192.168.1.117:/lib/modules/4.19.111/
root@ubuntu:/opt/atk-rv1126_app/led_test_n# scp dev_led_app_n root@192.168.1.117:/lib/modules/4.19.111/

 

4.2、加载模块

[root@ATK-DLRV1126:/lib/modules/4.19.111]# modprobe dev_led_n
加载的的模块

009.png

 

自动创建的设备号

010.png

 

4.3、控制GPIO口输出

[root@ATK-DLRV1126:/lib/modules/4.19.111]# ./dev_led_app_n /dev/dev_led_n 0
[root@ATK-DLRV1126:/lib/modules/4.19.111]# ./dev_led_app_n /dev/dev_led_n 1


使用示波器查看GPIO0_A4引脚输出电平信号。

本帖最后由 TL-LED 于 2024-3-29 15:07 编辑

回复评论 (1)

root@ubuntu:/opt/atk-rv1126_app/led_test_n# scp dev_led_n.ko root@192.168.1.117:/lib/modules/4.19.111/ root@ubuntu:/opt/atk-rv1126_app/led_test_n# scp dev_led_app_n root@192.168.1.117:/lib/modules/4.19.111/
点赞  2024-5-22 11:28
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复