【正点原子RV1126 AI Linux开发板】 GPIO驱动程序测试
[复制链接]
本帖最后由 TL-LED 于 2024-3-29 15:07 编辑
编写字符驱动程序,测试下GPIO口的输出。
一、硬件部分
测试使用端口GPIO0_A4,开发板端口电路图如下:
二、硬件驱动分析
开发上LED端口对应的是GPIO3_D4端口,驱动程序也是配置这个端口测试的,下里面来分析下使用GPIO0_A4端口怎么配置。
开发板主芯片资料有些地方没有看太明白,根据芯片手册和开发板教程来理解测试。
配置端口需要配置以下方面:
PMUGRF_GPIO0A_IOMUX_H和PMUGRF_GPIO0A_DS_H寄存器来配置GPIO0_A4~GPIO0_A7端口的复用功能和驱动能力,下面是的偏移地址。
2.1、端口引脚复用配置
寄存器PMUGRF_GPIO0A_IOMUX_H的配置gpio0_A4选择GPIO功能,write_enable相应的为置1后,可以写入寄存器。
2.2、端口驱动能力配置
寄存器MUGRF_GPIO0A_DS_H的配置gpio0_A4_ds选择端口的驱动能力,这部分手册上也没有找到介绍,这里选择默认0。
write_enable相应的为置1后,可以写入寄存器。
2.3、GPIO输入输出设置
设置寄存器GPIO0_SWPORT_DDR_L
2.4、GPIO数据寄存器
端口的输入输出电平在寄存器GPIO0_SWPORT_DR_L中设置或读取
关于GPIO方向和输入\输出的电平设置可以参考开发板驱动手册中有管的讲解。
三、程序
3.1、驱动程序dev_led_n.c
- //dev_led.c
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 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; //设备
-
-
- //
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- /* 映射后的寄存器虚拟地址指针 */
- 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、应用程序
-
-
-
-
-
-
-
-
-
-
-
- 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
加载的的模块
自动创建的设备号
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引脚输出电平信号。
|