建设网站是不是要买服务器,网站外链是什么意思,商家入驻网站开发,做配件出口上什么网站1. 从“买内核”到“改内核”#xff1a;一个智能车开发者的思维转变 最近和不少参加智能车竞赛的朋友聊天#xff0c;发现一个挺有意思的现象。大家遇到龙芯主控板上的编码器读数飘忽不定、PWM电机控制时好时坏这类问题时#xff0c;第一反应往往是#xff1a;“兄弟…1. 从“买内核”到“改内核”一个智能车开发者的思维转变最近和不少参加智能车竞赛的朋友聊天发现一个挺有意思的现象。大家遇到龙芯主控板上的编码器读数飘忽不定、PWM电机控制时好时坏这类问题时第一反应往往是“兄弟你那边有稳定可用的内核吗卖我一份呗。” 甚至有人直接找到我问我能不能直接出售我调试好的内核文件。说实话刚开始听到这种请求我心里是有点失落的。倒不是舍不得分享而是觉得咱们搞技术的尤其是参加这种硬核竞赛的解决问题的第一路径怎么就变成了“购买”呢这和我们当年泡在实验室里对着原理图和示波器死磕到底的劲头好像不太一样了。智能车开发尤其是基于龙芯这类国产平台其魅力恰恰在于“可控”和“深度”。你用的每一行驱动代码配置的每一个设备树节点都直接决定了你的小车是“健步如飞”还是“步履蹒跚”。内核就是这个系统的“大脑”和“神经中枢”。一个配置不当的内核就像是一个接线混乱的控制板传感器信号编码器传不进来控制指令PWM发不出去车子自然问题百出。但这些问题绝大多数都不是硬件缺陷而是软件配置特别是内核设备树Device Tree的配置问题。直接用一个“黑盒”的、不知其所以然的内核就像是开着一辆你不知道刹车在哪里的车速度快的时候心里永远是虚的。所以我决定不再仅仅提供“鱼”而是尝试分享“渔”的方法并直接开源我用于比赛的内核。这篇文章就是想把我从被各种配置困扰到最终让智能车稳定狂奔的实战经验掰开揉碎了讲清楚。我们会聚焦最让人头疼的引脚冲突比如SPI和PWM打架、外设驱动适配以及如何将这一切与你们手头的龙邱智能车库无缝结合。目标很简单让你能看懂内核配置能动手修改最终打造出专属于你自己赛车的、最稳定的“大脑”。让我们忘掉“买内核”的念头一起进入“改内核”的实战世界。2. 龙芯内核配置入门设备树才是真正的钥匙很多刚接触龙芯Linux开发的同学一听到“内核配置”就发怵总觉得要面对浩如烟海的make menuconfig菜单去勾选成千上万个看都看不懂的选项。其实对于智能车应用这种特定场景我们绝大多数工作并不在那个复杂的图形化配置界面里而是在一个叫做设备树Device Tree的文件中。你可以把设备树理解为一份写给内核的“硬件说明书”。内核启动时会读取这份说明书从而知道“哦我这块板子上GPIO65这个引脚是用来输出PWM1信号的GPIO40和GPIO41被接成了串口0而SPI2控制器和PWM0控制器不能同时开启因为它们共用了一些引脚资源。”我这次开源和讲解所基于的内核版本是linux-4.19-0802这是一个在龙芯2K0300平台上非常稳定且常用的版本。拿到内核源码后我们关心的核心目录是arch/loongarch/boot/dts/loongson/。这里面存放的.dts或.dtsi文件就是我们要修改的“硬件说明书”。其中.dtsi文件可以理解为“通用章节”会被多个板级.dts文件引用而最终的板级.dts文件则是针对我们手中这块具体开发板的完整说明书。2.1 引脚复用配置让每个引脚“名正言顺”龙芯芯片的引脚通常都有多种功能这叫引脚复用。一个物理引脚既可以作为普通的GPIO点灯也可以作为PWM输出控制电机还可以作为SPI的时钟线。具体让它干啥就在设备树里的pinctrl引脚控制器节点中定义。以配置PWM引脚为例。我们打开2k0300-pinctrl.dtsi文件搜索pwm0_pin你会看到类似下面的代码段pwm0_pin: pwm0-pin { loongson,pins LS2K0300_GPIO(4, 0) LS2K0300_FUNC(0); };这行代码是理解引脚配置的关键。LS2K0300_GPIO(4, 0)是什么意思呢龙芯2K0300的GPIO是按组管理的这里的(4, 0)表示GPIO组4的第0号引脚换算成大家更熟悉的全局GPIO编号就是GPIO64计算方式组号4 * 16 组内序号0 64。后面的LS2K0300_FUNC(0)表示将这个引脚配置为它的主功能对于这个引脚而言主功能可能就是PWM0。那么如果我们想使用PWM0到PWM3这四个通道分别对应GPIO64, 65, 66, 67并且它们都使用主功能配置就应该像下面这样pwm0_pin: pwm0-pin { loongson,pins LS2K0300_GPIO(4, 0) LS2K0300_FUNC(0); }; pwm1_pin: pwm1-pin { loongson,pins LS2K0300_GPIO(4, 1) LS2K0300_FUNC(0); // GPIO65 }; pwm2_pin: pwm2-pin { loongson,pins LS2K0300_GPIO(4, 2) LS2K0300_FUNC(0); // GPIO66 }; pwm3_pin: pwm3-pin { loongson,pins LS2K0300_GPIO(4, 3) LS2K0300_FUNC(0); // GPIO67 };在实际操作中你可能看到的是类似gpa4 0 0的写法这其实是上述宏定义的另一种等价表示意思就是“GPIO组A4即第4组的第0个引脚功能号为0”。理解了这个对应关系你就能自由地查阅芯片手册将任意引脚配置成你需要的功能了。2.2 解决外设冲突关闭“打架”的SPI配置好引脚功能只是第一步更关键的一步是解决外设间的资源冲突。这是智能车开发中最常见的“坑”。比如在我遇到的案例中SPI2控制器和PWM0-3通道就存在严重的引脚冲突。因为它们复用了GPIO64-GPIO67这一组引脚。你不可能让同一个引脚既作为SPI的时钟线传输数据又作为PWM输出方波控制电机。如果设备树中两者都使能了结果就是谁都无法正常工作或者行为极其诡异。这时我们需要打开最终的板级设备树文件比如loongson_2k0300_pai_99_wifi.dts。在这个文件里会引用并启用各种外设控制器。我们的任务就是找到冲突的源头并关闭不需要的那个。通常我们会选择保留PWM因为智能车的电机、舵机控制离不开它而SPI2或许有别的替代方案比如用SPI0或SPI1连接屏幕、传感器。在设备树文件中找到SPI2的节点它可能长这样spi2 { status okay; // 当前状态是启用 ... // 其他SPI设备定义 };为了给PWM让路我们需要将它的状态改为disabledspi2 { status disabled; // 关键关闭SPI2以释放PWM引脚 ... // 其他定义可以保留 };同时确保PWM控制器的状态是okaypwm { status okay; pinctrl-names default; pinctrl-0 pwm0_pin pwm1_pin pwm2_pin pwm3_pin; // 引用我们刚才配置的引脚 };这一步操作是解决很多外设无法识别、驱动加载失败问题的关键。它告诉内核“硬件上SPI2和PWM有冲突我选择使用PWM所以请忽略SPI2。” 修改后重新编译内核并烧写你就会发现PWM设备通常是/sys/class/pwm/pwmchip0等可以正常访问了。3. 实战为智能车打造专属内核配置方案理解了基本原理我们就可以动手为自己赛车定制内核了。下面我以一套典型的智能车外设配置为例详细说明从设备树修改到与龙邱库适配的全过程。这套配置包括了电机、编码器、舵机、按键、屏幕等核心模块。3.1 我的引脚分配与设备树编写首先你需要一份清晰的引脚规划表。这是我根据龙芯2K0300开发板和常见智能车模块设计的一份方案你可以直接参考也可以根据自己的布线调整功能模块使用引脚复用功能 (主/第一/第二)设备树中的配置要点左电机 PWMGPIO65PWM1 / SPI2_MISO / UART5_TX在pinctrl中配置为PWM1确保SPI2已禁用。右电机 PWMGPIO66PWM2 / SPI2_MOSI / UART9_TX在pinctrl中配置为PWM2确保SPI2已禁用。左编码器 A相GPIO64PWM0 / SPI2_CLK / UART5_RX关键此处仅作GPIO输入用。需在pinctrl中配置为GPIO输入模式而非PWM0。同时确保SPI2禁用。右编码器 A相GPIO67PWM3 / SPI2_CS / UART9_RX同上配置为GPIO输入模式。舵机 PWMGPIO88TIM2_CH2 / SDIO1_D6 / PWM2龙芯2K0300的PWM控制器通常只有4路(PWM0-3)。GPIO88的PWM是第二复用功能可能需通过其他定时器或GPIO模拟实现。更简单的做法是使用硬件PWM2(GPIO66)或PWM3(GPIO67)之一或使用软件定时器模拟。龙邱屏幕 (SPI)GPIOxxSPI0 或 SPI1 相关引脚避开冲突的SPI2。将屏幕连接到SPI0或SPI1并在设备树中正确启用对应的SPI控制器和片选引脚。按键/拨码开关GPIO43, 42, 45, 44...GPIO / UART / ...配置为GPIO输入模式并启用内部上拉或下拉电阻根据电路设计。调试串口GPIO41(TX), 40(RX)UART0通常默认已启用用于连接电脑进行调试打印。根据上表我们需要综合编写pinctrl配置和板级设备树文件。核心原则是一个物理引脚在同一时刻只能有一种功能被激活。对于编码器引脚GPIO64, 67虽然它们硬件上支持PWM但我们只用来读取脉冲所以要在设备树中明确设置为GPIO输入。这需要在pinctrl中创建一个新的节点例如encoder_pins: encoder-pins { loongson,pins LS2K0300_GPIO(4, 0) LS2K0300_FUNC(1) // GPIO64 设为GPIO功能假设FUNC1是GPIO LS2K0300_GPIO(4, 3) LS2K0300_FUNC(1) // GPIO67 设为GPIO功能 ; };然后在板级.dts文件中确保这些引脚被应用到正确的控制器上通常是通过GPIO控制器。3.2 编译与测试让内核跑起来修改好设备树后就可以编译内核了。这个过程需要配置好交叉编译工具链。假设你的工具链已经就绪基本的编译步骤如下# 1. 进入内核源码根目录 cd linux-4.19-0802 # 2. 导入默认配置龙芯通常会提供默认配置文件如 loongson2k0300_defconfig make ARCHloongarch CROSS_COMPILEloongarch64-linux-gnu- loongson2k0300_defconfig # 3. 如果需要微调内核选项比如增加某些驱动模块可以打开图形化配置非必须 # make ARCHloongarch CROSS_COMPILEloongarch64-linux-gnu- menuconfig # 4. 编译内核映像和设备树二进制文件(.dtb) make ARCHloongarch CROSS_COMPILEloongarch64-linux-gnu- -j$(nproc)编译完成后在arch/loongarch/boot/目录下你会找到内核映像文件如vmlinuz在arch/loongarch/boot/dts/loongson/目录下找到编译好的设备树二进制文件如loongson_2k0300_pai_99_wifi.dtb。接下来的测试至关重要。将编译好的vmlinuz和.dtb文件拷贝到开发板的启动分区如SD卡的第一个FAT分区并修改启动脚本如U-Boot的boot.scr来加载它们。上电启动后通过串口登录系统开始关键检查检查PWM设备ls /sys/class/pwm/。你应该能看到pwmchip0,pwmchip1等目录。进入其中一个尝试导出和使能一个PWM通道看看能否在对应引脚用示波器测量到波形。检查GPIO编码器ls /sys/class/gpio/。可以通过echo 64 /sys/class/gpio/export来导出GPIO64然后cat /sys/class/gpio/gpio64/value读取其值。手动转动编码器观察值是否在0和1之间变化。检查SPI设备ls /dev/。如果SPI驱动加载成功应该能看到spidev0.0或类似的设备节点。用spi-tools等工具可以进行简单的收发测试。检查串口ls /dev/ttyS*。确认你的调试串口如ttyS0存在并能正常收发数据。这个过程可能会反复几次。如果某个设备没出现首先回头检查设备树中该设备的status是否为okay其次检查pinctrl配置是否正确最后查看内核启动日志dmesg | grep -E pwm|spi|gpio里面常有驱动加载失败的具体原因是排错的金钥匙。4. 与龙邱开源库的完美适配内核配置好了相当于给小车搭好了稳固的“身体骨架”和“神经系统”。接下来就要让“大脑”——也就是你的控制程序——能够灵活地指挥身体。龙邱智能车开源库这里我们简称龙邱库就是一个非常优秀的“大脑”程序框架它封装了电机控制、编码器读取、PID运算等常用功能。我们的目标是让我们自定义的内核能够被龙邱库正确识别和调用。龙邱库底层操作硬件无非是通过几种标准的Linux接口PWM通过sysfs接口GPIO编码器、按键也通过sysfs或libgpiod库SPI通过/dev/spidev设备文件。因此适配工作的核心就是确保龙邱库中关于设备路径和参数的配置与我们内核实际生成的设备节点相匹配。4.1 PWM与电机控制适配龙邱库中控制电机的代码通常会去操作/sys/class/pwm/pwmchipX/pwmY/下的文件。你需要确认的是pwmchipX的编号是否与你的设备树配置一致PWM0通常对应pwmchip0。你希望使用的PWM通道Y是否已成功导出echo Y export在龙邱库的硬件初始化文件可能是hardware.c或motor.c之类的中找到类似下面的代码段#define MOTOR_PWM_CHIP_ID 0 // PWM控制器编号 #define MOTOR_PWM_CHANNEL_LEFT 1 // 左电机使用的PWM通道 #define MOTOR_PWM_CHANNEL_RIGHT 2 // 右电机使用的PWM通道你需要根据自己内核中PWM的实际布局来修改这些宏定义。比如如果你的左电机接在GPIO65PWM1上而PWM1属于pwmchip0的第1通道那么MOTOR_PWM_CHANNEL_LEFT就应该设为1。4.2 编码器输入适配编码器读取一般有两种方式一是使用GPIO中断在用户空间轮询或等待中断事件二是有些内核可能提供了正交编码器硬件外设驱动。更常见的是第一种。龙邱库会通过读取GPIO值的变化来计数。你需要修改龙邱库中编码器引脚的定义// 假设原库定义是基于其他平台的GPIO编号 #define ENCODER_LEFT_A_PIN 123 // 旧的引脚号 #define ENCODER_RIGHT_A_PIN 456 // 旧的引脚号 // 修改为龙芯2K0300上实际的全局GPIO编号 #define ENCODER_LEFT_A_PIN 64 // GPIO64 #define ENCODER_RIGHT_A_PIN 67 // GPIO67同时要确保龙邱库使用的GPIO操作库如sysfs或libgpiod能正确工作。如果使用sysfs需要确认内核已编译CONFIG_GPIO_SYSFS支持并且你的引脚已经成功导出。4.3 SPI屏幕驱动适配如果你的屏幕是SPI接口的如常见的LCD彩屏并且你按照前文建议使用了SPI0或SPI1那么需要在龙邱库的显示驱动部分修改SPI设备路径。在屏幕初始化代码中寻找打开SPI设备文件的语句int spi_fd open(/dev/spidev0.0, O_RDWR); // 可能是0.0, 0.1, 1.0, 1.1等使用命令ls /dev/spidev*查看你的系统中实际生成了哪个SPI设备节点然后修改这里的路径。此外SPI的模式、速度和位宽等参数也需要与屏幕数据手册的要求一致这些参数通常在ioctl(spi_fd, SPI_IOC_WR_MODE, mode)等调用中设置。完成这些适配后理论上你的龙邱库程序就应该能编译成功并在你的定制内核上运行起来控制电机转动、读取编码器数据、在屏幕上显示信息了。这个过程可能需要一些调试比如调整PID参数、屏幕刷新率等但底层硬件通信的障碍已经扫清。5. 开源与共享为什么我选择分享vmlinuz在文章开头我提到了开源我自己的内核。这里提供的不仅仅是那个编译好的vmlinuz文件更重要的是附上了我完整的引脚分配方案和设备树修改思路。我分享这个主要基于两点考虑第一降低入门门槛提供一个“可用的起点”。我知道很多同学卡在第一步编译环境搭建复杂设备树语法陌生一个配置错误就导致系统无法启动非常打击信心。提供一个经过实测可用的内核可以让你们先把车跑起来获得正反馈。在跑起来的基础上再去对照我的配置表学习、修改、优化理解每一步的意义这个学习曲线会平滑很多。第二倡导一种“基于理解”的解决问题方式而非“基于购买”。智能车竞赛的本质是学习和创新。直接使用一个未经验证、不明所以的内核比赛中一旦出现玄学问题比如电磁干扰导致某个驱动异常你将毫无排查能力。而如果你亲手配置过内核熟悉每个外设的来龙去脉你就能快速定位问题是出在硬件连接、内核配置还是应用层代码。这份解决问题的能力比任何现成的内核都宝贵。我开源的配置方案已经处理了SPI2与PWM的冲突配置了基本的电机PWM、编码器GPIO、调试串口并适配了龙邱库。你可以把它当作一个模板。如果你的传感器更多比如需要多个ADC、I2C设备或者使用了不同的屏幕那么就需要在这个模板上继续添加和修改设备树节点。这就像搭积木我给了你一个稳固的地基和几面承重墙房间怎么隔、装修成什么风格取决于你的具体需求。最后我想说技术社区之所以能蓬勃发展正是源于这种无私的分享和接力。我当年也是看着无数前辈的博客和开源代码才一步步走过来的。今天我把在龙芯智能车开发中踩过的坑、总结的经验分享出来希望能帮助正在这条路上摸索的你。当你也能稳定地控制你的小车飞驰时或许也可以想想如何将你的独特优化、对新传感器的支持再分享给后来者。这种正向的循环才是推动我们整个智能车技术栈不断成熟、国产平台生态日益繁荣的真正动力。