3. 开发环境搭建:交叉编译工具链、libsocketcan库、can-utils工具集安装与验证

做嵌入式Linux开发,第一步就是搭环境。很多人觉得这事简单,照着文档敲命令就行。但我见过太多工程师,卡在环境搭建上两三天,最后发现是工具链版本不对,或者库没编进去。

这一章,我就带你一步步把CAN总线的开发环境搭起来。咱们的目标很明确:交叉编译工具链、libsocketcan库、can-utils工具集,三个东西装好、验证通过。

3.1 交叉编译工具链的选择与安装

交叉编译,说白了就是在PC上编译出能在ARM板子上跑的程序。你想想看,你的开发板CPU是ARM架构,而你的电脑是x86架构,不用交叉编译,编译出来的程序根本跑不起来。

我个人习惯用Linaro GCC,它针对ARM Cortex-A系列做了优化,稳定性和性能都不错。当然,如果你用厂家提供的SDK,那直接用厂家的工具链也行。

3.1.1 下载与解压

以ARM Cortex-A7为例,我常用的是gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf这个版本。下载后解压到/opt目录:

sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/

3.1.2 配置环境变量

解压完,需要把工具链的bin目录加到PATH里。我习惯在~/.bashrc里加一行:

export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin

然后source ~/.bashrc让它生效。

3.1.3 验证工具链

验证很简单,执行:

arm-linux-gnueabihf-gcc --version

如果看到版本信息,说明工具链装好了。

小提示: 我曾经遇到过一个问题——工具链版本和内核头文件不匹配,导致编译出来的程序在板子上段错误。后来我养成了一个习惯:工具链的gcc版本,尽量和开发板内核编译时用的gcc版本一致。差一两个小版本通常没事,但大版本别跳。

3.2 libsocketcan库的交叉编译

libsocketcan是一个轻量级的C库,封装了SocketCAN的很多底层操作。比如设置CAN接口的波特率、开启/关闭接口、获取接口状态等。有了它,你就不用每次都自己写ioctl了。

为什么要交叉编译这个库?因为你的程序最终要在ARM板子上跑,所以库也得是ARM架构的。

3.2.1 下载源码

从GitHub上克隆:

git clone https://github.com/socketcan/libsocketcan.git
cd libsocketcan

3.2.2 配置交叉编译

这里有个关键点:一定要指定--host参数,告诉configure脚本你的目标架构是ARM。

./autogen.sh
./configure --host=arm-linux-gnueabihf --prefix=$PWD/install

--prefix指定安装路径,我习惯在当前目录下建一个install文件夹,这样编译出来的头文件和库文件都集中在一起,方便后续使用。

3.2.3 编译与安装

make
make install

编译完成后,在install目录下你会看到:

  • include/ — 头文件
  • lib/ — 库文件(.a静态库和.so动态库)
注意: 如果你在configure时没有指定--host,默认会编译成x86版本。这个库在PC上能编译通过,但拷到板子上会报"Exec format error"。我刚开始学的时候犯过这个错,折腾了半天才发现。

3.3 can-utils工具集的交叉编译

can-utils是SocketCAN的瑞士军刀。它包含candump、cansend、cangen、canconfig等工具,调试CAN总线时几乎天天用。

3.3.1 下载源码

git clone https://github.com/linux-can/can-utils.git
cd can-utils

3.3.2 修改Makefile

can-utils的编译方式比较原始,它没有configure脚本,直接是Makefile。你需要手动指定交叉编译器:

export CC=arm-linux-gnueabihf-gcc
export AR=arm-linux-gnueabihf-ar
export RANLIB=arm-linux-gnueabihf-ranlib

然后执行:

make

3.3.3 安装到目标目录

make install DESTDIR=$PWD/install

编译完成后,在install/usr/local/bin/下你会看到一堆可执行文件,比如candump、cansend等。

核心要点: can-utils编译时,不需要链接libsocketcan。它直接使用Linux内核的SocketCAN接口,通过标准socket API通信。所以can-utils的编译比libsocketcan简单很多。

3.4 验证环境是否搭建成功

环境搭没搭好,得跑个例子验证一下。我一般分两步走:

3.4.1 验证libsocketcan库

写一个简单的测试程序,调用libsocketcan的API:

#include <stdio.h>
#include <libsocketcan.h>

int main() {
    int ret;
    ret = can_do_start("can0");
    if (ret < 0) {
        printf("Failed to start can0\n");
        return -1;
    }
    printf("can0 started successfully\n");
    return 0;
}

交叉编译:

arm-linux-gnueabihf-gcc test.c -o test -I /path/to/libsocketcan/install/include -L /path/to/libsocketcan/install/lib -lsocketcan

把生成的test文件拷到板子上,运行。如果输出"can0 started successfully",说明库没问题。

3.4.2 验证can-utils工具

把编译好的candump和cansend拷到板子上。先在一个终端运行:

./candump can0

然后在另一个终端发送一帧数据:

./cansend can0 123#1122334455667788

如果candump终端打印出这帧数据,说明can-utils工作正常。

避坑指南: 我曾经在验证时,candump一直收不到数据。排查了半天,发现是板子上的CAN接口没有激活。记得先执行ip link set can0 up type can bitrate 500000,把接口打开。

3.5 整体流程回顾

为了让你对整个搭建流程有个全局认识,我画了一张图:

开发环境搭建整体流程 交叉编译工具链 libsocketcan库 can-utils工具集 ./configure --host=arm-linux-gnueabihf export CC=arm-linux-gnueabihf-gcc libsocketcan.a / .so candump / cansend / cangen 板端验证:candump + cansend 确认数据收发正常 关键:工具链版本与内核匹配 | libsocketcan必须指定--host | can-utils无需额外库

3.6 常见问题与排查

环境搭建过程中,我遇到过不少坑。这里列几个典型的:

问题现象 可能原因 解决方法
编译时报"unknown architecture" 工具链的--host参数写错了 检查目标架构,比如ARM Cortex-A7用arm-linux-gnueabihf
链接时报"cannot find -lsocketcan" 库路径没指定,或者库没编译成功 检查-L参数,确认libsocketcan.a存在
板子上运行报"Exec format error" 编译出来的程序是x86架构的 用file命令查看文件类型,确认是ARM架构
candump收不到数据 CAN接口没激活或波特率不匹配 执行ip link set can0 up type can bitrate 500000
重要提醒: 每次编译完,记得用file命令检查一下生成的文件:file candump。如果输出是"ELF 32-bit LSB executable, ARM",说明编译对了。如果是"x86-64",那说明你忘了设置交叉编译器。这个习惯帮我省了不少时间。