第二章:CANopenNode源码获取与编译

好,咱们正式开始动手了。这一章,我带你走一遍CANopenNode的源码获取和编译流程。说白了,就是先把代码拿到手,然后在Linux上跑通,最后为移植到STM32做准备。

我个人习惯,拿到一个开源项目,第一件事就是看它的目录结构和编译方式。这就像你拿到一个新工具,先掂量掂量分量,看看手感。CANopenNode这个项目,结构挺清晰的,咱们一步步来。

2.1 从GitHub上把代码拉下来

这个没啥好说的,用git clone就行。但我建议你fork一下再clone,这样你后面改代码、提PR都方便。

git clone https://github.com/CANopenNode/CANopenNode.git
cd CANopenNode
git checkout master

嗯,这里要注意:别用默认的develop分支。我在项目中遇到过,develop分支有时候会有一些实验性的改动,不够稳定。master分支是经过测试的,拿来用最靠谱。

拉下来之后,看看目录结构:

  • CANopenNode/ — 核心协议栈代码
  • CANopenSocket/ — Linux下的socketCAN接口
  • example/ — 示例程序
  • doc/ — 文档
  • test/ — 测试代码

你想想看,一个协议栈项目,核心代码其实没多少。真正让你头疼的,往往是硬件接口和操作系统适配。CANopenNode把这两部分分得很开,这点做得不错。

2.2 Linux环境下的编译

我建议你在Ubuntu 20.04或22.04上编译。为什么?因为这两个版本的gcc和cmake版本都比较新,而且社区支持好。我曾经在CentOS 7上折腾过,那叫一个痛苦——cmake版本太老,还得手动升级。

先装依赖:

sudo apt-get update
sudo apt-get install build-essential cmake git
sudo apt-get install libsocketcan-dev  # 如果你要用socketCAN

然后编译:

mkdir build && cd build
cmake ..
make -j4

这里有个小坑:cmake .. 的时候,可能会报找不到某些库。别慌,看看错误信息,缺什么装什么。我遇到过缺libsocketcan的情况,装一下就好了。

编译成功后,你会看到几个可执行文件:

  • CANopenDemo — 一个完整的CANopen从站示例
  • CANopenMaster — 主站示例
  • CANopenTest — 测试程序
小提示: 如果你没有CAN硬件,可以用虚拟CAN(vcan)来测试。Linux内核自带这个功能,modprobe vcan一下就能用。我在没有硬件的时候,全靠vcan调试。

2.3 编译选项的玄机

cmake的时候,你可以加一些选项来控制编译内容。我个人常用的几个:

cmake .. -DCANOPEN_SUPPORT_SDO=ON -DCANOPEN_SUPPORT_EMCY=ON

这些选项控制着协议栈的功能模块。你想想看,如果你的设备不需要紧急报文(EMCY),那就可以关掉,省点代码空间。对于STM32这种资源受限的平台,这个功能特别有用。

常用的选项有:

选项 说明 默认值
CANOPEN_SUPPORT_SDO SDO服务 ON
CANOPEN_SUPPORT_PDO PDO服务 ON
CANOPEN_SUPPORT_EMCY 紧急报文 ON
CANOPEN_SUPPORT_LSS LSS服务 OFF

嗯,这里要注意:LSS默认是关的。如果你需要LSS(比如做节点ID自动分配),记得手动打开。

2.4 为STM32移植做准备

好了,Linux上跑通了,接下来要考虑怎么移植到STM32上。这一步其实是最关键的,因为STM32的资源比PC少得多。

首先,你得理解CANopenNode的架构。它把硬件抽象层(HAL)单独拿出来了。你只需要实现几个接口函数:

  • CANopenNode_Init() — 初始化
  • CANopenNode_Process() — 主循环调用
  • CANopenNode_Stop() — 停止

说白了,你只需要关心这三个函数。其他的,协议栈自己搞定。

我建议你先把STM32的CAN外设驱动写好,然后照着Linux版本的HAL实现,把接口填上。具体怎么做,下一章我会详细讲。

警告: 移植的时候,一定要注意内存对齐和字节序。STM32是little-endian,而CANopen协议用的是little-endian,所以没问题。但如果你用其他MCU,比如某些DSP,就得小心了。

另外,定时器中断的优先级也要注意。CANopenNode内部用了一个定时器来做心跳和超时处理。这个中断的优先级不能太低,否则会影响实时性。我曾经在一个项目里,把定时器中断优先级设得太低,结果心跳报文老是超时,排查了半天才发现是这个问题。

2.5 一个简单的测试

移植之前,我建议你先在Linux上跑一下示例程序,熟悉一下流程。这样你移植到STM32后,心里有底。

# 启动虚拟CAN
sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0

# 运行示例
./build/CANopenDemo vcan0

如果一切正常,你会看到终端打印出一些状态信息。嗯,看到这些信息,说明你的环境已经准备好了。

好了,这一章就到这里。下一章,我们正式进入STM32移植的实战环节。到时候,我会手把手教你如何把CANopenNode跑在STM32上。