ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32F103开发板入门到进阶:标准库与Keil实战指南

STM32F103开发板入门到进阶:标准库与Keil实战指南 1. 拿到板子先别急着点灯搞清楚你手里这块STM32-103到底是个啥STM32-103开发板到手的那一刻很多人的第一反应是插上USB线看灯亮不亮。这个冲动我完全理解但如果你真想把这块板子学透而不是停留在“点个灯就跑”的阶段那第一步应该是把板子的底细摸清楚。STM32F103这颗芯片在嵌入式圈子里有个外号叫“单片机界的五菱宏光”——不是因为它低端而是因为它皮实、耐造、资料多、什么活都能干。你手里这块开发板大概率搭载的是STM32F103C8T6或者STM32F103ZET6前者是48脚的精简版后者是144脚的增强版两者在引脚数量、Flash容量、外设资源上差距不小但内核都是ARM Cortex-M3主频最高72MHz。为什么103系列能成为无数人入门嵌入式的第一块板子核心原因有三个。第一它的外设足够全GPIO、USART、SPI、I2C、ADC、DAC、TIM、CAN、USB基本上你能想到的嵌入式基础外设它都有学完这一颗芯片换到其他型号也不会发怵。第二它的资料生态极其丰富中文参考手册、标准外设库、HAL库、各种教程和开源项目铺天盖地遇到问题几乎都能搜到答案。第三它的价格足够低核心板十几块钱就能买到就算烧了也不心疼这对新手来说太重要了。但“资料多”这件事本身也是把双刃剑。你搜“STM32入门”会出来标准库、HAL库、LL库、寄存器操作、CubeMX配置、Keil、IAR、VSCode加PlatformIO等等一大堆路线新手最容易在这里迷路。我的建议很明确如果你手里是STM32F103先用标准外设库加Keil MDK这条最经典的路线把基础外设跑一遍不要一上来就追HAL和CubeMX。原因很简单标准库的代码结构更贴近寄存器操作你能看清楚每一个外设到底配置了哪些寄存器、每一位是什么意思这对建立底层认知至关重要。等你把GPIO、串口、定时器、中断、ADC这些基础外设用标准库摸透了再转HAL库和CubeMX你会发现那只是换了一套API底层逻辑完全一样。还有一个新手经常忽略的点拿到板子后要先确认芯片的具体型号和封装。同样是F103C8T6只有64KB Flash和20KB RAM而ZET6有512KB Flash和64KB RAM这直接决定了你能跑多大的程序、能不能上RTOS、能不能做复杂的物联网网关项目。板子上一般会印型号如果看不清可以用ST-Link连上之后在Keil或STM32CubeProgrammer里读芯片ID。这个动作花不了两分钟但能帮你避免后面“为什么我的程序编译出来放不下”这种低级困惑。2. 开发环境搭建别在工具链上浪费超过一个晚上2.1 Keil MDK的安装与芯片包配置Keil MDK依然是STM32F103最主流的开发环境没有之一。它的优势在于调试体验成熟、断点查看变量方便、对标准库和HAL库的支持都很完善。安装Keil本身没什么好说的官网下载安装包一路下一步就行但芯片包的安装是新手第一个容易卡住的地方。Keil安装完之后默认是不带STM32芯片支持的你需要单独下载并安装Keil.STM32F1xx_DFP这个Device Family Pack。这个包里面包含了STM32F1系列的启动文件、外设寄存器定义、Flash烧录算法等关键内容没有它你新建工程时根本找不到STM32F103这个器件。安装芯片包有两种方式一种是在Keil的Pack Installer里在线下载另一种是去官网手动下载.pack文件然后双击安装。我强烈建议用手动下载的方式因为Pack Installer的在线下载在国内网络环境下经常慢得让人想砸键盘。下载的时候注意版本尽量选比较新的稳定版太老的版本可能缺少某些器件的支持。安装完成后在Keil里新建工程搜索STM32F103如果能正常列出C8、RB、ZE等型号说明芯片包装好了。注意如果你用的是Keil MDK 5.37及以上版本编译器默认是AC6Arm Compiler 6而很多老教程和标准库例程是基于AC5的。AC6对代码的语法要求更严格直接编译老工程可能会报一堆警告甚至错误。解决办法是在工程设置的Target选项卡里把编译器切回AC5或者手动把AC6的警告等级调低。这个坑我踩过不止一次尤其是拿网上的例程来跑的时候。2.2 ST-Link驱动与下载调试配置STM32F103开发板通常板载了ST-Link或者预留了SWD接口。如果你用的是板载ST-Link插上USB线之后设备管理器里应该能识别出一个ST-Link Debugger设备。如果识别不出来或者显示黄色感叹号说明驱动没装好。去ST官网下载ST-Link USB驱动安装即可装完之后最好再用STM32CubeProgrammer连一下确认能正常读取芯片信息。在Keil里配置ST-Link下载需要在Options for Target的Debug选项卡里选择ST-Link Debugger然后点Settings在Flash Download选项卡里确认勾选了Reset and Run。这个选项的作用是下载完程序后自动复位运行省得你每次都要手动按复位键。另外SWD接口的速度建议不要设太高默认的4MHz或者降到1MHz都可以速度太高有时候会导致连接不稳定尤其是在杜邦线比较长或者板子供电不太干净的情况下。2.3 VSCode加PlatformIO另一条值得尝试的路线如果你已经习惯了VSCode的编辑体验那PlatformIO插件是一个很舒服的选择。它把编译器、调试器、库管理都集成在了一起新建STM32F103工程的时候会自动帮你下载对应的工具链和框架。PlatformIO支持Arduino框架、STM32Cube框架和CMSIS框架你可以根据自己的需求选择。不过PlatformIO的调试配置比Keil要麻烦一些需要手动写launch.json和platformio.ini新手可能会觉得门槛偏高。我的建议是先用Keil把基础外设跑通建立信心之后再折腾VSCode加PlatformIO不要一上来就在环境配置上耗掉全部热情。3. 从点灯到串口基础外设的实操路线图3.1 GPIO点灯看似简单但你真的理解推挽和开漏吗点灯是每个嵌入式工程师的Hello World但很多人点完灯就过去了没有深究GPIO的几种输出模式到底有什么区别。STM32F103的GPIO有八种模式输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽输出、开漏复用、推挽复用。点灯用的是推挽输出但你知道为什么用推挽而不是开漏吗推挽输出的结构是上面一个P-MOS、下面一个N-MOS输出高电平时P-MOS导通输出低电平时N-MOS导通驱动能力强高低电平都很“硬”。开漏输出则是只有下面的N-MOS工作输出低电平时N-MOS导通输出高电平时N-MOS截止此时引脚处于高阻态必须外接上拉电阻才能输出高电平。开漏的好处是可以做电平转换和线与逻辑比如I2C总线就必须用开漏输出。点灯这种需要强驱动能力的场景推挽是唯一正确的选择。实操步骤上用标准库配置一个GPIO输出的典型流程是先使能GPIO端口的时钟RCC_APB2PeriphClockCmd然后定义GPIO_InitTypeDef结构体设置Pin、Speed、Mode最后调用GPIO_Init。这里有个细节时钟使能一定要放在GPIO配置之前因为STM32的外设时钟默认是关闭的不使能时钟就去配置寄存器配置不会生效。这个顺序问题新手经常搞反然后对着不亮的灯怀疑人生。3.2 串口通信嵌入式调试的半条命串口的重要性怎么强调都不过分。在实际项目里串口几乎是你唯一的调试输出手段printf重定向到串口之后你可以在代码里随时打印变量值、打印状态信息比单步调试效率高得多。STM32F103通常有3个USART加2个UARTUSART1挂在APB2总线上USART2和USART3挂在APB1总线上配置的时候时钟使能函数不一样这点要注意。串口配置的核心参数是波特率、数据位、停止位、校验位。最常用的配置是115200-8-N-1也就是波特率115200、8位数据位、无校验、1位停止位。波特率的计算涉及到APB总线的时钟频率和USART的波特率寄存器标准库的USART_Init函数会帮你算好但你最好知道背后的公式波特率 fPCLK / (16 * USARTDIV)其中USARTDIV是一个浮点数整数部分写进BRR寄存器的高12位小数部分乘以16后写进低4位。理解这个公式的好处是当你需要非标准波特率的时候你知道该怎么调。printf重定向的标准做法是重写fputc函数在里面调用USART_SendData然后等待发送完成标志位。但这里有个坑如果你在中断里调用printf而fputc里又在死等发送完成标志可能会导致中断响应延迟甚至死锁。更稳妥的做法是用DMA发送或者自己写一个带环形缓冲区的串口发送函数把数据丢进缓冲区就返回由发送中断或DMA来实际发送。这个优化在你后面做物联网网关或者需要频繁打印日志的项目里会非常关键。3.3 定时器不只是延时更是整个系统的节拍器STM32F103的定时器资源很丰富有高级定时器TIM1和TIM8、通用定时器TIM2到TIM5、基本定时器TIM6和TIM7。新手最开始用定时器多半是为了做精确延时比如用TIM做微秒级延时来驱动WS2812灯带或者超声波模块。但定时器真正的价值在于中断和PWM。用定时器做1毫秒中断在中断里维护一个全局的毫秒计数器这就是一个最基础的系统滴答。有了这个滴答你可以实现非阻塞的延时、任务调度、超时检测等等。很多从Arduino转过来的人习惯用delay()但在STM32项目里delay()会阻塞整个CPU什么都干不了。正确的做法是用定时器中断加状态机把需要延时的事情拆成一个个状态在主循环里根据滴答计数来判断是否该执行下一步。PWM输出是另一个重点。控制电机速度、调节LED亮度、驱动舵机都离不开PWM。STM32的定时器PWM模式配置起来不算复杂设置ARR寄存器决定周期设置CCR寄存器决定占空比配置输出比较模式为PWM1或PWM2使能预装载寄存器最后使能定时器和对应的输出通道。频率的计算公式是PWM频率 72MHz / ((ARR1) * (PSC1))。比如你要20kHz的PWM频率来驱动电机可以设PSC0ARR3599这样72MHz除以3600正好是20kHz。3.4 ADC采样从电位器到多通道扫描ADC是连接模拟世界和数字世界的桥梁。STM32F103的ADC是12位的最多有18个通道支持单次转换、连续转换、扫描模式、间断模式等多种工作方式。新手最常做的实验是用电位器分压然后ADC读取电压值再通过串口打印出来。这里有几个实操要点。第一ADC的参考电压通常接的是VDDA也就是3.3V所以12位ADC的精度是3.3V除以4096大约0.8毫伏。如果你要测更高的电压必须用电阻分压而且分压后的电压不能超过3.3V否则会烧引脚。第二ADC采样时间要设置合理采样时间太短的话采样保持电容充不满读数会不准。对于输出阻抗比较高的信号源采样时间要设长一些比如239.5个ADC时钟周期。第三如果你要用多通道扫描必须开启DMA否则ADC转换完一个通道后不会自动切换到下一个通道你需要手动切换规则组的通道号非常麻烦。关于ADC通道切换有个细节值得单独说STM32F103的ADC规则组最多支持16个通道但注入组只有4个。规则组适合常规的轮询采样注入组适合需要高优先级插入的采样场景。在实际项目中我通常用规则组加DMA来做多通道数据采集DMA搬运完成之后触发中断在中断里处理数据。这样CPU的负担最小采样也最稳定。4. 进阶外设与通信协议让板子真正“连”起来4.1 I2C与SPI驱动屏幕和传感器的必经之路当你点完灯、调通串口之后下一步大概率是要接一个屏幕或者传感器。ILI9341驱动的TFT屏幕是很多人的选择但ILI9341的读ID经常出问题网上搜“stm32使用ili9341读id是a1a1”的人不在少数。这个现象通常说明SPI通信本身是通的但读回来的ID不对可能的原因有几个SPI模式设置不对ILI9341通常用Mode 0或Mode 3、读时序里没有正确处理 dummy clock、片选信号没有正确拉低拉高。我的经验是先用逻辑分析仪或者示波器抓一下SPI的时钟和数据线确认时序没问题再检查代码里的SPI配置。I2C的坑更多。STM32F103的硬件I2C在早期版本里有一些已知的问题比如在特定条件下会死锁。很多老工程师宁愿用软件模拟I2C也不愿意用硬件I2C。软件模拟I2C的好处是引脚随便选、时序完全可控、调试方便缺点是速度慢、占CPU。如果你只是接一个OLED屏幕或者EEPROM软件I2C完全够用。如果你要做高速数据采集那还是得用硬件I2C但要做好错误处理和超时重试。4.2 CAN通信工业场景的硬通货CAN总线在汽车电子和工业控制领域是绝对的主流。STM32F103自带CAN控制器配合一个CAN收发器比如TJA1050或者SN65HVD230就能接入CAN网络。CAN通信突然连不上的问题我遇到过好几次排查下来无非几个原因终端电阻没接CAN总线两端各需要120欧姆的终端电阻、波特率不匹配、收发器供电不对、CAN_H和CAN_L接反了。用示波器看CAN_H和CAN_L的差分信号是最直接的排查手段正常的CAN差分信号幅度大约2V左右如果幅度不对或者波形畸变那肯定是硬件问题。4.3 USB设备开发从虚拟串口到自定义HID“STM32如何做USB设备”是搜索量很高的一个问题。STM32F103自带USB 2.0全速设备控制器可以做虚拟串口CDC、键盘鼠标HID、大容量存储MSC等设备。最常用的是虚拟串口因为很多新款电脑已经没有物理串口了用USB虚拟串口可以省掉一个USB转串口芯片。ST官方提供了USB FS Device库里面包含了CDC、HID、MSC等类的例程移植到F103上不算太难但要注意时钟配置USB模块需要48MHz的时钟而F103的USB时钟来源是PLL分频必须保证系统时钟配置正确否则USB枚举会失败。5. 常见问题与排查技巧实录5.1 下载失败与芯片识别问题现象可能原因排查方法Keil提示“No Cortex-M Device found”ST-Link连接不良、芯片供电不足、SWD引脚被复用检查杜邦线、测量3.3V供电、检查SWDIO和SWCLK是否被其他外设占用能识别芯片但下载报错Flash算法未添加、芯片写保护在Flash Download里添加STM32F1xx Flash算法、用STM32CubeProgrammer解除写保护下载后程序不运行复位模式不对、BOOT引脚状态错误检查BOOT0和BOOT1引脚、确认下载后自动复位5.2 串口乱码与通信失败串口乱码九成以上是波特率不对或者时钟配置错误。STM32F103的外部晶振通常是8MHz但有些板子用的是12MHz或者内部RC振荡器。如果你的SystemInit函数里默认按8MHz晶振计算PLL而板子上实际是12MHz那系统时钟就不是72MHz串口波特率自然就偏了。解决办法是修改system_stm32f10x.c里的HSE_VALUE宏定义改成实际晶振频率然后重新编译。另一个常见问题是串口接收丢数据。如果你用的是中断接收每次收到一个字节就进一次中断在高波特率下中断频率很高如果中断服务函数里做了太多事情就会丢数据。更好的做法是用DMA接收加空闲中断DMA把数据搬到缓冲区空闲中断触发时一次性处理整帧数据。这个方案我在多个项目里用过115200波特率下连续接收几百字节都不会丢。5.3 程序跑飞与HardFault定位HardFault是STM32开发中最让人头疼的问题之一。程序跑飞的原因可能是数组越界、空指针解引用、栈溢出、中断优先级配置错误等等。定位HardFault的关键是查看LR和PC寄存器的值在HardFault_Handler里把这两个寄存器打印出来然后去反汇编文件里找对应的地址就能定位到出问题的代码行。更高级的做法是使用Keil的Event Recorder或者自己实现一个HardFault诊断函数把出错时的寄存器状态保存到备份寄存器或者Flash里下次上电时读取。6. 从开发板到实际项目几条值得走的路6.1 物联网网关STM32加ESP32的组合拳STM32F103做物联网网关是一个很经典的方案。F103负责采集传感器数据、控制执行器、处理本地逻辑ESP32负责WiFi连接和云端通信两者之间通过串口AT指令或者SPI通信。搜索热词里提到的“stm32 巴法云”和“freertos stm32物联网网关”都指向这个方向。巴法云是一个国内的物联网云平台提供了MQTT接入和TCP接入两种方式STM32可以通过ESP32的AT指令连上巴法云实现远程数据上报和指令下发。这个方案的关键在于串口通信的稳定性。STM32和ESP32之间的串口通信要有协议帧头帧尾、校验和、超时重传机制不能裸发数据。我通常定义一个简单的协议帧头0xAA 0x55、长度字节、命令字、数据区、校验和、帧尾0x0D 0x0A。接收方用状态机解析收到完整帧后校验校验通过再处理。这个协议虽然简单但足够可靠我在多个量产项目里都用过。6.2 电机控制从步进电机到伺服驱动“五线四相步进电机stm32”和“stm32控制伺服电机485”是搜索量很高的关键词。五线四相步进电机通常用ULN2003或者DRV8323驱动STM32输出四路PWM或者GPIO时序来控制步进电机的转动。关键点是加减速曲线如果直接以固定频率启动步进电机可能会失步或者堵转。正确的做法是用定时器中断生成脉冲在中断里根据当前速度调整下一个脉冲的间隔时间实现梯形或者S形加减速曲线。伺服电机控制通常用485通信发送Modbus协议指令STM32作为主站伺服驱动器作为从站。Modbus RTU的帧格式是地址码、功能码、数据区、CRC校验。STM32的串口加上一个485收发器比如MAX485就能实现。注意485收发器的收发切换需要控制DE/RE引脚发送前拉高发送完拉低这个切换时机要把握好太早会截断数据太晚会影响总线释放。6.3 毕业设计选题基于STM32的完整项目“基于stm32的毕业设计”是一个巨大的搜索流量入口。如果你正在做毕业设计我的建议是选一个有完整闭环的项目而不是只做一个开发板例程的拼凑。比如“基于STM32的智能鱼缸控制系统”这个项目包含了温度采集DS18B20、水位检测超声波或者压力传感器、加热控制继电器加加热棒、喂食控制舵机或者步进电机、显示OLED或者TFT、远程通信ESP32加云平台。这个项目涉及了GPIO、ADC、定时器、串口、I2C、SPI、PWM等多个外设工作量适中演示效果直观答辩的时候也好讲。做毕业设计的时候代码的模块化非常重要。不要把所有代码都堆在main.c里要按照功能划分成单独的.c和.h文件比如bsp_gpio.c、bsp_uart.c、bsp_timer.c、app_sensor.c、app_control.c。这样代码结构清晰调试的时候也容易定位问题。另外一定要写一份详细的README说明硬件连接、编译方法、使用说明这会给你的答辩加分不少。7. 一些踩过坑之后才明白的经验第一不要迷信例程。网上的例程质量参差不齐有些例程本身就有bug有些例程是针对特定板子写的换一块板子就跑不起来。拿到例程之后先看懂它的配置逻辑再根据自己的板子做适配不要直接复制粘贴然后抱怨“为什么我的不行”。第二学会看参考手册和数据手册。参考手册讲的是外设的工作原理和寄存器定义数据手册讲的是芯片的电气特性和引脚定义。遇到问题先翻手册比在群里问人快得多。STM32F103的参考手册有一千多页不需要从头看到尾但至少要知道GPIO、USART、TIM、ADC、I2C、SPI这几个章节在哪里需要的时候能快速翻到。第三备一个逻辑分析仪。几十块钱的八通道逻辑分析仪配合开源软件能帮你抓SPI、I2C、串口、CAN的波形排查通信问题的时候效率提升十倍不止。很多你觉得“玄学”的问题在逻辑分析仪面前一目了然。第四代码版本管理要趁早。用Git管理你的代码每次调通一个功能就提交一次。这样当你后面改出问题的时候可以随时回退到上一个可用的版本。不要等到代码改得面目全非才想起来没有备份那时候哭都来不及。第五加入一个活跃的社区。STM32的生态之所以强大很大程度上是因为社区活跃。遇到问题的时候除了搜索引擎还可以去相关的技术论坛和社区提问。提问的时候要提供足够的信息芯片型号、开发环境、代码片段、现象描述、你已经尝试过的排查步骤。信息越充分别人越容易帮你定位问题。最后再分享一个小技巧如果你在调试的时候发现程序行为不符合预期但又不确定是硬件问题还是软件问题可以写一个最简单的测试程序来验证。比如怀疑GPIO配置有问题就写一个只翻转GPIO的程序用示波器或者LED看波形。如果最简单的程序都不对那说明硬件或者底层配置有问题如果最简单的程序是对的那问题就在你的应用代码里。这个“最小化复现”的思路是我调试嵌入式系统时最常用的方法没有之一。
返回列表