尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32F103开发板入门到实战:环境搭建、点灯串口与避坑指南

STM32F103开发板入门到实战:环境搭建、点灯串口与避坑指南 1. 拿到板子先别急着点灯搞清楚你手里这块东西到底是什么STM32F103开发板到手的那一刻大部分人的第一反应是插上USB线打开Keil找个点灯例程烧进去看看。这个冲动我完全理解但如果你真想把STM32学明白而不是停留在“烧个例程发朋友圈”的阶段我建议你先花二十分钟把板子上的东西认全。STM32F103这颗芯片属于ARM Cortex-M3内核主频72MHz常见封装有LQFP48、LQFP64、LQFP100、LQFP144几种你买到的开发板大概率是C8T648脚或者ZET6144脚这两种之一。C8T6便宜、体积小适合做小项目ZET6引脚多、外设全适合学习和扩展。拿到板子第一件事翻过来看背面丝印确认芯片型号再看晶振是8MHz还是12MHz这直接决定了你后面配置时钟树的参数。很多人忽略的一个细节是STM32芯片的第一脚怎么确认。这个问题在热搜里出现过说明确实有人被坑过。芯片表面会有一个小圆点或者一个斜切的角那个位置对应的就是第1脚。如果你把芯片焊反了通电瞬间芯片就会发烫基本就废了。开发板上一般不会焊反但如果你自己画板子或者买的是核心板这个细节必须确认。另外开发板的供电方式也要搞清楚是USB直接供电还是外部DC供电有没有AMS1117-3.3稳压芯片VDD和VDDA是否分开滤波。这些看起来是硬件细节但后面你调ADC的时候如果VDDA没处理好采样值跳得你怀疑人生。我个人的习惯是拿到一块新板子先做三件事第一用万用表蜂鸣档测一下VCC和GND有没有短路第二插上USB量一下3.3V输出是否稳定第三看板子上的LED接在哪个引脚按键接在哪个引脚串口TX/RX有没有引出。这三步做完你心里就有底了。STM32F103的开发板市面上很多种有普中、正点原子、野火这些常见品牌也有各种 unnamed 的最小系统板。不管哪种核心逻辑是一样的电源、时钟、复位、下载接口、外设引出。把这五样搞清楚后面学起来就不会慌。还有一点热搜里有人问“apt32101开发板怎么选jlink类型”这说明下载器的选择也是个高频问题。STM32F103支持SWD和JTAG两种调试接口SWD只需要SWCLK和SWDIO两根线占用引脚少速度也够用我强烈建议新手直接用SWD模式。J-Link、ST-Link、DAP-Link都可以ST-Link性价比最高原厂的和国产的都能用。如果你买的是带板载ST-Link的开发板那就更省事了一根USB线搞定供电和下载。2. 开发环境搭建Keil、CubeMX、VS Code到底怎么选2.1 Keil MDK的安装与芯片包配置Keil MDK是STM32开发的老牌工具链资料多、教程全缺点是界面丑、代码补全弱。安装Keil5的时候有一个经典坑Keil5本身不包含STM32的器件支持包你需要单独安装芯片包。热搜里“stm32芯片包安装”能上榜说明这个问题困扰了不少人。具体操作是去Keil官网下载对应的Device Family Pack比如Keil.STM32F1xx_DFP.2.4.0.pack双击安装即可。如果你同时要开发C51和STM32热搜里“keil5兼容c51和stm32安装”也是个常见需求。解决办法是装两个版本的Keil分别放在不同目录或者用Keil5的Pack Installer管理。我个人的建议是如果只是学STM32就别折腾C51了直接用Keil5专注一个平台省得环境冲突。安装完芯片包之后新建工程时选择STM32F103C8或者ZET6Keil会自动加载启动文件和系统初始化代码。这里有一个细节启动文件的选择。startup_stm32f10x_md.s对应中等容量64KB或128KB Flashstartup_stm32f10x_hd.s对应大容量256KB以上。选错了会导致中断向量表偏移不对程序跑飞。C8T6用mdZET6用hd这个必须匹配。2.2 STM32CubeMX图形化配置的利与弊CubeMX是ST官方推出的图形化配置工具可以自动生成初始化代码支持HAL库和LL库。对于新手来说CubeMX能大幅降低入门门槛你只需要在界面上点一点时钟树、GPIO、UART、SPI、I2C都能自动生成。但我的经验是不要完全依赖CubeMX。原因很简单如果你连时钟树怎么配、寄存器怎么算都不清楚后面遇到问题根本无从排查。我建议的做法是前期用CubeMX生成代码但生成之后一定要逐行看一遍搞清楚每个配置对应哪个寄存器HAL库函数底层做了什么。这样学下来你既有效率又有深度。CubeMX配置时钟的时候有一个参数叫HCLK也就是AHB总线时钟。STM32F103最高72MHz来源是外部8MHz晶振经过PLL倍频。具体计算过程是8MHz先经过PREDIV1分频通常不分频然后PLLSRC选择HSEPLLMUL选择9倍频得到72MHz。如果你用的是12MHz晶振PLLMUL就要选6倍频。这个计算过程在CubeMX的时钟树界面里会实时显示但你要理解背后的逻辑而不是只看结果。2.3 VS Code PlatformIO轻量级方案热搜里有人提到“vs code里编译成功却怎么也烧录不进开发板”这个问题我太熟悉了。VS Code本身只是一个编辑器编译和烧录靠的是插件和工具链。如果你用的是PlatformIO编译成功但烧录失败大概率是以下几个原因下载器驱动没装好、烧录算法选错了、芯片被读保护了、或者BOOT0引脚状态不对。STM32F103的BOOT0决定启动模式BOOT00从Flash启动BOOT01从系统存储器启动。正常烧录时BOOT0应该接地。如果你不小心把BOOT0拉高了程序烧进去也不会运行。PlatformIO的好处是跨平台、插件生态丰富、支持自动下载依赖。缺点是国内网络环境下下载工具链可能比较慢而且遇到问题排查起来比Keil麻烦。我的建议是新手先用Keil或者CubeIDE把基础打牢等熟悉了之后再尝试VS Code PlatformIO。如果你非要用VS Code那一定要把STM32CubeProgrammer或者OpenOCD配置好烧录失败的时候用命令行工具单独测试排除是IDE的问题还是硬件的问题。3. 从点灯到串口第一个真正能跑起来的项目3.1 GPIO输出LED点灯背后的寄存器操作点灯是嵌入式的Hello World但很多人只是复制粘贴代码根本没搞清楚GPIO的工作原理。STM32F103的GPIO有8种模式输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽输出、开漏复用、推挽复用。点灯用的是推挽输出模式输出速度一般选2MHz或50MHz。配置GPIO需要操作三个寄存器CRL/CRH配置寄存器、ODR输出数据寄存器、BSRR位设置/清除寄存器。用HAL库的话就是HAL_GPIO_Init()和HAL_GPIO_WritePin()两个函数。但我要说的是你一定要至少手动配置一次寄存器。比如让PA5输出高电平直接写GPIOA-BSRR GPIO_PIN_5让PA5输出低电平写GPIOA-BRR GPIO_PIN_5。这样你才能理解BSRR和BRR的区别以及为什么STM32要设计这两个寄存器。BSRR是原子操作设置和清除可以同时进行不会被打断。这在多任务或者中断环境下非常重要。热搜里有个“stm32电量一个led小灯”我猜是“点亮一个LED小灯”的笔误。这个需求太基础了但基础不代表简单。你要搞清楚LED是共阳极还是共阴极限流电阻多大GPIO驱动能力够不够。STM32F103单个GPIO最大输出20mALED一般需要5-10mA所以直接驱动小功率LED没问题但如果是大功率LED或者多个LED并联就要加三极管或者驱动芯片。3.2 串口通信USB虚拟串口与UART收发串口是调试利器也是STM32最常用的通信接口之一。STM32F103一般有3个USART和2个UARTUSART1挂在APB2总线上最高时钟72MHzUSART2和USART3挂在APB1总线上最高时钟36MHz。配置串口需要设置波特率、数据位、停止位、校验位、硬件流控。波特率的计算公式是baud fCK / (16 * USARTDIV)其中USARTDIV是一个浮点数整数部分写在BRR的高12位小数部分写在低4位。比如72MHz时钟下要得到115200波特率USARTDIV 72000000 / (16 * 115200) 39.0625整数部分39小数部分0.0625*161所以BRR 0x271。热搜里“stm32 usb虚拟串口发送数据”是个高频需求。STM32F103自带USB外设可以配置成CDC设备也就是虚拟串口。这样做的好处是不需要额外的USB转串口芯片一根USB线就能实现供电和通信。但USB虚拟串口的代码比普通UART复杂得多涉及USB协议栈、端点配置、描述符定义。新手建议先用UART转USB模块等熟悉了再折腾原生USB。我实测下来STM32F103的USB虚拟串口在Windows和Linux下都能免驱识别但有一个坑USB时钟必须配置成48MHz。STM32F103的USB外设要求时钟精度较高一般用外部8MHz晶振经过PLL倍频到72MHz再经过1.5分频得到48MHz。如果你用的是内部RC振荡器USB大概率枚举失败。所以玩USB一定要用外部晶振。3.3 定时器从延时函数到PWM输出热搜里“stm32延时函数delay卡死”是个经典问题。很多人用for循环做延时结果优化等级一变延时就不准了。正确的做法是用SysTick定时器或者通用定时器做精确延时。SysTick是Cortex-M3内核自带的24位递减计数器配置简单适合做毫秒级延时。通用定时器TIM2-TIM5功能更强支持PWM输出、输入捕获、编码器模式等。STM32F103的定时器分为高级定时器TIM1、TIM8、通用定时器TIM2-TIM5、基本定时器TIM6、TIM7。高级定时器支持互补输出和死区控制适合驱动电机通用定时器适合PWM、测频、测周基本定时器只能做定时中断。热搜里“stm32定时器捕获测频率”和“stm32定时器模式”都是定时器的典型应用。输入捕获测频率的原理是被测信号接到定时器的捕获引脚定时器在信号上升沿或下降沿触发捕获记录当前计数值两次捕获值之差就是信号周期取倒数就是频率。这个方法测量范围广精度取决于定时器时钟和预分频系数。PWM输出的配置稍微复杂一点需要设置ARR自动重装载值和CCR捕获比较值占空比 CCR / ARR。比如ARR999CCR300占空比就是30%。PWM频率 定时器时钟 / ((ARR1) * (PSC1))。假设72MHz时钟PSC71ARR999PWM频率就是72000000 / (72 * 1000) 1000Hz。这个计算过程你要烂熟于心后面调电机、调LED亮度都靠它。4. 进阶实战那些让你踩坑又让你成长的典型项目4.1 超声波测距输入捕获的经典应用热搜里“stm32超声波测距”是个很受欢迎的项目。HC-SR04超声波模块的工作原理是Trig引脚给一个10us以上的高电平脉冲模块内部发出8个40kHz的方波然后Echo引脚拉高高电平持续时间就是超声波往返的时间。距离 高电平时间 * 声速 / 2。声速取340m/s也就是34000cm/s。如果Echo高电平时间是580us距离就是580 * 0.000001 * 34000 / 2 9.86cm。实现方式有两种一种是轮询用定时器计数等待Echo变高再变低另一种是用输入捕获Echo接到定时器的捕获通道上升沿捕获一次下降沿捕获一次两次差值就是高电平时间。输入捕获的方式更精确不占用CPU但配置稍微复杂。我建议先用轮询方式把逻辑跑通再用输入捕获优化。注意HC-SR04是5V供电Echo输出也是5V而STM32的GPIO是3.3V容忍但为了安全最好加一个电平转换或者分压电阻。我见过有人直接把5V Echo接到STM32引脚短期能用长期有风险。4.2 智能台灯PWM调光与ADC采样“基于stm32的智能台灯”这个项目很适合练手涉及PWM调光、ADC采样光敏电阻、按键控制、串口通信等多个模块。PWM调光就是用定时器输出不同占空比的方波控制LED的亮度。ADC采样光敏电阻的分压值根据环境光强度自动调节亮度。按键用来手动切换模式串口用来输出调试信息。ADC配置有几个关键点采样时间、分辨率、参考电压。STM32F103的ADC是12位逐次逼近型参考电压一般是3.3V所以最小分辨率是3.3V / 4096 0.8mV。采样时间越长转换结果越稳定但转换速度越慢。光敏电阻的阻值变化范围大分压电阻要选合适否则ADC读数要么饱和要么接近零。我一般用10kΩ的固定电阻和光敏电阻串联光敏电阻在亮处约1kΩ暗处约100kΩ分压范围大概在0.3V到3V之间正好覆盖ADC的测量范围。4.3 Modbus通信agile_modbus在STM32上的移植热搜里“agile_modbus stm32”说明有人在工业场景下用STM32做Modbus通信。Modbus是工业领域最常用的通信协议之一分为RTU和TCP两种。STM32上一般跑Modbus RTU通过RS485收发器连接总线。agile_modbus是一个轻量级的Modbus协议栈支持主站和从站代码量小适合嵌入式平台。移植agile_modbus的关键是提供三个回调函数发送数据、接收数据、获取时间戳。发送和接收用UART的DMA或者中断实现时间戳用SysTick或者定时器。RS485收发器需要控制DE/RE引脚切换收发方向发送前拉高DE发送完拉低DE。这个切换时机很关键切换太早数据发不完切换太晚总线冲突。我一般用UART的TC中断来判断发送完成然后在TC中断里切换方向。4.4 STM32控制伺服电机485通信与定时器PWM“stm32控制伺服电机485”这个需求在工业控制中很常见。伺服电机一般支持RS485或者CAN通信通过Modbus协议或者厂商自定义协议控制位置、速度、力矩。STM32这边需要配置UARTRS485收发器定时器输出PWM作为辅助控制信号。伺服电机的控制周期一般是1ms到10ms对实时性要求较高。我建议用定时器中断触发控制循环在中断里发送控制指令和读取反馈数据。注意中断服务函数要尽量短耗时操作放到主循环或者DMA里处理。5. 常见问题与排查技巧实录5.1 烧录失败从硬件到软件逐层排查烧录失败是新手遇到最多的问题没有之一。我整理了一个排查顺序按这个顺序走基本能定位到问题排查步骤检查内容常见问题解决方法1供电USB线只供电不传数据换一根带数据线的USB线2下载器驱动设备管理器有黄色感叹号重装ST-Link或J-Link驱动3接线SWCLK/SWDIO接反或接触不良检查杜邦线确认引脚定义4BOOT引脚BOOT0被拉高BOOT0接地复位后重试5芯片读保护提示“Flash read protected”用STM32CubeProgrammer解除读保护6烧录算法选错了Flash大小根据芯片型号选择对应算法7时钟配置HSE起振失败检查晶振和负载电容热搜里“load ... error: fla”这个报错大概率是Flash算法选错了或者芯片被读保护了。Keil的报错信息有时候很模糊这时候用STM32CubeProgrammer单独连接一下如果能连上说明硬件没问题问题在Keil配置如果连不上那就是硬件或者驱动的问题。5.2 程序跑飞HardFault的定位方法STM32程序跑飞最常见的原因是数组越界、空指针、栈溢出、中断优先级配置错误。HardFault发生时芯片会进入死循环这时候你需要查看LR寄存器和PC寄存器来确定出错位置。在Keil里进入调试模式查看Call Stack窗口能看到函数调用关系。如果Call Stack是乱的那就在HardFault_Handler里加一句while(1)然后查看SCB-CFSR寄存器的值根据错误标志位判断是总线错误、存储器错误还是用法错误。我个人的经验是栈溢出是新手最容易忽略的问题。STM32F103的默认栈大小在启动文件里定义一般是0x4001KB。如果你在函数里定义了大数组比如uint8_t buf[2048]栈直接爆掉。解决办法是把大数组定义成全局变量或者用malloc动态分配或者增大栈大小。启动文件里的Stack_Size改成0x800或者0x1000。5.3 串口乱码波特率与时钟的匹配问题串口乱码的原因通常有三个波特率不匹配、时钟配置错误、硬件流控没关。波特率不匹配最常见比如你代码里设的是115200串口助手设的是9600那肯定乱码。时钟配置错误比较隐蔽比如你用的是8MHz晶振但代码里按12MHz算波特率结果实际波特率偏差超过3%通信就不稳定了。硬件流控如果没关RTS/CTS引脚状态不对数据也发不出来。排查方法先用示波器或者逻辑分析仪量一下TX引脚的实际波特率跟理论值对比。如果没有仪器就换一个已知正确的波特率试试比如9600。另外串口助手的编码格式也要注意GBK和UTF-8混用也会显示乱码。5.4 中断不触发NVIC配置与优先级分组STM32的中断优先级分为抢占优先级和响应优先级通过NVIC_SetPriorityGrouping()设置分组。分组不同抢占和响应的位数分配不同。比如分组2是2位抢占、2位响应分组3是3位抢占、1位响应。如果两个中断的抢占优先级相同响应优先级高的先执行如果抢占优先级不同高的可以打断低的。配置中断的时候别忘了使能NVIC通道也就是NVIC_EnableIRQ()。很多人只写了中断服务函数忘了使能NVIC结果中断永远不触发。6. 学习路径与资源推荐从入门到能做项目6.1 分阶段学习路线STM32的学习曲线比较陡我建议分四个阶段第一阶段GPIO、UART、定时器。目标是能点灯、能串口收发、能定时中断。这个阶段大概需要一到两周。第二阶段ADC、DMA、SPI、I2C。目标是能读传感器、能驱动屏幕、能读写EEPROM。这个阶段需要两到三周。第三阶段USB、CAN、FSMC、SDIO。目标是能做USB设备、能组网通信、能驱动外部存储器。这个阶段需要一个月左右。第四阶段RTOS、文件系统、网络协议栈。目标是能跑FreeRTOS、能读写SD卡文件、能实现简单的TCP通信。这个阶段需要持续学习。热搜里“基于stm32的毕业设计”和“stm32项目”说明很多人是为了做项目而学。我的建议是不要为了做项目而跳过基础。基础不牢项目做到一半就会卡住到时候回头补基础更浪费时间。6.2 工具与资源清单开发工具Keil MDK、STM32CubeMX、STM32CubeProgrammer、VS Code PlatformIO调试工具ST-Link V2、J-Link、逻辑分析仪、示波器参考文档《STM32F10x参考手册》、《Cortex-M3权威指南》、ST官方数据手册社区资源ST官方论坛、电子工程世界、CSDN、GitHub上的开源项目我个人最推荐的入门方式是买一块带板载ST-Link的开发板跟着官方例程走一遍然后自己改代码实现新功能。不要一上来就啃参考手册那样容易劝退。先跑起来再深入原理这个顺序对新手最友好。6.3 常见误区与避坑建议第一个误区只看视频不动手。嵌入式是实践性极强的学科看十遍视频不如自己写一遍代码。第二个误区遇到问题就换方案。比如串口调不通就换SPISPI调不通就换I2C最后什么都没学透。正确的做法是死磕一个问题查手册、查论坛、用调试工具直到解决为止。第三个误区不重视硬件。软件写得再好硬件有问题照样跑不起来。学会看原理图、用万用表、用示波器这些技能和写代码一样重要。我在实际项目中踩过最大的坑是忽略了电源纹波对ADC的影响。当时用开关电源给STM32供电ADC采样值跳了十几个LSB后来换成LDO加了滤波电容问题才解决。所以如果你要做精密测量电源一定要干净。最后再分享一个小技巧善用STM32的硬件错误异常和断言机制。在关键代码里加assert_param()在HardFault_Handler里打印出错地址能帮你快速定位问题。这个习惯一旦养成调试效率至少提升一倍。
返回列表