4、开发环境搭建:Linux下编译CANopenNode、STM32平台移植、树莓派配置
好,咱们开始搭建干活的环境了。
说实话,做机器人控制,环境搭不好,后面全是坑。我见过太多人,代码写得挺溜,结果编译不过、烧录失败、通信不通,一查全是环境问题。所以这一章,咱们把Linux编译、STM32移植、树莓派配置这三个核心环节,一个一个捋清楚。
4.1 Linux下编译CANopenNode
我个人习惯在Ubuntu 20.04或22.04上做开发。CANopenNode本身是纯C写的,移植性很好。但编译时有些细节,不注意就会卡住。
4.1.1 获取源码
从GitHub拉下来就行:
git clone https://github.com/CANopenNode/CANopenNode.git
cd CANopenNode
嗯,这里要注意,CANopenNode依赖一个叫CANopenNode/stack的子模块。很多人忘了拉子模块,编译直接报错。
正确的做法:
git submodule update --init --recursive
4.1.2 编译选项与配置
CANopenNode用CMake构建。我个人建议用cmake-gui或者直接命令行:
mkdir build && cd build
cmake ..
make -j4
这里有几个关键选项,你想想看:
- CAN_DRIVER:选哪个底层CAN驱动。Linux下常用
socketCAN,也就是CAN_DRIVER_SOCKETCAN。 - CO_ENABLE_LSS:要不要LSS(Layer Setting Services)?如果你需要动态分配节点ID,就打开。我一般开着,省事。
- CO_ENABLE_SDO_BLOCK:SDO块传输。大数据传输时有用,但会增加代码体积。
cmake -DCMAKE_BUILD_TYPE=Debug ..打开调试信息。出问题时能少掉不少头发。
4.1.3 编译测试
编译成功后,会生成一个叫canopend的可执行文件。跑一下:
./canopend -i 1 -s 0 -c "can0"
这个命令的意思是:节点ID为1,不启动SDO服务器,使用can0接口。如果看到终端打印出NMT: bootup,说明环境通了。
4.2 STM32平台移植
STM32是机器人控制里用得最多的MCU之一。CANopenNode移植到STM32,说白了就是搞定两件事:CAN驱动和定时器。
4.2.1 硬件准备
我建议用STM32F4或F7系列,带双CAN的型号。比如STM32F407VGT6,性价比很高。接线很简单:
- CAN_TX → PA12 (CAN1)
- CAN_RX → PA11 (CAN1)
- CAN收发器:TJA1050或SN65HVD230,注意3.3V和5V电平匹配
4.2.2 移植步骤
CANopenNode的移植,核心是填充三个回调函数:
- CAN发送:
CO_CANsend() - CAN接收:中断里调用
CO_CANreceive() - 定时器:1ms的定时中断,调用
CO_TMRreload()
我一般用HAL库,代码结构大概这样:
// CAN发送回调
static int8_t canSend(CAN_HandleTypeDef *hcan, uint16_t cob_id, uint8_t *data, uint8_t len) {
CAN_TxHeaderTypeDef txHeader;
txHeader.StdId = cob_id;
txHeader.IDE = CAN_ID_STD;
txHeader.RTR = CAN_RTR_DATA;
txHeader.DLC = len;
// 发送
if (HAL_CAN_AddTxMessage(hcan, &txHeader, data, &txMailbox) != HAL_OK)
return -1;
return 0;
}
// 定时器中断(1ms)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM3) {
CO_TMRreload(&canopenNode.TMR);
}
}
canopen_app.c。这样主循环里只调用canopen_app_process(),清爽很多。
4.2.3 时钟配置
CANopenNode要求一个1ms的定时器。我通常用TIM3,APB1时钟84MHz,分频后得到1ms中断:
// 定时器配置
TIM_HandleTypeDef htim3;
htim3.Instance = TIM3;
htim3.Init.Prescaler = 84-1; // 84MHz / 84 = 1MHz
htim3.Init.Period = 1000-1; // 1MHz / 1000 = 1ms
HAL_TIM_Base_Start_IT(&htim3);
4.3 树莓派配置
树莓派在机器人里常作为上位机或网关。配置CANopenNode,主要靠SocketCAN。
4.3.1 启用CAN接口
树莓派本身没有CAN控制器,需要外接SPI转CAN模块,比如MCP2515。我推荐用mcp251x驱动:
# 在/boot/config.txt中添加
dtoverlay=mcp2515-can0,oscillator=16000000,interrupt=25
dtoverlay=spi-bcm2835-overlay
重启后,用ip link show应该能看到can0接口。
4.3.2 设置SocketCAN
启用并设置波特率:
sudo ip link set can0 up type can bitrate 250000
sudo ifconfig can0 up
验证是否成功:
candump can0
如果没报错,说明CAN接口已经活了。
4.3.3 编译并运行CANopenNode
在树莓派上编译CANopenNode,和Linux下一样。但要注意:树莓派是ARM架构,有些依赖库需要重新编译。我建议用apt安装基础工具:
sudo apt install cmake build-essential libsocketcan-dev
然后编译:
mkdir build && cd build
cmake .. -DCAN_DRIVER=socketcan
make -j4
运行:
./canopend -i 1 -s 0 -c "can0"
[Unit]
Description=CANopenNode
After=network.target
[Service]
ExecStart=/home/pi/canopend -i 1 -s 0 -c "can0"
Restart=always
[Install]
WantedBy=multi-user.target
4.4 小结
环境搭建这块,说白了就是三个字:耐心试。Linux下编译,注意子模块;STM32移植,搞定CAN和定时器;树莓派配置,SocketCAN是核心。
我刚开始做的时候,光STM32的CAN中断就调了两天。后来发现是中断优先级没配好,导致接收丢帧。嗯,这些坑踩过了,你们就能少走弯路。
下一章,咱们开始写第一个CANopen节点,让电机转起来。