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

[实战] Petalinux驱动开发以及代码框架解读

目录

  • Petalinux驱动开发以及代码框架解读
    • 一、引言
    • 二、步骤
      • 2.1 创建PetaLinux工程
      • 2.2 配置硬件描述文件
      • 2.3 设备树配置
      • 2.4 建立驱动框架
      • 2.5 编辑 `.bb` 文件
      • 2.6 编写驱动文件
      • 2.7 编写 `Makefile`
      • 2.8 验证配方配置
      • 2.9 集成驱动到 RootFS
      • 2.10 全系统编译与部署
      • 2.11 启动验证
    • 三、框架解读
      • 3.1 模块基本信息
      • 3.2 模块参数
      • 3.3 数据结构:customGpioExport_local
      • 3.4 中断处理函数
      • 3.5 Probe函数:设备初始化
        • **功能**:
        • **流程**:
      • 3.5 Remove函数:资源释放
      • 3.7 设备树匹配
      • 3.8 平台驱动结构体
      • 3.9 模块初始化和退出
      • 3.10 代码执行流程
      • 3.11 潜在改进点
    • 四、结语

Petalinux驱动开发以及代码框架解读

一、引言

Petalinux是Xilinx提供的Linux开发工具链。通过Petalinux,可以很轻松的完成Xilinx SOC的Linux系统定制,驱动和应用开发。本文主要从驱动开发角度,基于petalinux2022.2版本,介绍如何同通过Petalinux开发自己的驱动。


二、步骤

安装Petalinux以及使用Vivado创建SOC工程,这些基础步骤,网上由太多教程,这里就不累述了。这里假设大家已经安装好了Vivado,Petalinux,并已经通过Vivado工程导出了硬件描述文件(.xsa文件)。

2.1 创建PetaLinux工程

petalinux-create -t project --name zynq_gpio_demo --template zynq
cd zynq_gpio_demo

2.2 配置硬件描述文件

  1. 将Vivado生成的system.xsa文件复制到工程目录。
  2. 导入硬件配置:
    petalinux-config --get-hw-description=.
    

2.3 设备树配置

project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi中添加GPIO节点:

/ {custom_gpios {compatible = "customGpioExport";status = "okay";gpios = <&gpio0 54 GPIO_ACTIVE_HIGH>,  // EMIO GPIO0<&gpio0 55 GPIO_ACTIVE_HIGH>; // EMIO GPIO1};
};
  • 关键属性
    • compatible:驱动匹配标识符。
    • gpios:指定GPIO控制器、引脚号和激活电平。

2.4 建立驱动框架

petalinux为用户开发自己的驱动建立了完整易用的驱动框架,可以通过petalinux-create -t modules命令创建,如下所示:

petalinux-create -t modules --name customGpioExport --enable

通过上述指令,就会在{petalinux project}/project-spec/meta-user/recipes-modules/目录下生成custom_gpio_export 目录,在该目录下,自动生成了custom_gpio_export .bb文件以及files文件加,在files文件夹下,成了驱动框架文件customGpioExport .c以及Makefile,以下是目录结构图:

{petalinux project}/
└── project-spec/└── meta-user/└── recipes-modules/└── customGpioExport/├── customGpioExport.bb└── files/├── customGpioExport.c└── Makefile

2.5 编辑 .bb 文件

打开{petalinux project}/project-spec/meta-user/recipes-modules/customGpioExport下的customGpioExport.bb文件并修改内容如下:

# 配方基础配置
SUMMARY = "Custom GPIO Export Driver"
LICENSE = "GPL-2.0-only"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6"# 定义模块名称和源码路径
MODULE_NAME = "customGpioExport"
SRC_URI = " \file://${MODULE_NAME}.c \file://Makefile \
"# 指定源码目录(默认为当前目录)
S = "${WORKDIR}"# 继承内核模块构建类
inherit module# 定义模块编译参数
EXTRA_OEMAKE:append = " \KERNEL_SRC=${STAGING_KERNEL_DIR} \KERNEL_VERSION=${KERNEL_VERSION} \
"# 安装目标(将模块复制到 rootfs 的 /lib/modules/ 目录)
do_install() {install -d ${D}/lib/modules/${KERNEL_VERSION}/extrainstall -m 0644 ${B}/${MODULE_NAME}.ko ${D}/lib/modules/${KERNEL_VERSION}/extra/
}# 定义模块依赖(可选)
# DEPENDS = "virtual/kernel"

关键配置解释

  • SRC_URI
    指定驱动源码路径。此处假设源码文件为 customGpioExport.cMakefile,位于 files 子目录。
    若源码文件名为 customGpioExport.c,需同步修改:

    SRC_URI = " \file://customGpioExport.c \file://Makefile \
    "
    
  • inherit module
    继承内核模块构建类,自动处理模块编译和签名(如需 Secure Boot)。

  • EXTRA_OEMAKE
    make 命令传递额外参数:

    • KERNEL_SRC:指向内核源码目录。
    • KERNEL_VERSION:内核版本号(自动填充)。
  • do_install
    定义模块安装逻辑,将生成的 .ko 文件复制到 rootfs 的 /lib/modules/$(uname -r)/extra/ 目录。


2.6 编写驱动文件

根据自己需要以customGpioExport.c为基础框架,增加自己模块需要的功能实现即可,不在这里累述。

2.7 编写 Makefile

确保 files/Makefile 内容如下(适配内核模块编译):

obj-m := custom_gpio_export.oKERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)all:$(MAKE) -C $(KERNEL_SRC) M=$(PWD) modulesclean:$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean
  • 注意
    • obj-m 后的目标名必须与 .c 文件名一致(例如 customGpioExport.ccustomGpioExport.o)。
    • 若源码文件名为 customGpioExport.c,需修改为:
      obj-m := customGpioExport.o
      customGpioExport-objs := customGpioExport.o
      

2.8 验证配方配置

执行以下命令检查语法和路径:

# 进入 PetaLinux 工程根目录
cd zynq_gpio_demo# 运行 BitBake 解析配方
petalinux-build -c customGpioExport --force
  • 预期输出
    若配置正确,将输出编译日志并在 build/tmp/work/.../customGpioExport/ 下生成 .ko 文件。

  • 常见错误

    • 文件未找到:检查 SRC_URIfiles/ 目录中的文件名是否一致。
    • 编译错误:查看 build/tmp/work/.../temp/log.do_compile 中的详细日志。

2.9 集成驱动到 RootFS

petalinux-config 中启用模块自动加载:

petalinux-config -c rootfs

进入菜单:

Filesystem Packages  --->module  --->customGpioExport  --->[*] customGpioExport

2.10 全系统编译与部署

petalinux-build
petalinux-package --boot --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/system.bit --u-boot

2.11 启动验证

系统启动后检查模块加载:

# 查看模块是否加载
lsmod | grep customGpioExport# 手动加载(若未自动加载)
modprobe customGpioExport# 检查 GPIO 导出状态
ls /sys/class/gpio/

三、框架解读

3.1 模块基本信息

代码开头通过宏定义了模块的基本信息:

  • MODULE_LICENSE(“GPL”):声明模块遵循GPL协议。
  • MODULE_AUTHORMODULE_DESCRIPTION:提供作者和模块描述。
  • MODULE_DEVICE_TABLE(of, …):用于与设备树中的节点匹配(后文详述)。

3.2 模块参数

通过module_param定义了两个模块参数:

unsigned myint = 0xdeadbeef;
char *mystr = "default";
module_param(myint, int, S_IRUGO);  // 整型参数
module_param(mystr, charp, S_IRUGO); // 字符串参数
  • 这些参数可在加载模块时通过命令行指定,例如:
    insmod customGpioExport.ko myint=1234 mystr="hello"
    
  • 参数权限为S_IRUGO,表示用户空间可读但不可写。

3.3 数据结构:customGpioExport_local

struct customGpioExport_local {int irq;                   // 中断号unsigned long mem_start;   // 内存起始地址unsigned long mem_end;     // 内存结束地址void __iomem *base_addr;   // 映射后的虚拟地址
};
  • 该结构体保存设备的硬件资源信息,如中断号、寄存器物理地址及其映射后的虚拟地址。

3.4 中断处理函数

static irqreturn_t customGpioExport_irq(int irq, void *lp) {printk("customGpioExport interrupt\n");return IRQ_HANDLED;
}
  • 当设备触发中断时,此函数被调用。
  • 当前仅打印一条日志,实际应用中需添加具体的中断处理逻辑(如读取状态寄存器、处理数据等)。
  • 注意request_irq的第三个参数(标志位)为0,未指定触发方式(如上升沿或电平触发),可能需要根据硬件需求调整。

3.5 Probe函数:设备初始化

static int customGpioExport_probe(struct platform_device *pdev)
功能
  • 在设备树匹配到驱动时调用,负责初始化设备资源(内存、中断等)。
流程
  1. 获取内存资源

    r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    
    • 从设备树中获取寄存器内存范围(mem_startmem_end)。
  2. 分配结构体内存

    lp = kmalloc(sizeof(struct customGpioExport_local), GFP_KERNEL);
    
    • customGpioExport_local分配内存,保存设备信息。
  3. 请求内存区域

    request_mem_region(lp->mem_start, ...);
    
    • 锁定物理内存区域,防止其他驱动占用。
  4. 内存映射

    lp->base_addr = ioremap(lp->mem_start, ...);
    
    • 将物理地址映射为内核虚拟地址,后续可通过iowrite32/ioread32等函数访问寄存器。
  5. 获取中断资源

    r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    
    • 若设备树中定义了中断,注册中断处理函数:
      request_irq(lp->irq, &customGpioExport_irq, 0, ...);
      
  6. 错误处理

    • 若某步骤失败,依次释放已申请的资源(如release_mem_regioniounmapkfree等)。

3.5 Remove函数:资源释放

static int customGpioExport_remove(struct platform_device *pdev)
  • 在模块卸载或设备移除时调用。
  • 释放所有资源:中断、虚拟地址映射、内存区域及结构体内存。

3.7 设备树匹配

static struct of_device_id customGpioExport_of_match[] = {{ .compatible = "vendor,customGpioExport", },{ /* end of list */ },
};
  • 通过compatible属性与设备树中的节点匹配。例如,设备树中需包含:
    customGpioExport@0x12340000 {compatible = "vendor,customGpioExport";reg = <0x12340000 0x1000>;interrupts = <0 29 4>;
    };
    

3.8 平台驱动结构体

static struct platform_driver customGpioExport_driver = {.driver = {.name = DRIVER_NAME,.of_match_table = customGpioExport_of_match,},.probe = customGpioExport_probe,.remove = customGpioExport_remove,
};
  • 定义平台驱动的名称、设备树匹配表、Probe和Remove函数。

3.9 模块初始化和退出

  • 初始化

    static int __init customGpioExport_init(void) {platform_driver_register(&customGpioExport_driver);printk("Hello module world.\n");
    }
    
    • 注册平台驱动,并打印初始化信息。
  • 退出

    static void __exit customGpioExport_exit(void) {platform_driver_unregister(&customGpioExport_driver);printk("Goodbye module world.\n");
    }
    
    • 注销驱动并清理资源。

3.10 代码执行流程

  1. 模块加载

    • 执行customGpioExport_init,注册平台驱动。
    • 内核扫描设备树,若找到匹配的compatible节点,调用customGpioExport_probe初始化设备。
  2. 设备初始化

    • 申请内存、映射地址、注册中断(如有)。
  3. 模块卸载

    • 执行customGpioExport_exit,注销驱动并调用customGpioExport_remove释放资源。

3.11 潜在改进点

  1. 中断标志位request_irq未指定触发方式(如IRQF_TRIGGER_RISING),需根据硬件配置。
  2. GPIO功能:当前代码未实现GPIO的导出或操作逻辑,需补充gpio_requestgpio_direction_output等函数。
  3. 模块参数应用myintmystr未被使用,可扩展为配置参数(如设置GPIO初始状态)。

总结
此代码是一个典型的内核模块框架,实现了设备树匹配、资源管理、中断处理等基础功能,但具体功能(如GPIO操作)需进一步扩展。开发者可根据实际需求,在Probe函数中添加硬件初始化代码,并在中断处理函数中实现业务逻辑。


四、结语

在petalinux框架下,完成linux驱动开发门槛大大降低,效率提高不少,希望这篇文章能够抛砖引玉,对大家有所帮助。


研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)


http://www.xdnf.cn/news/231823.html

相关文章:

  • Mac下安装Python3,并配置环境变量设置为默认
  • 深度学习论文: Describe Anything: Detailed Localized Image and Video Captioning
  • 分组密码算法ShengLooog设计原理详解
  • 如何正确使用日程表
  • 【Java】equals、==、hashcode详解
  • 单片机的各个种类及其详细介绍
  • 复杂度和顺序表(双指针方法)
  • 国标GB28181视频平台EasyGBS在物业视频安防管理服务中的应用方案​
  • 进程地址空间
  • 在柯希霍夫积分法偏移成像中,旅行时计算中振幅和相位信息
  • 兰亭妙微:全流程交互设计和设计前后对比
  • 详细说明c++函数传参常量引用const T传递和值传递的区别
  • 【25软考网工】第四章(4)无线局域网WLAN安全技术、无线个人网WPAN
  • 【Kubernets知识】Secret组件更新大全
  • 设备安全管理:AI赋能的智能守护者
  • 建筑兔零基础python自学记录88|time库文本进度条(下)11
  • x-cmd install | Tewi - 终端里的 Transmission 掌控者,功能全面的 BT 下载管理工具!
  • 适配 AGP8.5,maven 私服发布报错(七)
  • Rust 学习笔记:枚举与模式匹配
  • HTTP 快速解析
  • php+mysql活动报名学生选课产品预定旅游报名系统网站源码
  • Spyglass:官方Hands-on Training(一)
  • 【容器化】Linux环境Docker在线与离线安装手册
  • vscode中设置eslint保存时自动格式化未生效
  • 网易爆米花 1.8.8 | 免费无广告,支持多网盘聚合和智能刮削技术,提供顶级画质和逼真音效的影视管理应用
  • 【大模型系列篇】Qwen3开源全新一代大语言模型来了,深入思考,更快行动
  • Python 用一等函数重新审视“命令”设计模式
  • CMake解析参数用法示例
  • 【模型量化】量化基础
  • 大连理工大学选修课——机器学习笔记(7):集成学习及随机森林