一、GUN工具链常用工具说明及Makefile编写

1.1 GUN工具链常用工具

 gcc (GNU Compiler Collection)
功能:编译器(Compiler)

  • 将C/C++等高级语言源代码转换为汇编代码
  • 再将汇编代码转换为目标文件,生成.o文件
  • 支持多种编程语言和目标平台
  • 提供丰富的优化选项和编译控制选项

eg:arm-linux-gnueabihf-gcc -c -g -o start.o start.S 

 ld (GNU Linker)
功能:链接器(Linker)

  • 将多个目标文件链接成可执行文件,生成.elf文件
  • 解析和处理符号引用
  • 进行地址分配和重定位
  • 处理库文件的链接 

eg:arm-linux-gnueabihf-ld -Ttext 0x87800000 -ostart.elf start.o

objcopy (Binary File Converter)
功能:目标文件格式转换器

  • 在不同格式的目标文件之间进行转换
  • 将ELF格式转换为纯二进制格式(.bin)
  • 从目标文件中提取特定的段(sections)
  • 修改目标文件的内容
  • 常用于嵌入式开发中的固件生成 

eg:arm-linux-gnueabihf-objcopy -O binary -S -g start.elf start.bin

objdump (Object File Disassembler)
功能:目标文件分析器/反汇编器

  • 将机器码反汇编为汇编代码 
  • 查看目标文件的详细信息
  • 分析程序的执行指令
  • 显示各种文件格式信息

eg: arm-linux-gnueabihf-objdump -D start.elf > start.dis

 1.2 编写Makefile文件(方便后期使用)

优化1:$@指的是目标        $^指的是源        内部自动寻找,并产生

优化2:

clean:

    rm *.o $(name).o $(name).elf $(name).bin $(name).dis -rf

clean——用来删除编译过程中产生的中间产物:

-rf————“-r”表示删除文件夹,,,“-f”如果删除过程中没有这个文件,则不会报错

1.3 编写C代码

使用CPS指令,在汇编start.S中设置irq寄存器,system寄存器中的栈存储地址;

volatile (易变的、多变的):禁止编译器进行优化,必须根据指针间接访问所指向的地址;

功能引脚:

方向寄存器:

数据寄存器:

代码展示:

 二、SDK(软件开发工具)

  • 直接使用定义好的相关寄存器,节省时间

2.1 SDK使用原理

    寄存器地址是连续的,利用结构体装载内容,并宏定义该结构体的首地址;

2.1 SDK代码的使用

 

main.c
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"

void enable_all_clocks(void)
{
    CCM->CCGR0 = 0xFFFFFFFF;
    CCM->CCGR1 = 0xFFFFFFFF;
    CCM->CCGR2 = 0xFFFFFFFF;
    CCM->CCGR3 = 0xFFFFFFFF;
    CCM->CCGR4 = 0xFFFFFFFF;
    CCM->CCGR5 = 0xFFFFFFFF;
    CCM->CCGR6 = 0xFFFFFFFF;

}

void delay(unsigned int n)
{
    while(n--);
}

int main(void)
{
    enable_all_clocks();
    init_led();
    while(1)
    {
        led_nor();
        delay(0xFFFFF);
    }

}

led.c

#include"MCIMX6Y2.h"
#include"fsl_iomuxc.h"
#include "led.h"

void init_led(void)
{
    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03,0);
    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03,0X10B0);


    GPIO1->GDIR |= (1 << 3);
    GPIO1->DR &= ~(1 << 3);
}

void led_on(void)
{
    // GPIO1_DR &= ~(1 << 3); 
    GPIO1->DR &= ~(1 << 3); 
}

void led_off(void)
{
    // GPIO1_DR |= (1 << 3);
    GPIO1->DR |= (1 << 3);
}

void led_nor(void)
{
    // GPIO1_DR ^= (1 << 3);
    GPIO1->DR ^= (1 << 3);
}

三、链接脚本

3.1 内存段介绍

1. .bss段

用于存放未初始化或初始化为0的数据
在运行时会被自动清零


- 典型例子:
static int bss_var;           // 静态变量(全局或局部)
int explicit_var = 0;         // 显式初始化为0的全局变量 

2.COMMON段

用于存放未初始化的非静态全局变量
链接时才确定最终大小和位置
允许多个目标文件定义同名符号


- 典型例子:
int common_var;               // 未初始化的非静态全局变量 

3. .data段

用于存放已初始化的全局变量和静态变量(非零值)
需要在程序文件中保存实际的初始值


- 典型例子:
int global_var = 100;         // 初始化为非零值
int global_array[] = {1,2,3}; // 初始化为非零数组 

4. .rodata段(只读数据段)

- 存放程序的只读数据
- 运行时受保护,不可修改
- 包含的数据类型:
  1. 字符串常量
  2. const修饰的全局变量
  3. 全局只读数组
  4. switch跳转表
  5. 浮点数常量


- 典型例子:
const int MAX_VALUE = 100;    // const全局变量
char* str = "Hello World";    // 字符串常量
const int lookup[] = {1,2,3}; // 只读数组 

5. .text段(代码段) 

## 各段的特点比较

|         段名        |        初始化     |    运行时可写 |         链接特性 |

|--------------------|-------------------|--------------------|--------------------|

| .bss    |                 自动清零 |          可写 |         编译时确定大小 |

| COMMON  |         自动清零 |          可写 |        链接时确定大小 |

| .data   |               需要初始值 |        可写 |        编译时确定大小 |

| .rodata |             需要初始值 |          只读 |        编译时确定大小 |

3.2 链接脚本(不同的段放在哪里) 

  • 按四字节对齐//ALIGN(4)

 

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐