3、开发环境搭建:硬件平台选择(STM32/ARM/Linux)、软件工具链(编译器、调试器)、CANopenNode源码获取与编译、第一个Hello World程序

好,咱们正式开始动手了。

这一章,说白了就是“把家伙事儿备齐”。做嵌入式开发,最怕什么?最怕环境搭了三天,代码一行没写。我当年刚入行时,光装一个IDE就折腾了一整天,最后发现是杀毒软件把编译器给干掉了。嗯,这种坑,咱们今天尽量避开。

3.1 硬件平台怎么选?

CANopenNode是个开源协议栈,移植性很好。但作为教学,我建议你选一个“皮实”的平台。我个人习惯把平台分成三类:

平台类型 代表芯片/板卡 适合场景 我的评价
STM32系列 STM32F103 / F407 / H743 入门、工业控制、从站开发 资料最多,社区最活跃,首选
ARM通用MCU NXP LPC系列、TI Tiva C系列 特定项目、成本敏感 寄存器级移植,适合进阶
Linux平台 树莓派、BeagleBone、i.MX6 主站开发、网关、复杂应用 调试方便,但实时性需注意

我个人建议:初学者先用STM32F407开发板。为什么?因为CANopenNode官方例程里就有STM32的移植,你拿来就能跑。我在项目中遇到过用STM32F103做从站,结果CAN过滤器配置搞反了,总线上一堆错误帧。后来换成F407,调试器直接看寄存器,十分钟就定位了问题。

小提示: 如果你手头只有Linux板子(比如树莓派),也别慌。CANopenNode在Linux上跑主站非常稳,而且可以用socketCAN直接操作总线,调试起来比MCU还方便。

3.2 软件工具链:编译器与调试器

工具链这块,我踩过的坑比硬件还多。咱们分两种情况说:

3.2.1 针对STM32/ARM MCU

  • 编译器: 我推荐ARM GCC(gcc-arm-none-eabi)。免费、开源、性能不输IAR。你想想看,IAR一个License好几万,个人学习真没必要。
  • 调试器: J-Link或ST-Link。J-Link速度快,但贵。ST-Link买开发板时基本都送,够用。
  • IDE: 我习惯用VS Code + CMake。当然,如果你喜欢图形化,STM32CubeIDE也行,它集成了GCC和调试器,开箱即用。
注意: 我曾经在Windows上装ARM GCC时,路径里带了中文,结果编译死活报错。折腾了两小时才发现是环境变量的问题。所以,安装路径不要有中文和空格

3.2.2 针对Linux平台

Linux下就简单多了。你只需要:

sudo apt-get install gcc make cmake
sudo apt-get install can-utils  # 用于调试CAN总线

编译器用系统自带的GCC就行。调试器?Linux下我一般用gdb,配合OpenOCD或者直接print大法。说实话,Linux平台开发CANopenNode,比MCU省心一半。

3.3 获取CANopenNode源码

源码在GitHub上,地址是:https://github.com/CANopenNode/CANopenNode。怎么拿?两种方式:

  1. 直接下载ZIP: 点页面上的“Code” -> “Download ZIP”。简单粗暴,但后续更新麻烦。
  2. 用Git克隆: 我推荐这种方式。因为CANopenNode还在更新,你随时可以 git pull 拉取最新代码。
git clone https://github.com/CANopenNode/CANopenNode.git
cd CANopenNode
git checkout master  # 或者选一个稳定版本,比如v4.0

克隆下来后,目录结构是这样的:

CANopenNode/
├── CANopen.h          # 主头文件
├── CANopen.c          # 核心实现
├── stack/             # 协议栈各层
├── driver/            # 硬件驱动层(需要你移植)
├── example/           # 示例代码
└── doc/               # 文档

嗯,这里要注意:driver/ 目录下是空的,只有模板文件。你需要根据你的硬件,实现CAN收发、定时器、EEPROM读写这几个接口。别怕,咱们后面会手把手教你填。

3.4 编译第一个程序

咱们先不急着写业务逻辑。先让代码能编译通过,这叫“Hello World”级别的验证。

3.4.1 在Linux上编译(最简单)

CANopenNode自带CMakeLists.txt。你直接:

mkdir build && cd build
cmake ..
make

如果一切顺利,你会看到一堆 .o 文件和一个静态库 libCANopen.a。我在项目中第一次编译时,卡在了“找不到socketCAN头文件”上。后来发现是没装 libsocketcan-dev。所以,如果你报错,先检查依赖:

sudo apt-get install libsocketcan-dev

3.4.2 在STM32上编译(需要移植)

对于STM32,我建议你直接用STM32CubeIDE新建一个工程,然后把CANopenNode的源文件加进去。具体步骤:

  1. 新建STM32工程,配置好时钟和CAN外设(波特率建议先设125kbps)。
  2. CANopenNode/stack/CANopenNode/driver/ 下的源文件复制到工程里。
  3. 实现三个驱动函数:CANopenNode_canRead()CANopenNode_canWrite()CANopenNode_timer()
  4. 在main函数里调用 CANopenNode_init()CANopenNode_process()
核心代码骨架:
#include "CANopen.h"

int main(void) {
    // 硬件初始化(时钟、GPIO、CAN、定时器)
    HAL_Init();
    SystemClock_Config();
    MX_CAN_Init();
    MX_TIM_Init();

    // CANopen协议栈初始化
    CANopenNode_init();

    while(1) {
        CANopenNode_process();  // 主循环,处理CAN报文
        // 你的应用逻辑可以写在这里
    }
}

编译通过后,下载到板子里。用USB-CAN分析仪(或者另一块板子)发一个NMT启动命令(COB-ID=0x000,数据=0x01),如果板子回复了心跳报文(COB-ID=0x700+NodeID),恭喜你,第一个程序跑起来了!

避坑指南: 我曾经在STM32上忘了使能CAN的时钟中断,结果协议栈一直收不到报文。排查了半天,最后发现是HAL库的CAN中断回调函数没写。记住:CANopenNode依赖定时器中断和CAN接收中断,这两个必须配好。

3.5 验证:你的第一个CANopen节点

程序跑起来后,怎么确认它活着?我教你一个土办法:

  • candump can0(Linux)或CAN分析工具抓总线数据。
  • 正常情况,你会看到节点每隔一定时间(默认1000ms)发送一个心跳报文。比如Node-ID=1,心跳COB-ID就是0x701,数据字节表示节点状态(0x00=启动中,0x05=运行中,0x7F=预操作)。
  • 如果看不到心跳,先检查CAN收发器有没有共地,终端电阻有没有接(120Ω,总线两端各一个)。

嗯,到这里,你的开发环境就算搭好了。虽然只是点亮了一个“心跳”,但这是CANopen通信的第一步。后面咱们会在这个基础上,添加对象字典、PDO、SDO……一步步把节点做完整。

下一章,咱们聊聊对象字典——CANopen的灵魂。准备好了吗?