当前位置: 首页 > news >正文

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

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

文档基础信息

文档名称:MLK-L2 ZYNQ7020 Linux字符驱动HelloWorld实战教程

适用硬件:米联客MLK-L2-CZ06-7020(XC7Z020)

配套教程:米联客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.1

2. 交叉编译虚拟机:米联客官方uisrc-lab-xlnx Ubuntu编译环境

3. 交叉编译工具链:arm-linux-gnueabihf

4. 开发板系统:米联客预编译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。

二、Vivado+Vitis硬件工程搭建流程

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_sdk;

2. 点击Xilinx → Software Repositories,导入配套device-tree-xlnx设备树模板;

3. 新建平台工程File → New → Platform Project,工程名设置为soc_base,导入上一步生成的xsa文件;

4. 操作系统选择device_tree,处理器默认Cortex-A9;

5. 点击编译按钮,自动生成fsbl.elf、system_wrapper.bit、基础设备树文件,备用。

2.3 虚拟机编译SD系统镜像

1. 将编译完成的bit、fsbl、dts设备树文件拷贝至Ubuntu虚拟机;

2. 终端进入米联客环境目录:/home/uisrc/uisrc-lab-xlnx;

3. 依次执行环境编译脚本:

bash
source scripts/mz7xcfg.sh
./move_files.sh
./make_uboot.sh
./make_kernel.sh
./create_image.sh

4. 将生成的完整SD系统镜像烧录至SD卡,插入MLK-L2开发板,拨码开关调整为SD启动模式(ON OFF OFF)。

三、全套可直接运行源码

3.1 内核驱动程序 KernelPrint.c

c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/uaccess.h>

static 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");
else
printk("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);
else
printk("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.c

c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

int 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("Can't 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");
else
printf("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");
else
printf("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/kernel
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
CURRENT_DIR = $(shell pwd)
MODULE = KernelPrint
APP = KernelPrintApp

all :
make -C $(KERNEL_DIR) M=$(CURRENT_DIR) modules
rm -rf *.symvers *.order *.o *.mod.o *.mod.c
ifneq ($(APP), )
$(CROSS_COMPILE)gcc $(APP).c -o $(APP)
endif

clean :
make -C $(KERNEL_DIR) M=$(CURRENT_DIR) clean
rm $(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. 文件权限配置

bash
ls KernelPrint.ko KernelPrintApp
chmod 777 KernelPrintApp

2. 加载驱动模块

bash
sudo insmod KernelPrint.ko
lsmod
ls /dev/KernelPrint_0

3. 读取测试(应用读取内核预设字符串)

bash
./KernelPrintApp /dev/KernelPrint_0 r

预期输出:User receive data: This message comes from kernel.

4. 写入测试(应用向内核传输自定义字符串)

bash
./KernelPrintApp /dev/KernelPrint_0 w Hello MLK-L2 ZYNQ7020
dmesg | tail

内核预期打印:Kernel receive data: Hello MLK-L2 ZYNQ7020

5. 卸载驱动模块

bash
sudo 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

http://www.jsqmd.com/news/1109887/

相关文章:

  • GPU并行计算架构与性能优化实战指南
  • 如何用TVBoxOSC打造你的智能电视文档中心?
  • 2026在线考试系统采购避坑指南与终极推荐
  • 【总结】2026年中总结
  • 【Agent 实战】Phase 3:LangGraph 复杂工作流(代码审查 + 条件分支 + 人机确认 interrupt)
  • Agent Triangle:2026企业AI落地的三条组织化路径
  • 大模型参数量谣言辨析:MoE架构与真实激活机制科普
  • 备份不该是负担,养成随手存一份的习惯有多重要
  • ConcurrentHashMap的putIfAbsent方法详解与应用_元一软件
  • 终极Windows任务栏监控神器:TrafficMonitor插件完全指南
  • 润博一站式活动服务适配企业
  • STM32嵌入式开发终极指南:从零构建智能温控系统
  • 魔兽世界技能自动化终极方案:GSE宏编辑器完全指南
  • 5分钟快速搭建个人HTTP文件服务器:chfsgui图形化共享工具完整指南
  • Linux防火墙实战:从Firewalld/UFW配置到云安全组联动
  • 暗黑破坏神2存档编辑器技术解析:基于MPQ数据解析的Web可视化编辑方案
  • 【分布式训练中 各种并行方案 分别用什么通信 为什么?比如DP会用到 ALL reduce】
  • paperxie AI 科研绘图:一站式科研出图工具,告别 Origin 与 Visio 繁琐制图
  • 2024年AI原生应用开发实战指南
  • 2026年横评:16款降AIGC工具横评,这款降AI率效果一骑绝尘!
  • 6DoF运动跟踪技术:IIM-42652与STM32L162ZE实战解析
  • CM/Ethyl/HP-HA,HA-Glycyrrhetinic acid,甘草次酸修饰透明质酸的特点
  • 【BUG已解决】CondaHTTPError: HTTP 000 CONNECTION FAILED for url 解决方案
  • 无监督学习与聚类算法实战解析
  • 大模型开发实战:轻量化技术与推理优化新范式
  • 全日制mba论文选题怎么选
  • 音乐转录神器:一键将钢琴录音转为专业乐谱
  • 2026年选空间设计公司,这3家专业度拉满
  • 5大核心功能解析:BepInEx如何成为Unity游戏模组开发的首选框架
  • IMU传感器与MCU实现6DoF运动追踪技术解析