ARTICLE DETAIL

资讯详情

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

从零搭建Keil MDK的ARM Cortex-M工程:从芯片选型到调试排错全指南

从零搭建Keil MDK的ARM Cortex-M工程:从芯片选型到调试排错全指南 1. 开工前你必须想明白的三件事先说结论在Keil MDK里创建ARM Cortex-M工程技术动作本身不到十分钟就能完成但你踩过的那些坑百分之八十都是在真正动手之前埋下的。很多刚接触嵌入式开发的朋友拿到一块STM32或其它Cortex-M内核的开发板第一反应是打开Keil点Project——New uVision Project然后一路Next下去。这么做确实能跑出一个小灯闪烁程序但一旦换芯片、换库、加中间件、换编译器版本整个工程就散架了。原因很简单Keil的工程文件本质是一堆配置的集合你根本不理解每一项配置的含义就等于在一堆精密仪表全亮的情况下开飞机。所以在动手创建工程之前我强烈建议你先回答清楚三个选择题用哪颗芯片、用哪种固件库、用哪个编译器版本。这三个选择会直接决定后面工程目录长什么样、启动文件选哪个、下载算法配什么、编译选项怎么调。芯片型号这块Cortex-M家族包含M0、M0、M3、M4、M7、M23、M33、M55等等。不同内核的差异非常直观M0/M0主频低、功耗低、指令集是ARMv6-M适合简单控制类应用M3是ARMv7-M指令集完整性价比高M4在M3基础上加了DSP指令和单精度浮点适合信号处理M7性能强带双精度浮点但配套的硬件设计和代码优化要求也更高。如果你只是入门现阶段选一颗资料最多、生态环境最成熟的M3或M4内核芯片性价比最高。做产品选型时则要评估外设资源、功耗、价格和长期供货这些细节影响的是后面的整体方案不是Keil配置层面的问题但选型错了后边再怎么折腾Keil也补不回来。固件库的选择也很关键。现在市面上大体是两条路一条是用芯片厂商提供的HAL库或标准外设库另一条是纯寄存器操作。寄存器操作的学习价值高能帮你理解芯片底层的寄存器映射但工程效率偏低而且可移植性差。标准外设库是早期ST等厂商主推的做法代码直接操作外设寄存器API封装层很薄执行效率高但现在已经停止更新了。HAL库是当前的主流方向它把外设初始化逻辑做成了结构体和回调函数配合CubeMX之类的图形化配置工具可以极大缩短开发周期。对大多数现代项目来说选HAL库是更合理的选择因为芯片厂商把新芯片的支持重点都放在HAL上你再从标准外设库起步等于一开始就走上了一条断头路。编译器版本的问题很多新手根本没意识到。Keil MDK从5.36版本开始默认不再随安装包附带ARM Compiler 5需要你自己去官网补充下载。ARM Compiler 5也叫AC5是很多人用惯了的编译器对旧工程的兼容性好ARM Compiler 6AC6基于Clang架构编译速度快很多对C99和C11的支持也更好但在代码优化策略和语法检查的严格程度上与AC5有差异。同一个工程用AC5和AC6编译结果可能完全不同最常见的现象是AC6对类型转换告警更敏感稍不注意就把warning当成error。后面我会专门讲这两个编译器的对比和切换方法这里你只需要记住新工程建议直接用AC6老工程先保持AC5不要混着用。2. 软件安装与基础环境搭建2.1 Keil MDK的下载和安装要点Keil MDK的安装包可以从ARM官网下载不要随便找个下载站拿修改过的安装包这既关系到版权合规也关系到工具链的可靠性。安装过程本身没什么难度一路Next就行。但有三个细节值得注意。第一安装路径尽量不要带中文和空格。我见过太多人把Keil装在“D:\软件\Keil_v5”这种路径下结果ARM Compiler、CMSIS、Pack的路径全部跟着带中文后面编译的时候各种奇怪报错。第二安装完成后去“Pack Installer”里把当前芯片对应的Device Family Pack装上。比如你用STM32F103就要装Keil::STM32F1xx_DFP这个包。这个Pack里面包含芯片的头文件、启动文件、分散加载文件、Flash算法是整个工程创建的关键依赖。第三管理员的权限问题。Keil在安装驱动和更新Pack时需要管理员权限建议安装时勾选“Add to PATH”之类选项的同时直接右键“以管理员身份运行”安装包省得后边ST-Link驱动装不上。2.2 三种常见开发环境的选型参考用Keil的同时不少工程师也会搭配ST的CubeMX、NXP的MCUXpresso Config Tools等图形化配置工具。这类工具的作用是帮你生成初始化代码时钟树怎么配、引脚怎么复用、外设参数怎么填全部可视化完成最后点击生成直接得到一份结构清晰的工程。CubeMX生成工程的时候可以直接选择Keil MDK作为目标工具链生成之后用Keil打开就能编译下载两者配合是非常主流的开发模式。不过我的个人观点是初学者不要一上来就依赖CubeMX。原因很简单如果连时钟树和引脚复用的概念都没有那你生成的代码出了问题根本不知道怎么排查。建议先用寄存器或标准外设库手写一个简单工程的初始化流程理解RCC、GPIO、AFIO这些基础外设的工作原理再切换到CubeMX提速。当然如果项目周期紧、任务重直接用CubeMX也是合理的商业选择毕竟工具就是拿来提效的。另外VS Code搭配Keil工程也是一种值得了解的组合方式。VS Code负责代码编辑和Git管理Keil负责编译调试通过一些插件实现两者联动。这种模式适合那些对代码编辑体验要求比较高的工程师但配置过程相对繁琐涉及到编译命令的生成和映射建议有经验以后再尝试。3. Keil工程创建的完整流程拆解3.1 新建工程与器件选择打开Keil uVision5点击“Project”——“New uVision Project”在弹窗里输入工程名选择保存位置。这里说一个我自己的习惯工程目录不要建得乱七八糟建议按下面的结构来组织ProjectName/ ├── Core/ 存放main.c、中断处理文件 ├── Drivers/ 芯片厂商的HAL库或标准外设库 ├── Middlewares/ RTOS、FATFS、LwIP等中间件 ├── Hardware/ 自己写的板级外设驱动如LED、按键、传感器 ├── App/ 应用层逻辑代码 ├── MDK-ARM/ Keil工程文件所在目录 └── Doc/ 文档、数据手册、原理图新建工程时把Keil的工程文件直接放在MDK-ARM文件夹下。这样做的好处是源码和工程分离换电脑、换工作目录、用Git管理时非常清爽。很多初学者喜欢把工程和源码一股脑堆在一起散落得到处都是后来项目规模一大连头文件路径都理不清。新建工程后Keil会弹出“Select Device”窗口。这里可以直接搜索芯片型号也可以按厂商、内核分类去选。选完芯片后Keil会提示你是否要添加启动文件到工程。启动文件startup_xxx.s负责设置初始栈指针、初始化中断向量表、调用SystemInit和main函数是整个Cortex-M程序运行的“第一块砖”。正常情况下直接选“是”让Keil自动添加默认启动文件。不要自己去网上随便找一份启动文件替代除非你很清楚自己在做什么。3.2 配置ROM和RAM地址范围器件选型完成以后先打开“Options for Target”窗口在“Target”选项卡里检查ROM和RAM的地址范围。Keil会根据你选择的芯片自动填好默认值比如STM32F103C8T6的Flash是64KB、RAM是20KB但如果你用的是外扩存储器的型号或者有特殊的内存分区需求就需要在这里手动改。这里有个很容易踩的坑某些芯片的内部Flash有多块区域或者有独立的Boot区域默认配置不一定覆盖到你需要的存储区间。这时候先查数据手册的内存映射章节确认清楚再填不要凭印象填。一个不准确的ROM地址范围轻则烧录时Flash算法报错重则程序跑飞。在Target选项卡里还有一个“ARM Compiler”选项这里就是切换AC5和AC6的地方。如果你是新建工程建议选择“Use default compiler version 6”如果加载的是老工程要先去确认工程创建时用的哪个编译器再选用对应版本除非你愿意为升级编译器额外付出一轮测试成本。3.3 固件库的添加与头文件路径管理接下来的步骤是把固件库源码复制到工程目录里。以STM32的HAL库为例通常需要保留这几个核心文件夹CMSIS设备头文件目录、HAL驱动源码目录、以及芯片相关的系统初始化文件。在Keil工程中添加文件的方式是右键点击左侧的“Project”面板里的目标组选择“Add Existing Files to Group”。按之前的目录结构建议建立如下分组Target1 ├── Application/ main.c、app层代码 ├── Drivers/ HAL库源码、CMSIS相关文件 ├── Hardware/ 板级驱动 ├── Middlewares/ RTOS等 └── Startup/ 启动文件分组只是工程层面的逻辑归类不影响编译结果但好的分组能让团队协作时更容易找到对应模块也让编译输出的map文件更容易分析。添加完源文件后别忘了一键配置头文件路径。在“Options for Target”——“C/C”选项卡的“Include Paths”里把每个源码目录都加进去。我强烈建议这里全部使用相对路径也就是不写“E:\work\STM32\ProjectName\Core”而是写“..\Core”。写相对路径的好处是工程拷给别人、或者整体换目录时头文件路径依然有效。用绝对路径虽然当下省事但换一台电脑大概率会全部报错到时候一个一个改路径真的会怀疑人生。3.4 宏定义与编译选项的配置在“C/C”选项卡的“Define”输入框里填写全局宏定义。不同的芯片和不同库需要的宏也不一样。比如STM32F1系列HAL库需要定义STM32F103xBSTM32F4系列需要定义STM32F407xx和USE_HAL_DRIVER。这些宏通常可以在芯片厂商提供的模板工程里找到或者看库文件里的条件编译分支。C语言标准建议选择C99或C11只要编译器支持就尽量选新的。优化等级方面调试阶段建议选-O0或-Og发布版本再按需选择。这里我要强调一个细节Cortex-M4和M7内核带有硬件浮点单元如果在Target选项卡里没有正确启用FPU浮点运算会退化为软件模拟性能差距可能达到几十倍。启用方式是在Target选项卡中选择正确的“Floating Point Hardware”选项然后在C/C选项卡中加“__FPU_PRESENT1”和“__FPU_USED1”宏具体宏名以库文件要求为准。还有一个容易被忽略的选项是“MicroLIB”。精简版C运行库能显著缩小程序体积适合资源紧张的MCU。但代价是某些标准C库功能比如完整的printf浮点格式化可能会受限。如果你调试时发现printf输出16进制的整数没问题但输出浮点数就出错多半和MicroLIB的选择有关。3.5 分散加载文件与链接配置Keil工程在链接阶段需要一份分散加载文件也就是后缀为.sct的文件它告诉链接器代码放在哪个地址、数据放在哪个地址、堆栈大小是多少。大部分情况下你不需要手工编辑它Keil会在“Linker”选项卡里根据Target配置自动生成。但有一种情况你必须介入当你的系统有外部SDRAM或外部Flash并且希望将部分数据或代码放在外部存储器时就需要修改分散加载文件增加对应的执行域。比如把大数组定位到外部SDRAM可以在sct文件里增加一个区域把变量放进去。这块内容偏进阶新手先理解机制即可真正用到的项目再深入学。3.6 下载调试配置与Flash算法写好的程序要烧进芯片需要在“Debug”选项卡里选择调试器。常见的有ST-Link、J-Link、CMSIS-DAP等。选择调试器之后点击旁边的“Settings”检查调试器是否识别到芯片。在这个界面里你可以看到SW Device中是否列出了芯片的IDCODE。如果显示“No target connected”或者“Cannot find target”先检查接线和驱动不要急着找Keil的问题。接下来是“Flash Download”选项卡。这里要确保“Programming Algorithm”列表里选对了Flash算法。Keil会在Pack安装时自动匹配算法但如果你换了型号或者用了外部Flash就需要手动添加。选错算法最典型的报错是“Flash Download failed - Cortex-M4”后面我还会详细讲。下载方式上SWD只需要四根线SWDIO、SWCLK、GND、VCC比JTAG省引脚是目前主流调试方式。时钟频率的选择也有讲究一般10MHz以内比较稳定如果使用杜邦线连接频率太高可能导致通讯不稳定。我见过很多人在这个界面把频率拉到几十MHz然后下载器频繁掉线其实降回几MHz就稳了。3.7 第一个程序最小系统工程配置完成以后写一个最基础的程序验证工程是否正常。我的习惯是先不碰任何外设只配置SysTick和一个GPIO翻转或者干脆点个LED。如果连LED都无法点亮那后面所有代码都没有意义。下面是一个最精简的main.c框架以STM32的标准外设库为例使用HAL库时逻辑类似只是外设初始化方式不同#include stm32f10x.h void Delay(volatile uint32_t count) { while(count--) {} } int main(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOC, GPIO_InitStructure); while(1) { GPIO_SetBits(GPIOC, GPIO_Pin_13); Delay(1000000); GPIO_ResetBits(GPIOC, GPIO_Pin_13); Delay(1000000); } }第一次编译时如果一切配置正确Build Output窗口会显示0 Error(s), 0 Warning(s)。看到这个结果就可以接上调试器下载运行了。4. 编译器选型与兼容性排查4.1 AC5与AC6到底怎么选ARM Compiler 5和ARM Compiler 6的底层实现完全不同。AC5是传统的ARMCC编译架构它的优点是稳定、保守、和老工程的兼容性好很多历史遗留的代码都是用AC5编译的。AC6是基于LLVM/Clang的现代编译器编译速度快、支持的语言标准更新、代码体积和性能优化也更好但代价是对代码规范的要求更高。我整理了一个简单的对比表对比项ARM Compiler 5ARM Compiler 6编译速度慢明显更快C99/C11支持一般完善告警严格程度较宽松严格类型问题更容易暴露代码密度中规中矩优化后可更小老工程兼容性好可能需要改代码兼容官方支持状态已停止更新当前主流在工程里切换编译器的方法是在“Options for Target”——“Target”选项卡里修改“ARM Compiler”选项但切换后务必重新编译并测试所有功能。AC6下最常见的兼容问题是结构体对齐、位域的内存布局、以及一些隐式类型转换告警这些问题在AC5下根本不体现到AC6就原形毕露。如果遇到这类报错别慌基本都能通过显式强转类型或调整代码规范修复。4.2 老工程的迁移经验很多项目组手里有一堆陈年代码想要从AC5迁移到AC6。我的建议是分步走先保证AC5版本下的代码在主干分支保持可发布状态然后开一个分支做迁移。迁移过程中不要试图一次性消除所有告警先把Error清零然后逐个处理Warning。告警不是洪水猛兽但大量告警会让真正重要的问题淹没在信息流里所以迁移完成后把告警清零也是一个值得追求的目标。迁移过程中要特别关注以下几类代码使用编译器内建关键字的代码、依赖特定优化行为的代码、以及直接操作栈或寄存器的汇编文件。这些部分在AC6下很可能需要改造或替换。5. 工程模板固化与版本管理5.1 建立一份可复用的空白工程模板反复从零搭建工程的效率太低了。我更建议你在第一次成功创建工程后将一份“干净”的工程作为模板保存下来。所谓“干净”是指没有任何业务逻辑、只包含系统初始化和一个空的main循环、所有编译下载配置完整可用的状态。后续启动新项目时直接把模板目录复制一份改工程名、改芯片型号如果芯片不同、添加外设驱动剩下的编译选项和文件路径全是现成的。一套合理的模板可以帮你在每个新项目上省下最少半天时间。模板的目录结构和管理方式可以参考我前面提到的方案。另外建议把模板放在一个固定的模板目录里做好版本管理每次有新的工程技巧或配置优化可以同步更新模板。5.2 Git管理与Keil工程的协作实践Keil的工程文件包括.uvprojx、.uvoptx以及分散加载文件本质都是XML文本格式可以纳入Git管理。但要注意.uvoptx文件里记录了个人化的窗口布局、断点、调试视图信息不同开发者的这个文件各不相同。如果团队协作中不需要共享调试视图建议把.uvoptx加入.gitignore只提交.uvprojx。不然每次拉代码工程里到处都是别人的断点体验极差。另外建议把编译生成物目录如Listings、Objects全部忽略掉。这些文件是本地生成的不应该进入代码仓库。只跟踪源码、头文件、启动文件、链接脚本和工程配置文件这是最基本的Git管理纪律。5.3 从模板到追加RTOS与中间件很多项目最终会用到RTOS比如FreeRTOS、RT-Thread、ThreadX。创建工程时即使目前用不到RTOS也建议在模板中预留出Middlewares目录以便后续添加。FreeRTOS的移植在Keil中已经特别简单了。以ST的HAL库为例可以通过CubeMX直接勾选FreeRTOS生成也可以手动将FreeRTOS源码添加进工程。核心要改的文件是FreeRTOSConfig.h它里面定义了系统节拍频率、堆大小、任务数量上限等关键参数。有一个常见的坑是很多初学者把FreeRTOS的例程代码直接复制过来但没有修改FreeRTOSConfig.h里的中断优先级配置导致与HAL库的优先级分组冲突系统运行不稳定。这一点在移植时一定要仔细核对。6. 常见报错与调试故障排查手册6.1 “could not stop cortex-m device”深度分析只要你在Keil里调试过Cortex-M芯片大概率见过这个报错。完整提示一般是“Cannot access target. Target error: could not stop cortex-m device”。遇到这种报错很多人的第一反应是“板子坏了”但实际上绝大多数情况下是配置或环境问题板子本身好着呢。我根据经验把原因分成几类第一类是调试接口连接问题。SWDIO和SWCLK两条线接反、接触不良、杜邦线太长导致信号质量差都可能导致调试器无法稳定控制内核。这时候先检查接线把频率降低再试一次。第二类是芯片进入了低功耗模式。如果你的程序里调用了WFI或WFE之类的指令让CPU休眠而没有配置调试器在连接时唤醒内核就会出现“could not stop”的情况。解决办法是在调试设置里使能“Reset and Run”或者先按住复位键在芯片复位瞬间点击下载让调试器抓住CPU。第三类是时钟配置错误。特别是你在程序中重新配置了系统时钟比如把HSE倍频到很高的主频但外部晶振没焊或者没起振CPU可能直接在时钟切换过程中跑飞。内核无法响应调试请求自然就stop不下来。第四类是SWD引脚被复用。有些工程师在初始化代码里把SWDIO或SWCLK所对应的引脚重映射成普通GPIO了。程序一旦跑起来调试引脚功能被关闭调试器就失去了控制通道。遇到这种情况最直接的办法是按住复位键点击下载或者用Flash Loader工具先把芯片擦除再用Keil连接。排查顺序建议是先看调试器自身状态再看线序和供电然后按住复位键尝试连接最后排查程序里是否有低功耗或引脚复用问题。6.2 “No target connected”与驱动问题“No target connected”这个报错比上面那个更基础意思是调试器根本没有发现目标芯片。第一步先看Keil的“Settings”界面里有没有列出调试器设备比如ST-Link有没有被识别为“ST-LINK”。如果没有通常是驱动问题。ST-Link的驱动经常在Windows更新、或者同时装了多个STM32工具链后冲突。解决方法是卸载旧驱动重新安装最新驱动。J-Link也一样旧版本J-Link驱动对新型号芯片的支持可能不完整导致连接失败。遇到连接问题把驱动更新到官方最新版试试大概率能解决。还有一类情况是目标板供电不足。调试器本身可以给板子供电但很多调试器的供电能力有限如果板上还有其他外设模块在取电3.3V电源会被拉低芯片无法正常启动。这时候改用外部供电并让调试器和目标板共地问题往往迎刃而解。6.3 “Flash Download failed”与Flash算法不匹配下载程序时报“Flash Download failed - Cortex-Mx”绝大多数原因是Flash Programming Algorithm选错了。打开“Flash Download”选项卡查看算法列表中的芯片型号是否和你的目标芯片一致。比如你是STM32F103C8T6算法列表里却是STM32F103ZE就会因为Flash容量不匹配而失败。外部Flash或内部多块Flash区域的芯片也常遇到这个问题比如有些芯片的Bootloader占用了一段Flash而你仍然尝试从0地址写入会被芯片的读保护或写保护拒绝。这时候要么选择正确的Flash起始地址要么先解除芯片的读保护。6.4 程序跑飞后如何用调试器定位HardFaultHardFault是Cortex-M内核上最常见的异常也是让新手最头疼的问题。好在定位方法已经很成熟。先说一个很快的粗暴定位法程序进入HardFault后在Keil的调试界面里打开“Call Stack Locals”窗口查看当前的函数调用栈直接双击故障函数观察PC和LR寄存器的值一般能很快找到出错位置。更精确的方法是查看Cortex-M内核的几个关键寄存器。HardFault发生时确认下面这些值PC程序计数器表示出错的指令地址LR链接寄存器表示函数调用返回地址R0-R3函数调用参数或临时变量PSP或MSP当前使用的堆栈指针CFSR可配置故障状态寄存器进一步区分是总线错误、用法错误还是状态错误在Keil的寄存器窗口里默认可以看到R0-R15和xPSR但要看CFSR、HFSR、MMFAR、BFAR这些故障状态寄存器需要在外设窗口里添加或使用命令行调试器输入寄存器名。我的习惯是直接打开“Peripherals”——“Core Peripherals”——“Fault Reports”这里会列出详细的故障信息。定位到故障后最常见的两种原因一是访问了非法地址比如空指针、野指针或者操作了没有使能时钟的外设寄存器二是指令总线访问错误通常是程序跳转到了一个不存在的地址多半是函数指针被篡改或者栈溢出。还有一种隐蔽情况是栈溢出。Cortex-M的栈增长方向是向下的当数据量超出栈的容量时会踩到栈底之外的内存破坏相邻变量甚至代码段最终导致HardFault。排查方法是在启动文件里给栈设置一个已知填充值比如0xAAAAAAAA运行一段时间后暂停程序查看栈区域的填充值是否被破坏。如果填充值大量消失说明栈容量不够需要增加Stack_Size。6.5 调试时printf输出重定向调试嵌入式程序最常见的需求之一就是打印日志。但MCU上没有显示器printf的输出必须经过重定向才能在调试器或串口上看到。常见的做法有两种一种是重定向到串口也就是把fputc函数改为向USART发送一个字节另一种是使用调试器自带的ITM/SWO机制比如J-Link的RTT功能这种方式不占用额外的串口引脚而且输出速度非常快。我推荐在开发调试阶段同时保留两种方式串口打印作为通用方案适用于所有环境代码改动也小调试器RTT作为高性能方案适合打印大量日志、或者串口资源紧张的项目。重定向printf到串口的常见写法如下int fputc(int ch, FILE *f) { while((USART1-SR USART_FLAG_TXE) 0); USART1-DR (ch 0x1FF); return ch; }这段代码要注意关闭半主机模式不然编译链接时会报错。在Keil中要么在工程里加“use MicroLIB”选项要么在代码中实现_sys_exit等半主机接口否则链接器会找不到相关符号。7. 尽量避开的坑以及我最后想说的话创建Keil工程这件事表面上看只是点几个按钮、填几个路径但背后真正考验的是你对芯片架构、存储映射、启动流程、编译和链接这套完整工具链的理解。回顾我自己带过的项目组新人最容易踩的坑通常不是某条具体配置不会设而是“出了问题不知道往哪个方向想”。比如下载失败第一反应不是检查接线和驱动而是重装软件编译告警第一反应不是看具体代码而是搜“如何屏蔽告警”跑飞了第一反应不是定位栈和PC而是觉得“是编译器优化的问题”。所以我再给几条实在的建议建立工程模板比每次从零建工程效率高得多。把一份跑通的工程收拾干净沉淀成模板以后所有项目都从这里起步能少走很多弯路。不要同时学好几种工具链。Keil、IAR、GCC各有各的长处但初学阶段最好只沿着一条路径深挖。先能把Keil工程的所有配置选项说清楚再去接触其它工具链认知才不会混乱。不要害怕看map文件和反汇编。很多看似无解的问题比如代码体积异常增大、变量意外被优化掉、程序运行地址不对其实只要打开Build Output窗口里的.map文件瞬间就能定位。Keil的“Options for Target”——“Listing”选项卡里可以设置生成map文件的详细程度建议把“Linker Listing”里的各项全部勾上。最后再分享一个小技巧。调试时如果程序逻辑复杂建议在Keil工程里多建立几个“Target”比如Debug版、Release版、Minimal版。Debug版开全部调试信息和-O0优化Release版开-O2优化用于测试真实性能Minimal版用于排查资源冲突。三个Target共用一套源码只是编译选项不同切换起来很方便。这个习惯让我在处理一些隐蔽的时序和资源类问题时省了很多时间。说到底搭建一个高质量的嵌入式工程本质上是建立一条从源代码到芯片内部的可控流水线。理解每一个环节的含义比记住任何一个具体操作都重要。希望你读完这篇之后能扔掉“照着教程点按钮”的拐杖真正把工程创建这件事变成自己的基本功。
返回列表