大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家讲的是嵌入式开发里的project文件

  前面两节课里,痞子衡分别给大家介绍了嵌入式开发中的两种典型input文件:源文件(.c/.h/.s)、链接文件(.icf)。痞子衡要再次提问了,还有没有input文件呢?答案确实是有,但这次真的是有且仅有了,本文要介绍的主角project文件也属于半个input文件。为什么说是半个?因为project文件不仅包含开发者指定的input信息,还包含很多其他辅助调试的input/output信息,算是嵌入式开发中承前启后的文件。而本文侧重点在于project文件中与开发者应用相关的input信息,仅当得到了这些input信息,再加上前面介绍的source和linker文件,那么你就已经得到了application所有的信息,你可以用它们来可以生成无歧义的可执行image binary。
  随着嵌入式软件工程的发展,为了应对日益复杂的需求,现代IDE的功能也越来越强大了,IDE版本更迭让人应接不暇,Keil MDK已然踏入5.0时代,IAR EWARM更是进入了8.0时代,IDE各有千秋,但本文要讲的内容却是每个IDE必须具有的基本功能,还是继续以IAR EWARM为例开始今天的内容:

一、标准IDE功能

  在开始今天的主题之前,痞子衡觉得有必要先简要给大家科普一下标准IDE应该具有的功能。现代IDE基本都是由组件构成,嵌入式开发中的每个阶段都对应着相应的组件,由这些组件去实现各阶段的需求。

1.1 IDE组件

  标准嵌入式开发应该至少包括以下6个阶段,而IAR里对于每个阶段都有1个或多个组件:

  • 输入(IAR Editor):编辑源文件代码。
  • 编译(ICCARM、IASMARM):编译源文件代码生成可执行二进制机器码。
  • 分析(C-STAT、MISRA-C):编译过程中检查代码中潜在的问题。
  • 链接(ILINK):链接可执行二进制机器码到指定ARM存储空间地址。
  • 下载(I-jet、flashloader):将链接好的可执行二进制机器码下载进芯片内部非易失性存储器。
  • 调试(C-SPY、C-RUN):在线调试代码在芯片中执行情况。

  project文件主要用来记录整合上述6个阶段的所有开发需求。

1.2 IDE文件类型

  既然IDE有很多组件,那么同时也会存在不同类型的文件以存储这些组件的所需要的信息。IAR里支持的文件扩展类型非常多,痞子衡在这里仅列举你所创建的工程根目录下的与工程同名的扩展文件,相信你一定会觉得眼熟。

.eww           // Workspace file
.ewp           // IAR Embedded Workbench project
.ewd           // Project settings for C-SPY
.ewt           // Project settings for C-STAT and C-RUN</td>
.dep           // Dependency information

  本文要讲的内容都包含在.ewp文件里,ewp文件记录了开发者为应用指定的不可缺少的input信息,没有这些信息,application工程是不完整的。换句话说,如果你得到了application的所有source文件和linker文件,但没有ewp文件的话,可能导致最终生成的image binary文件是不同的。

Note:更多IAR支持的扩展文件类型请查阅IAR软件安装目录下\IAR Systems\Embedded Workbench xxx\arm\doc\EWARM_IDEGuide.ENU.pdf文档里的File types一节。

二、解析project(ewp)文件

  前面痞子衡铺垫了很多IDE/project基础概念,该是直奔主题的时候了,本文主角ewp工程文件到底包含哪些开发者指定的input信息?痞子衡从下面3个方面为大家揭秘:

2.1 源文件组织

  一个稍微复杂一点的嵌入式工程,应用代码行数应该是以百行/千行为单位计算的(此处仅指的是由开发者自己创建的文件与代码),我们在组织代码的时候肯定不会只创建一个.c文件,单文件会导致代码功能模块结构不清晰,不方便工程的管理与维护。
  当我们为工程创建多个文件时,就会涉及到一个必然问题:引用路径问题(所以路径信息就是本文要说的第一个input信息)。当源文件数目较多时,通常我们会创建不同文件夹把相同功能的源文件都放在一起,当编译器开始编译.c源文件时会搜索include语句所包含的头文件。熟悉C语言的朋友肯定清楚下面两种不同include语句的用法:

#include <file.h>           // 引用编译器类库下的头文件(IDE安装路径)
#include "file.h"           // 引用当前工程下的头文件(project路径)

  所以在ewp文件里会包含路径信息,所有路径都应该列在Options->C/C++ Compiler->Preprocessor下有Additional include directories里,这个路径既可以是当前PC的绝对路径,也可以是以ewp文件为基准的相对路径,为了保证工程可以在任意PC任意位置下正常编译,推荐使用如下相对路径方式列出所有路径:

ewp当前路径:$PROJ_DIR$/
ewp下级路径:$PROJ_DIR$/xxFolder/
ewp上级路径:$PROJ_DIR$/../

  说到路径问题,痞子衡在这里顺便给大家介绍一种经典的嵌入式工程文件目录组织方式:

\projectDir\doc                            --放置工程文档\bsp                            --放置bsp(板级)相关的source file\linker                    --工程linker文件\src                       --板级相关的源文件(比如pinout,clock等)\builds\xxBuild\.ewp       --工程ewp文件.eww                       --工程workspace文件\src                            --放置bsp无关的source file\platform                  --芯片头文件及CMSIS文件\drivers                   --芯片片内外设driver\include                   --要被所有source引用的头文件\startup                   --标准的startup code\utilities                 --标准的通用函数\middleware                --独立的中间件\components                --板级外设组件driver\application               --当前应用主逻辑代码

2.2 全局宏定义

  经常使用条件编译的朋友肯定知道workspace文件与project文件的关系,一个项目通常只会有一个eww文件,但却可能会有多个ewp文件,这是因为源代码里常常会有条件编译,我们有时候会给项目不同的配置从而编译出不同的结果(速度优先/面积优先,特性控制...),这些配置就是由全局宏定义来实现的,打开Options->C/C++ Compiler->Preprocessor下的Defined symbols,在框内写入你需要定义的全局宏:

MACRO1            // 等价于源文件里的#define MACRO1 (1)
MACRO2=2          // 等价于源文件里的#define MACRO2 (2)

  全局宏信息就是本文要说的第二个input信息,如果全局宏信息丢失,有时候工程编译并不会报错,因为编译器在处理如下普遍用法里的条件编译语句时会默认未定义的宏为0,而在处理普遍用法里的条件编译语句则会报错,所以推荐大家使用第二种条件编译用法来规避全局宏问题。

// 普遍用法
#if MACRO// your code block 1
#else// your code block 2
#endif// 推荐用法
#if !defined(MACRO)#error "No valid MACRO defined!"
#elif (MACRO == 1)// your code block 1
#else// your code block 2
#endif

2.3 编译选项

  编译选项包含了编译器所需要的所有信息,代码需经过编译器编译才能生成二进制机器码,不同的编译器选项配置会生成不同的机器码,那么需要指定哪些选项呢?打开project的Options选项卡,分别设置下表item:

Position Item Description
General Options->Target-> Processor variant->Core 指定ARM内核版本
Endian mode 指定内核大小端模式
Floating point settings->FPU 指定内核支持的FPU版本
General Options->Library Configuration-> Library 选择C/C++动态链接库版本
General Options->Library Option 2-> Heap selection 选择HEAP实现版本
C/C++ Compiler-> Language 1->Language 指定编程语言类型
Language 1->C dialect 指定C语言标准
Language 1->Language conformance 选择对标准C/C++的遵循程度
Language 2->Plain 'char' is 选择对char的符号性默认处理方法
Language 2->Floating-point semantics 选择对浮点数的处理遵循C标准的程度
Code->Process mode 指定内核指令集模式
Code->Position-independence 选择要生成位置无关代码的对象
Optimizations->Level 选择优化等级

Note:更多ewp文件中option解释请查阅IAR软件安装目录下\IAR Systems\Embedded Workbench xxx\arm\doc\EWARM_IDEGuide.ENU.pdf文档里的General Options和Compiler Options俩小节。

  编译设置信息就是本文要说的第三个input信息,当在project中组织好源文件并设置好正确的全局宏定义和编译选项,那么恭喜你,你的application设计工作已经基本完成了。

三、创建demo工程

  为方便后续课程的进行,本节课在最后顺便创建一个demo工程,以下是demo工程的信息:

IDE:        IAR EWARM v8.11.2
Device:     NXP MKL25Z128VLH4
project layout:   \D\myProject\bsp\builds\demo\demo.ewp\D\myProject\bsp\linker\iar\KL25Z128xxx4_flash.icf\D\myProject\bsp\src\startup_MKL25Z4.s   (仅保留前16个系统中断)\D\myProject\bsp\src\system_MKL25Z4.c   (仅做关闭WDOG操作)\D\myProject\bsp\src\system_MKL25Z4.h\D\myProject\bsp\helloArm.eww\D\myProject\src\platfrom\CMSIS\D\myProject\src\platfrom\devices\MKL25Z4\D\myProject\src\startup\reset.s\D\myProject\src\startup\startup.c\D\myProject\src\startup\startup.h\D\myProject\src\application\main.c\D\myProject\src\application\task.c\D\myProject\src\application\task.h
// main.c
//
#include "task.h"
const uint32_t s_constant = 0x7f;
int main(void)
{uint32_t l_variable = 0x7f;if (s_constant == l_variable){normal_task();ram_task();heap_task();}while (1);
}// task.c
//
#include "task.h"
static    uint32_t s_variable0;
__no_init uint32_t n_variable1;
static    uint32_t s_variable2 = 0x5a;
static uint8_t s_array[16];
void normal_task(void)
{s_variable0 *= 2;
}
__ramfunc void ram_task(void)
{n_variable1++;
}
void heap_task(void)
{uint8_t *heap = (uint8_t *)malloc(16 * sizeof(uint8_t));if (heap != NULL){memset(heap, 0xa5+s_variable2, 16);memcpy(s_array, heap, 16);s_variable0 = (uint32_t)heap;free(heap);}
}

番外一、几个小技巧

  又来到痞子衡番外时间了,细心的朋友看到上表有两处标蓝,是的没错,今天的番外内容就是标蓝的项目有关。

技巧1:运行于异构双核

  目前嵌入式产品越来越复杂,对MCU的性能要求也越来越高,各大ARM厂商也在不断推出性能越来越强劲的ARM MCU产品,超高主频,双核,四核MCU已经不鲜见了。对于其中的一些异构双核MCU产品,有时在开发中会有这样的需求:你有一份的middleware会被异构双核同时调用,而两个不同内核的指令集有可能是不一致的,怎么解决这个问题?有朋友会想到分别在每个核下面都编译一份binary放置于存储器不同位置,运行时各自指向对应的binary,这是一个办法,但比较浪费存储空间,且有可能会搞混淆导致误调用。有没有更好的方法?
  为了能做到Cortex-M软件重用,ARM公司在设计Cortex-M处理器时为其赋予了处理器向下兼容软件二进制向上兼容特性。通俗的话来说就是在较低版本处理器上编译的代码可以在较高版本处理器上执行。所以解决方法就是选用异构双核里较低版本的内核在编译middleware,这样这份middleware可以同时被两个核调用。

技巧2:生成PIC代码

  经常和bootloader打交道的朋友肯定知道,代码在经过链接阶段生成binary文件后,这个binary并不是可以放在任意位置的,必须放到linker文件指定的位置,如果位置没有放正确,可能会导致执行出错。究其原因,是因为编译器在汇编源代码时因为一些策略并不总是将所有function都汇编成位置无关代码。如果我们借助于IDE编译选项将middleware汇编成PIC代码,那么我们可以在工程中直接加入middleware的binary,然后借助linker的自定义section功能将其放置于任意某个位置,最后只要为这个middleware binary建立一个以binary首地址为基准的函数指针地址列表即可无障碍调用这个middleware。

技巧3:引用.c文件

  在项目开发中,我们在一个workspace下会创建多个project,常常是因为不同project需要包含不同的.c文件以完成不同的功能。那么能不能只创建一个project呢能实现不同功能呢?当然可以!通常情况下我们在.c文件中只会用#include "xx.h"语句来引用.h头文件,其实我们也同样可以引用.c文件,比如这样#include "xx.c",只是需要注意尽量不要在.h文件中引用.c文件(除非该.h只会被一个.c文件include)。看到这里的朋友如果脑洞再大一点,你甚至可以做到工程里只需要添加一个.c文件,而其他.c文件全部由添加进工程的那个.c文件逐级(仅能单级)引用进工程。

  至此,嵌入式开发里的project文件痞子衡便介绍完毕了,掌声在哪里~~~

欢迎订阅

文章会同时发布到我的 博客园主页、CSDN主页、微信公众号 平台上。

微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

pzhMcu_qrcode_258x258.jpg

痞子衡嵌入式:ARM Cortex-M文件那些事(3)- 工程文件(.ewp)相关推荐

  1. 痞子衡嵌入式:IAR内部C-SPY调试组件配套宏文件(.mac)用法介绍

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是IAR内部C-SPY调试组件配套宏文件(.mac)用法. 痞子衡之前写过一篇 <JLink Script文件基础及其在IAR下调用 ...

  2. 痞子衡嵌入式:ARM Cortex-M文件那些事(4)- 可重定向文件(.o/.a)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家讲的是嵌入式开发里的relocatable文件(object, library). 前三节课里,痞子衡都是在给大家介绍嵌入式开发中的input文 ...

  3. 痞子衡嵌入式:ARM Cortex-M文件那些事(6)- 可执行文件(.out/.elf)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家讲的是嵌入式开发里的executable文件(elf). 第四.五节课里,痞子衡已经给大家介绍了2种output文件,本文继续给大家讲proje ...

  4. 痞子衡嵌入式:ARM Cortex-M文件那些事(8)- 镜像文件(.bin/.hex/.s19)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家讲的是嵌入式开发里的image文件(.bin, .hex, .s19). 今天这节课是痞子衡<ARM Cortex-M文件那些事>主 ...

  5. 痞子衡嵌入式:ARM Cortex-M内核那些事(5)- 一表搜罗指令集

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是ARM Cortex-M指令集. 指令集 指令长度 (bits) 包含指令 Cortex M0 Cortex M0+ Cortex M1 ...

  6. 痞子衡嵌入式:IAR环境下无法直接下载调试i.MXRT分散链接工程的解决方案(宏文件.mac+双Flashloader)...

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是IAR环境下无法直接下载调试i.MXRT分散链接工程的解决方案. 分散链接与加载一直是嵌入式领域比较劝退新手的难题,在恩智浦 i.MXR ...

  7. 痞子衡嵌入式:ARM Cortex-M内核那些事(9.1)- 存储保护(MPU - PMSAv6/7)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是ARM Cortex-M存储保护模块(MPU). <ARM Cortex-M内核MCU开发那些事>的内核篇连载最早是 201 ...

  8. 痞子衡嵌入式:MCUXpresso IDE下工程链接文件配置管理与自动生成机制

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是MCUXpresso IDE下工程链接文件配置管理与自动生成机制. 痞子衡在 2018 年初写过一个专题 <嵌入式开发文件系列&g ...

  9. 痞子衡嵌入式:单片机AI的春天已来,ARM Cortex-M55发布了

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是ARM Cortex-M55. 鼠年春节,大家都在时刻关心 2019nCoV 疫情发展,没太多心思搞技术,就在这个时候,ARM 不声不响 ...

最新文章

  1. python实现简单的用户密码登录控制(输入三次就锁定用户)
  2. Idea使用Lombok简化实体类代码
  3. 在一颗度为4的树T中,若有20个度为4的结点,10个度为3的结点,1个度为2的结点,10个度为1的结点,则树T的叶结点个数是( )
  4. 网络编程(part8)--传输层服务之TCP和UDP
  5. linux编译ffmpeg_[Cmake-Android音视频]ffmpeg3.4编译支持neon,硬解码
  6. javscript之数组(一)
  7. pythonunicode和str_python2 中 unicode 和 str 之间的转换及与python3 str 的区别
  8. 表格细线边款(HTML、CSS)
  9. 中图杯获奖作品计算机组,地理奥赛网-首页
  10. Scintilla Documentation
  11. make px4_sitl gazebo报错:FAILED: ROMFS/romfs_extract.stamp
  12. 【读书笔记】浪潮之巅——方法论篇
  13. python八卦时间代码_Python实践代码总结第5集(日期相关处理)
  14. LightOJ1220 —— 质因数分解
  15. 一个题目引发的数组和指针复习
  16. php 判断字符串类型及长度
  17. Old-school 老派 2016-10-01
  18. 数据分析团队正成为手游公司的标配,但我为什么说解散他
  19. 清道夫第一季/全集Ray Donovan迅雷下载
  20. 浏览器端大屏百度地图常用控件 3D/2D 测距 地图风格 卫星地图 路况...

热门文章

  1. java程序开发实用教程上机实训答案,京东高级java面试
  2. 微信零钱提现还要手续费?不存在的
  3. js 前端常用时间操作:时间戳、当前时间
  4. win7一直显示正在关机_win7系统一直卡在正在关机无法关机怎么解决
  5. 重庆实时路况(基于百度MAP API)
  6. 印刷方式——标识丝印工艺
  7. 推荐几个不错的DOTNET控件网址
  8. Android图文混排(仿QQ空间评论)
  9. 人工智能数学课高等数学线性微积分数学教程笔记(7. 最优化)
  10. BNET创始人刘建军:BNET破解当今通信网络三大痛点