豹哥嵌入式讲堂:ARM Cortex-M开发之文件详解(3)- 工程文件(.ewp)


  大家好,我是豹哥,猎豹的金钱豹,犀利哥的父兄。今天豹哥为大家讲的凡嵌入式开发里之project文件

  前面两节课里,豹哥分别被大家介绍了嵌入式开发被的一定量种植典型input文件:source文件、linker文件。豹哥要再次提问了,还生没有发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文件豹哥就介绍完了,掌声以乌~~~