网站优化怎么做今天的新闻联播文字版
网站优化怎么做,今天的新闻联播文字版,wordpress商城微信支付,wordpress 图文排版1. 环境准备与内核配置#xff1a;为驱动开发铺好路
很多朋友第一次接触Petalinux驱动开发#xff0c;上来就想写代码、编译、上板#xff0c;结果第一步就被卡住了。最常见的问题就是#xff1a;“我的内核源码到底在哪里#xff1f;” 我刚开始用Petalinux2018.3的时候也…1. 环境准备与内核配置为驱动开发铺好路很多朋友第一次接触Petalinux驱动开发上来就想写代码、编译、上板结果第一步就被卡住了。最常见的问题就是“我的内核源码到底在哪里” 我刚开始用Petalinux2018.3的时候也踩过这个坑按照网上一些老教程去找build/tmp/work下的路径结果发现目录是空的或者根本对不上。这其实是因为Petalinux的工作流程和我们纯手动编译Linux内核不太一样它有一套基于Yocto的构建系统很多东西需要你执行了特定命令后才会生成。所以咱们的第一步不是急着写驱动而是先把“厨房”收拾好把“灶台”——也就是内核构建环境——给搭建起来。对于Petalinux2018.3我强烈建议你从一开始就搞清楚两个概念内部内核和外部内核。内部内核就是Petalinux工具链自带的、已经为你的硬件平台比如Zynq或Zynq MPSoC预配置好的内核源码树。使用内部内核的好处是省心Xilinx已经帮你把基本的设备树、硬件支持都配好了特别适合新手快速上手驱动开发。我们今天实战就从这个内部内核开始。那么怎么找到并激活这个内核呢关键的命令是petalinux-config -c kernel。你需要在你的Petalinux项目根目录下执行它。这个命令会启动一个内核配置菜单界面就像我们配置桌面Linux内核时的make menuconfig一样。第一次运行时Petalinux会去解析你的硬件平台信息来自你创建项目时指定的HDF或XSA文件并生成对应的内核源码构建环境。你不需要在这个菜单里做太多改动直接保存退出就行。这一步操作就像一个“开关”告诉Petalinux“我要开始用内核了请把内核源码树准备好。”执行完配置后再运行一次petalinux-build。注意这次构建的目的不是为了生成最终的镜像而是为了让内核的构建目录完全展开。构建完成后你再去找那个传说中的路径比如{你的项目}/build/tmp/work/plnx_zynqmp-xilinx-linux/linux-xlnx/4.14-xilinx-v2018.3gitAUTOINC.../linux-plnx_zynqmp-standard-build这时候它肯定就存在了。这个目录就是我们后续编写驱动时Makefile里需要指向的KERNEL_DIR。它里面包含了完整的内核头文件、编译脚本和模块构建所需的一切。我建议你把这个绝对路径记下来或者保存到一个环境变量里后面会频繁用到。1.1 安装与验证交叉编译工具链光有内核源码还不够我们是在x86的电脑上开发最终程序要跑在ARM架构的Zynq板卡上所以交叉编译工具链是绝对的核心。Petalinux在安装时其实已经自带了一套工具链但有时候我们可能需要单独使用它或者验证它是否正确。Petalinux2018.3安装后工具链的环境设置脚本通常位于安装目录下的settings.sh或settings64.sh64位系统。你只需要source一下这个脚本比如source /opt/pkg/petalinux/2018.3/settings.sh它就会自动设置好CROSS_COMPILE、ARCH等一堆环境变量。设置完后你可以在终端里输入echo $CROSS_COMPILE看看应该会输出aarch64-linux-gnu-对于Zynq UltraScale MPSoC或arm-linux-gnueabihf-对于Zynq-7000。这就是交叉编译器的前缀。为了确保万无一失我习惯再手动验证一下编译器是否能正常工作。打开终端直接输入命令aarch64-linux-gnu-gcc --version。如果能看到输出GCC的版本信息比如“gcc version 7.3.0”那就说明工具链已经就绪。这一步看似简单但能避免后面编译时出现“命令未找到”这种让人头疼的低级错误。记住后续我们写Makefile时里面用到的CROSS_COMPILE变量值就是这里看到的这个前缀。2. 编写驱动程序的Makefile连接主机与目标的桥梁找到了内核路径验证了工具链接下来就要动手写驱动了。但在写第一行C代码之前我们必须先搞定Makefile。这个Makefile是专门用来编译内核模块的它和编译普通应用程序的Makefile有很大不同。你可以把它想象成一个“翻译官”它知道如何利用我们刚才找到的内核构建环境KERNEL_DIR和交叉编译工具链把我们在x86电脑上写的C代码“翻译”成能在ARM板卡上运行的内核模块.ko文件。为什么不能直接用普通的gcc编译因为内核模块需要插入到运行中的内核里它必须使用和当前运行内核完全一致的内核头文件和函数符号。我们的Makefile核心任务就是“钻进”目标板卡的内核源码树里去进行编译。下面是我在项目中常用的一个Makefile模板我把它拆开揉碎了讲给你听。# 1. 指定内核源码树的绝对路径这就是你上一步找到的那个路径 KERNEL_DIR /home/your_username/petalinux_prj/my_project/build/tmp/work/.../linux-plnx_zynqmp-standard-build # 2. 指定架构和交叉编译器前缀非常重要 export ARCH arm64 export CROSS_COMPILE aarch64-linux-gnu- # 3. 获取当前驱动代码所在的目录 CURRENT_DIR $(shell pwd) # 4. 默认编译目标生成模块 all: make -C $(KERNEL_DIR) M$(CURRENT_DIR) modules # 5. 清理目标 clean: make -C $(KERNEL_DIR) M$(CURRENT_DIR) clean # 6. 指定要编译成模块的目标文件.o文件多个文件用空格隔开 obj-m hello_world.o我来解释一下关键点。KERNEL_DIR必须是你自己环境的绝对路径直接复制我的肯定不行。ARCH和CROSS_COMPILE这两个环境变量通过export导出它们会传递给内核的构建系统。make -C $(KERNEL_DIR)这个命令的意思是“先切换目录到KERNEL_DIR然后在那里执行make”。M$(CURRENT_DIR)是内核构建系统的一个特殊参数意思是“模块的源码在另一个目录$(CURRENT_DIR)里你到那边去编译”。最终编译过程实际上是在内核的构建系统中完成的只是源码是我们自己的。obj-m这一行是灵魂。它告诉内核构建系统“请把hello_world.o这个目标文件编译成一个可加载的内核模块”。如果你的驱动由多个.c文件组成比如main.c和helper.c那么你需要这样写obj-m mydriver.o然后额外加一行mydriver-objs : main.o helper.o表示mydriver.ko是由main.o和helper.o链接而成的。写好这个Makefile后把它保存到你的驱动源码目录下。接下来你就可以在这个目录下执行make命令了。如果一切配置正确你会看到编译输出信息飞速滚动最后生成一个hello_world.ko文件。这个.ko文件就是我们要放到板子上加载的驱动模块。3. 从Hello World到GPIO驱动实战代码解析有了Makefile这把钥匙我们终于可以打开驱动开发的大门了。让我们从最简单的“Hello World”开始然后再挑战一个实实在在的硬件操作——GPIO驱动。别怕我会把每一行关键代码都讲明白。3.1 Hello World模块驱动开发的“第一课”Hello World驱动不操作任何硬件它的唯一使命就是在加载和卸载时在内核日志里打印一句话。这就像学编程先写“Hello World”一样用来验证整个编译、加载的流程是否通畅。下面是完整的代码hello_world.c#include linux/module.h #include linux/kernel.h #include linux/init.h // 包含 __init 和 __exit 宏 // 模块加载时自动执行的函数 static int __init hello_init(void) { // 使用 printk 向内核日志输出信息KERN_INFO 是日志级别 printk(KERN_INFO Hello, World! Driver loaded successfully.\n); return 0; // 返回0表示初始化成功 } // 模块卸载时自动执行的函数 static void __exit hello_exit(void) { printk(KERN_INFO Goodbye, World! Driver unloaded.\n); } // 这两行是核心告诉内核哪个是入口函数哪个是出口函数 module_init(hello_init); module_exit(hello_exit); // 以下是一些模块声明信息非必需但强烈建议加上 MODULE_LICENSE(GPL); // 声明模块采用GPL协议必须要有 MODULE_DESCRIPTION(A simple Hello World driver for learning); MODULE_AUTHOR(Your Name Here); MODULE_VERSION(1.0);我来划几个重点。__init和__exit是给函数打的“标签”。__init修饰的函数会在初始化完成后其占用的内存被释放掉因为只用一次。__exit修饰的函数只有在模块被卸载时才会被编译进去。printk是内核的打印函数相当于用户空间的printf它的输出不会显示在终端上而是输出到内核环形缓冲区可以用dmesg命令查看。module_init和module_exit是最关键的宏它们把我们的hello_init和hello_exit函数注册为模块的入口和出口。把这个文件保存为hello_world.c确保Makefile里的obj-m hello_world.o然后make编译。你会得到hello_world.ko。通过scp命令把它拷贝到开发板上在板子的Linux终端里先insmod hello_world.ko加载模块再用dmesg | tail -5查看日志应该就能看到“Hello, World!”的欢迎词了。卸载用rmmod hello_world再看日志就会出现告别语。走通这一步恭喜你驱动开发的基本流程你已经掌握了3.2 GPIO驱动实战与硬件对话玩嵌入式怎么能不点个灯呢接下来我们搞一个实实在在的GPIO驱动。原始文章里给出的代码是针对特定硬件Zynq MPSoC的MIO引脚的寄存器地址都是硬编码的。我在这里会讲清楚它的框架和原理你可以根据自己板子的手册进行修改。一个完整的字符设备驱动框架通常包含这些部分模块加载/卸载函数、文件操作集合file_operations虽然示例里是空的但实际需要实现open、read、write等、设备号管理、设备类与设备节点创建以及最核心的硬件寄存器操作。原始代码的流程非常清晰模块加载 (led_drv_v1_init)注册字符设备获取主设备号。创建设备类 (class_create) 和设备节点 (device_create)。这样在板子的/dev目录下就会出现一个设备文件比如/dev/Myled0用户程序就可以通过操作这个文件来控制硬件了。内存映射这是驱动访问硬件寄存器的标准方式。CPU不能直接访问物理地址需要通过ioremap函数将GPIO控制器的物理地址如0xFF0A0000映射到内核的虚拟地址空间得到一个指针gpio_addr之后读写这个指针就相当于读写硬件寄存器。引脚复用像MIO40、MIO42这样的引脚可以配置为GPIO、UART等多种功能。代码里通过写IOU_SLCR模块的相应寄存器将其设置为GPIO模式。GPIO属性配置配置驱动电流、上下拉电阻、速率等。这些配置通常有默认值但精细控制时需要设置。设置方向将GPIO配置为输出模式通过GPIO_DIRM寄存器。初始输出电平将对应的GPIO数据寄存器位拉低或拉高以点亮或熄灭LED。硬件寄存器操作 代码中大量使用了ioread32和iowrite32这对函数。这是进行内存映射I/OMMIO的标准、安全的方式。它们的操作是先读出现有寄存器值然后用位操作清位、|置位修改特定位最后写回去。例如iowrite32((ioread32(GPIO_DIRM_1) | 0x00014000), GPIO_DIRM_1);就是将GPIO_DIRM_1寄存器的第14位和16位设置为1输出而不影响其他位。模块卸载 (led_drv_v1_exit)将硬件恢复到安全状态如熄灭LED。释放映射用iounmap释放掉ioremap申请的内核虚拟地址。这一步非常重要否则会造成资源泄漏。按创建顺序的逆序销毁设备节点、设备类注销设备号。这个驱动虽然还没有实现file_operations里的具体操作函数所以用户程序还无法控制它但它已经完成了从软件到硬件的整个通路建立。当你加载这个模块时LED应该会根据初始化代码的状态发生变化。这是一个非常宝贵的起点你可以在其基础上去补充open、write函数实现通过echo 1 /dev/Myled0这样的命令来控制LED亮灭。4. 交叉编译与上板调试最后的临门一脚代码写好了也编译出了.ko文件最后一步就是把它放到板子上运行和调试。这一步的坑也不少我分享几个实测下来很稳的经验。首先传输文件。我习惯用scp命令简单直接。在主机终端执行scp hello_world.ko root192.168.1.100:/home/root/。你需要将192.168.1.100替换成你开发板的实际IP地址。输入密码后文件就传过去了。如果网络配置复杂也可以用U盘或者SD卡拷贝。其次加载模块。通过串口或ssh登录到开发板的Linux终端进入模块所在目录执行insmod hello_world.ko。如果成功不会有任何输出这就是Linux的“没有消息就是好消息”哲学。这时立刻用dmesg查看内核日志你应该能看到驱动初始化函数里printk打印的信息。这是最重要的调试手段。如果insmod失败了通常会报错。最常见的几个错误和解决办法如下Invalid module format或version magic不匹配这几乎是必遇的坑意思是当前运行的内核版本、配置和你编译模块时用的内核版本不匹配。确保你Makefile里的KERNEL_DIR路径就是用来编译你板子上正在运行的这个内核的源码树。用uname -r查看板子内核版本和你主机上KERNEL_DIR的路径版本对比一下。Petalinux项目编译出的内核和模块具有强一致性用项目内的内核目录编译通常不会出这个问题。Unknown symbol模块依赖某个内核导出的函数或变量但没找到。可能是你用的内核配置没有导出那个符号或者你拼写错了。可以尝试在主机上到KERNEL_DIR目录下用grep命令搜索一下这个符号。File exists设备号或设备节点已经存在。先rmmod旧模块或者手动rm /dev/Myled0删除节点。对于GPIO这类硬件驱动加载后除了看日志还要观察硬件现象。用lsmod可以查看已加载的模块列表。调试时可以多加入一些printk输出关键寄存器的值帮助你理解程序执行到了哪一步寄存器的配置是否符合预期。最后模块卸载。使用rmmod hello_world注意不是rmmod hello_world.ko。同样卸载后记得dmesg一下确认退出函数被执行资源被正确释放。整个流程走下来你会发现Petalinux驱动开发的核心思路就是在主机上利用目标板内核的构建环境和交叉工具链进行编译在目标板上以内核模块的形式进行加载和运行。把环境配置、Makefile编写、驱动框架、硬件访问和调试方法这几点吃透你就能举一反三去开发更复杂的字符设备、平台设备甚至网络设备驱动了。我刚开始学的时候一个简单的LED驱动折腾了好几天不是路径不对就是编译出错但把这些问题都解决后后面的路就越走越顺了。驱动开发就是这样动手踩一遍坑比看十篇教程都管用。