米联客MLK-L2-CZ06-7020 ZYNQ7020 Linux驱动HelloWorld实战文档

米联客MLK-L2-CZ06-7020 ZYNQ7020 Linux驱动HelloWorld实战文档
米联客MLK-L2-CZ06-7020 ZYNQ7020 Linux驱动HelloWorld实战文档文档基础信息文档名称MLK-L2 ZYNQ7020 Linux字符驱动HelloWorld实战教程适用硬件米联客MLK-L2-CZ06-7020XC7Z020配套教程米联客2024版ZYNQ Linux驱动开发篇 第1章软件环境Vivado2021.1 Vitis2021.1 米联客Ubuntu交叉编译虚拟机适配人群ZYNQ嵌入式Linux驱动零基础学习者一、实验环境说明1.1 硬件平台米联客MLK-L2-CZ06-7020 ZYNQ7020开发板搭载双核Cortex-A9处理器支持SD卡启动、串口调试、网口SSH文件传输是嵌入式ZYNQ Linux驱动开发的入门主流硬件平台。1.2 全套软件环境1. Windows端开发工具Vivado 2021.1、Vitis 2021.12. 交叉编译虚拟机米联客官方uisrc-lab-xlnx Ubuntu编译环境3. 交叉编译工具链arm-linux-gnueabihf4. 开发板系统米联客预编译Ubuntu根文件系统镜像1.3 实验学习目标1. 区分Linux三大设备字符设备、块设备、网络设备熟练掌握字符设备应用场景2. 吃透标准Linux字符驱动完整模块化开发模板掌握驱动基础框架3. 熟练使用class_create、device_create函数自动创建设备节点4. 掌握copy_to_user、copy_from_user函数实现内核与用户空间数据交互5. 打通完整开发链路Vivado硬件工程搭建 → Vitis设备树配置 → 虚拟机交叉编译 → 开发板模块加载与调试。1.4 基础核心原理Linux操作系统严格划分为用户空间应用程序APP和内核空间驱动程序两个空间内存相互隔离无法直接通过指针访问数据必须依靠专用拷贝函数完成数据传递。字符设备以字节流顺序读写数据无随机访问特性日常开发中LED、按键、串口、ADC外设均属于典型字符设备。Linux设备号分为主设备号与次设备号主设备号用于区分不同类型的驱动程序次设备号用于区分同一驱动下的多个外设。本次实验采用动态分配主设备号方式系统可自动在/dev目录生成设备文件/dev/KernelPrint_0。二、VivadoVitis硬件工程搭建流程2.1 Vivado导出XSA硬件描述文件1. 打开米联客配套soc_prj工程双击system.bd块设计文件2. 双击ZYNQ7020 IP核采用米联客默认PS配置开启DDR、SD、UART、GPIO全部基础外设3. 点击菜单栏Generate Bitstream编译生成PL比特流文件4. 依次点击菜单File → Export → Export Hardware弹窗勾选Include bitstream导出至soc_hw文件夹最终生成system_wrapper.xsa硬件描述文件。2.2 Vitis创建设备树工程1. 打开Vitis软件新建工作空间并命名为soc_sdk2. 点击Xilinx → Software Repositories导入配套device-tree-xlnx设备树模板3. 新建平台工程File → New → Platform Project工程名设置为soc_base导入上一步生成的xsa文件4. 操作系统选择device_tree处理器默认Cortex-A95. 点击编译按钮自动生成fsbl.elf、system_wrapper.bit、基础设备树文件备用。2.3 虚拟机编译SD系统镜像1. 将编译完成的bit、fsbl、dts设备树文件拷贝至Ubuntu虚拟机2. 终端进入米联客环境目录/home/uisrc/uisrc-lab-xlnx3. 依次执行环境编译脚本bashsource scripts/mz7xcfg.sh./move_files.sh./make_uboot.sh./make_kernel.sh./create_image.sh4. 将生成的完整SD系统镜像烧录至SD卡插入MLK-L2开发板拨码开关调整为SD启动模式ON OFF OFF。三、全套可直接运行源码3.1 内核驱动程序 KernelPrint.cc#include linux/init.h#include linux/module.h#include linux/uaccess.hstatic char readbuf[100];static char writebuf[100];static char message[] This message comes from kernel.;static int drive_major;static struct class *KernelPrint_cls;// 设备打开函数static int KernelPrint_open(struct inode *inode, struct file *filp){printk(-KernelPrint open-\n);return 0;}// 内核向应用层发送数据static ssize_t KernelPrint_read(struct file *filp, char __user *buf, size_t count, loff_t *fops){int flag 0;memcpy(readbuf, message, sizeof(message));flag copy_to_user(buf, readbuf, count);if(flag 0)printk(Kernel send data success!\n);elseprintk(Kernel send data failed!\n);printk(-KernelPrint read-\n);return 0;}// 应用层向内核写入数据static ssize_t KernelPrint_write(struct file *filp, const char __user *buf, size_t count, loff_t *fops){int flag 0;flag copy_from_user(writebuf, buf, count);if(flag 0)printk(KERN_CRIT Kernel receive data: %s\n, writebuf);elseprintk(Kernel receive data failed!\n);printk(-KernelPrint write-\n);return 0;}// 设备关闭释放函数static int KernelPrint_release(struct inode *inode, struct file *filp){printk(-KernelPrint release-\n);return 0;}// 文件操作绑定结构体static struct file_operations drive_fops {.owner THIS_MODULE,.open KernelPrint_open,.read KernelPrint_read,.write KernelPrint_write,.release KernelPrint_release,};// 驱动加载入口函数static __init int KernelPrint_init(void){printk(-------^v^-------\n);printk(-KernelPrint init-\n);// 动态申请主设备号drive_major register_chrdev(0, KernelPrint, drive_fops);if(drive_major 0){printk(register chrdev faile!\n);return drive_major;}printk(register chrdev ok!\n);// 创建设备类KernelPrint_cls class_create(THIS_MODULE, KernelPrint_class);printk(class create ok!\n);// 自动生成/dev下设备节点device_create(KernelPrint_cls, NULL, MKDEV(drive_major, 0), NULL, KernelPrint%d, 0);printk(device create ok!\n);return 0;}// 驱动卸载入口函数static __exit void KernelPrint_exit(void){printk(-------^v^-------\n);printk(-KernelPrint exit-\n);// 反向释放所有资源device_destroy(KernelPrint_cls, MKDEV(drive_major, 0));class_destroy(KernelPrint_cls);unregister_chrdev(drive_major, KernelPrint);}module_init(KernelPrint_init);module_exit(KernelPrint_exit);MODULE_LICENSE(GPL);MODULE_AUTHOR(msxbo);3.2 用户层测试程序 KernelPrintApp.cc#include stdio.h#include unistd.h#include sys/types.h#include sys/stat.h#include fcntl.h#include stdlib.h#include string.hint main(int argc, char *argv[]){int fd, retvalue;char *filename;char readbuf[100], writebuf[100];filename argv[1];fd open(filename, O_RDWR);if(fd 0){printf(Cant open file %s\n, filename);return -1;}switch (*argv[2]){case r:if(argc ! 3){printf(读取用法./KernelPrintApp /dev/KernelPrint_0 r\n);return -1;}retvalue read(fd, readbuf, 100);if(retvalue 0)printf(Read failed!\n);elseprintf(User receive data: %s\n, readbuf);break;case w:if(argc ! 4){printf(写入用法./KernelPrintApp /dev/KernelPrint_0 w 自定义字符串\n);return -2;}memcpy(writebuf, argv[3], strlen(argv[3]));retvalue write(fd, writebuf, 50);if(retvalue 0)printf(Write file failed!\n);elseprintf(Write file success!\n);break;default:printf(未知操作仅支持 r(读) / w(写)\n);break;}close(fd);return 0;}3.3 交叉编译Makefile文件makefile# 米联客固定内核路径请勿修改KERNEL_DIR /home/uisrc/uisrc-lab-xlnx/sources/kernelexport ARCHarmexport CROSS_COMPILEarm-linux-gnueabihf-CURRENT_DIR $(shell pwd)MODULE KernelPrintAPP KernelPrintAppall :make -C $(KERNEL_DIR) M$(CURRENT_DIR) modulesrm -rf *.symvers *.order *.o *.mod.o *.mod.cifneq ($(APP), )$(CROSS_COMPILE)gcc $(APP).c -o $(APP)endifclean :make -C $(KERNEL_DIR) M$(CURRENT_DIR) cleanrm $(APP)obj-m $(MODULE).o四、虚拟机交叉编译操作步骤1. 将KernelPrint.c、KernelPrintApp.c、Makefile三个文件放置在同一文件夹上传至米联客Ubuntu虚拟机2. 在文件目录下打开终端执行make命令进行编译3. 编译产物说明KernelPrint.ko为Linux内核驱动模块KernelPrintApp为ARM架构可执行测试程序4. 通过Xshell、PSCP、U盘等方式将两个编译产物传输至MLK-L2开发板。五、开发板完整测试流程5.1 硬件连接要求MLK-L2-CZ06-7020开发板插入烧录好系统的SD卡拨码开关设置为SD启动模式USB串口连接电脑用于调试打印可接网线实现SSH高速文件传输。5.2 开发板终端执行命令1. 文件权限配置bashls KernelPrint.ko KernelPrintAppchmod 777 KernelPrintApp2. 加载驱动模块bashsudo insmod KernelPrint.kolsmodls /dev/KernelPrint_03. 读取测试应用读取内核预设字符串bash./KernelPrintApp /dev/KernelPrint_0 r预期输出User receive data: This message comes from kernel.4. 写入测试应用向内核传输自定义字符串bash./KernelPrintApp /dev/KernelPrint_0 w Hello MLK-L2 ZYNQ7020dmesg | tail内核预期打印Kernel receive data: Hello MLK-L2 ZYNQ70205. 卸载驱动模块bashsudo rmmod KernelPrint六、核心知识点总结1. 字符驱动标准接口open、read、write、release依靠file_operations结构体完成应用程序与驱动的绑定2. 空间数据交互规则copy_to_user实现内核数据传输至用户空间copy_from_user实现用户数据传输至内核空间3. 自动创建设备流程register_chrdev注册驱动 → class_create创建设备类 → device_create生成/dev设备节点卸载流程反向执行4. 内核模块规范module_init为模块加载入口、module_exit为模块卸载入口必须配置MODULE_LICENSE(GPL)避免内核污染警告5. 硬件适配要点MLK-L2开发板必须使用米联客配套编译环境禁止随意修改内核路径、交叉工具链配置避免编译报错。七、常见问题与解决方案1. make编译报错找不到内核原因虚拟机未配置环境变量解决方案进入米联客环境目录执行source scripts/mz7xcfg.sh后重新编译。2. insmod加载驱动提示版本不匹配原因编译内核源码与开发板运行内核版本不一致解决方案使用米联客原厂配套内核源码编译驱动。3. 无法打开/dev/KernelPrint_0设备原因驱动未加载或文件权限不足解决方案重新加载驱动使用root权限执行测试程序。4. 读写数据为空、乱码原因缓冲区长度溢出、数据拷贝参数错误解决方案核对缓冲区大小保证拷贝参数不越界。5. 串口无调试打印信息原因开发板拨码错误、串口波特率不匹配解决方案确认SD启动拨码串口工具波特率设置为115200。八、配套资源信息官方技术网站https://www.uisrc.com配套教程米联客2024版ZYNQ Linux驱动开发篇 第1章开发板型号MLK-L2-CZ06-7020 ZYNQ7020