ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

打开别人的Keil工程满屏报错?芯片型号、编译器、头文件路径排查指南

打开别人的Keil工程满屏报错?芯片型号、编译器、头文件路径排查指南 把别人发过来的 Keil 工程文件打开扑面而来的先是各种 Error 和 Warning再一看芯片型号不对、头文件找不到、编译器版本压根不兼容。这种情况在嵌入式开发里太常见了尤其是刚从网盘、GitHub、同事 U 盘里拿到工程的时候。我前阵子帮人调试一个 STM32F103C8T6 的工程双击打开后满屏报错折腾了差不多一个下午才理清头绪最后把问题全部解决掉。这篇就专门说清楚打开别人的 Keil 工程文件到底会遇到哪些坑以及每一个坑的排查思路和解决办法都是亲测有效的东西。我自己平时用的是 Keil MDK 5.x也就是 uVision5主流的 MCU 开发基本都是这套环境。你拿到手的工程文件一般就几个东西.uvprojx工程文件、.uvoptx界面配置文件、User或App之类的源码目录、可能还有一堆.s启动文件、.ld链接脚本。真正让你打不开、编译不过的往往不是代码本身而是工程配置和你本地环境不匹配。下面我就从拿到工程文件的第一步开始把整个流程过一遍。1. 拿到别人的工程先别急着双击打开1.1 为什么换台电脑打开工程就各种报错很多人都有一个习惯文件发过来双击.uvprojx就直接点编译。这套流程放在自己电脑上没问题但换成别人的工程十有八九会出幺蛾子。原因其实很简单——Keil 工程里存的不只是源码还有一堆和本机环境绑定的绝对路径、编译工具链配置、芯片支持包信息和调试器设置。这些信息并不是说你打开工程就能自动适配的。举例来说别人用的是C:\Users\zhangsan\Desktop\stm32_project你这边是D:\MCU_Projects\demo那工程文件里记录的头文件路径、输出目录就全都不对路。再比如别人的电脑装了 MDK 5.37 且勾选了 ARM Compiler 5你这边只装了默认的 Compiler 6那编译时的语法检查方式就完全不同报错也是理所当然的。所以拿到工程后的第一心态应当是不要指望双击后能直接编译通过而是要先做环境对齐。你把这个工程当作“需要适配到自己环境里的半成品”来看待就不会因为一堆报错而慌了神。1.2 先看工程文件的“三件套”判断工程是用什么版本建的打开工程之前先看一眼文件结构通常一个完整的 Keil 工程有这几样东西.uvprojx工程主文件XML 格式记录了所有源文件、头文件路径、编译选项、芯片型号。.uvoptx窗口布局、断点、调试配置等个人化设置不影响编译但也会和本机环境绑定。.uvguix.*GUI 状态文件这个最容易在换电脑后出问题其实删掉也无所谓。*.uvmpw多工程工作区文件如果对方发的是解决方案级别的工程你有这个文件才能一次性打开多个工程。如果对方还发了RTE目录、.pack文件、JLinkSettings.ini、DebugConfig之类的说明他用了运行时环境和调试配置这种工程在打开时更容易因为缺少组件而报错。拿到手之后我建议先右键.uvprojx用记事本或 VS Code 打开搜索一下Device、Cpu、TargetName这几个标签就能在打开之前就判断出对方的芯片型号和内核类型。然后再看一下C:\Keil_v5\ARM\PACK\ARM\CMSIS这种本地路径是否存在对应版本以此判断你缺不缺芯片包。这一步虽然是土办法但能帮你省掉很多打开工程之后瞎猜的时间。1.3 一个必须建立的意识工程文件路径的“锚点”Keil 在编译时会以.uvprojx所在目录为基准去解析相对路径。很多工程模板里用的是..\..\Libraries\CMSIS这种相对路径那只要整个工程目录文件夹是完整拷贝过来的不管放在哪里都不会丢文件。但如果对方用的是C:\Users\...这种绝对路径那就必须你手动改了。我这里有一个习惯拿到任何人的工程都会先把整个文件夹完整复制到自己本地然后检查有没有中文路径或空格。Keil 对路径里的空格支持很差像D:\My Projects\Demo有时候就会在链接阶段冒出莫名其妙的错误。最好的办法是统一放到纯英文无空格的路径下比如D:\Keil_Projects\stm32_demo。不要嫌麻烦这一条能帮你避免后面无数个坑。2. 打开后第一步配置芯片型号和 Device Pack2.1 芯片型号对不上整个工程就是废的双击打开工程之后你首先要看的是Options for Target快捷键是 AltF7。打开之后看Device选项卡这里显示的是目标芯片的具体型号。如果你发现这里显示的芯片型号你本地根本没有安装对应的 Pack界面会直接变红或者给出警告编译时也会报Device not found或者missing device pack。遇到这种情况处理方式不是随便选一个相近型号凑合而是要到 Pack Installer 里安装对应的芯片支持包。具体操作是点工具栏上的Pack Installer图标然后在搜索框里输入芯片型号比如STM32F103C8右侧就会列出可安装的 Pack 版本。安装的时候要注意Keil 的 Pack 是分大类和版本的比如 STM32F1 系列的 Pack 是Keil::STM32F1xx_DFP你装错了 F4 系列那是解决不了 F1 的问题的。另外还有一个容易忽略的点Pack 版本不是越新越好。有时候你装的是最新版 Pack但对方工程是在旧版 Pack 下创建的打开后虽然能编译但某些外设库的头文件版本变了可能导致 API 对不上。如果你遇到的是“函数未声明”这种报错而代码本身看着又没错那大概率就是 Pack 版本差异引起的。这种时候可以到Project-Manage-Packages里勾选旧版本 Pack 试试。2.2 没有 Pack 的情况下如何快速判断缺的是哪个很多新手不知道怎么看自己缺了什么。这里教大家一个快速办法用记事本打开.uvprojx搜索Device把里面的内容复制下来去搜索引擎搜一下就知道对应的 Pack 是什么。比如DeviceSTM32F103C8/Device那你要装的就是Keil::STM32F1xx_DFP这个 Pack。还有另一种情况你打开工程后Build 输出的第一个报错就是core_cm3.h: No such file or directory。这个报错基本上就是 CMSIS 核心文件找不到原因大概率是 Pack 没装。因为core_cm3.h一般不在你的工程目录里而是由 Keil 从 Pack 目录的ARM\CMSIS\Include下自动引用的。如果你本地没有这个 Pack哪怕代码放在那里也编译不过去。2.3 装完 Pack 后还是要检查 Target 标签页芯片型号选对了、Pack 也装了不代表配置就完事了。接着要检查Target标签页里的几个关键参数ARM Compiler版本、Use Cross-Module Optimization、Optimization等级以及右下角的Read/Write Memory Areas。这里特别说一下ARM Compiler。Keil MDK 5.36 及以后版本默认不再预装 ARM Compiler 5也就是 AC5只带 AC6。而很多老工程是用 AC5 建的打开后在 Target 里显示的编译器可能是V5.06 update 6或者Use default compiler version 5。如果你没装 AC5编译就会直接报错最常见的报错是#error Please select first the target STM32F103... device in the Device tab或者说编译器路径找不到。解决办法有两个一是回到 Keil 官网下载 ARM Compiler 5 的安装包装上之后在 Target 里重新选择二是把工程迁移到 AC6。我建议如果是小工程直接迁到 AC6 就行但要注意 AC6 对代码的规范性要求更高很多 AC5 能容忍的写法在 AC6 下会变成 Error 或者 Warning比如#pragma的写法、变量声明的的位置等。这属于另外的话题了这里不展开。2.4 一个容易忽略的细节编译器版本和 C99/C11 标准编译器版本确定之后还要看 C/C 选项卡里的语言标准设置。Keil 默认情况下AC5 用的是 C90AC6 用的则是 C11。如果对方的代码里用了//注释、for(int i 0; ...)这种 C99 风格而你的编译器是 AC5 且没勾选 C99 Mode就会报一堆语法错误。遇到这种情况你需要到C/C选项卡里找到Language C这个下拉框把它改成C99或gnu99然后再重新编译。如果是 AC6则一般用默认的c99或c11就能过但要注意 AC6 对 GNU 扩展的支持要靠--gnu选项有时候还需要在 Misc Controls 里手动加参数。3. 编译报错的重灾区头文件路径、宏定义和启动文件3.1 Fatal error: file not found90% 是 Include Paths 的问题编译时最常见的报错就是各类头文件找不到比如stm32f10x.h: No such file or directory。这种问题几乎都出在C/C选项卡的Include Paths配置上。对方工程里的头文件路径是相对路径但层次结构和你的不一样或者对方直接把头文件路径写成了绝对路径那你在自己电脑上编译必然找不到。修改方法是在C/C选项卡里找到Include Paths右边的三个点按钮打开后把缺失的路径重新指定。你不需要管对方原来写的路径是什么只需要按照自己工程目录的实际路径把含有.h文件的目录添加进去就行。比如你的工程里有Libraries\CMSIS\Include目录那就在这里添加Libraries\CMSIS\Include。这里有个小技巧Keil 的 Include Paths 里每行一个路径既支持相对路径也支持绝对路径。为了方便工程移植我一般都改成相对路径也就是相对于.uvprojx所在目录的路径。你可以用.\表示当前目录用..\表示上一级目录例如..\..\Driver\Inc。3.2 宏定义缺失会导致整个芯片寄存器表都是错的有时候头文件路径没错但编译时仍然报一些很诡异的错误比如某些宏没定义、寄存器名冲突、#ifndef分支不对。这往往是因为C/C选项卡里的Define配置不对。以 STM32 标准外设库为例工程里通常需要定义两个宏一个是芯片型号宏如STM32F10X_MD一个是时钟源宏如SYSCLK_FREQ_72MHz。如果对方在工程里定义的是STM32F103C8T6这种完整型号而你的库函数版本用的是STM32F10X_MD这个分类宏那编译时就会进入错误的芯片分支导致寄存器地址配置错误。通过记事本查看.uvprojx里的Define字段可以快速知道对方原本的宏定义。如果打开后Define框是空的那就要回到源码里去搜一下看看有没有用#ifdef的地方反推出需要定义的宏。这个问题排查起来比较费时但只要你把宏定义对齐了很多“莫名其妙”的报错就没了。3.3 启动文件不匹配链接阶段必炸还有一个非常隐蔽的问题就是启动文件不匹配。Keil 工程里一般会有一个startup_stm32f10x_hd.s或者startup_stm32f103xe.s这样的汇编文件。这个文件的作用是初始化堆栈、中断向量表。如果对方的芯片是STM32F103C8T6中等容量64KB Flash而工程里误加了startup_stm32f10x_cl.s大容量互联型编译也能通过但在链接阶段可能会报错或者运行后程序直接跑飞。检查方法很简单在 Project 窗口里找到启动文件右键看它的属性或者直接看文件名的后缀。ld、md、hd、cl分别对应低容量、中等容量、高容量和互联型。你用的什么芯片就必须配对应的启动文件。如果你从别人的工程里复制启动文件一定要看清楚这个后缀。3.4 拷贝丢失的“隐形依赖”RTE 目录和软件组件对于使用了RTERun-Time Environment的工程比如用了 RTX5、CMSIS-Driver、FreeRTOS 官方组件包的工程光有源码是不够的还需要有.RTE目录以及正确的 Pack 组件配置。如果你打开工程后发现 Project 窗口的RTE文件夹下是空的或者报红说明 Keil 没有识别到软件组件。这种情况下你需要在Project-Manage-Run-Time Environment里重新勾选需要的组件。以 FreeRTOS 为例你需要在 CMSIS 分类下勾选CMSIS-RTOS2在 Real-Time OS 分类下勾选FreeRTOS的相应版本。勾选后 Keil 会自动把相关的源文件添加到工程里并配置好 include path。如果不勾选编译就会报找不到cmsis_os2.h之类的错误。4. 调试和烧录环节的问题也是“打不开”的一部分4.1 Debug 配置里的调试器和你手头的不一样有些人所谓的“工程打不开”其实是烧录和调试不了。工程能编译通过但是一点 Download 就报错No ULINK Device Found、Cannot access target之类。这种问题的本质在于对方工程里选的是 ULINK2而你手上是 ST-Link或者对方用的是 ST-Link你的是 J-Link。解决方法是到Options for Target-Debug选项卡把右上角的调试器从ULINK2/ME Cortex Debugger改成你实际使用的型号比如ST-Link Debugger或者J-LINK/J-TRACE Cortex。改完之后还要点旁边的Settings在里面确认一下SW Device列表里能识别到目标芯片如果能识别到说明连接正常。之后还有一个容易忽略的地方Utilities选项卡。这里如果不勾选Use Debug Driver烧录时会用别的算法导致无法下载。一般我的习惯是 Debug 和 Utilities 全部统一成同一个调试器避免两套配置打架。4.2 Flash Download 设置里 Programming Algorithm 缺失如果你在下载程序时报错Flash Timeout. Reset the Target and try it again或者Error: Flash Download failed - Cortex-M3这多半是Flash Download里的编程算法不对。点击 Debug 选项卡里的Settings会弹出一个窗口左侧是Debug右侧是Flash Download。你需要在Programming Algorithm区域里添加与你芯片匹配的算法比如 STM32F103C8 需要STM32F10x Med-density 64K Flash。如果这个列表是空的说明你没有正确选择芯片型号或者 Pack 里的 flash 算法文件缺失需要回到 Device 选项卡重新选择芯片。顺便说一句很多人遇到No Flash Device的报错就以为是芯片坏了其实大概率就是这里没配置对。先把Erase Full Chip、Program、Reset and Run这几个多选框勾上再把 Flash 算法加好基本就能烧录了。4.3 外部文件夹改名后工程找不到源文件的解决思路这个场景我估计很多人经历过从网上下了一个工程编译的时候 Keil 报错找不到.c文件但文件明明就在目录里。多数原因是对方给的工程文件是完整可以用的但你在解压或者复制时把某个上层文件夹的名字改了导致工程里的相对路径错位了。解决办法有两种第一种是改回原来文件夹的名字但这需要你知道原来叫什么第二种更通用——在 Keil 里把找不到的源文件删掉重新从目录里添加一次。操作方法是在 Project 窗口里右键对应文件选择Remove File然后在 Group 上右键选Add Existing Files重新定位到实际的.c文件位置。这个操作本质上就是“重新建立文件索引”能解决绝大多数因改路径导致的问题。5. 实操总结和问题速查表5.1 打开他人 Keil 工程的标准化流程经过上面的分析我这里总结一条比较稳妥的路径你拿到工程后按这个顺序来能避免大部分问题把整个工程目录复制到纯英文路径下比如D:\Keil_Projects\xxx。用记事本打开.uvprojx确认芯片型号、宏定义、编译工具链信息。打开 Keil 工程在 Device 选项卡检查芯片型号安装对应的 DFP Pack。在 Target 选项卡检查 ARM Compiler 版本缺失的组件要去官网下载补装。在 C/C 选项卡检查 Include Paths、Define、语言标准逐项对齐。在 Asm 选项卡检查汇编器的条件控制符号和路径。在 Debug 和 Utilities 选项卡检查调试器和 Flash 下载算法。先编译记录下所有报错逐个排除再烧录确认硬件链路没问题。这套流程我实际操作过很多次基本能在十几分钟内定位到所有问题。遇到特别复杂的工程最耗时的往往不是编译报错本身而是 Debug 器的配置差异。5.2 常见报错和解决对照表报错信息核心原因快速解决device not found芯片 Pack 未安装Pack Installer 搜索型号安装 DFPcore_cm3.h: No such fileCMSIS Pack 缺失安装对应的器件 Packfatal error: xxx.h: No such fileInclude Paths 不匹配C/C 里重新添加头文件目录identifier xxx is undefined缺少 Define 宏或库版本不对检查 Define 配置补上芯片宏No ULINK Device Found调试器类型不匹配Debug 选项卡改成实际调试器Flash Download failed编程算法未配置Settings 里添加 Flash 算法L6218E: Undefined symbol缺少源文件或启动文件不对添加对应 .c 文件核对启动文件#error Please select first the target device编译器版本不对或 Device 未选检查 Target 编译器版本重选 Devicecould not open file .\obj\xxx.axf输出目录不存在或路径冲突Target 里设置 Output 目录为.\obj并创建该目录5.3 最后提几个实操中的小经验第一拿到别人工程后先备份一份原始文件。因为你在改 Include Paths、编译器设置的时候很可能越改越乱至少需要一个可以回退的版本。我一般会把压缩包原样保留把解压出来的那个文件夹改一个“_modify”后缀再操作。第二尽量用相对路径。为了以后自己的工程也能方便地发给别人所有头文件路径、输出目录、Listing 目录全部用相对路径。这样整个文件夹拷给别人对方只要环境一致就能直接编译。养成这个习惯以后你发给别人的工程出问题的概率会小很多。第三遇到 AC5 转 AC6 的工程不要图省事直接切编译器。先看一下代码里有没有用到编译器相关的特殊语法比如__asm、__forceinline、#pragma pack这些AC5 和 AC6 的写法有差异盲切会让你看到一屏的编译错误。稳妥的做法是先搜一下有没有#pragma指令有的话大概率要逐条改。第四如果工程里有多个 Target比如Debug和Release两个配置打开后记得看看你当前激活的是哪一个。我之前就遇到过朋友发来工程他自己用的是 Debug 配置而我打开后默认激活的是 Release两个配置的宏定义和优化等级不一样导致编译报错完全对不上。在工具栏的下拉框里切换一下就好这个细节特别容易忽略。5.4 这个问题的本质和意义说到底打开别人的 Keil 工程报错本质上不是“代码写得有问题”而是“工程文件的自我描述”和你本地的工具链不一致。搞清楚这一点你就不会在报错面前手足无措了。你只需要照着.uvprojx里记录的配置一条一条去对齐自己的环境问题都会逐步解开。另外说一句如果你是经常需要接手别人代码的开发者或者在团队里负责整合不同人的工程强烈建议统一团队的编译器版本、Pack 版本和工程模板。很多时候成员之间代码互传消耗的时间远比写代码消耗的时间多。如果能形成一份团队的 “环境搭建说明文档”那大家的效率都能提升一个档次。这个做法受益的绝对不只是你自己。
返回列表