STM32F103C8T6蓝药丸实战指南:引脚、避坑、项目案例
2026/9/5 11:11:31 网站建设 项目流程

STM32F103C8T6这颗芯片,江湖人称“蓝药丸”,在电子工程师圈子里几乎是无人不知的存在。不管你是刚入坑的大学生,还是干了三五年的嵌入式老手,手里大概率都摸过这块蓝色的小板子。它便宜、皮实、资料多,Cortex-M3内核,72MHz主频,20KB RAM,64KB Flash,放在今天看参数确实不起眼,但就是这颗不起眼的芯片,撑起了无数人的毕设、产品原型和个人项目。

这篇博文我不打算讲那种“从零开始学STM32”的流水账,而是基于我这些年实际用这颗芯片踩过的坑、走过的弯路,把最小系统板、引脚功能、CubeMX建工程、常见项目实战、国产替代这些事儿一次性说透。无论你是准备拿它做密码锁、智能家居,还是想移植FreeRTOS、LVGL,这篇文章都能给你一份可以直接照着干的参考。

1. 内容整体设计与思路拆解

1.1 “蓝药丸”到底厉害在哪

“蓝药丸”这个外号,最早来源于市面上最常见的STM32F103C8T6最小系统板——蓝色的PCB、蓝色的按键、蓝色的排针,加上板载一颗AMS1117降压芯片和一颗8MHz晶振,整体就是蓝色调。这块板子在淘宝上常年只卖十几块钱,性能却足以跑轻量级嵌入式系统,于是成了国内嵌入式学习的事实标准。

拆开来看,这颗芯片的核心配置是:

  • 内核:ARM Cortex-M3,最高主频72MHz
  • Flash:64KB(但早期批次有128KB,属于福利型号)
  • SRAM:20KB
  • 引脚:LQFP48封装,可用GPIO约37个
  • 外设:3个USART、2个SPI、2个I2C、1个USB、3个定时器(含高级定时器TIM1)、1个12位ADC(10通道)

这套配置放在2010年前后算是中端偏上的水平,放到今天也完全够用。很多工程师第一次接触RTOS、第一次写驱动程序、第一次调PID,都是在这颗芯片上完成的。它的意义不在于性能多强,而在于它足够简单、足够透明,让你能看清楚单片机的每一个细节。

1.2 为什么是“绕不开”

可能有人会问,现在那么多新款芯片,为什么还要折腾这颗老古董?我自己的体会是,F103系列的产品生态太成熟了。你在开发中遇到的99%的问题,前人早就踩过了,随便一搜就是解决方案。而且HAL库、标准外设库、LL库三套开发方式全部支持,从寄存器级到高度封装,想怎么学就怎么学。

更重要的是,这颗芯片的引脚和很多国产替代芯片(如GD32、APM32、AT32)是pin-to-pin兼容的。也就是说,你用F103做好了设计,如果遇到缺货或者成本压力,可以直接换成国产芯片,改动量极小。这种“退可守、进可攻”的灵活性,是它至今没有被淘汰的核心原因。

2. 核心细节解析与实操要点

2.1 引脚功能速查与避坑指南

拿到板子第一件事,就是认清各个引脚能干什么、不能干什么。我把常用的引脚分组整理一下:

电源与复位

  • 3.3V:主电源,多数最小系统板板载AMS1117,可以从5V输入转3.3V
  • 5V:USB供电输入或者外部电源输入
  • GND:共地必须做扎实,多根杜邦线并联,避免供电不足
  • NRST:复位引脚,低电平复位,板子上通常接了按键

下载调试

  • PA13(SWDIO)、PA14(SWCLK):SWD下载口,接ST-Link/J-Link
  • PA15(JTDI)、PB3(JTDO)、PB4(JNTRST):JTAG引脚,若被复用为GPIO会在上电时产生短暂抖动

这一点特别容易坑新人。如果你把PA13、PA14以外的那几个引脚(PA15、PB3、PB4)配置成普通GPIO,程序烧进去之后下一次下载可能会失败,因为SWD默认调试口被占用了。解决办法是按住复位键的同时点击下载,程序跑起来瞬间把引脚释放,就能重新烧录。

串口

  • PA9(USART1_TX)、PA10(USART1_RX):最常用的调试串口
  • PA2(USART2_TX)、PA3(USART2_RX)
  • PB10(USART3_TX)、PB11(USART3_RX)

推荐把调试信息默认打到USART1上,因为最小系统板通常没焊USB转串口芯片,你外接CH340模块时最先接的就是这两个引脚。

I2C与SPI

  • PB6(I2C1_SCL)、PB7(I2C1_SDA)
  • PB8(I2C1_SCL重映射)、PB9(I2C1_SDA重映射)
  • PA5(SPI1_SCK)、PA6(SPI1_MISO)、PA7(SPI1_MOSI)
  • PB13(SPI2_SCK)、PB14(SPI2_MISO)、PB15(SPI2_MOSI)

软件模拟I2C建议选任意两个GPIO,硬件I2C用PB6/PB7。我实测过,F103的硬件I2C确实有坑,但用HAL库配合超时机制也能跑稳,只是不如模拟I2C好调。

其它

  • PC13:板上蓝色LED,低电平点亮,绝大多数板子用它
  • PA0:ADC通道0,也是WKUP引脚
  • BOOT0、BOOT1:跳线切换启动模式

2.2 最小系统板原理图阅读要点

市面上90%的最小系统板原理图都大同小异。核心部分就四块:3.3V电源、8MHz主晶振、32.768KHz(部分板子没有)、下载电路。我刚开始看不懂原理图的时候,总觉得拉了一堆电阻电容很复杂,后来发现其实就是几个固定的套路。

3.3V电源部分,USB的5V输入经过一个SS14肖特基二极管防反接,然后进入AMS1117-3.3稳压芯片,输出端接两个10uF和一个0.1uF电容滤波。AMS1117的压差比较大,输入不能低于4.5V左右,如果你用锂电池供电(3.7V-4.2V),直接接5V引脚是不行的,会稳压不出来。

8MHz晶振两端各接一个20pF左右的负载电容,这两个电容决定晶振起振的稳定性。如果换用16MHz晶振,记得改CubeMX里的HSE配置,否则系统时钟会跑偏。

下载电路里,SWD引脚直接引出到排针,Boot跳线对应BOOT0和BOOT1的上下拉电阻。说到这里必须提醒一句:BOOT0悬空偶尔会导致下载不稳定,建议拿跳线帽把BOOT0接到GND。

提示:如果下载时提示“No target connected”,先量一下3.3V有没有电压,再查SWD线序,最后按住复位键点烧录,三步排查法能解决九成下载问题。

3. 实操过程与核心环节实现

3.1 5分钟用CubeMX点亮LED

点灯是嵌入式界的“Hello World”,但很多人却在第一步CubeMX配置上卡住。我带你过一遍我用CubeMX创建STM32F103C8T6工程的完整流程,包括几个关键配置项。

第一步:新建项目并选型号

打开STM32CubeMX,点击“New Project”,在Part Number搜索框输入“STM32F103C8”,双击选中“STM32F103C8Tx”那块芯片。注意不要选成“CBT6”或者“RCT6”,虽然也能用,但引脚数量不一样。

第二步:配置调试口和时钟

在System Core下找到SYS,Debug选项选“Serial Wire”,这一步非常关键。如果你不选,会默认使用JTAG,PA15、PB3、PB4就不能当普通GPIO用了,而且SWD下载也可能不稳定。

接下来配置RCC,HSE选“Crystal/Ceramic Resonator”,这样系统才能用外部8MHz晶振。然后在Clock Configuration页面里,把HCLK输入“72”,回车,CubeMX会自动帮你算出所有分频系数。

第三步:配置GPIO

在左侧找到PC13,点击它,选择“GPIO_Output”。然后在GPIO设置里把Maximum output speed选为“Low”(点LED用低速完全够,还能减少EMI),用户标签命名为“LED1”。

第四步:生成代码并编写点灯逻辑

Project Manager里填好工程名和路径,Toolchain选“MDK-ARM”,勾选“Generate under root”,点击右上角的“GENERATE CODE”。

生成之后用Keil打开,在main函数的主循环里写:

while (1) { HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); HAL_Delay(200); }

编译下载,如果一切顺利,蓝药丸上的LED每隔200毫秒翻转一次。如果你发现板载LED常亮或常灭,多半是PC13电平极性搞反了——多数板子是低电平点亮,你改成HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET)就能亮。

注意:下载时确认Keil的Utilities设置里勾选了“Reset and Run”,否则程序烧进去之后需要手动按复位键才能跑。

3.2 HAL库手工创建项目的思路

CubeMX能自动生成工程,但有些时候你想手工创建,能更清楚每个文件是干嘛的。HAL库手工建项目,本质上就三件事:准备启动文件、准备HAL库源文件、写一个main.c和一个stm32f1xx_hal_conf.h配置文件。

启动文件startup_stm32f103xb.s在HAL库包的Projects/STM32F103RB-Nucleo/Templates/SW4STM32目录里能翻到。HAL库源文件在Drivers/STM32F1xx_HAL_Driver/Src,你不需要全部加入,用几个加几个。最少要加的是:

  • stm32f1xx_hal.c(HAL核心初始化)
  • stm32f1xx_hal_cortex.c(NVIC和SysTick)
  • stm32f1xx_hal_rcc.c(时钟控制)
  • stm32f1xx_hal_gpio.c(GPIO操作)

stm32f1xx_hal_conf.h里面有很多宏定义,HAL_MODULE_ENABLED这种,你把需要的外设对应宏放开注释就行。

手工建项目的意义,不是让你以后都手写,而是让你理解CubeMX帮你干了什么。有一次我遇到一个诡异问题,芯片跑着跑着突然死机,最后排查发现是HAL库的HAL_TimeBase和某个中断优先级冲突了。如果当时对手工流程足够熟,应该第一时间就能想到SysTick的中断配置问题。

3.3 串口打印调试的正确姿势

调试嵌入式程序,最顺手的就是串口打印。我这里分享一套适合F103的串口重定向写法,用了很多年,稳定好用。

usart.c里加一段:

#include <stdio.h> int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF); return ch; }

然后在Keil里勾选“Use MicroLIB”,这样就能直接用printf往串口输出。

需要注意,HAL_UART_Transmit是阻塞发送,如果串口波特率太低或者发得太频繁,会拖慢主循环。调试阶段无所谓,量产代码里建议改成DMA发送或者用环形缓冲区。我见过一个项目,就因为主循环里高频printf,导致定时器中断响应不及时,电机控制出现周期性抖动,排查了三天。

传送的波特率我一般用115200,8数据位、无校验、1停止位,这个组合在大部分调试助手和逻辑分析仪里都是默认配置,减少忘记改参数的尴尬。

4. 常见问题与排查技巧实录

4.1 下载失败与烧录器识别不了

这是“蓝药丸”玩家遇到最多的问题,没有之一。现象一般是三种:Keil提示No ULINK Device foundCannot access target,或者ST-Link指示灯异常。

我总结的排查顺序是:

  1. 量3.3V电压是否正常,AMS1117输出应为3.26-3.35V之间
  2. 检查SWD接线,SWDIO接PA13,SWCLK接PA14,GND必须共地
  3. 确认BOOT0跳线帽接到了GND(低电平)
  4. 按住板子复位键,点击下载,出现“Connecting”瞬间松开复位
  5. 换一个USB口,有些前置USB口供电不稳

第五点看着不靠谱,但我真遇到过。USB hub供电不足,导致ST-Link上的LED忽明忽暗,下载永远失败,换了直连主板的口就好了。

4.2 程序跑飞和HardFault排查

芯片跑着跑着突然什么都不动了,多半是进了HardFault中断。新手容易慌,其实排查方法很固定。

先用CubeMX把hard_fault对应的中断打开,然后在stm32f1xx_it.c里的HardFault_Handler打个断点,跑起来之后如果停在断点,就说明真的进了HardFault。接着看Keil的Call Stack窗口,会显示是哪个函数里出了问题。最常见的是数组越界、空指针、以及堆栈溢出。

堆栈溢出特别隐蔽。FreeRTOS移植后,任务栈分配太小,跑一会儿就HardFault。用uC/Probe或者SEGGER SystemView能看到任务栈水线,实在没有工具就临时把任务栈加大一倍,如果不再死机,基本就是栈的问题。

4.3 引脚不够用怎么办

F103C8T6只有48脚,遇到项目需求复杂,引脚真的会不够用。我的建议是分级扩引脚:

  • 第一级:把不用的外设拆掉,比如只用I2C软件模拟,省出硬件I2C引脚
  • 第二级:用74HC595做串转并扩展输出,3个引脚换8个输出
  • 第三级:用PCF8574走I2C扩GPIO,一个I2C口能扩出8个引脚,级联最多64个
  • 第四级:直接换大封装芯片,比如F103RCT6(64脚)

我之前做过一个智能家居网关项目,总共需要控制16路继电器,还要同时接温湿度、OLED、按键,最后就是用了两片PCF8574解决的。这样做的额外好处是,I2C总线上挂多个设备,用不同地址区分,接线也简单。

4.4 供电不足与ADC采样不准

很多人在用F103做电池电压检测时发现,ADC读出来的数值偏大或者跳变厉害。原因多半是参考电压不稳。芯片的VREF引脚如果直接接3.3V,而它又被LED、WIFI模块等吃掉很多电流,参考电压就会波动。

我个人的做法是,ADC的参考电压用TL431搭一个2.5V基准源,然后所有模拟量按比例换算。如果你嫌麻烦,至少在VREF引脚旁边加大电容(10uF+0.1uF并联),并且让ADC采样时间尽量长一些。

HAL库的ADC采样时间设置里,有1.5周期到239.5周期可选。默认的1.5周期是快但噪声大,我习惯把采样时间改成55.5或239.5周期。反正F103跑72MHz,ADC时钟12MHz(分频系数6),采样一个通道几十微秒而已,对大多数场景完全可以接受。

5. 围绕“蓝药丸”的常见项目实战解析

5.1 密码锁项目的架构设计

密码锁是STM32入门第一个像样的项目,它把按键输入、显示、存储、控制输出这几个核心模块全串起来了。我基于蓝药丸做的一款密码锁,整体方案是这样的:

  • 4x4矩阵键盘接PA0-PA7,分列扫描法读取按键
  • OLED(I2C方式,用软件模拟)接PB6/PB7
  • E2PROM(AT24C02)接PB8/PB9,用来存密码
  • 继电器驱动模块接PC13,控制电磁锁
  • 蜂鸣器接PA8,按键音和报警音

核心逻辑分四层:

  1. 矩阵键盘驱动层:轮询扫描,8个GPIO完成16个按键输入,消抖用简单的延时
  2. 业务逻辑层:检测按键动作,拼接输入密码,满6位后自动比对
  3. 存储层:密码第一次开机写入默认密码“000000”,之后支持修改
  4. 输出层:OLED显示当前输入状态,继电器动作,蜂鸣器反馈

有人问我为什么不用硬件I2C接OLED,说实话硬件I2C确实更快,但需要配置中断和超时,软件模拟I2C只需要两个GPIO定时翻转,对24C02这种低速EEPROM完全够用,代码也更直观。

注意:这个项目最大的坑在密码比对环节,建议把密码数组清零后再接收新输入,否则上次输入的残留数据会干扰判断。我当时就是没清空数组,导致密码永远是“111111”才能开。

5.2 MT6701磁编码器的IIC读取与校准

这个项目属于进阶玩法。MT6701是一款14位磁编码器芯片,输出方式支持SPI、I2C、ABZ、UVW多种。用F103的HAL库模拟IIC去读它,能感受一把“外设驱动的细节拉满”是什么体验。

MT6701的I2C地址是0x06(7位地址,含读写位后实际发送0x0C读/0x0D写),它内部寄存器有两个字节存角度数据。读取流程很标准:发起始信号,发送设备地址+写位,发送寄存器地址,重启总线(Restart),发送设备地址+读位,收两个字节,最后发NACK和停止位。

角度计算方式是:

uint16_t raw = (reg1 << 8) | reg2; float angle = (raw >> 2) / 16384.0f * 360.0f;

MT6701的高14位是角度数据,最低两位是状态位,所以右移2位再除以16384换算成0-360度。

我实测下来,直接读出来的角度有轻微的跳动,大概±0.3度左右。如果想更平滑,需要做一阶低通滤波:

float filtered = alpha * current + (1 - alpha) * last;

alpha一般取0.2到0.5之间。注意,滤波会带来相位滞后,电机高速转的时候角度会偏慢,如果你是做FOC闭环控制,滤波系数不能太大。

校准部分,我推荐做零点校准。上电时记录一个初始角度作为零点,之后所有角度都减去这个零点,这样装配时不需要刻意对齐磁铁,程序上就能自动找零。

5.3 LVGL图形库移植与内存优化

LVGL(Light and Versatile Graphics Library)是目前最流行的嵌入式GUI库。很多人觉得F103C8T6只有20KB RAM,跑LVGL会很吃力,其实只要选对配置,跑一个基础仪表盘界面完全没问题。

我移植LVGL v8.3时的关键配置:

  • LV_COLOR_DEPTH设为16,RGB565格式
  • LV_MEM_SIZE设为10KB(LVGL自带内存池,分配在静态数组里)
  • LV_TICKHAL_GetTick()提供时间基准
  • 显示器驱动用SPI接口的ST7735(1.8寸TFT)或者ST7789(1.3寸/1.54寸)

刷新率方面,F103的SPI2最高18MHz,实际跑下来刷一屏大概需要50到80ms,静态界面完全够用,简单动画也能接受。如果发现刷屏卡顿,优先检查是不是SPI模式配置错了,ST7735用SPI Mode 0或者Mode 3,不同屏幕有差异。

内存紧张是另一个常遇到的问题。我在LVGL里开了三个屏幕页面,每个页面放几个控件,编译下载后直接白屏。排查发现LV_MEM_SIZE给的4KB太小了,控件一多就分配失败。最后调到12KB,并把不需要的动画宏关闭,稳定运行。

心得:LVGL移植最耗时间的不是显示驱动,而是输入设备驱动。如果用触摸屏,记得把SPI读取触摸IC的速率和采样周期调好;如果只是按键操作,映射逻辑做好比什么都强。

5.4 FreeRTOS移植与任务划分

F103跑FreeRTOS非常顺,用CubeMX集成的FreeRTOS中间件勾选几下就能生成基础工程。但很多人生成之后发现任务调度不对,或者串口打印乱码,问题通常出在堆栈大小和中断优先级上。

CubeMX里默认任务栈大小是128字节能(对应128*4=512字节),跑复杂逻辑很容易爆。我的习惯是最小任务栈给256Words,带浮点运算的任务给512Words,带LVGL刷屏的任务给1024Words。

中断优先级方面,HAL库要求SysTick的优先级必须是最高的(数值最小),否则HAL_GetTick()会和FreeRTOS的tick冲突。具体操作是在stm32f1xx_hal_conf.h里把TICK_INT_PRIORITY设置为0,然后在CubeMX的NVIC设置里把SysTick和PendSV优先级都调成最低(15),这样才符合FreeRTOS的预期。

我实际项目里的任务划分一般是:

  • 控制任务(最高优先级,512Words):电机控制环
  • 显示任务(中优先级,1024Words):刷新OLED或者TFT
  • 按键/通信任务(低优先级,256Words):扫码、按键扫描
  • 空闲任务:系统兜底

如果你想更稳妥,任务里尽量不要用HAL_Delay(),改用vTaskDelay(),否则在FreeRTOS下会把整个调度器卡住,低优先级任务永远得不到运行时间。

5.5 HX711称重模块与智能家居场景

HX711是24位高精度ADC芯片,专门为电子秤设计,和F103组合做称重传感器几乎零门槛。我在这套方案基础上加了一个智能家居场景:每当米桶重量变化超过阈值,就通过ESP01S把数据发到服务器,完成自动补货提醒。

HX711的通信协议是类SPI:两个引脚SCK和DOUT。F103这边任意GPIO都能接。读数据流程是:等待DOUT从高电平变低电平(表示AD转换完毕),然后SCK发24个脉冲读取数据,最后发1到25个脉冲控制增益和通道。

官方手册推荐增益128,对应通道A。我读出来的原始值和实际重量是线性关系,需要做两点标定。具体做法是,先放一个已知重量(比如100g),记录原始值,再放一个500g,记录另一个原始值,然后算斜率和截距。

float slope = (raw2 - raw1) / (weight2 - weight1); float weight = (raw - raw1) / slope + weight1;

这个项目看起来简单,实际上有几个细节很折磨人:

  1. HX711电源纹波对采样值影响极大,建议DOUT和SCK上各接一个0.1uF电容到GND
  2. 称重传感器输出的是差分毫伏级信号,杜邦线一长就容易引入噪声,最好用双绞线
  3. ESP01S出厂是AT固件,直接接串口发AT指令就能用,但它的供电峰值电流能到300mA,3.3V用板载AMS1117带不动,得外接独立的3.3V电源

6. 国产替代与资源获取方向

6.1 F103的国产平替怎么选

前几年芯片缺货潮之后,国产替代成了工程师绕不开的话题。F103C8T6的国产替代方案主要有三条线:

  • 兆易创新GD32E103系列:主频最高120MHz,引脚兼容,但内部ADC精度略逊
  • 极海APM32F103系列:号称软硬件双兼容,可以直接烧F103的hex文件,省去移植
  • 雅特力AT32F403A系列:主频可达240MHz,RAM大,但外设寄存器和F103有一定差异

我的实际体验是:如果只是跑点灯、UART这种外设,替换几乎无感;但涉及到ADC、定时器PWM这种模拟特性强的外设,必须重新做一次硬件测试。GD32的ADC线性度和F103有差距,做称重项目时我从F103换到GD32E103,发现同样的HX711读数波动比原来大一倍,最后是加了硬件滤波电容才压下来。

替换时还有一个隐藏坑:国产芯片的烧录序列号不同,如果你们的产线烧录工具只支持ST-Link的SWD协议,换成GD32通常没问题,但某些型号需要专用烧录器。我建议在选型阶段就同步验证烧录产线。

6.2 关于“蓝药丸”你还可以这样扩展

蓝药丸能做的东西远不止上面这些项目。我在它上面跑过CAN总线通信(虽然C8T6只有1个CAN),也挂过GPS+北斗双模模块做定位记录仪,还试过用它的USB外设模拟键盘鼠标,做了一套自动化输入工具。

如果你想继续深入,我推荐三个方向:

  1. USB HID免驱方案:F103内置USB,模拟HID设备,不需要额外安装驱动
  2. CANOpen总线:配合串口屏或者电机驱动器,把小系统板变成工业控制节点
  3. 低功耗设计:虽然F103不是低功耗标杆,但用STOP模式+RTC唤醒,能做到电池供电的温湿度记录仪

这三个方向的共同点是,都在榨干F103的最后一滴价值。当你把这颗芯片玩到这种程度,再去看更高端的H7系列或者MP1系列,也不会觉得陌生,外设框架都是相通的。

最后再分享一个小技巧:蓝药丸上那颗PC13的LED引脚比较特殊,它内部有掉电检测逻辑,不能像普通GPIO那样当输出用,偶尔会看到LED亮度随系统负载变化。如果你项目里需要稳定的状态指示,建议换PA1或者PA2接LED,别跟PC13较劲。这块板子陪伴了无数人从入门到进阶,哪怕你后来用上了更贵更快的芯片,偶尔翻出这块蓝药丸,接个OLED跑个示波器波形,依然会有种老朋友还在的踏实感。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询