2. CMS32开发环境搭建:Keil MDK安装与配置、CMS32固件库导入、第一个串口工程创建

说实话,做嵌入式开发这么多年,我换过不少IDE。从早期的IAR到后来的Eclipse,再到现在的Keil MDK。但要说上手最快、资料最多的,还得是Keil。尤其是做CMS32这种ARM内核的单片机,Keil MDK几乎是标配。

这一章,咱们就把开发环境从头到尾捋一遍。你跟着我走,保证不出错。

2.1 Keil MDK的安装与配置

先说说版本问题。我建议你用MDK 5.38或更新的版本。为什么?因为老版本对CMS32的器件包支持不够好。我之前有个同事用MDK 5.26折腾了一下午,器件包死活装不上,换了新版五分钟搞定。

2.1.1 安装步骤

  1. 下载安装包:去Keil官网下载MDK-ARM安装程序。注意区分32位和64位版本。
  2. 运行安装:双击安装包,一路Next。安装路径我建议用默认的 C:\Keil_v5,别改到中文目录下,否则后面编译会出怪问题。
  3. 激活License:安装完成后打开Keil,点击 File → License Management。输入你的License ID Code(正版用户)或使用评估版(有代码大小限制,但CMS32的工程一般够用)。
  4. 安装器件包:点击 Pack Installer 图标,在搜索框输入 CMS32,找到对应的Device Family Pack,点击Install。
我的小技巧:Pack Installer有时候下载很慢。你可以去中微官网手动下载器件包的.pack文件,然后双击安装。速度能快好几倍。

2.1.2 配置编译器与调试器

装完只是第一步,配置才是关键。我个人习惯在安装完成后做两件事:

  • 设置编译器路径:点击 Project → Manage → Project Components,在 Folders/Extensions 标签页确认ARM Compiler版本。我推荐用V6版本,编译速度快,生成的代码也更小。
  • 配置调试器:如果你用J-Link,点击 Project → Options for Target → Debug,选择 J-LINK/J-TRACE Cortex。然后点 Settings,确认SWD接口能识别到设备ID。如果识别不到,八成是接线问题或者驱动没装好。
注意:调试器驱动一定要装对。我曾经遇到过用Win10自动安装的驱动,结果Keil死活连不上目标板。后来手动装了SEGGER官网的驱动,问题立刻解决。别偷懒,去官网下。

2.2 CMS32固件库导入

固件库说白了就是官方帮你写好的底层驱动。你想想看,如果每个寄存器都要自己翻手册配置,那项目得做到猴年马月?中微官方提供了完整的CMS32标准外设库,咱们直接拿来用就行。

2.2.1 获取固件库

去中微半导体官网的下载中心,找到 CMS32系列标准外设库。下载后解压,你会看到这样的目录结构:

CMS32_StdPeriph_Lib/
├── Libraries/
│   ├── CMSIS/
│   └── CMS32_StdPeriph_Driver/
├── Project/
│   ├── MDK-ARM/
│   └── Templates/
├── Utilities/
└── Documentation/

嗯,这里要注意:Libraries/CMSIS 是ARM官方的内核抽象层,CMS32_StdPeriph_Driver 才是中微写的外设驱动。这两个都得用上。

2.2.2 导入到Keil工程

我习惯新建一个空白工程,然后手动添加固件库文件。这样做的好处是——你清楚每个文件是干什么的。具体步骤:

  1. 新建工程:点击 Project → New μVision Project,选择你的芯片型号(比如CMS32L051)。
  2. 添加分组:在Project窗口右键,Add Group。我一般建这几个组:
    • Startup — 放启动文件
    • CMSIS — 放内核相关文件
    • StdPeriph_Driver — 放外设驱动源文件
    • User — 放你自己的代码
  3. 添加文件:在每个组里右键 Add Existing Files to Group,从固件库目录找到对应的.c和.h文件加进来。
  4. 设置头文件路径:点击 Options for Target → C/C++,在 Include Paths 里添加:
    • Libraries/CMSIS/Include
    • Libraries/CMS32_StdPeriph_Driver/Inc
关键点:头文件路径一定要加对,否则编译时会报 fatal error: 'CMS32L051.h' file not found。我见过太多新手卡在这一步了。

2.3 第一个串口工程创建

环境搭好了,固件库也导入了。接下来咱们写个串口通信的小程序。说白了就是让单片机通过串口发一句 "Hello CMS32!" 出来。

2.3.1 工程结构

先看看整个工程长什么样:

CMS32串口工程结构图 CMS32_UART_Demo Startup CMSIS StdPeriph_Driver startup_cms32l051.s system_cms32l051.c cms32l051_uart.c core_cm0plus.h CMS32L051.h cms32l051_uart.h User: main.c 说明:绿色为分组,橙色为汇编/系统文件,紫色为头文件,红色为用户代码

2.3.2 编写main.c

代码其实不复杂。我直接贴出来,你照着敲一遍:

#include "CMS32L051.h"
#include "cms32l051_uart.h"

void UART0_Init(void)
{
    /* 使能UART0时钟 */
    CLK_EnableModuleClock(UART0_MODULE);
    
    /* 配置GPIO:PA0为TX,PA1为RX */
    SYS->PA_AFS0 &= ~(0x3 << 0);  // 清除功能选择
    SYS->PA_AFS0 |= (0x1 << 0);   // PA0设为UART0_TX
    SYS->PA_AFS0 |= (0x1 << 2);   // PA1设为UART0_RX
    
    /* 配置UART参数:115200-8-N-1 */
    UART_Open(UART0, 115200);
}

int main(void)
{
    uint8_t msg[] = "Hello CMS32!\r\n";
    
    UART0_Init();
    
    while(1)
    {
        /* 发送字符串 */
        for(uint8_t i = 0; msg[i] != '\0'; i++)
        {
            UART_Write(UART0, msg[i]);
        }
        
        /* 延时约1秒 */
        for(volatile uint32_t delay = 0; delay < 4800000; delay++);
    }
}

2.3.3 编译与下载

代码写完了,按 F7 编译。如果没报错,你会看到 0 Error(s), 0 Warning(s)。嗯,这里要注意:如果出现警告,别直接忽略。我见过有人因为一个未使用的变量警告,结果调试时程序跑飞了——其实就是优化等级把变量优化掉了。

编译通过后,按 Ctrl+F5 开始下载。下载成功后,打开串口助手(波特率115200,数据位8,停止位1,无校验),你应该能看到单片机每隔1秒发一次 "Hello CMS32!"。

调试小技巧:如果串口助手收不到数据,先别急着怀疑代码。用示波器量一下TX引脚的波形。有方波说明代码没问题,检查串口助手配置;没波形,八成是GPIO复用配置错了。我当年第一次调UART,折腾了两小时,最后发现是AFS0寄存器写错了位。

2.4 常见问题与避坑指南

问题现象 可能原因 解决办法
编译报错 "cannot open source input file" 头文件路径未添加 检查Options→C/C++→Include Paths
下载时提示 "No Cortex-M SW Device Found" 调试器驱动问题或接线错误 重装驱动,检查SWDIO/SWCLK/GND三根线
串口收到乱码 波特率不匹配或时钟配置错误 核对串口助手波特率,检查系统时钟初始化
程序跑不起来,停在while(1) 启动文件或SystemInit未正确执行 检查startup文件是否添加,SystemInit是否调用
曾经踩过的坑:有一次我帮同事调一个CMS32的工程,代码逻辑完全正确,但就是跑不起来。折腾了半天,发现他用的Keil版本是MDK 5.24,器件包版本太老,启动文件里中断向量表偏移地址不对。升级到MDK 5.38后,问题立刻消失。所以,版本兼容性这事,真不能马虎。

好了,环境搭好了,第一个串口工程也跑通了。接下来你就可以在这个基础上,开始折腾各种外设了。记住,嵌入式开发没有捷径,多动手、多踩坑,经验就是这么积累起来的。