C语言豹哥嵌入式讲堂:ARubiconM 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
EWACR-VM更是进入了8.0年代,IDE各有千秋,但本文要讲的情节却是各样IDE必须具备的基本作用,依然接二连三以IAR
EWA酷威M为例伊始明天的内容:

一、标准IDE功能

  在早先明天的主旨以前,豹哥觉得有须求先简要给大家普遍一下正经IDE应该享有的效应。现代IDE基本都以由组件构成,嵌入式开发中的每种阶段都对应着相应的零件,由那么些零部件去落到实处各等级的急需。

1.1 IDE组件

  标准嵌入式开发相应至少包涵以下五个阶段,而IA卡宴里对于各种阶段都有一个或三个零部件:

  • 输入(IA库罗德 艾德itor):编辑源文件代码。
  • 编写翻译(ICCA牧马人M、IASMA安德拉M):编写翻译源文件代码生成可实施二进制机器码。
  • 浅析(C-STAT、MISRA-C):编写翻译进程中反省代码中潜在的标题。
  • 链接(ILINK):链接可进行二进制机器码到内定A奥迪Q3M存款和储蓄空间地址。
  • 下载(I-jet、flashloader):将链接好的可实行二进制机器码下载进芯片内部非易失性存款和储蓄器。
  • 调剂(C-SPY、C-RUN):在线调节和测试代码在芯片中实践境况。

  project文件根本用来记录整合上述五个阶段的兼具费用须要。

1.2 IDE文件类型

  既然IDE有过多零部件,那么与此同时也会存在不一致类型的公文以存款和储蓄这么些组件的所急需的消息。IALacrosse里帮忙的文件扩张项目分外多,豹哥在此地仅列举你所创办的工程根目录下的与工程同名的壮大文件,相信您势必会认为熟练。

.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:越多IA大切诺基援助的扩展文件类型请查阅IA福睿斯软件设置目录下\IAR
Systems\Embedded Workbench
xxx\arm\doc\EWARM_IDEGuide.ENU.pdf文书档案里的File types一节。

二、解析project(ewp)文件

  前面豹哥铺垫了许多IDE/project基础概念,该是直奔主旨的时候了,本文主演ewp工程文件到底包罗哪些开发者钦定的input新闻?豹哥从底下二个地点为大家揭发:

2.1 源文件组织

  二个稍稍复杂一点的嵌入式工程,应用代码行数应该是以百行/千行为单位总括的(此处仅指的是由开发者本人创设的文书与代码),大家在集体代码的时候肯定不会只开创3个.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

钦赐A奥德赛M内核版本

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解释请查阅IAPAJERO软件设置目录下\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的性质必要也进一步高,各大A中华VM厂商也在不断推出质量更加强大的ACRUISERM
MCU产品,超高主频,双核,四核MCU已经不鲜见了。对于内部的一对异构双核MCU产品,有时在支付中会有那般的急需:你有一份的middleware会被异构双核同时调用,而四个例外基础的一声令下集有或许是不同的,怎么化解那么些标题?有意中人会想到分别在各类核下边都编写翻译一份binary停放于存款和储蓄器不一致职位,运营时分别指向对应的binary,那是2个艺术,但比较浪费存款和储蓄空间,且有恐怕会搞混淆导致误调用。有没有更好的艺术?
  为了能到位Cortex-M软件重用,A逍客M公司在筹划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只会被1个.c文件include)。看到此间的对象若是脑洞再大学一年级点,你还能形成工程里只要求丰裕一个.c文件,而其余.c文件全部由添加进工程的不行.c文件逐级(仅能单级)引用进工程。

  至此,嵌入式开发里的project文件豹哥便介绍落成了,掌声在哪个地方~~~