简介:面向基于中微BAT32G137的嵌入式开发者,这份例程集提供从工程模板到外设驱动的完整参考,覆盖常用外设与典型应用,如PWM输出、定时器延时、SPI DMA传输、RTC实时时钟、单脉冲、方波输出、比较器、事件计数等,适合初学者快速上手,也便于有经验者直接移植。包内共三千六百六十四份文件,以C语言源代码和头文件为主,同时包含Keil工程文件、编译链接生成的列表与映像、可烧写镜像和说明文档,整体约十五点三六兆字节,目录分类清晰,便于按模块查阅。目前已有两千一百二十三人学习下载。通过研读示例代码和库函数,可以系统掌握外设初始化、中断服务、通信协议配置、低功耗处理等关键环节,同时参考工程文件理解编译器与调试环境设置,减少开发中的弯路,有效缩短实际项目的评估和落地周期。
1. 拿到 BAT32G137 例程包,先别急着点灯
很多人把 BAT32G137 的“项目开发例程.rar”当成一个点灯模板:解压、打开、下载、灯亮,然后就没有然后了。但 BAT32G137 这类 Cortex-M0+ 内核的国产 MCU,例程包的价值恰恰不在“能跑”,而在它是你了解这颗芯片时钟树、外设寄存器、低功耗设计和 IAP 方案的最快途径。这颗芯片和 NXP 的 LPC824 在引脚和部分外设上有兼容性,但换到国产芯片后,寄存器级细节、启动文件、烧录算法都变了。本文就从解压 RAR 开始,一路拆到你能独立把例程改造成自己产品的底层代码,重点讲透时钟配置、外设骨架、J-Link 调试和几个真正让你卡壳的坑。
2. 开发环境选型与 RAR 包里真正有用的东西
2.1 RAR 解压不是双击右键那么随便
例程包名字是“项目开发例程.rar”,但实际从网盘或官方渠道拿到手后,很多人第一步就栽了:直接双击 RAR 在预览窗口里看文件,然后双击某个.uvprojx,发现 Keil 根本打不开,或者打开了但编译报一堆头文件找不到。
正确解压方式:用 WinRAR 或 7-Zip 把整个 RAR 解压到纯英文路径下,比如D:\workspace\BAT32G137\。不要放进桌面带中文、带空格的目录。Keil 的老版本对中文路径支持很差,V5 编译器尤其敏感,即便你能打开工程,编译也会出现cannot open source input file这类莫名其妙的错误。
解压后先看目录结构,而不是急着打开工程。常见做法是先确认 RAR 包里是否带Doc或UM目录,里面通常有芯片用户手册(User Manual)、数据手册(Datasheet)和例程说明 PDF。这三个文件比任何博客都有说服力,寄存器字段、波特率计算公式、时钟树图都在里面。如果解压过程中提示 RAR 密码错误,可以先用 16 进制编辑器打开文件头确认这不是一个伪加密包;不过正规渠道的例程包几乎没有加密这种操作,遇到密码报错第一反应应该是重新下载一遍,排除文件损坏。
2.2 用 Keil MDK 把工程跑起来,先别急着连板子
打开 Keil 工程后,我一般会做四件事,按顺序来,能避开 80% 的后续麻烦:
- 在
Project – Manage – Pack Installer里确认芯片对应的 Device Family Pack(DFP)已安装。BAT32G137 在 Keil 的 Pack 列表中属于中微半导体(Megawin / Midchip)的 BAT32G13x 系列。编译前如果报Device not found,说明 DFP 缺失,去官网或 Pack 列表里找到对应系列装上,而不是手动改芯片型号硬编译。 - 不连开发板,先按
F7编译。例程包理论上应该是干净的,但实际拿到的包经常夹杂缺失源文件、未生成的中间目录,或者带了不存在的调试脚本,导致编译不过。先确保工程能 build 出 hex。 - 确认编译器版本。老例程多半用 ARM Compiler V5,新版本 Keil(5.37 之后)默认配 ARM Compiler V6。V6 对语法检查更严格,老例程在 V6 下经常因为类型转换不匹配或旧式函数声明报 error,换来换去浪费时间。我的做法是:如果 RAR 包内带了说明文档,按文档要求选编译器版本;没有说明就先用 V5 编译,V5 过了再说。
- 查看编译输出里的 Code / RO-data / RW-data 大小。BAT32G137 的 Flash 和 RAM 都不算大,例程编译出来的体积通常不会超,但这个数字能帮你判断是不是把 Debug 信息或调试打印整个编进了 hex 里。
2.3 例程目录结构的读法:哪些该改,哪些不该动
打开解压后的例程目录,典型结构长这样:
| 目录 / 文件 | 内容 | 建议 |
|---|---|---|
Libraries/或Device/ | 启动文件、系统时钟初始化代码、寄存器定义头文件 | 不该动,属于芯片底层 |
Bsp/或Drv/ | 外设驱动例程,如 uart.c、gpio.c、adc.c | 项目复用时按需拷贝,可以改动 |
App/或User/ | main.c 和任务逻辑 | 改动的主要区域 |
Project/ | Keil 工程文件.uvprojx | 按项目环境自己维护 |
Doc/ | 用户手册、勘误表、例程说明 | 最重要,先看 |
有个容易被忽略的文件是 system 初始化相关源码,比如system_bat32g137.c。这个文件里定义了系统时钟从哪个振荡器启动、主频切到多少 MHz。BAT32G137 内部有高速 RC(HRC)、外部晶振等时钟源,例程默认往往用内部 HRC 48MHz。你要是启动后直接改外设寄存器,却不看时钟频率,后面所有外设参数都会算错。例程的 BSP 层实际上是一个「可裁剪的驱动集合」,它不是给产品用的,但它是你写自己驱动的起点。
3. 拆一个点灯例程,讲清楚外设初始化的套路
3.1 GPIO 例程过一遍:时钟从哪里来,引脚怎么选
先看例程main.c里 GPIO 初始化这一段,市面上 BAT32G137 例程的基本骨架是相似的:
void gpio_init(void) { /* 开启 PORTA 时钟,BAT32G137 的 GPIO 按端口分组挂在 APB 总线上 */ SYSREG->APBEN |= (1UL << 1); /* 配置 P1.5 为推挽输出,初始输出低电平 */ GPIOA->PMOD |= (1UL << 5); /* 设为输出模式 */ GPIOA->POD &= ~(1UL << 5); /* 输出低 */ /* 若引脚带复用功能,例如 P0.4 复用为 UART0_TXD,还要配置 PMUX 寄存器 */ }这段代码的逻辑是:先把端口外设时钟打开,然后对引脚方向、输出数据寄存器操作。和前几年用的 51 单片机裸操作不同,Cortex-M0+ 上的外设都要先给时钟再操作寄存器,否则写进去的数据直接丢失。
PMOD方向寄存器的 1 是输入、0 是输出(具体极性以对应型号的芯片手册为准,这不是通用标准,不同厂商定义可能相反),POD是输出数据寄存器。这里反直觉的点在于:例程里引脚编号、端口号并不是纯物理意义上的 PA5/PB5,而是要对照 Datasheet 里的引脚分配表去确认功能复用。BAT32G137 的引脚不少是复用的,例如同一个引脚可以是 P1.5、也可以是 SPI 时钟或定时器输出,具体由PMUX寄存器决定。做产品选引脚时,先把硬件原理图里用到的功能列成一张表,再对照手册查复用关系,比到时候改板子省事得多。
提示:不要在例程的
gpio_init()里直接改几个引脚号就当产品初始化写完了。例程的延时、按键消抖、LED 驱动和你的硬件往往不同,最好把你的板级外设封装成 board.c,把例程代码当成参考实现而不是产品代码。
3.2 串口收发例程:波特率是怎么算出来的
串口是例程里最有参考价值的部分,因为波特率误差直接暴露你对时钟树的理解。BAT32G137 的 UART 波特率通常由外设时钟分频而来,典型初始化片段:
void uart0_init(uint32_t baud) { uint32_t uart_clk = 48000000UL; /* 假设外设时钟 48MHz */ uint32_t dl = uart_clk / (16 * baud); /* 配置引脚复用为 UART0_RXD / UART0_TXD */ GPIOA->PMUX &= ~(0x3UL << (4 * 2)); GPIOA->PMUX |= (0x1UL << (4 * 2)); /* 设置波特率分频寄存器 */ UART0->BRG = dl; UART0->CTL |= (1UL << 0); /* 使能 UART0 */ }这里的关键不是代码本身,而是uart_clk的分频时钟源。如果例程用内部 48MHz,那么波特率 115200 时分频系数是 48000000 / (16 * 115200) = 26.04,整数部分是 26,实际波特率会偏差约 0.15%,余量还能接受。但如果你把系统主频改成了 24MHz,分频整数变成 13,误差还在;改成其他不见得整除的频率,比如外部晶振用 12.8MHz,误差可能到 1% 以上,串口就会出现偶发乱码。例程存在的意义就是给你一个正确推导的起点:先确认时钟,再谈波特率。
串口例程里一般会配套中断接收、环形缓冲的实现,这些代码的架构可以直接抄到产品里,但要注意中断优先级分组、嵌套向量中断控制器(NVIC)的使能是否被例程写死在别的文件里。
3.3 外设例程的通用骨架:SystemInit → 外设时钟 → 引脚复用 → 配置外设
把 GPIO、UART、SPI、I2C、ADC 这几个例程的初始化函数放在一起对比,你会发现套路高度统一:
SystemInit()设置系统时钟和 Flash 等待周期- 在外设模块寄存器上使能对应外设的时钟门控(APB/ABP 分频)
- 配置引脚复用(PMUX 或数字/模拟切换)
- 配置外设自身的控制寄存器、数据寄存器
- 按需开启中断并注册中断服务函数
这个骨架不是 BAT32G137 独有的,但例程包把它做成了可对照抄写的样板。我的建议是:不要逐个例程去读,而是挑两个你最关心的外设例程通读,读的过程中把三步画出来——时钟怎么来、引脚怎么复用、外设寄存器怎么配。等你画完 UART 和 ADC 两条,剩下的 SPI、I2C 基本能猜个七七八八。
4. 从例程到产品:时钟树、启动文件和调试器选型
4.1 时钟树按例程默认来,还是改外部晶振
BAT32G137 例程默认使用内部 HRC,频率常见为 48MHz。这对大多数产品够用:省掉外部晶振的两个负载电容,降低成本、减少起振失败风险。但有两类场景必须换成外部晶振:一是做低功耗应用,需要低频 LXT 32.768kHz 给 RTC 做计时,睡眠时主时钟停掉但 RTC 继续跑;二是做高精度通信,例如 CAN、USB 这类对时钟精度敏感的外设,内部 RC 在温度变化下可能偏差超过 1%,这时候用外部晶振作为系统时钟来源,配合锁相环或直接分频得到外设时钟,误差能压到 0.1% 以内。
修改例程时,不要直接在SystemInit()里在旁边硬改寄存器,我一般会按例程原有的CLK_SOURCE_SELECT这类宏定义切换,例程一般不缺这个配置,只是默认走的内部时钟分支。确认修改生效的方法是读系统时钟状态寄存器,把当前的时钟源和分频值打印或调试观察一遍,别猜。
4.2 startup 文件与 Flash 烧录算法
BAT32G137 例程包里的启动文件通常是汇编,叫startup_bat32g137.s之类的名字。它负责初始化堆栈指针、调用SystemInit()、然后跳main()。产品开发时启动文件几乎不需要改,但要注意两点:
第一,堆栈大小定义。启动文件顶部通常有Stack_Size EQU 0x400、Heap_Size EQU 0x200这样的定义。如果你的代码里用了较深的函数嵌套、较大的局部变量数组、或者引入 RTOS,Stack_Size不够会导致程序跑飞,表现为随机死机。例程的堆栈大小是按例程功能设定的,到了你产品里可能要加大到 0x800 或 0x1000。
第二,烧录算法。在 Keil 的Options for Target – Utilities – Settings里,要正确选择 BAT32G137 对应的 Flash 烧录算法文件(Flash Algorithm),选错会出现下载时Erase Failed或Flash Timeout。换了调试器后这个配置容易丢,因为算法文件名往往和芯片型号强相关,重新选择即可。如果例程包里带了独立的烧录工具(ISP 串口下载工具),也可以不用 J-Link,直接串口下载,步骤更简单,只是每次烧录要手动进 Boot 模式。
4.3 调试器:J-Link 和 CMSIS-DAP 在 Keil 里的配置
拿到例程后最常见的报错是No target connected,尤其是新买的下载器第一次用,或者 V10、V11 固件版本和 Keil 版本不匹配,驱动装完却识别不了。建议在 Keil 的Options – Debug – Settings右侧看SW Device是否列出芯片 ID。SWDIO和SWCLK必须接到 MCU 对应引脚,GND 共地,这是三条线的底线。某些板子复位引脚上接了较大的电容,导致 SWD 连接时复位时序被破坏,连不上或连上后立刻断开,解决办法是先断开 NRST 线,用三线连接试试——大部分情况三线就够了。
调试器下拉速度也要注意:连不上时把 SWD 时钟从 4MHz 降到 1MHz 或更低,飞线连接质量不好的情况下,降速比检查接线更有效。CMSIS-DAP 的兼容性通常优于杂牌 J-Link,价格也便宜,但如果 Keil 版本较老,CMSIS-DAP 可能出现无法设置硬件断点的问题,和例程本身无关。例程的.uvprojx里通常默认配了某一个调试器型号,换用不同调试器时记得在 Debug 下拉框切换,否则例程会在启动调试时弹错。
提示:例程里自带工程文件的调试器配置未必适合你的硬件,建议把工程文件复制一份后,单独维护你自己的版本,不要把例程工程直接当产品工程长期改下去。
5. 例程的高级玩法:低功耗、双区 IAP 和几个排查技巧
5.1 低功耗例程:SLEEP/STOP 模式的入口在哪里
BAT32G137 的低功耗例程一般提供 SLEEP 和 STOP 两种模式示例。SLEEP 模式下 CPU 停、外设时钟照跑,适合定时唤醒的任务;STOP 模式几乎全部时钟停止,只有特定唤醒源能拉起系统,例程里通常会用外部中断或 RTC 定时中断配合演示。产品做低功耗不是简单调一个进入停止模式函数,你要先逐个关掉不用的外设时钟,把 GPIO 配置成合适状态(输入上拉或输出固定电平),避免引脚悬空带来的漏电。例程的功耗数据和你的板级设计关系很大,官方手册里的电流指标只能在理想条件下复现,别把例程的功耗数字当成你自己的产品指标。
5.2 用 16 进制编辑器对齐 IAP 跳转地址
产品需要远程升级时,常见做法是双区 IAP:Boot 区放在 Flash 起始地址,App 区放在偏移地址,比如 0x4000 处。例程包可能会带 IAP 跳转的参考代码,但跳转地址和 App 工程的链接地址必须一致。我习惯在生成 App 的 hex 后用 16 进制编辑器打开,查看第一条有效指令所在地址是否落在我设定的 Flash 偏移处。如果 App 工程编译出来的代码起始地址和 Boot 跳转地址不一致,现象是跳转后直接死机或运行异常,而且这种 bug 用调试器很难跟踪,因为跳过去的 PC 已经和 Keil 下载时的地址段不一致了。
5.3 三个实战排查技巧:烧不进、跑飞、串口乱码
| 现象 | 排查顺序 | 常见原因 |
|---|---|---|
烧录报No target | 1. 接线 2. 降速 3. 查复位电容 | SWDIO/SWCLK 接反、目标板没供电、调试器固件太老 |
| 程序跑飞/进 HardFault | 1. 查栈溢出 2. 查指针越界 3. 查外设时钟没开 | 例程堆栈太小、数组越界写坏栈、GPIO 复用冲突 |
| 串口乱码 | 1. 确认时钟源频率 2. 算波特率误差 3. 查接线 | 实际主频和计算用主频不一致、分频系数舍入误差过大 |
最后一个技巧:按复位键能跑、调试器连上就跑飞的现象,通常是调试器和例程工程里的启动脚本不匹配,把Debug – Settings – Flash Download里的Reset and Run勾上或去掉,往往就好了。例程包给你的不是一个终点,而是你验证自己修改的基线,每次改动单独编译、单独验证,比一口气改完十几个外设再排查省时间得多。
本文还有配套的精品资源,点击获取