ARM C语言编程优化策略

8 篇文章 1 订阅
订阅专栏

ARM C语言编程优化策略(KEIL平台)

95 人 赞同了该文章
  • ARM C语言编程优化策略
    • 1. 内容介绍
    • 2. 优化实战
      • 2.1. 编译器优化选项
      • 2.2. C循环优化
      • 2.3. 内联函数
      • 2.4. volatile 关键字的使用
      • 2.5. 纯净函数
      • 2.6. 数据对齐特性
      • 2.7. C99 中易用的特性
      • 2.8. C对栈和寄存器的使用
      • 2.9. 阻止未初始化变量初始为0
    • 3. 编译器特性
      • 3.1. 关键字
      • 3.2. __declspec 属性
      • 3.3. __attribute__
      • 3.4. pragmas
      • 3.5. 使用及说明
      • 3.6. 内置指令
    • 4. 常用编译器支持语言拓展
      • 4.1. C89/90 下可以使用的 C99标准
      • 4.2. 标准C语言拓展
    • 5. 链接器应用
      • 5.1. 访问 section 的相关特性
      • 5.2. 使用 $Super$$ 和 $Sub$$ 打补丁

1. 内容介绍

KEIL平台主要有四个工具:

  • armcc,C/C++编译器
  • armasm, 汇编器
  • armlnk,链接器
  • fromelf,产生二进制代码

其作用及关系如下图所示:


本文主要讲述KEIL平台编程的优化策略,主要内容如下:

  • 优化实战
  • 编译器特性
  • 语言拓展
  • 链接器应用

本文主要内容来源于 ARM® Compiler v5.06 for μVision® Version 5 armcc User Guide

2. 优化实战

2.1. 编译器优化选项

代码体积vs执行速度

  • -Ospace

Keil编译器默认配置,主要目的是减少代码体积

  • -Otime

目的是加快执行速度

优化等级及调试信息

  • -O0

最少的优化,可以最大程度上配合产生代码调试信息,可以在任何代码行打断点,特别是死代码处。

  • -O1

有限的优化,去除无用的inline和无用的static函数、死代码消除等,在影响到调试信息的地方均不进行优化。在适当的代码体积和充分的调试之间平衡,代码编写阶段最常用的优化等级。

  • -O2

高度优化,调试信息不友好,有可能会修改代码和函数调用执行流程,自动对函数进行内联等。

  • -O3

最大程度优化,产生极少量的调试信息。会进行更多代码优化,例如循环展开,更激进的函数内联等。

另外,可以通过单独设置 --loop_optimization_level=option 来控制循环展开的优化等级。

2.2. C循环优化

C代码的循环结束条件

  • + + 式
int fact1(int n)
{
    int i, fact = 1;
    for (i = 1; i <= n; i++)
        fact *= i;
    return (fact);
}

在 -O2 -Otime条件下汇编为:

fact1 PROC
MOV r2, r0
MOV r0, #1
CMP r2, #1
MOV r1, r0
BXLT lr
|L1.20|
MUL r0, r1, r0
ADD r1, r1, #1
CMP r1, r2
BLE |L1.20|
BX lr
ENDP
  • - -式
int fact2(int n)
{
    unsigned int i, fact = 1;
    for (i = n; i != 0; i--)
         fact *= i;
    return (fact);
}

在 -O2 -Otime条件下汇编为

fact2 PROC
MOVS r1, r0
MOV r0, #1
BXEQ lr
|L1.12|
MUL r0, r1, r0
SUBS r1, r1, #1
BNE |L1.12|
BX lr
ENDP

以上可以看到, ++式中 ADD 和 CMP 指令增大了汇编指令条数。

同样,在 while 和 do 也是同样情况。

C代码中的循环展开

普通循环

int countbit1(unsigned int n)
{
    int bits = 0;
    while (n != 0)
    {
        if (n & 1) bits++;
             n >>= 1;
    }
    return bits;
}

循环展开

int countbit2(unsigned int n)
{
    int bits = 0;
    while (n != 0)
    {
        if (n & 1) bits++;
        if (n & 2) bits++;
        if (n & 4) bits++;
        if (n & 8) bits++;
            n >>= 4;
    }
    return bits;
}

代码的循环展开可以减少循环体执行的次数,但是也在一定程度上增大了代码的体积,循环展开需要适量,一般循环展开4个一组。

在-O3优化等级下,编译器会适当进行循环展开。

2.3. 内联函数

编译器在函数内联时的决策

函数内联是在代码体积和执行性能之间所做的权衡,是否内联由编译器决定。

如果优化选项是 -Ospace , 则编译器会倾向于比较少的内联,以减少代码体积;如果优化选项为 -Otime , 则编译器则会倾向于更多的内联,但也会避免代码体积的大量增加。

使用 __inline, __forceinline 等关键字(或者 attribute 属性控制, 或者 #pragma 属性控制也有相应的用法,详细下文会说), 可以给编译器建议,但是最终是否内联还是由编译器决定。

另外,内联函数的体积应该比较小。

编译器在以下情况下,如果有可能内联,则尽量内联:

  • __forceinline , __attribute__((always_inline)) 定义的函数
  • 编译选项中有 --forceinline

编译器在以下情况下,会在合适的情况下进行内联(编译器自己决定):

  • __inline, __attribute__((inline))修饰的函数
  • 优化选项为-O2,或-O3,或者存在 --autoinline,及时有些函数没有显式声明inline,也有可能inline

编译在决定是否内联时会综合考虑一下因素:

  • 函数的体积和被调用次数,小函数更易被内联,调用次数少更易被内联
  • 当前的优化选项等级,等级越高越易被内联
  • -Ospace vs -Otime, -Otime函数更易被内联
  • 函数链接属性是 external 还是 static,static更易被内联
  • 函数有多少形参
  • 函数的返回值是否被使用等

在编译器认为不合适的情况下,即使声明了 __forceinline 函数也不会内联。

inline和static

具有外部链接属性的函数在内联时会保留一份函数代码在最终的二进制文件中,所以外部链接属性的函数内联会增大代码体积。

但是声明为 static 的函数就不会存在这个问题,static 表明函数只需要内部调用,所以不需要再保留原来的代码。

注意,如果明确不需外部调用的函数,请加上 static

链接器在链接时不会自动去掉具有外部链接属性的函数,以防某些外部程序调用,可以使用以下方式去除无用函数:

  • --split_sections 这个编译选项会把每一个函数都单独放到一个section
  • __attribute__((section("name"))) 把特定的函数放到自定义的section中
  • --feedback 这个编译选项,会对代码进行两次编译分析其代码的使用,并去掉无用函数,keil推荐使用此种方案

避免 inline

由于inline会对debug造成影响,可以通过 - -no_inline 编译选项禁止所有的内联,或者通过 - -no_autoinline 禁止编译器自动内联。

2.4. volatile 关键字的使用

优化等级较高时,会出现一些低优化等级不会出现的问题,例如没有使用关键字 volatile(其他的部分主要是代码中出现未定义行为,即UB).

没有使用 volatile 修饰变量时:

int buffer_full;
int read_stream(void)
{
    int count = 0;
    while (!buffer_full)
    {
        count++;
    }
    return count;
}

-O2 优化等级其汇编代码为:

read_stream PROC
LDR r1, |L1.28|
MOV r0, #0
LDR r1, [r1, #0]
|L1.12|
CMP r1, #0
ADDEQ r0, r0, #1
BEQ |L1.12| ; infinite loop
BX lr
ENDP
|L1.28|
DCD ||.data||
AREA ||.data||, DATA, ALIGN=2
buffer_full
DCD 0x00000000

使用 volatile 修饰 buffer_full 时,-O2下汇编代码为:

read_stream PROC
LDR r1, |L1.28|
MOV r0, #0
|L1.8|
LDR r2, [r1, #0]; ; buffer_full
CMP r2, #0
ADDEQ r0, r0, #1
BEQ |L1.8|
BX lr
ENDP
|L1.28|
DCD ||.data||
AREA ||.data||, DATA, ALIGN=2
buffer_full
DCD 0x00000000

可以看到,没有使用 volatile 时,while 循环查询的变量被优化了(只查询一次变量,并把它存在寄存器中,循环结束条件判断直接和保存变量值的寄存器进行比较,而不再更新寄存器的值),而 volatile 修饰后,则每次循环都查询(可以在汇编中看到,每一次循环体执行开始,首先会加载变量值到寄存器中)。

特别是在中断、多线程及寄存器读取中一定要注意使用 volatile 修饰易变的变量。

2.5. 纯净函数

  • 函数没有读、写全局内存

当函数没有读、或者写全局变量的函数,可以使用 __attribute__((const)) 或者 __pure 修饰

  • 函数没有写全局内存

可以使用__attribute__((pure))修饰

没有读写全局变量的函数,任何时候调用(只要传入参数相同)都会返回相同的结果,编译器可以利用此信息进行优化,示例如下:

int fact(int n)
{
    int f = 1;
    while (n > 0)
    f *= n--;
    return f;
}
int foo(int n)
{
    return fact(n)+fact(n);
}

-O2 下生成的汇编

fact PROC
...
foo PROC
MOV r3, r0
PUSH {lr}
BL fact
MOV r2, r0
MOV r0, r3
BL fact
ADD r0, r0, r2
POP {pc}
ENDP

可以看到,此时汇编代码中调用了两次fact函数,然后对其结果进行累加。

fact 由 __pure 修饰

int fact(int n) __pure
{
    int f = 1;
    while (n > 0)
    f *= n--;
    return f;
}
int foo(int n)
{
    return fact(n)+fact(n);
}

同样编译条件下生成的汇编为:

fact PROC
...
foo PROC
PUSH {lr}
BL fact
LSL r0,r0,#1
POP {pc}
ENDP

这时,只调用了一次fact函数,然后对结果直接 LSL 左移一位。

2.6. 数据对齐特性

  • 自然对齐特性

C 语言编译出来的汇编代码具有自然对齐特性,如下所示:

以上自然对齐特性编译出来的汇编指令执行效率很高。

例如以下 struct 在 a 和 c 之间会有三个字节空隙:

struct example_st {
    int a;
    char b;
    int c;
}
  • 利用 __packed 或 __attribute__((packed)) 非对齐数据

可以使用以上修饰的对象为包括:struct, union, pointer

对于结构体,有两种方式进行修饰:

__packed struct mystruct {
    char c;
    short s;
}   /* not recommended */

不建议使用这种方式,因为这样会增大结构体中自然对齐数据的访问时间。

建议单独对结构体重非对齐的数据进行定义:

struct mystruct {
    char c;
    __packed short s;
}

一般情况下不建议使用非自然对齐数据。

2.7. C99 中易用的特性

  • // 注释符号

//注释符使用起来更为方便

  • 定义和语句混合

C99支持以下循环方式:

for(int i = 100; i > 0; i--)
{
    //do something
}

使用起来更为方便

*结构体赋值方式

struct mystruct {
    const char *name;
    int age;
}

mustruct person = {.name = “wxj”, .age = 22};

  • 动态数组
#include <assert.h>

int test(int n)
{
assert(n > 0);

<span class="kt">int</span> <span class="n">array</span><span class="p">[</span><span class="n">n</span><span class="p">];</span>
<span class="c1">//do something

}

注意:动态数据只能用于局部变量,并且在生成之后不可以再改变其数据长度;动态数据的数据存在 heap 中。

  • func 宏定义

可以代表当前的函数

void foo(void)
{
printf(“This function is called ‘%s’.\n, func);
}

Keil中也可以使用 FUNC, 其功能和 FILE, LINE 相似,主要用于 DEBUG 输出调试信息。

  • 宏定义中可以使用变长参数
#define LOG(format, …) fprintf(stderr, format, VA_ARGS)
  • restrict 指针

表明两个指针指向同一个地址,下例是不允许的

void copy_array(int n, int restrict a, int restrict b)
{
while (n > 0)
a++ = b++;
}
  • 新增布尔类型
#include <stdbool.h>

bool flag = false;

2.8. C对栈和寄存器的使用

C对寄存器和栈的使用遵循 ARM Architecture Procedure Call Standard (AAPCS), 其内容主要规定了C函数调用时参数传递、结果返回、中间变量等C函数过程对寄存器和栈使用。

  • 其规定了C函数的形参通过R0-R3四个寄存器传递,其余通过栈传递,所以函数的参数一般不要超过四个,如果参数过多,可以通过结构指针的方式传入;
  • 调用函数时,父函数需要保存R0-R3中自己需要用到的寄存器,子函数刚进入时需要保存R4-R12,SP,LR,PC及XPSR需要使用的寄存器。

按照以上过程,就可以实现C和汇编的嵌入。

为了减少C执行过程中对栈的使用,可以采取如下方式:

  • 函数要小,并且使用少量的局部变量;
  • 避免大的数组和结构体;
  • 避免递归;
  • 使用C的局部域,并且只在变量需要时才声明,这样可以做到栈内存复用,例如:
int test(void)
{
int local;
{
int a;
// do something
}
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">b</span><span class="p">;</span>
    <span class="c1">// do something

}

<span class="k">return</span> <span class="n">local</span><span class="p">;</span>

}

2.9. 阻止未初始化变量初始为0

C默认未显式初始化的全局变量均初始化为0,但在某些情况下不希望被初始化为0,可以采用如下两种方式:

  • 方式一:使用pragma
#pragma arm section zidata = “non_initialized”
/* uninitialized data in non_initialized section
* (without the pragma, would be in .bss section by default)
/
int i, j;
#pragma arm section zidata / back to default (.bss section) /
int k = 0, l = 0; / zero-initialized data in .bss section */
  • 方式二:使用attribute
attribute((section(“no_initialized”))) int i, j;

其使用情境为错误快速恢复,当嵌入式系统出现错误时,可以设置保持RAM的供电,进行SoftReset,这时没有初始化的全局变量的值仍然保持,可以快速恢复现场。

3. 编译器特性

3.1. 关键字


3.2. __declspec 属性


3.3. attribute

  • 函数属性


  • 类型属性

The __packed qualifier does not affect type in GNU mode.

  • 变量属性

3.4. pragmas

3.5. 使用及说明

以上四种类型的编译器控制选项,可以只使用一种类型,也可以同时混合使用,有些控制选项的功能一致,可以互换,有些则为特有功能。

以下只是简单介绍,以便于读者理解,详细介绍请自行阅读ARM编译器使用手册,链接附于文末。

以下主要以 attribute 中常用的属性对其使用进行示例说明:

  • 内联控制

attribute((always_inline)) 函数最大可能内联

attribute((noinline)) 禁止函数内联

static int max(int x, int y) attribute((always_inline));
static int max(int x, int y)
{
return x > y ? x : y; // always inline if possible
}

int fn(void) attribute((noinline));
int fn(void)
{
return 42;
}

  • 纯净函数

attribute((pure)) 表示函数不写全局内存

attribute((const) 表示函数不读、写全局内存

#include <stdio.h>
// attribute((const)) functions do not read or modify any global memory
int my_double(int b) attribute((const));
int my_double(int b) {
return b*2;
}

int main(void) {
int i;
int result;
for (i = 0; i < 10; i++)
{
result = my_double(i);
printf (" i = %d ; result = %d \n", i, result);
}
}

  • 函数链接控制

attribute((constructor[(priority)])) 表示函数在程序进入 main() 之后自动执行, priority 为 100 及大于 100 的数, 数字越小,越先执行,并且 100 为默认值。

int my_constructor(void) attribute((constructor));
int my_constructor2(void) attribute((constructor(101)));
int my_constructor3(void) attribute((constructor(102)));

int my_constructor(void) /* This is the 3rd constructor /
{ / function to be called */

return 0;
}

int my_constructor2(void) /* This is the 1st constructor /
{ / function to be called */

return 0;
}

int my_constructor3(void) /* This is the 2nd constructor /
{ / function to be called /

return 0;
}

attribute((destructor[(priority)])) 表示函数在 main() 函数执行完成,或者 exit() 开始执行时调用

int my_destructor(void) attribute((destructor));
int my_destructor(void) / This function is called after main() /
{ /
completes or after exit() is called. */

return 0;
}

attribute((weak)) 表明函数是弱链接的,如果有比它强的连接,则调用其他函数

int func(void) attribute((weak));

int func(void) attribute((weak))
{
// do something
}

int func(void) ;

int func(void)
{
// do something
}

int main(void)
{
//do something
func();
// do something
return 0;
}

attribute((weakref(“target”))) 表明此函数应该链接名称为target的函数

extern void y(void);
static void x(void) attribute((weakref(“y”)));
void foo (void)
{

x();

}

以上函数中 foo 调用的是 y。

以上是用法的简单示例,编译器控制特性的使用,能够最大程度上优化代码,并具有极大的灵活性。

3.6. 内置指令

有些CPU的控制 C无法直接办到,需要使用内联汇编,但是以上这些内置指令直接实现为汇编,可以直接使用这些指令控制 CPU 的行为,例如 __wfe, __wfi可以控制 CPU 的休眠特性。

4. 常用编译器支持语言拓展

4.1. C89/90 下可以使用的 C99标准

  • 在 C89/90 语言标准下可以使用 // 注释
  • 可变参数宏
#define debug(format, …) fprintf (stderr, format, VA_ARGS)
void variadic_macros(void)
{
debug (“a test string is printed out along with %x %x %x\n, 12, 14, 20);
}
  • long long 与 unsigned long long, 和 C89/90 使用的 __int64 功能相同
  • restrict pointer

4.2. 标准C语言拓展

  • 常数表达式

例如:

static int y = c + 10;

在标准C语言上是不允许的, 但是编译器拓展允许这种方式。

  • void * 空指针可以和函数指针互相转换

在标准C语言里, void * 空指针只可以和结构体、联合体、变量、其他指针等互转,但是和函数指针的转换属于未定义行为,而Keil所做的编译器拓展,允许void * 和函数指针的互换。

  • register

制定变量存储于寄存器

void foo(void)
{
register int i;
int *j = &i;
}
  • 支持所有 GNU 对C语言的拓展

需要使用 - -gnu 编译选项

5. 链接器应用

5.1. 访问 section 的相关特性

extern char STACK 
         
          
           
           
             < 
            
           
             / 
            
           
             s 
            
           
             p 
            
           
             a 
            
           
             n 
            
           
             > 
            
           
             < 
            
           
             s 
            
           
             p 
            
           
             a 
            
           
             n 
            
           
             c 
            
           
             l 
            
           
             a 
            
           
             s 
            
           
             s 
            
           
             = 
            
           
             " 
            
           
             n 
            
           
             " 
            
           
             > 
            
           
             B 
            
           
             a 
            
           
             s 
            
           
             e 
            
           
             < 
            
           
             / 
            
           
             s 
            
           
             p 
            
           
             a 
            
           
             n 
            
           
             > 
            
           
             < 
            
           
             s 
            
           
             p 
            
           
             a 
            
           
             n 
            
           
             c 
            
           
             l 
            
           
             a 
            
           
             s 
            
           
             s 
            
           
             = 
            
           
             " 
            
           
             p 
            
           
             " 
            
           
             > 
            
           
             ; 
            
           
             < 
            
           
             / 
            
           
             s 
            
           
             p 
            
           
             a 
            
           
             n 
            
           
             > 
            
           
             < 
            
           
             s 
            
           
             p 
            
           
             a 
            
           
             n 
            
           
             c 
            
           
             l 
            
           
             a 
            
           
             s 
            
           
             s 
            
           
             = 
            
           
             " 
            
           
             k 
            
           
             " 
            
           
             > 
            
           
             e 
            
           
             x 
            
           
             t 
            
           
             e 
            
           
             r 
            
           
             n 
            
           
             < 
            
           
             / 
            
           
             s 
            
           
             p 
            
           
             a 
            
           
             n 
            
           
             > 
            
           
             < 
            
           
             s 
            
           
             p 
            
           
             a 
            
           
             n 
            
           
             c 
            
           
             l 
            
           
             a 
            
           
             s 
            
           
             s 
            
           
             = 
            
           
             " 
            
           
             k 
            
           
             t 
            
           
             " 
            
           
             > 
            
           
             c 
            
           
             h 
            
           
             a 
            
           
             r 
            
           
             < 
            
           
             / 
            
           
             s 
            
           
             p 
            
           
             a 
            
           
             n 
            
           
             > 
            
           
             < 
            
           
             s 
            
           
             p 
            
           
             a 
            
           
             n 
            
           
             c 
            
           
             l 
            
           
             a 
            
           
             s 
            
           
             s 
            
           
             = 
            
           
             " 
            
           
             n 
            
           
             " 
            
           
             > 
            
           
             S 
            
           
             T 
            
           
             A 
            
           
             C 
            
           
             K 
            
           
             < 
            
           
             / 
            
           
             s 
            
           
             p 
            
           
             a 
            
           
             n 
            
           
             > 
            
           
             < 
            
           
             s 
            
           
             p 
            
           
             a 
            
           
             n 
            
           
             c 
            
           
             l 
            
           
             a 
            
           
             s 
            
           
             s 
            
           
             = 
            
           
             " 
            
           
             e 
            
           
             r 
            
           
             r 
            
           
             " 
            
           
             > 
            
           
          
            </span><span class="n">Base</span><span class="p">;</span> <span class="k">extern</span> <span class="kt">char</span> <span class="n">STACK</span><span class="err"> 
           
          
        </span><spanclass="n">Base</span><spanclass="p">;</span><spanclass="k">extern</span><spanclass="kt">char</span><spanclass="n">STACK</span><spanclass="err">Length;
#define STACK_BASE &STACKKaTeX parse error: Expected 'EOF', got '#' at position 30: …pan class="cp">#̲define STACK_TO…Length))

5.2. 使用 S u p e r Super Super$ 和 S u b Sub Sub$ 打补丁

例如替换 foo() 函数:

extern void ExtraFunc(void);
extern void < / s p a n > < s p a n c l a s s = " n " > S u p e r < / s p a n > < s p a n c l a s s = " e r r " > </span><span class="n">Super</span><span class="err"> </span><spanclass="n">Super</span><spanclass="err">$foo(void):

/* this function is called instead of the original foo() */
void < / s p a n > < s p a n c l a s s = " n " > S u b < / s p a n > < s p a n c l a s s = " e r r " > </span><span class="n">Sub</span><span class="err"> </span><spanclass="n">Sub</span><spanclass="err">KaTeX parse error: Expected '}', got 'EOF' at end of input: …an class="err">SuperKaTeX parse error: Can't use function '$' in math mode at position 233: … * omit the $̲Superfoo(); function call.
*/
}

S u p p e r Supper Supper f o o < / e m > 指 代 原 来 的 函 数 < e m > foo</em> 指代原来的函数 <em> foo</em><em>Sub$$foo 用来替换的新函数,则链接器链接此函数取代原来的foo()函数。

以上为对KEIL平台的工具的基本认识,如果需要进一步学习,可以阅读KEIL的官方文档 ARM Product Manuals

Author : 王小军

Email :wcj.zju@hotmail.com

编辑于 2016-12-17
「真诚赞赏,手留余香」
赞赏

2 人已赞赏

赞赏用户 赞赏用户
ARM 编译器
C(编程语言)
嵌入式开发
赞同 95​ 12 条评论
分享
喜欢 ​ 收藏 ​ 申请转载
c++编译优化arm优化
jacke121的专栏
01-23 2653
c++编译优化arm优化
ARM NEON 编程系列9——ARM C语言编程优化策略(神文)
STN_LCD的专栏
08-19 2506
https://zhuanlan.zhihu.com/p/24402180 ARM C语言编程优化策略(KEIL平台) 王小军 8 个月前 ARM C语言编程优化策略 1. 内容介绍2. 优化实战 2.1. 编译器优化选项2.2. C循环优化2.3. 内联函数2.4. volatile 关键字的使用2.5. 纯净函数2.6. 数据对齐特性
keil(arm)中配置c99方法 及 C99特性
一无所知
06-26 3928
配置方法:option->c/c++->misc controls:--c99 附c99特性: 在ANSI的标准确立后,C语言的规范在一段时间内没有大的变动,然而C++在自己的标准化创建过程中继续发展壮大。《标准修正案一》在1994年为C语言创建了一个新标准,但是只修正了一些C89标准中的细节和增加更多更广的国际字符集支持。不过,这个标准引出了1999年ISO 9899:1999的发表。它通常被称为C99。C99被ANSI于2000年3月采用。 在C99中包括的特性有: 增加了对编译器的
ARM编译器5.06下载安装
最新发布
BO_S__的博客
02-16 1313
往下翻,找到如图位置的5.06 for windows的文件,点击下载,下载时需要登录账号。文件夹下新建一个文件夹,把编译器安装到新建的文件夹里面。先解压下载的压缩文件,在installer文件夹里面有一个。然后点确定,OK之类的。最后选择使用刚才安装的编译器。文件,双击它, 同意协议,在安装位置选择。打开keil,按照图示配置即可。
STM32 C++编程系列2.5:让Keil MDK工程支持现代C++特性及填坑
qq_31562655的博客
11-29 8359
一、问题背景 利用STM32CubeMX建立的Keil工程中,默认是使用AC5(Arm Compiler 5)编译器的,该编译器仅支持C99标准和C++98标准,无法支持现代C++C++11之后由于添加了大量现代编程语言特性而被称作现代C++),且AC5编译速度的慢也是有目共睹的,因此我们选用更新更强大的AC6(Arm Compiler 6)来给我们的开发(折腾)上一个新的台阶。毕竟C++11之后出现的大量新特性还是很香的,不在STM32这个平台上大显身手就可惜了。 比如强大的auto关键字: auto
ARM嵌入式编译器编译优化选项 -O
DayDayUp
04-25 5212
Arm嵌入式编译器可以执行一些优化来减少代码量并提高应用程序的性能。不同的优化级别有不同的优化目标,不仅如此,针对某个目标进行优化会对其他目标产生影响。比如想减小生成的代码量,势必会影响到该代码的性能。所以优化级别总是这些不同目标(代码量,程序性能,debug信息)之间的权衡。
ARM9编程C语言程序设计.ppt
11-12
ARM9编程C语言程序设计.ppt
arm嵌入式系统c语言编程
12-23
无操作系统支持的嵌入式系统软件 ,包括系统引导(BOOT) 、驱动程序、动态内存管理、IΠO、通信以及应用软件等方面。 本文详细介绍了嵌入式平台上用 C语言编写系统软件和应用软件的...关键词 嵌入式系统 软件 C语言 ARM
基于ARM的高效C语言编程.pdf
09-19
基于ARM的高效C语言编程.pdf
ARM高效C语言编程.pdf
09-05
ARM处理器以其高性能、低功耗、低成本等优势被广泛应用于各种成功的32位嵌入式系统中。提高执行速度和减小代码尺寸是嵌入式软件
基于ARM嵌入式系统的C语言编程初探-论文
05-18
基于ARM嵌入式系统的C语言编程初探
ARM嵌入式编译器-volatile关键字对编译器优化的影响
DayDayUp
05-07 4029
智能的(进行优化的)编译器可能会把变量的值临时储存在寄存器上,便于下次读取,以节约时间,这个过程被称为高速缓存。但是有一些agent在内存上改变了变量的值,寄存器上的还是旧数据,这样就出错了。如果被volatile 关键字修饰,编译器不会进行高速缓存,直接去内存中读取该变量的数据。编译器会优化什么将内存变量缓存到寄存器中。调整指令顺序,充分利用CPU指令流水线,进行指令重新排序读写指令。
armcc代码size优化
yazhouren的专栏
05-30 722
最初168kb -02后,为72Kb --thumb后,为54kb armcc.exe --feedback=feedback --split_sections和armlink.exe --feedback=feedback后,为40KB 代码减小比较明显 2019.5.30
编译器指令重排(Compiler Instruction Reordering)
carl361133244的专栏
03-21 3566
编译器指令重排(Compiler Instruction Reordering) compiler的主要工作就是将对人们可读的源码转化成机器语言,机器语言就是对CPU可读的代码。因此,compiler可以在背后做些不为人知的事情。我们考虑下面的C语言代码: int a, b; void foo(void) { a = b + 1; b = 0; } ...
ARM平台NEON指令的编译和优化
chenxiuli0810的专栏
07-11 1161
ARM平台NEON指令的编译和优化 2016年03月15日 22:56:17qiek阅读数 17652 https://blog.csdn.net/qiek/article/details/50900890 ARM平台NEON指令的编译和优化 本文介绍了ARM平台基于ARM v7-A架构的ARM Cortex-A系列处理器(Cortex-A5, Cortex-A7,Cortex...
ARM程序设计优化
jonsenwu的专栏
09-10 483
<br />程序优化是指软件编程结束后,利用软件开发工具对程序进行调整和改进,让程序充分利用资源, 提高运行效率, 缩减代码尺寸的过程。按照优化的侧重点不同, 程序ARM优化可分为运行速度优化和代码尺寸优化。运行速度优化是指在充分掌握软硬件特性的基础上, 通过<br />应用程序结构调整等手段来降低完成指定任务所需执行的指令数。在同一个处理器上, 经过速度优化的程序比未经优化的程序在完成指定任务时所需的时间更短,即前者比后者具有更高的运行效率。代码尺寸优化是指,采取措施使应用程序在能够正确完成所需功能的前提
arm64汇编篇-13编译器优化及寄存器与多线程问题
Clement_Gu的博客
06-11 1176
编译器模式  Debug Release不同模式下编译器的配置不同的,buildSettings -&gt; 搜索optimization 如下图: 以下几个模式是将编译器优化的几个方向。 编译器优化效果 1.将Debug选项的none 选择为跟Release 模式下的Fastest Smallest 2.去掉main函数的循环写入以下代码: int a = 1;...
ARM学习笔记二】去除未用到的函数,缩小可执行程序尺寸
briant_ccj的专栏
02-07 355
在嵌入式系统中经常会要求,可执行程序的尺寸尽可能的小 在代码中经常会有一些函数没有被用到,但是也会被编译到可执行文件中。 为了避免这种情况, 可以在编译时,添加选项:-ffunction-sections -fdata-sections 。该选项让每个函数和数据独自一段 在链接时,添加选项:-Wl,-gc-sections。该选项将去除无用的段。 即: CFLAGS 加上 -ffunction-sections -fdata-sections LDFLAGS 加上 --gc-sections
嵌入式ARM算法移植与优化
fuhanga123的博客
01-14 2774
目录 一、算法优化指导思想 1.算法优化基本原则 2.算法优化方法 二、编译器优化 1,函数内联 2,消除公共子表达式 3,循环展开 4,GCC优化选项 5,ARMCC优化选项 三、ARM内存系统优化 1,数据cache优化 2,循环分块 3,内部循环优化 4,结构对齐 5,综合相关性影响 6,优化指令cache的使用 7,优化L2与外部cache的使用 8,优化TLB使用 9,数据终止优化 10,预取一个内存块的访问 四、代码级优化 1,循环终止 2,循环融.
(1)掌握在keil MDK开发环境下进行ARM C语言编程的方法; (2)掌握LCD、AD、LED、按键等模块的检测与驱动方法。
05-27
好的,我可以为您提供相关的信息,帮助您掌握在Keil MDK开发环境下进行ARM C语言编程的方法,并了解LCD、AD、LED、按键等模块的检测与驱动方法。 1. 在Keil MDK开发环境下进行ARM C语言编程的方法 步骤如下: (1)打开Keil MDK软件,选择“Project”菜单,选择“New uVision Project”。 (2)在“New Project”对话框中,设置项目名称和存储路径,选择使用的芯片类型,并选择使用的编译器。 (3)创建完毕后,系统会自动打开“Project”窗口,可以在该窗口中添加源文件、头文件、链接器脚本等。 (4)在源文件中编写ARM C语言程序,如控制LED灯的亮灭,可以使用GPIO控制,如下代码所示: ```c #include "stm32f10x.h" int main(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能GPIO C端口时钟 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; //设置GPIO C13引脚 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置为50MHz GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化GPIO C端口 while (1) { GPIO_SetBits(GPIOC, GPIO_Pin_13); //设置GPIO C13引脚电平为高,点亮LED Delay(1000000); //延时 GPIO_ResetBits(GPIOC, GPIO_Pin_13); //设置GPIO C13引脚电平为低,熄灭LED Delay(1000000); //延时 } } void Delay(__IO uint32_t nCount) //延时函数 { while(nCount--) { } } ``` (5)编写完成后,进行编译、下载和调试操作。 2. LCD、AD、LED、按键等模块的检测与驱动方法 (1)LCD模块的检测与驱动方法 LCD模块需要使用相应的驱动芯片和驱动程序来控制,常见的驱动芯片有ST7735、SSD1289等。驱动程序可以使用官方提供的库函数,也可以自己编写。需要注意的是,在使用LCD模块时,需要设置相应的接口电平、时序等参数。 (2)AD模块的检测与驱动方法 AD模块通常需要使用ADC(模数转换器)进行检测。使用ADC时,需要设置相应的采样速率、精度等参数,以及使用DMA(直接存储访问)进行数据传输。使用ADC时还需要注意电源和接口电平等问题。 (3)LED模块的检测与驱动方法 LED模块通常使用GPIO控制。在使用LED模块时,需要设置相应的GPIO引脚、电平等参数,以及使用延时函数进行控制。需要注意的是,不同型号的开发板和芯片,GPIO的引脚编号和控制方式可能会有所不同。 (4)按键模块的检测与驱动方法 按键模块通常使用GPIO进行检测。在使用按键模块时,需要设置相应的GPIO引脚、电平等参数,以及使用中断或轮询方式进行检测。需要注意的是,不同型号的开发板和芯片,GPIO的引脚编号和控制方式可能会有所不同。同时,还需要注意消抖操作,以避免误触发按键。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • BLDC基础概念之机械角和电角度 13266
  • 常见滤波器简要介绍及Matlab实现 9054
  • AES加密算法详解 8167
  • adrc离散系统最速控制综合函数的理解 4652
  • 指令隔离DMB,DSB,ISB 3272

分类专栏

  • 加密解密 1篇
  • qpc 1篇
  • 选型 1篇
  • MCU 8篇
  • 运动控制 3篇
  • BLDC 1篇
  • 程序员的自我修养
  • uCOS III 1篇

最新评论

  • adrc离散系统最速控制综合函数的理解

    I_am_???: 我觉得你写得很棒

  • AES加密算法详解

    CSDN-Ada助手: 非常感谢CSDN博主的精彩分享,这篇博客对于了解AES加密算法的人来说非常有价值。我觉得博主可以考虑写一篇关于RSA加密算法的博客,讲解其原理、应用场景以及实现方式,这样的技术文章对于想要深入了解加密算法的读者会非常有帮助。下一篇你可以继续就RSA加密算法继续写,相信会有更多读者受益。期待更多精彩的技术分享! 为了方便博主创作,提高生产力,CSDN上线了AI写作助手功能,就在创作编辑器右侧哦~(https://mp.csdn.net/edit?utm_source=blog_comment_recall )诚邀您来加入测评,到此(https://activity.csdn.net/creatActivity?id=10450&utm_source=blog_comment_recall)发布测评文章即可获得「话题勋章」,同时还有机会拿定制奖牌。

  • BLDC基础概念之机械角和电角度

    muyepiao1: 第一次没看懂,第二次有点懂。

  • BLDC基础概念之机械角和电角度

    知信君: cos90 不是0吗?

  • 常见滤波器简要介绍及Matlab实现

    liuruoc: 很多图片和代码都看不到了

您愿意向朋友推荐“博客详情页”吗?

  • 强烈不推荐
  • 不推荐
  • 一般般
  • 推荐
  • 强烈推荐
提交

最新文章

  • AES加密算法详解
  • qpc之qk调度学习笔记
  • 传感器选型的六大原则
2021年1篇
2020年16篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

深圳SEO优化公司常州网站seo优化黄冈阿里店铺托管推荐合肥至尊标王报价南昌优秀网站设计价格广安优化多少钱海南建网站东营外贸网站制作报价爱联阿里店铺托管哪家好垦利外贸网站设计哪家好海南网站制作多少钱铜川营销网站哪家好张北网站开发哪家好坂田阿里店铺运营大浪模板推广多少钱防城港百度竞价报价许昌设计公司网站价格广东企业网站设计价格甘南阿里店铺托管报价爱联网站制作公司铁岭优化推荐保定网站优化多少钱舟山品牌网站设计报价百色网站定制哪家好上海百度标王多少钱清远网站优化按天计费推荐石岩建网站推荐商洛网站改版哪家好忻州网页设计价格吴忠网站设计模板报价荷坳SEO按天扣费价格歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化