[X]关闭

[米联客-XILINX-H3_CZ08_7100] LINUX驱动篇连载-12 AXI-GPIO

文档创建者:LINUX课程
浏览次数:294
最后更新:2024-09-10
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-SOC » 2_LINUX应用开发
本帖最后由 LINUX课程 于 2024-9-11 10:36 编辑

软件版本:vitis2021.1(vivado2021.1)
操作系统:WIN10 64bit
硬件平台:适用XILINX Z7/ZU系列FPGA
登录“米联客”FPGA社区-www.uisrc.com视频课程、答疑解惑!

1 概述
在前面的第四章,我们已经了解了如何使用GPIO子系统来实现对PS端的KEY进行控制,在该章我们将继续使用GPIO子系统来对PL端的KEY进行控制。
实验目的:
  • 掌握从LINUX对PL端的GPIO的KEY进行控制。
  • 了解AXI-GPIO IP的相关概念。
  • 掌握GPIO子系统的编程。

2 系统框图
图片2.jpg
上图是概念上的框架连接图。从AXI-GPIO中将会引出两个GPIO的管脚,一个是BTN按钮,一个是LED。本章实验将使用BTN按钮部分。
3 介绍
3.1 AXI-GPIO IP介绍
AXI-GPIO IP通过AXI-Lite-slave连接到PS。具体2个通道,每个通道32个GPIO。在IP里面可以固定配置为输入或者输出,也可以通过软件控制寄存器配置为输入或者输出。并且支持中断输入,AXI-GPIO的构架如下图所示。
image.jpg
3.2 特性
-支持axis4 - lite接口规范
-支持可配置的单或双GPIO通道
-支持GPIO引脚1 ~ 32位的通道宽度配置
-支持动态规划每个GPIO位作为输入或输出
-支持每个通道的单独配置
-支持所有寄存器的每个位的独立重置值
-支持可选的中断请求生成
3.3 寄存器说明
1:数据寄存器GPIOx_DATA
有两个GPIO数据寄存器(GPIO_DATA和GPIO2_DATA),每个通道对应一个。通道1数据寄存器(GPIO_DATA)始终存在;通道2数据寄存器(GPIO2_DATA)只有当IP配置为双通道(Enable dual channel = 1)时才存在。
image.jpg
image.jpg
2:三态寄存器GPIOx_TRI
有两个AXI GPIO 3-state控制寄存器(GPIO_TRI和GPIO2_TRI),每个通道对应一个。通道2 3状态控制寄存器(GPIO2_TRI)只有在IP配置为双通道使能双通道= 1时才存在。
image.jpg
image.jpg
3:全局中断使能寄存器(GIER)
设bit位为1使置31能中断。
image.jpg
image.jpg
4:IP-CORE中断使能寄存器(IPIER)
image.jpg
image.jpg
5:IP状态寄存器(IPISR)
image.jpg
image.jpg
4 搭建工程
该部分生成的文件已经分别保存在“3.boot”和“4.BOOT”文件夹内,可以直接使用。如果想要自己生成,按照下面步骤。
4.1 vivado工程搭建
详细的vivado工程搭建参考第一章,本章的vivado工程并非默认的vivado工程,请选择在该章目录中的vivado工程。
打开“soc_prj”文件夹。
image.jpg
进入vivado工程部分,打开,检查IP是否连接正确。
image.jpg
4.2 设备树文件
将vitis中生成的设备树文件中的pl.dtsi部分复制进原先的zynqmp-mzux.dts文件中。
image.jpg
4.3 制作系统文件
详细的制作系统文件参考第一章环境搭建部分,将从vivado和vitis中生成的文件复制进开发环境,使用脚本生成系统文件。也可以直接使用本章示例文件夹“3.boot”中的文件,按照第一章的步骤复制进开发环境进行系统制作,这里只做简单的介绍。(“4.BOOT”文件夹中有已经生成好的系统文件镜像文件,可以直接拷贝至系统SD卡的BOOT文件夹内)
5 程序分析
该实验的程序与之前的GPIO子系统的程序极为相似,GPIO子系统相关的代码如有疑问,可以参考之前的第四章GPIO子系统程序分析部分。
5.1 驱动程序分析
  1. //添加头文件
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/gpio.h>
  5. #include <linux/uaccess.h>

  6. #define PL_GPIO_BTN 1022

  7. static int key_major;
  8. static int ret = 0;
  9. static struct class *key_cls;

  10. int key_open(struct inode *inode, struct file *filp)
  11. {
  12. printk("----^v^-----key open\n");
  13. //2、将 PL_GPIO_BTN 设置为输入
  14. ret = gpio_direction_input(PL_GPIO_BTN);
  15. if (ret != 0)
  16. {
  17.   printk("gpio direction input MIO_PIN_42 fail!\n");
  18. }

  19. return 0;
  20. }

  21. ssize_t key_read(struct file *filp, char __user *buf, size_t count, loff_t *fops)
  22. {
  23. int value[4] = {0};
  24. printk("----^v^-----\n");
  25. printk("key read\n");
  26. //3、获取 PL_GPIO_BTN 的值
  27. value[0] = gpio_get_value(PL_GPIO_BTN);

  28. ret = copy_to_user(buf, value, count);
  29. if (ret < 0)
  30. {
  31.   printk("gpio get value failed!\n");
  32.   return ret;
  33. }
  34. return 0;
  35. }

  36. int key_close(struct inode *inode, struct file *filp)
  37. {
  38. printk("----^v^-----\n");
  39. printk("key close\n");
  40. return 0;
  41. }

  42. const struct file_operations key_fops = {
  43. .open = key_open,
  44. .read = key_read,
  45. .release = key_close,
  46. };

  47. //实现装载入口函数和卸载入口函数
  48. static __init int key_drv_v1_init(void)
  49. {
  50. printk("----^v^-----\n");
  51. printk("key drv v1 init\n");
  52. //申请主设备号
  53. //参数1----需要的主设备号,>0静态分配, ==0自动分配
  54. //参数2----设备的描述 信息,体现在cat /proc/devices, 一般自定义
  55. //参数3----文件描述集合
  56. //返回值,小于0报错
  57. key_major = register_chrdev(0, "key_drv_v1", &key_fops);
  58. if (key_major < 0)
  59. {
  60.   printk("register chrdev faile!\n");
  61.   return key_major;
  62. }
  63. printk("register chrdev ok!\n");

  64. //自动创建设备节点
  65. //创建设备的类别
  66. //参数1----设备的拥有者,当前模块,直接填THIS_MODULE
  67. //参数2----设备类别的名字,自定义
  68. //返回值:类别结构体指针,其实就是分配了一个结构体空间
  69. key_cls = class_create(THIS_MODULE, "key_class");
  70. printk("class create ok!\n");

  71. //创建设备
  72. //参数1----设备对应的类别
  73. //参数2----当前设备的父类,直接填NULL
  74. //参数3----设备节点关联的设备号
  75. //参数4----私有数据直接填NULL
  76. //参数5----设备节点的名字
  77. device_create(key_cls, NULL, MKDEV(key_major, 0), NULL, "mykey%d", 0);
  78. printk("device create ok!\n");

  79. //1、PL_GPIO_BTN 申请 GPIO 口
  80. ret = gpio_request(PL_GPIO_BTN, "key1");
  81. if (ret < 0)
  82. {
  83.   printk("gpio request key1 error!\n");
  84.   return ret;
  85. }

  86. return 0;
  87. }

  88. static __exit void key_drv_v1_exit(void)
  89. {
  90. printk("----^v^-----\n");
  91. printk("key drv v1 exit\n");

  92. //4、释放按键 GPIO
  93. gpio_free(PL_GPIO_BTN);

  94. //删除设备
  95. device_destroy(key_cls, MKDEV(key_major, 0));
  96. //删除类
  97. class_destroy(key_cls);
  98. //注销主设备号
  99. unregister_chrdev(key_major, "key_drv_v1");
  100. }

  101. //申明装载入口函数和卸载入口函数
  102. module_init(key_drv_v1_init);
  103. module_exit(key_drv_v1_exit);

  104. //添加GPL协议
  105. MODULE_LICENSE("GPL");
  106. MODULE_AUTHOR("msxbo");
复制代码
注意第7行的代码,第7行的代码中的511是之前在vivado中设置AXI-GPIO时分配的一个管脚,之前在终端输入“ls /sys/class/gpio”中展示的也就是这个地址。
5.2 应用程序分析
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6. #include <unistd.h>

  7. int main(void)
  8. {
  9. int fd = 0;
  10. int i = 0;
  11. int a[4] = {0};

  12. fd = open("/dev/mykey0", O_RDWR);
  13. if (fd < 0)
  14. {
  15.   perror("open fail!\n");
  16.   exit(1);
  17. }

  18. while (1)
  19. {
  20.   read(fd, a, sizeof(a));
  21.   printf("but value %d\n", a[0]);
  22.   sleep(3);
  23. }

  24. close(fd);
  25. return 0;
  26. }
复制代码
第21至26行,该程序将会每3秒读取一次设备中的值。
6 程序编译
详细的程序编译部分可以参考第一章程序编译部分。将编译好的文件上传至开发板。
6.1 Makefile文件分析
  1. #已经编译过的内核源码路径
  2. KERNEL_DIR = /home/uisrc/uisrc-lab-xlnx/sources/kernel

  3. export ARCH=arm
  4. export CROSS_COMPILE=arm-linux-gnueabihf-

  5. #当前路径
  6. CURRENT_DIR = $(shell pwd)

  7. all :
  8. #进入并调用内核源码目录中Makefile的规则, 将当前的目录中的源码编译成模块
  9. make -C $(KERNEL_DIR) M=$(CURRENT_DIR) modules
  10. rm -rf *.symvers *.order *.o *.mod.o *.mod.c

  11. clean :
  12. make -C $(KERNEL_DIR) M=$(CURRENT_DIR) clean

  13. #指定编译哪个文件
  14. obj-m += axi_user_gpio.o
复制代码
7 演示
7.1 硬件准备
SD2.0 启动 01 而模式开关为 ON OFF(7100 需要先将系统烧录进qspi,然后才能从qspi启动sd卡,“[米联客-XILINX-H3_CZ08_7100] LINUX基础篇连载-04 从vitis移植Ubuntu实现二次开发”)
2f5038eb9880afd532753935815b079.jpg
将 PS 端串口线连接电脑,如果要使用 ssh 登录,将网口线同样连接至电脑,最后给开发板通电。每次重新上电,需要重新插拔 PS 串口,否则会登录失败。
image.jpg
接入12V直流电源开机。
7.2 程序准备
将驱动与程序上传至开发板,查看文件是否传输成功。
在终端输入“ls”命令。确认当前所需文件,已经被上传到当前的文件夹内了。
image.jpg
然后在root 状态下 insmod 安装驱动。“lsmod”查看当前安装的驱动。
image.jpg
在管理员权限下,终端输入“chmod +x key_app_v1”,改变应用程序的权限。
image.jpg
通过以上操作可以确定应用程序,与驱动均已准备完成。
7.3 实验结果
在终端输入“./key_app_v1”,输出如下。可以看到,该应用程序每隔3秒就会读取了PL_BUT的值。按钮松开时是高电平,获取的值为1。
image.jpg
按住PL_BUT,再来查看终端输出,输出为0。
image.jpg
测试成功。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则