ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

从零搭建STM32工程:MDK配置、GPIO点灯与调试全流程

从零搭建STM32工程:MDK配置、GPIO点灯与调试全流程 很多朋友后台问我为什么我照着视频把例程下载到开发板LED也亮了但让我自己加一个按键控制或者换个引脚就完全不知道怎么改这个问题我太熟了。我见过太多人下载了几百个例程、收藏了几十个教程却连一个真正属于自己的工程都建不起来。原因很简单你一直在抄工程从来没有亲手建过工程。尤其是现在AI编程工具越来越强很多人直接让AI生成一段STM32代码粘进例程里就跑跑通了不知道怎么回事跑不通更不知道从哪里查。这样玩下去三五年后可能还是只会复制粘贴。这篇文章属于“嵌入式软件AI编程”这个系列的第08篇目标只有一个带你把第一个STM32工程完完整整地建出来。不跳过环境搭建不跳过启动文件不跳过编译下载也不回避那些新手必踩的坑。适配零基础刚入门的同学也适合那些能把例程跑起来、但一直没搞懂工程结构的人。看完之后你至少能做到给一块全新的STM32芯片不靠现成例程自己一步步搭出一个能点灯、能下载、能调试的工程。1. 为什么例程收藏了那么多你还是不会写自己的工程1.1 抄例程和建工程本质上是两件事抄例程的典型流程是这样的打开官方或开发板厂商的例程工程找到main.c改几行代码编译下载灯亮了收工。这个过程本质上不叫“会嵌入式”叫“会操作IDE”。一旦让你换一块芯片型号或者换一个开发板所有例程全部失效你会发现自己什么也没剩下来。真正会建工程的人脑子里装的是另一套东西芯片选型决定了用什么启动文件和什么宏定义外设时钟要开哪个总线GPIO配置成什么模式头文件路径从哪里找下载器用SWD还是JTAG烧录算法选哪个。这些知识不依赖任何一个具体例程而是围绕芯片本身和工具链沉淀下来的。这篇文章的整个逻辑就是帮你把这套“不依赖例程的知识”建立起来。AI编程工具可以帮你生成代码片段但工程骨架、芯片启动流程、外设配置逻辑这些是一个嵌入式软件工程师的基本功AI替代不了。如果你能独立建出第一个工程后面用AI辅助开发时你才看得出AI生成的东西对不对。1.2 第一个工程要解决的三件事为了不让第一篇文章变得又臭又长我把它拆成三条主线。第一件事是环境。工欲善其事必先利其器。开发IDE选什么、芯片支持包怎么装、ST-Link驱动为什么老是掉这些问题不解决后面一切免谈。第二件事是工程骨架。一个STM32工程不是把main.c扔进去就能编译的。你需要至少一个启动文件、一个内核支持文件、一个系统初始化文件以及外设驱动库。每个文件的职责是什么它放在哪个分组里头文件路径怎么填这些细节直接决定你换个芯片型号后还能不能独立干活。第三件事是硬件外设。点灯看着简单但背后涉及GPIO的推挽输出、限流电阻计算、时钟树的理解甚至寄存器层面的位操作。这些原理看起来是“了解即可”但实际调试时你会发现不搞清楚它们遇到问题就只能靠猜。这三件事拆开了讲每一件都不难。你只需要跟着步骤一步步走走完了你的第一个工程就真正属于你了。2. 开工之前先把环境收拾利索MDK、芯片包与下载器2.1 IDE选型为什么我坚持用Keil MDK讲入门STM32的开发环境现在主流的无非三个Keil MDK、STM32CubeIDE、IAR Embedded Workbench。IAR的代码优化确实强很多车规和工控项目在用但它的授权方式、编辑体验对新手不太友好STM32CubeIDE是ST官方近年主推的免费、内置CubeMX图形化配置确实很好用但回到F1系芯片的学习资源Keil MDK依然占据了绝对多数。这么说吧你随手搜一个STM32教程十有八九是基于Keil MDK和标准外设库的。尤其是STM32F103这种经典芯片从大学实验室到小公司产品大量代码都是Keil工程。所以入门阶段我建议直接选Keil MDK先把环境和工程结构吃透后面想转STMCubeIDE是非常容易的事。安装时有两个常见坑必须说。第一个是安装路径。MDK默认装在C:\Keil_v5这个路径本身没问题但一定不要改成带中文或者带空格的目录。我之前见过有人装在“D:\软件\Keil5”下面结果编译时ARM编译器各种找不到工具链折腾了一晚上最后把软件卸了重装到纯英文路径才好。第二个是版本选择。Keil MDK的板子是5.30到5.36之间比较稳新版本对老的标准库例程偶尔有兼容性问题遇到“奇奇怪怪的语法报错”时先去看魔术棒Target选项卡里编译器是不是AC6入门阶段用AC5会省心很多。还有个小细节如果你以前装过Keil C518051单片机用的再装MDK时要注意两个版本可以共存但License是不同的。打开License Management能看到“License for C51”和“License for MDK”两条C51的许可证对MDK不生效需要单独激活MDK。很多用STC单片机的同学先装了C51版后来搞STM32又说装不上多半是卡在这里。2.2 芯片支持包DFP不装选型列表里根本找不到芯片Keil MDK 5之后软件本体和芯片支持包是分离的。什么意思就是你只装MDK主程序打开新建工程时下拉列表里可能一个STM32芯片都看不到必须额外安装对应厂商的Device Family Pack也就是芯片支持包才能选择具体型号。打开Pack Installer在“Device”页签下找到STMicroelectronics → STM32F1 Series点击Install安装对应版本的DFP。如果你网络不太好也可以去官网手动下载.pack文件然后双击安装或者在Pack Installer里用File → Import菜单导入。装完之后确认一下C:\Keil_v5\ARM\PACK\Keil\STM32F1xx_DFP这个目录存在并且里面有对应版本文件夹就说明芯片支持包落位了。建议装完立刻重启一次Keil否则偶尔会出现“选择芯片时还是找不到型号”的假故障。顺便提一句网上经常有人问“keil5怎么兼容C51和STM32”其实就是MDK装一个C51装一个芯片支持包按需添加。两者共用同一个uVision外壳打开工程时Keil会自动根据工程里的设备类型选择对应的编译器工具链并不需要来回切换软件。2.3 ST-Link驱动USB设备识别不了的排查顺序下载调试器我默认用ST-Link V2因为它便宜、常见、跟着开发板就送。第一次插上ST-Link时系统通常会自动安装驱动但偶尔会在设备管理器里看到一个黄色感叹号的未知设备或者完全没有反应。遇到这种情况我的排查顺序是这样先换一根USB线。很多USB线只支持充电不支持数据这是最容易翻车的地方没有之一。然后去ST官网下载最新的ST-Link驱动安装包手动安装一遍。装完后设备管理器里应该能看到“STMicroelectronics STLink dongle”或者“STM32 ST-LINK”相关的设备有时还会枚举出一个虚拟串口和一块U盘。如果你用的是山寨版ST-Link V2驱动装好但仍识别不了时还可以试试按住板子上的复位键不放然后插入USB看能不能被识别。这个方法解决了我见过的不少山寨ST-Link“掉固件”的情况。实在不行再考虑用STM32 ST-Link Utility或STM32CubeProgrammer去升级一下固件。但这一步频率不高一般装上正规驱动就能跑。3. 新建工程的完整过程从空文件夹到可编译的点灯程序3.1 选芯片之前先回答一个问题为什么是STM32F103C8T6这篇文章里我以STM32F103C8T6为例。它是Cortex-M3内核主频72MHz64KB Flash20KB SRAM不到十块钱一片几乎是所有新手入门STM32的“默认选项”。更重要的是C8T6的开发资料极多从寄存器到标准库、HAL库随便搜都是教程。但这里先给大家埋一个知识点C8T6虽然不贵但它在ST官方的标准库分类里属于“中密度Medium-density”产品。这个“密度”指的是Flash和SRAM容量范围它决定了你新建工程时到底该选哪个启动文件。翻译成大白话STM32F103系列有多个Flash容量等级不同容量的芯片内部外设和中断向量表不完全一样所以启动文件也不一样。C8T6对应的是startup_stm32f10x_md.s而不是startup_stm32f10x_hd.s。很多网上例程是拿高端型号比如ZET6512KB Flash写的直接抄到C8T6上编译看着也能过但工程本质上就是不规范的。理解了这一点你新建工程时就不会盲目“照抄”别人的文件了。顺带说下目录规划。我会建议你在项目根目录下面建这么几层Project专门放Keil工程文件User放main.c和中断相关文件Core放启动文件、内核文件、系统初始化文件Hardware放LED、按键这些板级驱动代码Libraries放ST标准外设库源码和头文件这套结构不是ST官方强制的但它的好处是用户代码、底层库、工具链文件三者隔离后期换芯片、换库版本时你不会在几十个文件堆成一团的目录里崩溃。3.2 新建Keil工程不勾选自动复制一切手动添加打开Keil MDK选择Project → New uVision Project把工程文件放到Project目录下取名随意。然后会弹出芯片选择窗口在搜索框输入“STM32F103C8”选择列表里的“STM32F103C8”之后点OK。接下来会弹一个提示问你要不要复制STM32的启动文件到工程文件夹里。这里我建议先不要急着点“是”。习惯上我更喜欢把启动文件、系统文件全部手动添加进工程。这样做虽然多几步操作但每个文件是什么、从哪里来你心里一清二楚后面排查问题会非常有底气。现在在Keil左侧的Project窗口里需要先创建几个Group逻辑分组。右键Target选择Add Group依次创建User、Startup、CMSIS、Hardware、StdPeriph_Driver这几个分组。请注意分组只是工程里的“虚拟文件夹”就算磁盘上文件已经存在也要手动把文件添加进对应分组。下面是要添加的最小文件清单分组添加的文件来源说明Startupstartup_stm32f10x_md.s标准库的启动文件汇编文件CMSIScore_cm3.cCortex-M3内核支持来自CMSISCMSISsystem_stm32f10x.c系统时钟初始化文件Usermain.c自己写的用户主程序StdPeriph_Driverstm32f10x_gpio.c、stm32f10x_rcc.c等用到的外设驱动源文件按需添加这几个文件具体去哪里找如果你安装了标准外设库V3.5在Libraries目录下能找到CM3、DeviceSupport这些子目录。启动文件在Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm里里面有一堆不同密度的启动文件选md那个system_stm32f10x.c在同一个DeviceSupport目录下core_cm3.c和core_cm3.h在Libraries\CMSIS\CM3\CoreSupport里。外设驱动库则在Libraries\STM32F10x_StdPeriph_Driver\src和inc目录下点灯至少需要stm32f10x_gpio.c和stm32f10x_rcc.c。还有一个所有人都可能漏掉的关键文件stm32f10x.h。它不在分组里添加而是通过头文件包含路径来引用的。同时标准库为了管理大量外设驱动头文件还搞了一个stm32f10x_conf.h配置文件这个文件一定要在工程目录里能被编译器找到否则外设库里的源文件会因为无法包含对应头文件而报错。3.3 魔术棒配置编译、下载能否成功全看这里文件添加完之后先别急着写代码打开“魔术棒”对话框也就是Options for Target几项关键配置逐个过一遍。第一项是Target选项卡里的外部晶振频率。开发板上的外部晶振一般是8MHz在Xtal(MHz)这一栏填8。系统时钟初始化函数会根据这个值配合PLL倍频系数把主频锁定到72MHz。第二项是Output选项卡勾选Create HEX File。这个选项的意义在于编译后除了生成axf调试文件还会生成hex文件方便你用串口或者其他工具烧录。不勾选的话你用串口下载时可能会找不到烧录文件这个问题在群里出现过无数次。第三项是C/C选项卡这可能是劝退很多新手的地方。首先在Define框里填两个宏USE_STDPERIPH_DRIVER和STM32F10X_MD。第一个宏的意思是“启用标准外设驱动库”第二个宏表明当前芯片是STM32F10X_MD也就是中密度产品。然后点开Include Paths把下面这些头文件路径加进去标准库内核路径CoreSupportDeviceSupport的具体路径外设驱动库的inc目录User目录 把路径填准确少加一个就会出现编译器找不到stm32f10x.h的情况。第四项是Debug选项卡左侧选中ST-Link Debugger点旁边的Settings在Flash Download选项卡里确认勾选了Reset and Run。这个选项的作用是程序下载完成后自动复位运行否则你会看到下载成功但板子没反应需要手动按一下复位键。以上这些配置如果漏掉任何一个编译下载的过程都可能卡住。好消息是这套配置是“一次配置长期受益”的等你熟练了五分钟就能从头建好一个工程。4. 点灯代码背后GPIO、时钟和寄存器到底发生了什么4.1 先看硬件LED为什么能亮引脚为什么这样接代码之前先站在硬件角度看问题。开发板上的LED电路一般有两种接法一种是LED阳极接GPIO引脚输出高电平点亮另一种是LED阳极接3.3VGPIO输出低电平点亮。无论哪种板子上通常都串了一个限流电阻。你可以根据电阻值反推一下电流。比如LED工作电压大约2V限流电阻330Ω电源3.3V那流过LED的电流大约是(3.3 - 2) / 330约等于4mA。对于普通贴片LED来说这个电流足够让灯有比较明显的亮度又不会把引脚输出电流拉爆。如果换成1kΩ电阻亮度会明显暗一些但功耗更低。这就是为什么常说“点灯不是把线接上就完事”背后是欧姆定律在起作用。GPIO的模式里点灯用的是推挽输出GPIO_Mode_Out_PP。推挽的意思是内部有两个MOS管一个负责输出高电平一个负责输出低电平无论输出高还是低驱动能力都比较强。另外还有一种开漏模式GPIO_Mode_Out_OD它只能主动拉低输出高电平时引脚呈高阻态必须靠外部上拉电阻才有高电平。为什么点灯不用开漏因为推挽接法最简单不需要外部上拉单片机自己就能把引脚拉到高或低。4.2 代码逐行拆解为什么先开时钟再配GPIO来看点灯的核心代码。以PA1引脚输出高电平点亮LED为例#include stm32f10x.h void delay_ms(unsigned int ms) { unsigned int i, j; for (i 0; i ms; i) for (j 0; j 7200; j) ; } int main(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); while (1) { GPIO_SetBits(GPIOA, GPIO_Pin_1); delay_ms(500); GPIO_ResetBits(GPIOA, GPIO_Pin_1); delay_ms(500); } }第一行#include stm32f10x.h是总头文件它把寄存器定义、外设结构体声明、库函数的开关都串起来了。如果没有它GPIO_InitTypeDef、GPIO_Init这些符号完全不存在。然后是RCC_APB2PeriphClockCmd这一行。很多人第一次看到这里都会问为什么用GPIO之前要先“开时钟”你可以把GPIO外设想象成一个房间里的大功率设备时钟信号就是它的供电开关。STM32为了控制功耗默认情况下几乎所有外设的时钟都是关闭的你直接操作GPIO寄存器不会生效必须先向RCC模块申请打开对应外设的时钟。GPIOA挂在APB2总线上所以调用的是RCC_APB2PeriphClockCmd参数是RCC_APB2Periph_GPIOA。如果这一步漏了或者写成了GPIOB那你后面配置PA1引脚就完全无效LED死活没反应。接下来是GPIO_InitStructure这个结构体。GPIO_Pin指定引脚编号GPIO_Mode指定工作方式GPIO_Speed指定输出速率。GPIO_Speed在输出模式下有意义它决定引脚的翻转速度上限。点个灯2MHz都够用写50MHz完全是为了“以后复用串口、定时器时不用改配置”。这个结构体填好后调用GPIO_Init(GPIOA, GPIO_InitStructure)库函数会按照你的配置去设置GPIOA的寄存器和CRL、CRH端口配置寄存器。主循环里GPIO_SetBits和GPIO_ResetBits分别负责把引脚置高和置低。后面跟的delay_ms延时函数就是一个简单的软件延时。这个函数本身不精确因为它的延时时间取决于主频和编译器优化等级但作为第一次观察GPIO输出变化的实验已经足够了。4.3 看一眼寄存器库函数封装了什么避免“黑盒焦虑”很多同学用库函数点灯心里一直犯嘀咕我明明没有操作寄存器GPIO怎么就能输出了我们拿GPIO_SetBits来看它的背后其实是对BSRR寄存器的写操作。BSRR叫端口位设置/清除寄存器它是一个32位寄存器低16位写1对应把对应引脚置高高16位写1对应把对应引脚置低。比如GPIOA-BSRR (1 1)的意思是把PA1这位置1引脚输出高电平。同理GPIO_ResetBits其实也可以直接用GPIOA-BSRR (1 (1 16))来实现。库函数干的事就是帮你把这些位操作封装成了看得懂的函数调用。还有一个ODR寄存器它是输出数据寄存器直接读写引脚输出电平。用ODR操作的问题是如果我想只改某一个引脚而不影响其他引脚就得先读回整个ODR的值再改某一个位再写回去。这个过程叫“读-改-写”在中断里如果被打断可能出现别的引脚被误改的并发问题。而BSRR寄存器是“写1生效、写0忽略”可以直接只对目标位操作天然避免了一部分并发问题。理解了这一层你之后看到任何“基于寄存器的点灯代码”和“基于库函数的点灯代码”都不会再觉得它们是两套互不相干的东西就只是同一件事的不同封装粒度而已。5. 编译、下载、在线调试以及新手必踩的坑5.1 编译的产物是什么出错了怎么读配置做完代码写完按F7或点Build按钮工程开始编译。编译过程会依次处理启动文件、系统文件、外设驱动和用户代码最后链接生成两个关键文件一个是.axf这是调试用的完整镜像一个是.hex这是烧录用的十六进制文件。看到“0 Error(s), 0 Warning(s)”就算编译通过了如果有Warning可以暂时不管但“Error”必须处理。下面是我总结的一张新手最常见的编译报错排查表每一个都是新手群里反复出现的问题报错信息真正的原因解决办法error: #5: cannot open source input file stm32f10x.h头文件路径没配全在C/C的Include Paths里添加标准库头文件路径Error: L6218E: Undefined symbol SystemInit少了system_stm32f10x.c把该文件加入CMSIS分组Error: L6218E: Undefined symbol GPIO_Init没添加GPIO外设驱动源文件把stm32f10x_gpio.c加入工程Target not created工程里可能一个源文件都没有或芯片包配置异常检查是否有.c文件被添加进分组missing ARM compiler编译器路径被改动重新安装MDK或修复工具链看到没有绝大多数编译错误不是说你的代码写得不对而是“工程缺文件”或“路径不对”。这也是我一直在强调工程结构的原因。用AI生成了正确代码但工程缺失导致编译不过AI是帮不了你的只有你自己理解了文件依赖关系才知道怎么补。5.2 ST-Link接线与下载配置连线、驱动、烧录算法三件套下载之前先把ST-Link和板子接好。如果是SWD方式只需要四根线SWDIO、SWCLK、GND、3.3V。接线顺序没有强制要求但最好先接GND再接信号线避免地电位不一致导致调试器识别不稳。接好后设备管理器里能看到ST-Link设备并能在Keil的Debug设置里检测到。如果Keil提示“No ST-LINK detected”通常不是ST-Link坏了而是驱动或接线问题按第2.3节的顺序排查即可。在Debug → Settings → Flash Download里确认编程算法Programming Algorithm里添加了STM32F10x Med-density Flash大小64KB。这个算法是告诉调试器用什么样的方式擦写Flash。如果算法缺失或选错比如选成了High-density会导致Flash擦写地址范围不对下载就会报错。和启动文件一样这里也要响应当前芯片的密度等级。一切就绪后点Load按钮程序就会烧录到板子里LED开始闪烁。如果下载时弹出“Flash Download failed - Cortex-M3”通常是三种情况芯片被读保护锁住了SWD速度太快导致通信失败板子处于复位状态导致调试器无法连接。解决办法是先断开连接按住复位键再点下载或者在调试设置里把SWD速度从1.8MHz降到400kHz。如果芯片被锁可以用STM32 ST-Link Utility连接后做整片擦除把读保护去掉。5.3 在线调试不是摆设这样看GPIO寄存器最直观程序能跑起来之后强烈建议你用一下调试功能。点Debug按钮进入调试模式后打开Peripherals → GPIO → GPIOA这个窗口你会看到GPIOA的每个引脚状态实时显示在界面上。全速运行LED点亮时GPIOA的ODR寄存器对应位是1LED熄灭时是0这个过程一目了然。你还可以在delay_ms函数里打一个断点让程序每次循环到这就停下来结合Call Stack窗口和Watch窗口观察i和j的值是怎么循环递增的。这种“静态代码”和“运行状态”的对照是最能建立嵌入式软件调试感觉的方式。调试中如果发现全速运行没问题但打断点特别卡或者单步执行时跑飞先别怀疑是代码问题很有可能是调试器速度太低把SWD频率调高一点通常能改善。另外如果代码开了编译器高等级优化调试时你可能会看到源代码和实际执行顺序对不上这是正常的调试阶段建议把优化等级调到-O0。6. 工程跑通之后下一步从这三个方向走6.1 方向一加串口让单片机“开口说话”点灯是输出数字量串口则是输出一段数据。有了第一个工程的底子下一步几乎必然是加上USART让单片机通过串口把调试信息发到电脑上。这一步你需要做的核心工作有两件一是配置USART的工作参数包括波特率、数据位、停止位、校验位二是实现printf重定向让C语言标准库的printf函数往串口输出。很多新手在这里会卡在一个很烦人的问题上加了printf之后函数里的浮点数格式化会占用大量Flash空间导致链接失败。解决办法是在魔术棒里勾选MicroLIB这是一个精简版的C运行库专门为了嵌入式环境裁剪过能大幅降低printf的资源开销。串口调通之后你就有了一个“调试输出通道”。以后程序跑到哪个分支、外设返回了什么值都可以直接打印出来。我之前遇到过delay_ms函数“卡死”的现象其实不是函数本身卡死而是某个驱动库文件没有添加完整程序在启动阶段就进入了硬件错误中断看起来就像主循环根本没跑起来。这种情况用串口打印就非常好排查程序跑到哪一步就打印哪一行定位立刻清晰。6.2 方向二用定时器替代delay再延伸出测频率软件延时最大的问题是它会让CPU傻等期间什么都干不了。真实项目中更可靠的做法是使用硬件定时器让定时器到了设定时间产生中断主循环该干嘛干嘛中断里做周期性的事情。你可以把delay_ms改成基于TIM定时器的版本设置一个1ms的定时周期在中断里递增一个全局变量主循环读这个变量来实现非阻塞延时。理解了定时器中断之后后续再学定时器输入捕获就会顺理成章——输入捕获可以测量外部方波信号的频率。比如把信号接到定时器的捕获通道记录两个上升沿之间的计数值就能算出频率这就是STM32面试题里常出现的“测频法”。到这一步你已经从GPIO输出走向了MCU内部更复杂的硬件资源。6.3 方向三把AI编程工具当成“外挂”但前提是你看得懂这个系列既然叫“嵌入式软件AI编程”就绕不开AI这个话题。我自己的体会是AI在嵌入式开发里是一个非常好的外部工具它能快速生成初始化代码、写外设驱动框架、解释报错信息甚至可以帮你照着数据手册写寄存器操作函数这大大节省了翻手册的时间。但AI不是万能的。它的训练数据非常杂有时会把F1标准库的函数和F4 HAL库的API混着提有时会把中断向量表搞错有时生成的代码里引用了根本不存在的外设库函数。如果你不懂工程骨架不懂GPIO配置的时钟使能顺序遇到AI生成的代码编译不过时你就只能反复重新提问碰运气。相反如果你已经亲手建过工程能看懂启动流程那么AI生成的东西拿到手里扫一眼就知道哪里有问题需要改什么。我的建议是让AI生成代码前在提示词里把芯片型号、标准库版本、工程模式、时钟频率这些关键信息写清楚比如“使用STM32F103C8T6、标准外设库V3.5、外部晶振8MHz”。这样AI输出的代码离谱程度会低很多。同时AI给出的代码务必逐行检查涉及时钟树、引脚复用和中断配置的部分更要和自己正在使用的库版本核对。个人经验是第一个工程就像学骑自行车时“第一次成功保持平衡”它的价值不在于代码本身有多复杂而在于你把整条链路完整走通了。这篇文章发出之前我又特意拿一块F103C8T6最小系统板从新建文件夹开始一口气把工程搭完、点灯、调试整个过程大概二十分钟。如果你能扔掉网上的现成例程把这个过程独立走一遍你会明显感觉到从“跟着教程敲代码”到“自己掌控整个工程”中间的那道坎已经迈过去了。
返回列表