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
如果看到版本信息,说明工具链装好了。
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动态库)
--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等。
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 整体流程回顾
为了让你对整个搭建流程有个全局认识,我画了一张图:
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",那说明你忘了设置交叉编译器。这个习惯帮我省了不少时间。