第2章:开发环境搭建——STM32CubeMX工程创建、Keil/IAR编译器配置、CAN驱动移植准备
好,咱们正式开始动手了。上一章聊了CANopen的整体框架,这一章咱们把吃饭的家伙先支棱起来。说白了,就是搭一个能跑CANopen的工程环境。
我个人习惯,做嵌入式项目第一步永远是先搞定开发环境。环境不顺,后面全是坑。我记得刚入行那会儿,光配编译器就折腾了两天,后来发现是路径里有中文...嗯,这种低级错误咱们今天一次性避开。
2.1 STM32CubeMX工程创建——从零开始
STM32CubeMX是ST官方出的图形化配置工具。用它生成初始化代码,省时省力。我建议大家都用最新版,至少是6.x以上,对HAL库支持更完善。
咱们以STM32F407VGT6为例,选这个芯片是因为它带两个CAN控制器,做CANopen主从站都够用。你手头如果是F103、F429之类的,操作流程完全一样。
2.1.1 新建工程步骤
- 打开CubeMX,点击"New Project"
- 选择芯片:在Part Number搜索框输入"STM32F407VG",双击选中
- 配置时钟源:RCC → HSE → Crystal/Ceramic Resonator(外部晶振)
- 配置调试接口:SYS → Debug → Serial Wire(只用SWD,省两个引脚)
- 配置CAN外设:Connectivity → CAN1 → Mode: Activated
关键参数设置:
- CAN1的波特率:我一般设500kbps。为什么?因为CANopen默认推荐就是这个值,兼容性最好。
- Time Quantum:根据晶振和分频算,F407用8MHz晶振,设Prescaler=6,BS1=13,BS2=2,正好500k。
2.1.2 时钟树配置
时钟树这块容易乱。我教你一个口诀:先配HSE,再配PLL,最后看外设。
具体操作:
- HSE选8MHz(看你板子上的晶振)
- PLLM=8,PLLN=336,PLLP=2 → 得到168MHz主频
- APB1时钟=42MHz(CAN挂在APB1上,这个值决定了CAN时钟源)
你想想看,如果APB1时钟算错了,CAN波特率肯定不对。我在项目里遇到过,明明代码没问题,就是收不到数据,最后发现是时钟树配错了。所以这一步务必仔细核对。
2.2 Keil/IAR编译器配置——让代码跑起来
CubeMX生成代码后,咱们得用编译器打开。我个人偏爱Keil,但IAR也有它的优势。这里两个都讲,你根据习惯选。
2.2.1 Keil MDK配置
- 打开工程:在CubeMX里Project → Generate Code,选MDK-ARM V5
- 设置芯片型号:Project → Options for Target → Device → STM32F407VG
- 配置C/C++选项:
// 在Define框里添加:
USE_HAL_DRIVER, STM32F407xx
// 优化等级:我建议调试阶段选-O0,发布时再改-O2
// 勾选:C99 Mode(CANopen源码需要)
- 配置调试器:Debug → Use: ST-Link Debugger → Settings → Flash Download → 勾选Reset and Run
小技巧:Keil里有个"Browse Information"选项,勾上后可以跳转到函数定义。我每次建工程必开这个,查代码方便很多。
2.2.2 IAR EWARM配置
如果你用IAR,流程也差不多:
- CubeMX生成时选"IAR Embedded Workbench"
- 打开.eww工程文件
- Project → Options → General Options → Target → Device → STM32F407VG
- C/C++ Compiler → Preprocessor → Defined symbols:添加
USE_HAL_DRIVER, STM32F407xx - Debugger → Setup → Driver: ST-Link
这里有个坑:IAR默认的堆栈大小可能不够。CANopen协议栈需要至少2KB的堆空间。我建议把Heap Size设到0x800(2KB),Stack Size设到0x1000(4KB)。
2.3 CAN驱动移植准备——让硬件说话
环境搭好了,接下来要准备CAN驱动。CANopen跑在CAN硬件之上,所以底层驱动必须稳。
2.3.1 HAL库CAN初始化流程
CubeMX生成的代码里,CAN初始化在MX_CAN1_Init()函数中。但默认配置只开了基本功能,咱们需要手动加一些东西。
// 在main.c里添加CAN过滤器配置
static void CAN_Filter_Config(void)
{
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterBank = 0; // 使用过滤器组0
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位宽度
sFilterConfig.FilterIdHigh = 0x0000; // 标准ID高16位
sFilterConfig.FilterIdLow = 0x0000; // 标准ID低16位
sFilterConfig.FilterMaskIdHigh = 0x0000; // 掩码高16位
sFilterConfig.FilterMaskIdLow = 0x0000; // 掩码低16位
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 关联到FIFO0
sFilterConfig.FilterActivation = ENABLE; // 使能过滤器
sFilterConfig.SlaveStartFilterBank = 14; // 从CAN的起始过滤器组
HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig);
}
这段代码的意思是:不过滤任何ID,所有CAN报文都接收。为什么这么做?因为CANopen的SDO、PDO、NMT报文ID是动态的,一开始全收最保险。等后面协议栈跑起来了,再根据需要调整过滤器。
2.3.2 中断配置
CAN通信必须用中断,轮询方式在CANopen里根本跑不动。我见过有人用轮询做CANopen,结果CPU占用率飙到90%,啥也干不了。
// 使能CAN接收中断
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
// 中断服务函数(在stm32f4xx_it.c中)
void CAN1_RX0_IRQHandler(void)
{
HAL_CAN_IRQHandler(&hcan1);
}
注意:中断优先级要设好。CAN中断优先级建议设成1(0最高,15最低)。别设成0,否则会抢占系统滴答定时器,导致HAL_Delay()不准。我曾经因为这个bug查了两天...
2.3.3 收发测试
驱动移植完,先别急着上CANopen。做个简单的回环测试,确认硬件没问题。
// 发送一帧测试数据
CAN_TxHeaderTypeDef TxHeader;
uint8_t TxData[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
uint32_t TxMailbox;
TxHeader.StdId = 0x123; // 标准ID
TxHeader.ExtId = 0; // 扩展ID(不用)
TxHeader.IDE = CAN_ID_STD; // 标准帧
TxHeader.RTR = CAN_RTR_DATA; // 数据帧
TxHeader.DLC = 8; // 数据长度
HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox);
用CAN分析仪或者另一块板子接收,如果能收到0x123这帧数据,说明驱动移植成功了。收不到?检查波特率、时钟配置、还有物理层的终端电阻(120Ω别忘了接)。
2.4 工程目录结构建议
代码写多了,目录结构乱是最头疼的。我分享一个我用了多年的结构:
Project/
├── Core/ # 主程序、中断、系统文件
│ ├── Inc/
│ └── Src/
├── Drivers/ # HAL库、CMSIS
├── CANopen/ # CANopen协议栈源码(后面章节会加)
├── Middlewares/ # 其他中间件(比如FreeRTOS)
├── Doc/ # 文档、笔记
└── Output/ # 编译产物(.hex, .bin)
这样分层清晰,CANopen源码单独放一个文件夹,方便后续升级和维护。
2.5 本章小结
到这步,你的开发环境应该已经能跑起来了。总结一下咱们干了什么:
- 用CubeMX创建了STM32F407的工程,配好了CAN外设和时钟
- 在Keil或IAR里设置了编译选项,确保能正常编译下载
- 写了CAN驱动初始化代码,做了收发测试
下一章,咱们开始移植CANopenNode协议栈。那才是真正的重头戏。不过别急,先把今天的环境跑通,有问题随时在群里问我。
课后作业:用两块开发板,互相发CAN数据。一块发0x123,另一块收到后回0x456。能实现这个,说明你的CAN驱动已经稳了。
好,今天就到这。记住:环境搭得好,后面少烦恼。