3. 开发环境搭建:Keil MDK工程配置、CMS32标准外设库导入、CAN外口时钟使能与引脚映射
好,咱们正式开始动手。这一章我带你把开发环境搭起来。很多初学者觉得搭环境就是装个软件、点几下鼠标,没什么技术含量。其实不然——环境配得对不对,直接决定你后面三天能不能睡好觉。我在项目里见过太多“代码没问题,就是跑不起来”的案例,最后查出来都是工程配置埋的雷。
3.1 Keil MDK工程创建与基础配置
先装好Keil MDK,版本建议5.38以上。装完打开,咱们一步步来。
- 新建工程:Project → New μVision Project,选好存放路径。工程名我习惯用
CMS32_CAN_Demo,清晰好找。 - 选择芯片型号:在弹出的Select Device窗口中,找到CMS32系列。具体型号看你手里的板子,我用的是CMS32L051。选好后点OK。
- 管理运行环境:点Manage Run-Time Environment。这里要注意——不要一股脑全勾上。咱们只需要:
- CMSIS → CORE(必须)
- Device → Startup(启动文件)
- Device → StdPeriph Drivers(标准外设库,后面细说)
- 配置输出文件:点魔术棒(Options for Target)→ Output → 勾上Create HEX File。调试时用HEX文件烧录,方便。
User(放main.c等用户代码)、Driver(放外设驱动)、Doc(放笔记)。这样工程大了也不乱。
3.2 CMS32标准外设库导入
标准外设库,说白了就是芯片厂商给你写好的底层驱动函数。你不用自己对着寄存器手册一个个抠,直接调库函数就行。但导入方式有讲究。
CMS32的标准外设库通常是一个压缩包,解压后里面有 Libraries 和 Project 两个大文件夹。咱们只需要 Libraries 里的东西。
导入步骤:
- 把
Libraries/CMS32F0xx_StdPeriph_Driver整个文件夹复制到你的工程目录下。 - 在Keil里点Manage Project Items,新建一个Group,取名
StdPeriph_Driver。 - 把刚才复制进来的
.c文件(在src子目录里)全部添加进这个Group。 - 添加头文件路径:魔术棒 → C/C++ → Include Paths,把
inc子目录的路径加进去。
加完之后,在main.c里写一句 #include "CMS32F0xx.h",如果能编译通过,说明库导入成功了。
3.3 CAN外设时钟使能
CAN外设要工作,首先得给它供时钟。CMS32的CAN模块挂载在APB总线上,使能时钟需要操作RCC(Reset and Clock Control)寄存器。
标准外设库提供了现成的函数:
// 使能CAN1时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
// 使能CAN2时钟(如果芯片有多个CAN)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN2, ENABLE);
这里有个细节——时钟使能顺序。我个人建议在系统时钟初始化之后、GPIO配置之前做这件事。为什么?因为CAN的引脚映射需要GPIO模块已经准备好,而GPIO又依赖时钟。你想想看,如果GPIO时钟没开,你去配引脚映射,那寄存器根本写不进去。
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 配置系统时钟 | HSE/PLL等,确保主频稳定 |
| 2 | 使能CAN时钟 | RCC_APB1PeriphClockCmd |
| 3 | 使能GPIO时钟 | RCC_AHBPeriphClockCmd |
| 4 | 配置引脚映射 | GPIO_PinAFConfig等 |
3.4 CAN引脚映射与GPIO配置
CAN的TX和RX引脚不是随便选的。CMS32芯片内部有一个引脚复用矩阵,你得告诉芯片:“我要把CAN1的TX映射到PA12,RX映射到PA11”。
具体配置代码:
// 使能GPIOA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
// 配置CAN1 TX (PA12) 为复用推挽输出
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置CAN1 RX (PA11) 为复用浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 将PA11和PA12映射到CAN1
GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_CAN1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_CAN1);
这里有个容易忽略的地方——TX和RX的GPIO模式不一样。TX是输出,得配成推挽输出;RX是输入,配成浮空输入或者上拉输入。我见过有人把两个引脚配成一样的模式,结果CAN通信时好时坏,查了半天才发现RX引脚配置错了。
3.5 知识结构总览
下面这张图把本章的核心流程串起来了。你照着这个顺序做,基本不会漏步骤。
3.6 验证与常见问题
配置完成后,编译一下。如果0 Error 0 Warning,恭喜你,环境搭好了。但如果有报错,别慌,先看错误信息。我总结了几种常见情况:
- error: #5: cannot open source input file —— 头文件路径没加,或者路径写错了。
- error: L6218E: Undefined symbol —— 某个库函数的.c文件没添加到工程里。
- warning: #223-D: function "xxx" declared implicitly —— 忘了包含对应的头文件。