1. 项目概述
作为一名嵌入式开发老手,我深知一个标准化的工程模板对于STM32开发的重要性。今天要分享的是如何在Keil5环境下从零开始搭建STM32F103C8T6的标准库工程模板,这个模板将成为你后续所有项目的基础框架。
STM32F103C8T6(俗称"蓝莓派")是ST公司Cortex-M3内核的经典款,72MHz主频、64KB Flash、20KB RAM的配置,在工控、物联网等领域应用广泛。而Keil MDK作为ARM官方推荐的开发环境,其稳定性和调试功能深受工程师喜爱。
特别提醒:虽然现在HAL库很流行,但标准库(StdPeriph)仍然是理解STM32寄存器操作的最佳途径,特别适合想要深入掌握底层原理的开发者。
2. 准备工作
2.1 硬件准备
- STM32F103C8T6最小系统板(某宝约10元)
- ST-Link V2调试器(兼容版约15元)
- 杜邦线若干
2.2 软件准备
- Keil MDK5(建议版本5.25以上)
- STM32F10x标准外设库(3.5.0版本)
- ST-Link驱动
- 串口调试助手(推荐使用SSCOM或XCOM)
实测发现:Keil5.36版本对中文路径支持不佳,建议安装路径不要包含中文和空格。
3. 工程创建全流程
3.1 新建工程骨架
- 打开Keil5 → Project → New μVision Project
- 选择保存路径(建议:D:\STM32_Projects\Template)
- 在弹出的设备选择窗口搜索"STM32F103C8",选择"C8T6"型号
- 弹出的运行时环境管理器直接点"Cancel"(我们用标准库,不用CMSIS-RTOS)
3.2 文件目录结构搭建
建议采用以下目录结构(右键工程→Manage→Project Items):
code复制├── CMSIS // 内核相关文件
├── Libraries // 标准库文件
│ ├── INC // 头文件
│ └── SRC // 源文件
├── User // 用户代码
│ ├── main.c // 主程序
│ ├── stm32f10x_conf.h // 库配置文件
│ └── system_stm32f10x.c // 系统时钟配置
└── Output // 生成文件
3.3 关键文件配置
-
从标准库包中复制以下文件:
- Libraries/STM32F10x_StdPeriph_Driver下的inc和src文件夹
- Project/STM32F10x_StdPeriph_Template下的main.c、stm32f10x_conf.h、system_stm32f10x.c
- CMSIS/Device/ST/STM32F10x下的启动文件(startup_stm32f10x_md.s)
-
修改stm32f10x_conf.h:
c复制// 启用需要用到的外设库(按需开启)
#define _GPIO
#define _RCC
#define _USART
// 注释掉不用的外设以节省编译空间
// #define _ADC
- 修改system_stm32f10x.c中的时钟配置:
c复制// 将PLL倍频系数改为8(8MHz晶振*9=72MHz)
#define PLL_MUL RCC_PLLMul_9
4. 工程参数配置详解
4.1 目标选项配置
-
点击魔术棒 → Device → 确认选中STM32F103C8
-
Target选项卡:
- 勾选"Use MicroLIB"(精简版C库)
- ROM1地址:0x08000000 大小:0x10000(64KB)
- RAM1地址:0x20000000 大小:0x5000(20KB)
-
Output选项卡:
- 选择输出文件夹为Output
- 勾选"Create HEX File"
-
C/C++选项卡:
- Define中添加:USE_STDPERIPH_DRIVER,STM32F10X_MD
- Include Paths添加:../User;../Libraries/INC;../CMSIS
4.2 调试器配置
- Debug选项卡选择ST-Link Debugger
- 点击Settings → Flash Download:
- 添加Flash算法:STM32F10x Medium-density Flash
- 勾选"Reset and Run"
常见坑点:如果下载失败,尝试降低SWD时钟频率(默认1MHz可能不稳定,可降至500kHz)
5. 模板验证测试
5.1 基础测试代码
在main.c中添加LED闪烁测试:
c复制#include "stm32f10x.h"
void Delay(uint32_t nCount) {
for(; nCount != 0; nCount--);
}
int main(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// 开启GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 配置PB12为推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
while(1) {
GPIO_SetBits(GPIOB, GPIO_Pin_12);
Delay(0xFFFFF);
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
Delay(0xFFFFF);
}
}
5.2 编译下载
- 点击Rebuild All(应该0错误0警告)
- 连接ST-Link到板子(SWD接口)
- SWDIO → PA13
- SWCLK → PA14
- GND → GND
- 3.3V → 3.3V
- 点击Load按钮下载程序
实测技巧:如果出现"No ULINK Device found",尝试重新插拔ST-Link并重启Keil。
6. 工程优化技巧
6.1 编译速度优化
- 在C/C++选项卡添加:
- --optfor=size(优化代码大小)
- --loop_optimization_level=2(循环优化)
- 只包含必要的外设库文件
6.2 版本管理友好化
- 在Output和Listing选项卡中:
- 将生成文件集中到Output文件夹
- 取消生成Browse Information(减少文件变更)
- 创建.gitignore文件忽略中间文件:
code复制*.axf
*.build_log.htm
*.dep
*.d
*.crf
*.o
*.lst
*.htm
*.map
6.3 模板复用方案
- 备份整个工程文件夹
- 新建项目时:
- 复制模板文件夹
- 右键工程→Manage→Project Items重命名工程
- 修改魔术棒→Output→Name of Executable
7. 常见问题排查
7.1 编译错误集锦
| 错误提示 | 原因分析 | 解决方案 |
|---|---|---|
| "stm32f10x.h": No such file | 包含路径错误 | 检查C/C++选项卡的Include Paths |
| undefined SystemInit | 未添加system_stm32f10x.c | 确认文件已加入工程 |
| no space in execution regions | 内存设置错误 | 检查Target选项卡的ROM/RAM配置 |
7.2 下载调试问题
-
无法识别ST-Link:
- 检查驱动是否安装(设备管理器应显示ST-Link V2)
- 尝试降低SWD频率
- 检查接线(特别注意GND要共地)
-
程序下载后不运行:
- 确认Debug选项卡勾选了"Reset and Run"
- 检查BOOT0引脚是否接地
- 重新上电复位
8. 进阶扩展建议
8.1 添加串口打印支持
- 在stm32f10x_conf.h中启用_USART
- 添加以下初始化代码:
c复制USART_InitTypeDef USART_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_Mode = USART_Mode_Tx;
USART_Init(USART1, &USART_InitStruct);
USART_Cmd(USART1, ENABLE);
8.2 启用DWT精确延时
在system_stm32f10x.c中添加:
c复制void DWT_Init(void) {
if(!(CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)) {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
}
void DWT_DelayUs(uint32_t us) {
uint32_t start = DWT->CYCCNT;
uint32_t cycles = us * (SystemCoreClock / 1000000);
while((DWT->CYCCNT - start) < cycles);
}
经过这样配置的工程模板,编译后的代码体积通常在8-12KB左右(取决于启用的外设),为后续功能开发留出了充足的Flash空间。建议每完成一个功能模块就备份一次工程,逐步构建你自己的"黄金模板"库。
