拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

STM32入门实战:从点灯到物联网的完整避坑指南

STM32入门实战:从点灯到物联网的完整避坑指南

板子买回来了,拆快递那一瞬间的心情我太理解了——一块蓝色的STM32F103C8T6最小系统板,两根杜邦线,一个ST-Link或者CH340G的串口下载器,再加上一堆“等会儿再说”的教程书签。很多人的故事到这里就结束了,板上落灰,收藏夹吃灰。但如果你真想把这块板子变成自己手里的硬通货,这篇东西就是写给你的。我不会跟你讲什么高深的理论,就按一个普通爱好者实际摸索这条线,把STM32开发里最绕的、最容易被忽略的、以及最容易劝退新手的那些坑,一个一个摊开讲明白。

1. 板子还没吃灰之前,先搞清楚手里这是什么

1.1 丝印命名规则决定了你买的是什么

绝大多数第一次入手STM32的人,买的都是那块最经典的蓝色板,主控丝印写着STM32F103C8T6。这几个字母数字不是随便印上去的,每个字段都对应了芯片的具体规格。F103代表这是主流增强型系列,C8表示48引脚、64KB Flash,T是LQFP封装,6代表工业级温度范围。读懂这串字以后,你查资料、搜手册、下载器件包时就不会找错对象。

F103这个系列能成为入门的代名词不是偶然。它内核是Cortex-M3,主频最高72MHz,不算快,但外设覆盖得非常均衡——定时器、串口、I2C、SPI、CAN、ADC、DMA全都有。对学习来说这反而是优点:它的外设足够全,教程资料又铺天盖地,几乎你遇到的每一个报错,都有人在几年前踩过同一个坑并且留下了帖子。这是其他开发板很难比的生态红利。

板子上除了主控,还会有一两个不起眼但特别关键的小芯片。最常见的是CH340G或者CH340C,这是USB转串口芯片,负责让电脑通过USB口跟板子通信。有些新版板子用的是GD32E230兼容料或者CP2102,功能都一样。你插上USB线以后,电脑设备管理器里如果能看到一个“USB-SERIAL CH340”的COM口,说明板子的串口链路是通的,这块板至少没短路、没烧坏。

1.2 刚到手先别急着烧程序,花十分钟做个安全检查

我在群里见过太多人问“为什么我的板子灯不亮”,结果查到最后是BOOT跳帽没插、或者板子背面焊点短路。所以板子到手以后,我建议你先做三件事。

第一件事是核对板子上的丝印和卖家页面上的主控型号是否一致。如果发现板子印着C8T6但标称是C8T6兼容版,问题不大,但如果丝印是“STM32F103C6T6”,内存容量就少了,后面Keil里选芯片时得对应选C6而不是C8。第二件事是用万用表蜂鸣档量一下板子的3.3V和GND之间有没有短路,正常情况是几百欧到几千欧的电阻,如果直接导通,那就先别上电。第三件事是检查BOOT0和BOOT1两个跳帽的位置。绝大多数蓝色板出厂时BOOT0是拉低的,也就是从Flash启动,这符合正常使用条件。如果你发现BOOT0被跳到了1的位置,上电后程序是不会从Flash跑起来的,这就会造成“我烧了程序但没反应”的假象。

1.3 芯片第一脚怎么确认,别焊反了再后悔

“stm32芯片第一脚怎么确认”这个问题看着基础,但翻车的人真不少。LQFP48封装的芯片,正面朝上,左下角会有一个圆形凹点或者斜边缺口,那个位置就是第一脚。逆时针数是1到24,右半边是25到48,具体引脚定义要查数据手册里的引脚图。如果你在画板子或者飞线,最稳妥的办法是先找芯片丝印上的原点,再对照手册确认这一脚的功能,千万不要凭印象去猜。

确认芯片引脚这件事,本质上是训练读手册的习惯。STM32的功能不是靠背出来的,而是靠查出来的。刚开始一个个引脚去查好像很慢,但查过几次以后,你对GPIO复用、AFIO重映射、默认引脚功能的印象会深很多。这块板子用的LQFP48封装把很多引脚都引出来了,和Arduino那种把引脚标成D0~D13的方式不同,你必须对着原理图或者引脚图去找PA3、PB10这种命名。这个转换期大概会持续一两周,熬过去以后,换到任何一款STM32都能很快上手。

2. 别急着点灯,开发环境先弄利索

2.1 标准库和HAL库到底选哪个,别再纠结了

这是STM32新手问得最多的问题,也是被各路教程吵得最凶的问题。F103有两个主流的开发方式:标准外设库(Standard Peripheral Library)和HAL库。标准库的特点是离寄存器近,函数封装得比较薄,你看代码时能看到寄存器操作的味道,比如GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP这种写法。HAL库是ST官方后来的主推方向,配合STM32CubeMX图形化配置工具,生成的初始化代码非常规范,但封装层次厚,出了问题不好追到底层。

我给你的建议是:如果只是想快速看到现象、跟着老教程跑,标准库没问题,网上资料多,代码量也少;如果打算长期在这条路上走下去,那就直接学HAL库加CubeMX。我自己是标准库起步的,后来切到HAL库时确实花了一点时间重新适应,但说实话,HAL库的抽象能力在做大项目、换芯片型号时优势非常明显。标准库的痛点是官方已经不维护了,CubeMX根本不生成标准库代码,只是社区里老项目还在用而已。

不管选哪个库,有一件事必须搞清楚:库只是帮你封装寄存器的操作,底层还在操作那些地址。所以学的时候不要只看函数名,要偶尔打开库文件看看里面到底做了什么。举个例子,标准库的GPIO_Init里面其实就在写GPIOx->CRL和GPIOx->CRH;HAL库的HAL_GPIO_Init最终也是往ODR、IDR、CRL这些寄存器里填值。只要你理解了寄存器,两个库都不用背。

2.2 Keil MDK安装与器件包配置的完整流程

Kill MDK,也就是Keil MDK-ARM,是Windows下最主流的STM32开发IDE。安装本身没难度,但很多人卡在了器件包上。你新建工程时如果在Device列表里找不到STM32F103C8,那就是没装STM32F1系列的器件包。去Keil官网的Pack页面下载Keil.STM32F1xx_DFP这个包,双击安装,Keil会自动将其放进CMSIS Pack路径。这一步做完,Device里才会出现STM32F103C8这个选项。

新建一个标准库工程时,最关键的一步是选对启动文件。启动文件是芯片上电后最早执行的汇编代码,不同芯片的启动文件不能混用。F103系列里,根据你的Flash容量,要么选择startup_stm32f10x_md.s(中等容量,64KB和128KB适用),要么选ld.s(低密度)、hd.s(高密度)。C8T6属于中等容量,选startup_stm32f10x_md.s。

工程建好以后,魔术棒(Options for Target)里的设置也很关键。在Device标签确认芯片型号是STM32F103C8;在Target标签可以不用管Flash大小;在C/C++标签里需要加两个Define:USE_STDPERIPH_DEVICE和STM32F10X_MD。第一个是告诉标准库“我在用这个库”,第二个是告诉库“我这是中等容量的F10x芯片”,没有这两个宏定义,编译一团乱麻。Debug标签里选择你手头的下载器,比如ST-Link Debugger,然后点旁边的Settings确认能识别到芯片ID。如果这一步显示No Target Connected,后面烧录时也会失败。

2.3 用VSCode和PlatformIO搭开发环境的路子

Keil是主流,但不是唯一选择。现在不少人喜欢用VSCode配合PlatformIO插件开发STM32,好处是代码补全和Git集成体验比Keil好太多,坏处是需要一点折腾。我第一次配的时候在platformio.ini里折腾了好一会,其实核心配置很简单。新建一个工程,把platformio.ini写成这样:

[env:bluepill_f103c8] platform = ststm32 board = bluepill_f103c8 framework = stm32cube

然后让PlatformIO自动下载编译工具链和HAL库。烧录时用ST-Link的话,还可以在platformio.ini里加上upload_protocol = stlink。如果你已经熟悉Keil那套流程,VSCode方案可以后面再碰,不要同时学两套工具链,不然报错时你分不清是环境问题还是代码问题,会非常痛苦。

3. 从点灯到真正理解单片机:几个必做的实操

3.1 点灯背后到底发生了什么

点灯是单片机的“Hello World”,但STM32上的点灯比Arduino多了一层关键动作:开时钟。很多人看教程里点灯例程都有这么一句话:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

如果不写这句话,LED就是不亮。原因是STM32为了省电,默认情况下绝大部分外设的时钟都是关闭的,GPIO不例外。你可以把时钟理解为外设的电源闸刀,先用RCC函数合上闸刀,GPIOB里的寄存器才有意义,然后才能配置引脚模式、写输出数据。这个细节比点灯本身重要,因为它能解释你后面遇到的很多灵异现象——外设没反应先查时钟,这是排查一切问题的第一原则。

以标准库为例,点灯的完整流程是:先开GPIO时钟,再初始化引脚为推挽输出,最后向ODR寄存器写电平。如果用寄存器版本看,会更直观:

RCC->APB2ENR |= (1 << 3); // 打开GPIOB时钟 GPIOB->CRL &= ~(0xF << 20); // 清除PB5相关位 GPIOB->CRL |= (0x3 << 20); // PB5设为推挽输出 GPIOB->ODR &= ~(1 << 5); // PB5输出低电平

把这些代码一行一行读懂,你就算是踏进STM32的门了。LED接法不同,是高电平亮还是低电平亮,决定了你最后写ODR的值是置1还是清0,接线前一定看原理图。

3.2 按键输入与抖动处理

点完LED之后,下一步就是按键。输入和输出在寄存器层面的操作逻辑完全不同:输出是写ODR,输入是读IDR。做按键时,GPIO模式要设置成输入模式,一般配合上拉或者下拉。STM32的GPIO内部有上拉和下拉电阻,标准库里对应GPIO_Mode_IPU和GPIO_Mode_IPD。如果你的按键一端接GND,另一端接引脚,那应该用上拉输入,按键按下时引脚读到低电平。反过来,按键一端接3.3V,就用下拉输入,按下时读到高电平。

按键真正麻烦的不是读电平,而是抖动。按键触点是机械结构,按下弹起时会有几毫秒的不稳定抖动,直接读的话一次按键可能会被识别成好几次。最简单的软件消抖方式就是检测到电平变化后延时20毫秒再确认一次状态:

if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) { DelayMs(20); if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) { // 确认按键按下 } }

二十毫秒这个值不是拍脑袋定的,一般按键的抖动时间都在5到15毫秒,取20毫秒留出余量。如果你用扫描式的矩阵键盘,还要注意多键组合和消抖状态机的设计,这里就不展开了。

3.3 定时器延时、输入捕获与PWM

很多人在写完延时函数后发现一个诡异现象:程序跑得好好的,一旦开了某个中断,Delay函数就卡死。这大概率是卡在了延时的实现上。常见的简单延时函数是用SysTick做的,SysTick是内核里的一个24位递减计数器,很多人实现Delay时会在中断里处理计数。如果你开了中断但优先级没配好,或者中断服务函数没有及时清除标志位,Delay就会停在那里。

定时器是实现延时、PWM、输入捕获的核心外设。STM32F103有几个基本定时器、通用定时器和高级定时器,其中TIM2到TIM5是通用定时器。计算溢出时间的公式是:

溢出时间 = (PSC + 1) * (ARR + 1) / 定时器时钟频率

比如定时器时钟是72MHz,我想让TIM2每1毫秒中断一次,可以设PSC = 71(即72分频,变成1MHz)和ARR = 999(即计数1000次),那么溢出中断周期就是1MHz的1000个周期,等于1毫秒。

“stm32定时器捕获测频率”是很多人做转速计、流量计时的需求。输入捕获的原理是:定时器在计数时,如果引脚上来了一个指定的电平边沿,硬件会把当前的计数值锁存到捕获寄存器里。连续两次捕获的计数值之差,乘以计数周期,就是信号的一个周期。比如你用PSC = 71把计数频率设为1MHz,两次上升沿捕获的差值就是信号的周期微秒数,频率自然就算出来了。

PWM是定时器的另一个核心应用。定时器的输出比较功能可以让你在某个计数值时翻转或拉低引脚,从而实现占空比可调的方波。做呼吸灯、调电机速度、控制舵机,都用这个功能。我不建议你在学习阶段只用CubeMX去勾选生成PWM,而是打开定时器的寄存器手册,找到CCR、CCMR这些寄存器,看清楚了再回去用图形化工具,效率会高很多。

4. 把数据传出去:串口、I2C、SPI与典型外设

4.1 串口打印与重定向printf的那些事

调试STM32最常用的手段就是串口打印。F103的USART1默认引脚是PA9(TX)和PA10(RX),这不是绝对的,要看你的板子是否做了重映射。很多卖家给的原理图会标注PA9_TX1、PA10_RX1,连接板载的USB转串口芯片。我一直强调先看原理图再接线,就是因为引脚复用问题很容易踩坑。

printf重定向是让串口能直接打印字符串的关键。在Keil里,你需要在工程中添加一行代码:

int fputc(int ch, FILE *f) { while ((USART1->SR & USART_SR_TXE) == 0); // 等待发送缓冲区为空 USART1->DR = (uint8_t)ch; return ch; }

然后在魔法棒里勾选Use MicroLIB,这样printf就会通过fputc往串口写数据。不用MicroLIB的话,标准库的printf为了兼容浮点,会占用非常大的Flash空间,在64KB的C8T6上很容易把Flash塞爆。

串口的波特率设置也很值得搞清楚。F103的USART波特率由USARTDIV寄存器决定,计算公式是波特率 = 时钟 / (16 * USARTDIV)。例如在72MHz PCLK下要得到115200波特率,USARTDIV约等于39.0625,所以很多教程里会看到USART_InitStructure.USART_BaudRate = 115200,配置库函数会自己处理小数部分。如果外接设备的波特率不标准,串口就会出来乱码或者收不到数据,可以先降低速率比如9600试一试。

4.2 用I2C驱动OLED屏幕,注意模拟时序

如果你想让板子显示点内容,OLED模块是经典选择。绝大多数0.96寸OLED用的是SSD1306驱动芯片,接口有I2C和SPI两种。用I2C时,它的SDA和SCL通常接到I2C1的PB7和PB6,或者任选两个GPIO用软件模拟时序。

不少人在驱动OLED时碰到过画面不显示或者显示雪花,十有八九是I2C时序和地址问题。F103的硬件I2C在历史上有过一些兼容性问题,ST官方也多次更新勘误,所以民间普遍的做法是用软件模拟I2C时序。软件模拟的核心就是手动控制SDA和SCL电平的翻转:

void I2C_Start(void) { SDA_High(); SCL_High(); DelayUs(5); SDA_Low(); DelayUs(5); SCL_Low(); }

OLED的I2C地址一般是0x78或0x7A,取决于板上的SA0引脚电平。如果画面不出来,先用最简单的测试程序发一个清屏命令,看屏幕有没有反应。发送命令和数据时的区别在于控制字的最高位,命令是0x00,数据是0x40,这个细微的地方最容易漏。

4.3 SPI驱动ILI9341时读ID读成0xA1A1的排查

“stm32使用ili9341读id是a1a1”这个坑我见的太多了。ILI9341是2.4寸/2.8寸TFT屏的常用驱动芯片,读ID的初衷是为了确认屏幕型号、自动适配初始化代码。正常情况下,有些屏读回来的ID确实不是ILI9341,比如有的是0x9341,有的是0x8989。但如果你无论如何读,都是同一个稳定的0xA1A1,这就不是IC型号的问题,而是你的读取流程根本没进入状态。

0xA1A1这个值本身就有讲究。很多从SPI读回来的垃圾数据落在0xA1A1,说明MISO线上读到的全是这个固定电平。原因常见有几种:片选信号没有拉低,屏幕根本没被选中;SPI模式不对,ILI9341要求模式0(CPOL=0,CPHA=0),如果你的板子配置成了模式3,读写就会错位;还有可能是复位序列没做对,ILI9341上电后需要先拉低RST再拉高,如果复位不彻底,芯片可能还在忙状态。

排查方法是有顺序的。先用逻辑分析仪或者示波器看CS、SCK、MISO上的波形,确认SPI通信时序有没有出来。然后确认初始化序列对了没有,ILI9341的读取ID命令不是0x04而是0xD3,发送完命令后要等待一个参数0x00,然后连续读三个字节,最后两个是ID。如果你的代码读的是0x04命令,ID当然不对。

5. 从通信协议到物联网:进阶应用路线

5.1 CAN通信初始化与“突然连不上”的真相

汽车电子、工业控制里CAN总线用得非常多,F103内置bxCAN外设,所以很多人拿它做CAN通讯学习。CAN初始化时需要注意位时间参数。标准CAN的波特率由分频、段1(BS1)、段2(BS2)和同步跳转宽度(SJW)决定。F103里你可以通过CAN_BTR寄存器配置这些参数,CubeMX里也有图形化界面。

“stm32 can通信突然连不上”这个问题在群里出现过很多次。排查顺序一般是:两个节点的波特率是否一致,这是最常见的;终端电阻是否接了,CAN总线两端各需要120欧姆匹配电阻,如果都忘接了,信号反射会导致通信极不稳定;还有一点很隐蔽——收发器芯片的STB或RS引脚如果被配置成了静音模式,CAN_H和CAN_L上不会有差分信号,总线看上去是“通”的但就是没数据。

如果程序之前跑得好好的,突然连不上,可以先看CAN的错误寄存器。F103的CAN_ESR寄存器里记录了错误状态,比如错误被动(EPVF)和总线关闭(BOFF)。进入总线关闭状态后,CAN控制器会停止收发,必须软件复位或者等待协议规定的恢复时间。这类故障往往和总线上某个节点发送错误帧有关,排查时可以先只保留两个节点,排除多节点间的容错问题。

5.2 RS485控制伺服电机,别忽略方向引脚

伺服电机驱动器支持RS485通信已经很常见了,Modbus协议一统天下。F103本身有USART,接一个MAX3485或者SP3485芯片就能变成RS485接口。RS485是半双工总线,发送和接收共用一对差分线,所以必须有一个DE/RE引脚来控制方向。这是新手最常犯的错误:只配置了串口,忘了在发送前把方向引脚拉高,发送完成后又忘了拉低。

发送一组Modbus命令的流程是这样的:先把DE拉高,然后串口发出数据帧,等发送完成标志置位后,再把DE拉低,切回接收状态。这里要注意时序,如果拉低太早,最后一个字节可能还没发完;如果拉低太晚,对方的回复帧又收不进来。实际调试时可以在方向引脚上接一盏LED,通过闪烁状态直观判断方向切换是否正常。

485总线一般也要在两端加120欧姆匹配电阻。长距离传输时,如果用普通的USB转485模块,接法错误会导致收发不到数据。还有Modbus的CRC16校验,网上有现成的查表算法,手算的很多人会算错,导致对方设备一直返回错误应答。

5.3 从串口到巴法云:STM32也能做物联网

“stm32 巴法云”这个热搜说明有不少人在把单片机往物联网方向带。巴法云是一个国内物联网云服务平台,提供了MQTT和HTTP的接入通道,对嵌入式设备很友好,不需要复杂的证书配置。基本思路是用一个ESP8266或ESP32模块通过串口跟STM32通信,STM32通过AT指令控制WiFi模块连接巴法云的服务端,然后以MQTT协议上报数据、接收控制指令。

这种方案的通信拓扑其实很简单:STM32负责采集传感器数据和执行控制逻辑,WiFi模块只负责透传数据。你在STM32的串口中断里解析来自ESP8266的报文,按约定的JSON格式取出动作字段。我这里要提醒一句,物联网方向核心不在AT指令,而在云端的Topic设计和消息格式约定。很多教程是把所有业务逻辑都塞进主循环里,一旦网络阻塞,整个设备就卡死。好的做法是把网络状态机独立出来,用超时机制去控制连接、重连、发送心跳这些动作。

如果你不想折腾WiFi模块,也可以考虑直接选带网络的MCU,但F103本身没有以太网MAC,要做网络通常还是外挂方案。从学习性价比上,ESP8266是最省钱的联网跳板,20多块钱一个模块,先用它把串口和网络打通,以后换更高级的主控思路也是一样的。

6. 新手避坑清单:从编译、烧录到调试一次说清

6.1 标准库工程编译报错高频案例

建标准库工程时,最容易报的错是L6218E之类的链接错误,比如“Undefined symbol GPIO_Init”或“Undefined symbol Delay”。这通常不是你的代码问题,而是编译链接时根本没把库文件包含进来。在Keil工程里,你需要把标准库的src文件夹里的所有.c文件都加到工程里,或者至少把用到的外设源文件加进去,然后在魔术棒里把include路径指到标准库的inc文件夹,否则编译器找不到头文件。

还有一个高频问题是报错“unknown type name 'bool'”。STM32的C编译器默认可能不包含stdbool.h,在一些新版本库里会用bool类型。解决办法是包含头文件<pstdbool.h>或者直接不要用bool,用uint8_t代替。这个错真的能卡住人一晚上。

第三类高频问题是“core_cm3.h”报错,一般是你同时装了多个版本的CMSIS包导致冲突。Keil里查看Manage Run-Time Environment,把有冲突的CMSIS版本理清楚就好。如果工程是从旧版本迁移过来的,建议直接删掉重新生成,省时省力。

我把编译阶段最常见的错误整理成了这个表,方便你快速对照:

报错特征大概率原因处理办法
Undefined symbol GPIO_Init库源文件未加入工程把stm32f10x_gpio.c等文件加进工程
Unknown type name 'bool'缺stdbool头文件加入<stdbool.h>或用uint8_t代替
core_cm3.h not foundCMSIS路径或版本冲突重装器件包、检查include路径
L6218E: Undefined symbol启动文件或宏定义缺失选对启动文件、加USE_STDPERIPH_DEVICE
Flash下载超过64KBC8T6容量不够精简代码、去掉MicroLIB里多余库文件

6.2 烧录失败和“禁用JTAG”的相爱相杀

烧录失败常见提示是“No target connected”或者“RDDI-DAP error”。第一种情况是ST-Link根本没识别到芯片;第二种情况是调试接口连接断了。连接没问题的话,先看供电,有些ST-Link的3.3V输出电流很小,不足给板子供电,这时要用独立USB供电。另外一个隐蔽问题是BOOT0跳帽状态,如果BOOT0被拉高,芯片进入ISP模式,调试接口可能无法访问Flash,这时候把跳帽恢复为0再试。

“stm32禁用jtag”是一个很有迷惑性的操作。F103的部分引脚和JTAG复用,比如PA13、PA14、PA15、PB3、PB4,默认是调试口。有人想把这些引脚拿来当普通GPIO用,于是调用复用重映射函数关闭JTAG。但问题是很多人会把SWD也一起关掉,然后烧录器立刻连不上芯片,变成一个死循环:想烧录就得开SWD,但芯片现在不开SWD。

解决办法是把BOOT0拉高,用串口ISP方式下载一个恢复程序,把那行禁用代码去掉。或者用ST-Link的“Connect under reset”模式,在芯片复位瞬间抢到调试接口,然后擦除Flash。这个坑我强烈建议新手不要主动去踩,在产品上把JTAG关掉、保留SWD就够了,学习板根本没必要关调试接口。

6.3 调试辅助:逻辑分析仪远比想象中好用

很多人在Keil里找“查看IO输出波形”的功能,实际上Keil本身并不能直接当示波器用。除非你有特定的调试器支持的ETM跟踪接口,否则别指望在调试界面里看IO波形。看时序波形最便宜也最实用的工具就是逻辑分析仪。几十块钱的8通道逻辑分析仪配Saleae Logic软件,采样率能上24MHz,调试UART、I2C、SPI、CAN这些低速协议完全够用了。

我修过很多“串口乱码”的问题,都是先用逻辑分析仪抓波形,一看起始位和停止位的长度不对,立刻就能判定波特率算错了。如果是软件问题导致SPI片选拉低时间太短,逻辑分析仪也能抓出CS宽度的异常。相比示波器,逻辑分析仪不能看模拟电压,但对于数字板卡调试,它基本是万能的。你要是踩了“为什么时序不对”这种坑,接上逻辑分析仪看一遍波形,比自己瞎猜高效十倍。

7. 学习路线别走歪,用项目把技术焊死在手上

7.1 从点灯到毕业设计的路径规划

总有人问“学会了点灯,下一步干什么”。我提供一个比较稳的路线:GPIO和时钟概念,然后串口打印,再定时器中断和PWM,接着外部中断和按键消抖,再就是I2C和SPI驱动传感器与屏幕,然后是ADC采样,再是DMA。这套走完,你其实已经具备做企业里多数嵌入式小功能的能力了。CAN和以太网这些可以放到后面按项目需求学,不用一开始就铺开。

很多人的问题不是学不会,而是学得太散。今天看一个PWM呼吸灯,明天看一个外部中断,后天看一个LCD显示,代码都是copy下来的,跑通了就跑去下一个,最后什么也没沉淀。学任何一个外设,都至少要经历三个过程:看明白别人的代码逻辑,自己照着敲一遍(不是复制粘贴),然后改写成一个不同场景的应用。比如PWM,学完以后自己做一个软件模拟的呼吸灯,再做一个调节LED亮度的小旋钮,最后把它用进一个直流电机调速的小项目,这样才能算真正掌握。

用F103能做的成品项目非常多。热搜词里“stm32鱼缸”就是一个典型的小综合项目:水温传感器加ADC、水泵PWM调速、OLED屏幕显示参数、按键设定温度区间、ESP8266接入物联网远程查看状态。这个项目把基础模块全串起来了,做完以后你对整个F103的认识和只看教程完全不一样。类似的还有“stm32报站程序完整代码”那种,核心是串口解析、语音模块控制和状态机的配合,复杂度适中,也适合练手。

7.2 保持关注的价值:我会继续写这些实战内容

写这篇东西的时候,我的出发点很简单:让那些刚拿到板子的人少走一点我走过的弯路。点灯很简单,但搞清楚为什么灯亮、时钟和外设之间的关系是什么,才是真正值钱的部分。后续我会沿着这条线,继续分享怎么把ESP8266接进自己的项目、怎么用RTOS去管理多个任务、怎么把你的板子做成一个真正能跑起来的物联网节点,以及F103上那些奇奇怪怪的硬件坑。遇到具体问题,也可以带着你的板子型号和报错信息来交流,我看到了会抽时间帮你一起排查。

现在把开发环境搭好,把第一个点灯程序烧进去,然后一步步顺着这篇的节奏往下走。STM32这条路不窄,也不陡,你只是需要有人告诉你哪里该用力,哪里该绕路。

返回列表