1. 工程创建与基础配置
1.1 Keil工程创建与芯片选型
打开Keil MDK-ARM开发环境,点击"Project"→"New μVision Project",在弹出的对话框中指定工程保存路径并命名。这里我建议采用"STM32F103C8T6_Project"这类包含芯片型号的命名方式,方便后续管理。
在设备选择窗口中找到STMicroelectronics→STM32F1 Series→STM32F103→STM32F103C8。这个选择过程需要注意:
- STM32F103C8属于中等容量产品,Flash为64KB,SRAM为20KB
- 与STM32F103CB(128KB Flash)等型号引脚兼容但容量不同
- 确认选择的型号与实际硬件完全一致
注意:初学者常犯的错误是选错芯片系列(如误选STM32F0/F4),或混淆容量型号(如将C8误选为CB)。这些错误会导致后续编译通过但程序无法正常运行。
1.2 工程目录结构设计
规范的工程目录结构是项目可维护性的基础。我推荐采用以下结构:
code复制Project/
├── Start/ // 系统级文件
│ ├── CMSIS/ // 内核相关文件
│ └── STM32F10x/ // 芯片外设文件
├── User/ // 用户代码
│ ├── main.c
│ ├── ...
├── Drivers/ // 外设驱动
└── MDK-ARM/ // Keil工程文件
实际操作步骤:
- 在工程根目录右键"Add Group"创建Start组
- 在资源管理器中创建对应物理文件夹
- 将资料包中的核心文件复制到Start文件夹:
core_cm3.c/h(CMSIS核心)startup_stm32f10x_md.s(中容量启动文件)system_stm32f10x.c/hstm32f10x.h
2. 启动文件与系统配置
2.1 启动文件选择标准
STM32的启动文件根据Flash容量分为多个版本,选择依据如下表:
| 文件后缀 | 容量范围 | 适用型号示例 | 常见错误 |
|---|---|---|---|
| ld | 16-32KB | STM32F100C4 | 小容量选hd导致溢出 |
| md | 64-128KB | STM32F103C8 | 最常见选错类型 |
| hd | 256-512KB | STM32F103ZE | 大容量选md导致功能缺失 |
| cl | 特殊互联型 | STM32F105/107 | 误用于普通型号 |
对于STM32F103C8T6(64KB Flash),必须选择startup_stm32f10x_md.s。我曾遇到学员使用hd版本导致编译虽然通过但运行时HardFault的情况。
2.2 系统时钟初始化解析
system_stm32f10x.c中的关键函数调用链:
c复制// 启动文件(startup_*.s)中调用
Reset_Handler -> SystemInit()
// system_stm32f10x.c
void SystemInit(void)
{
RCC->CR |= 0x00000001; // 使能内部HSI
RCC->CFGR &= 0xF8FF0000; // 复位时钟配置
// ...更多寄存器配置
SetSysClock(); // 关键时钟设置函数
}
时钟树配置要点:
- 默认使用8MHz HSI(内部RC振荡器)
- 通过PLL可倍频至72MHz(HSI/2*9)
- 需要外部晶振时需修改
stm32f10x.h中的HSE_VALUE定义
3. 头文件与编译配置
3.1 头文件包含路径设置
在"Options for Target"→"C/C++"选项卡中,添加以下路径(根据实际位置调整):
code复制../Start/CMSIS
../Start/STM32F10x
../User
常见问题排查:
-
路径错误提示:
error: #5: cannot open source input file "stm32f10x.h"- 检查路径是否包含中文/特殊字符
- 确认路径是相对工程文件的正确相对路径
-
重复定义问题:
- 确保没有同时包含标准库和HAL库头文件
- 检查
stm32f10x_conf.h中的外设宏定义
3.2 预处理器宏定义配置
必须定义的宏(根据芯片型号):
code复制USE_STDPERIPH_DRIVER
STM32F10X_MD
实测经验:忘记定义STM32F10X_MD会导致
stm32f10x.h中寄存器映射错误,表现为读写寄存器无效果。
4. 用户代码框架搭建
4.1 main.c基础模板
c复制#include "stm32f10x.h"
void Delay(uint32_t nCount) {
for(; nCount != 0; nCount--);
}
int main(void) {
// 硬件初始化
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; // 使能GPIOC时钟
GPIOC->CRH &= 0xFF0FFFFF; // 清除PC13配置
GPIOC->CRH |= 0x00300000; // PC13推挽输出
while(1) {
GPIOC->ODR ^= GPIO_ODR_ODR13; // 翻转PC13
Delay(500000); // 简单延时
}
}
4.2 首次编译问题解决
典型报错案例:
code复制.\Objects\Template.axf: Error: L6218E: Undefined symbol SystemInit (referred from startup_stm32f10x_md.o).
解决方案:
- 确认
system_stm32f10x.c已加入工程 - 检查文件是否被排除编译(右键文件→Options→Include in Target Build)
- 验证头文件路径是否正确
5. 调试与验证
5.1 基础调试配置
在"Options for Target"→"Debug"选项卡中:
- 选择对应的调试器(如ST-Link Debugger)
- 勾选"Run to main()"
- 设置复位策略为"Normal"
5.2 硬件连接检查清单
| 检查项 | 正常状态 | 异常处理 |
|---|---|---|
| 供电电压 | 3.3V±5% | 检查稳压电路 |
| BOOT0引脚 | 接地(GND) | 确保不是浮空状态 |
| 复位电路 | 10kΩ上拉+100nF电容 | 检查阻容值 |
| SWD接口 | CLK/DIO正确连接 | 避免接反或短路 |
5.3 常见下载失败处理
-
No Target Connected:
- 检查调试器驱动安装
- 验证SWD线序(VCC-GND-SWCLK-SWDIO)
- 尝试降低SWD时钟速度(在Debug→Settings中)
-
Flash Download Failed:
- 检查芯片供电是否稳定
- 确认选择的芯片型号正确
- 尝试全片擦除后再下载
6. 工程优化建议
6.1 编译选项优化
推荐配置:
- Optimization Level: -O1(平衡优化与调试)
- One ELF Section per Function: 勾选(减少代码体积)
- Strict ANSI C: 不勾选(避免不必要的限制)
6.2 版本控制集成
建议初始就加入.gitignore:
code复制# Keil specific
*.uvoptx
*.uvguix.*
*.dep
__iar/
# Build outputs
*.axf
*.elf
*.map
*.lst
6.3 外设库使用技巧
寄存器操作与库函数对比示例:
c复制// 直接寄存器操作
GPIOA->CRL = 0x44444444;
// 库函数方式
GPIO_InitTypeDef init;
init.GPIO_Pin = GPIO_Pin_All;
init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &init);
选择建议:
- 学习阶段:建议混合使用,理解底层原理
- 项目开发:推荐使用库函数提高可维护性
7. 进阶准备
7.1 必备工具链扩展
- STM32CubeMX:图形化配置工具,可生成初始化代码
- ST-Link Utility:独立的烧录与擦除工具
- Serial Wire Viewer:低成本调试输出方案
7.2 推荐学习路径
- GPIO控制(LED/按键)
- 外部中断配置
- 定时器基础(PWM/输入捕获)
- USART通信
- ADC采样
每次实验建议新建分支(git branch),例如:
bash复制git checkout -b gpio_led
git checkout -b exti_button