当前位置: 首页 > news >正文

ARM知识点三和串口代码的编写流程

ARM的一些常见问题

  1. ARM 体系结构的主要特点是什么?
  • 精简指令集 (RISC):ARM 采用 RISC 结构,指令集较小且简单,执行效率高。相比于复杂指令集 (CISC),RISC 更强调每条指令的执行速度。
  • 低功耗设计:ARM 处理器通过简化指令集和减少每条指令的处理步骤,达到了低功耗的效果,适用于移动设备和嵌入式系统。
  • Thumb 和 Thumb-2 指令集:ARM 支持 16 位 Thumb 指令集,它能够提高代码密度,特别适合对内存有限的嵌入式系统。
  • 多核和对称多处理 (SMP):现代 ARM 处理器支持多核架构,并且多核处理器可以并行处理多个任务。
  • 向量浮点单元 (VFP) 和 NEON:ARM 处理器通常集成 VFP(向量浮点运算单元)和 NEON 技术来加速多媒体和信号处理任务。
  1. ARM 的中断处理机制是怎样的?
    ARM 的中断机制是通过中断向量表来处理的,不同的中断类型有不同的优先级。主要的中断处理流程如下:
  • 中断请求 (IRQ):常规外设或定时器产生的中断请求。
  • 快速中断 (FIQ):高优先级的中断请求,常用于紧急任务处理。FIQ 有独立的寄存器组来加快中断响应速度。
  • 中断向量表:中断向量表存储在一个固定的地址(如地址 0x00000000 或 0xFFFF0000),每种中断类型有一个固定的入口地址,处理器通过跳转到相应的中断向量表位置来响应中断。
  • 嵌套中断:在 ARM 架构中,通过管理中断屏蔽和优先级,可以支持嵌套中断,即在处理中断的过程中可以响应更高优先级的中断。
  1. ARM Cortex-M 系列的主要特点是什么?
    ARM Cortex-M 系列是专为嵌入式系统设计的,主要特点包括:
  • 基于 ARMv7-M 或 ARMv8-M 指令集。
  • 低功耗,非常适合电池供电的嵌入式设备。
  • 支持快速中断响应,通过 NVIC(嵌套向量中断控制器)实现高效的中断管理,最多支持 240 个中断通道。
  • 内置调试支持:如 DWT(数据观察点和跟踪)和 ITM(指令跟踪宏单元)。
  • 浮点运算单元 (FPU):Cortex-M4 和 Cortex-M7 支持硬件浮点运算,适合需要大量数值计算的应用。
  1. ARM 处理器的总线接口有哪些?
    ARM 处理器主要使用以下总线接口技术:
  • AMBA (Advanced Microcontroller Bus Architecture):AMBA 是 ARM 提出的总线协议,常用的总线类型包括:
  • AHB (Advanced High-performance Bus):主要用于高性能外围设备和高速数据传输。
  • APB (Advanced Peripheral Bus):用于低速外围设备,如 GPIO、串口等。
  • AXI (Advanced eXtensible Interface):用于高性能内存和外设之间的数据传输,支持并行和流水线操作。
  1. 如何在 ARM 体系结构中实现内存映射I/O?
    内存映射I/O 是 ARM 处理器中外设与内存地址空间共享的一种机制,具体步骤如下:
  • 内存映射:每个外设都分配有固定的内存地址,处理器可以通过读写这些内存地址来控制外设。
  • 地址分配:不同外设的地址范围通常在芯片手册中指定。例如,GPIO 的控制寄存器可能映射到一个特定的地址段。
  • 访问外设:通过普通的 ldr 和 str 指令,ARM 处理器可以访问这些内存映射的外设。
  1. ARM中的异常处理机制是什么?
    ARM 处理器的异常处理机制与中断类似,但处理的是软件错误或其他需要特殊处理的事件。常见的异常包括:
  • 复位异常 (Reset):设备复位时触发。
  • 未定义指令异常 (Undefined Instruction):执行未定义的指令时触发。
  • 数据预取异常 (Data Abort/Prefetch Abort):发生内存访问错误或非法地址访问时触发。
  • 软件中断 (SWI):用于用户态调用内核服务(类似于系统调用)。
  1. ARM 的虚拟内存管理是如何实现的?
    ARM 处理器支持虚拟内存管理,通过以下方式实现:
  • MMU (Memory Management Unit):用于将虚拟地址转换为物理地址,并提供访问控制。
  • 分页机制:ARM 使用分页来管理内存,将内存划分为固定大小的页,虚拟地址通过页表映射到物理地- 址。
  • TLB (Translation Lookaside Buffer):TLB 用于缓存最近使用的页表项,减少内存访问时的转换开销。

安装交叉编译工具

  1. 拷贝 gcc-4.6.4.tar.xz 到 Linux 环境,并解压
tar -xvf gcc-4.6.4.tar.xz
  1. 将整个 GCC 目录安装到系统
    • 方法:将 gcc-4.6.4/bin 目录添加到 PATH 环境变量
    • 修改 ~/.bashrc 文件,添加以下内容,在文件底部添加
    export PATH=$PATH:/home/lsf/source/gcc-4.6.4/bin
    
    • source ~/.bashrc 使修改立即生效
  2. 测试安装
    终端中输入 arm-none- 并使用 Tab 键自动补齐,检查工具是否可用
  3. 注意
    • 若是 64 位系统,可能需要安装 32 位支持库
sudo apt-get install lib32ncurses5 lib32z1

接口技术

  • CPU: 狭义上指的是 核, 广义上指的是 整个芯片
    • 狭义上的 CPU:指处理器的 核心 (core) 部分,负责执行指令、处理数据和控制计算过程。它包括了运算逻辑单元 (ALU)、寄存器、控制单元等,核心的任务是执行软件的指令集。
    • 广义上的 CPU:可以包括整个芯片,除了核心以外还包括缓存 (cache)、总线控制器、内存管理单元 (MMU) 等部件。它不仅仅是执行指令的逻辑单元,还包括一些周边的控制逻辑和接口。
  • SOC : system on chip 片上系统, 核+接口
    • SoC 可以看作是一个集成了 CPU、接口以及其他外设的单芯片系统,适合于嵌入式系统和移动设备应用
  • 裸机编程: 没有操作系统的编程,比如 单片机
    • STM32:基于 ARM Cortex-M 核心,广泛应用于工业控制和消费电子。
    • 51 单片机:经典的8位单片机,适合学习与小型应用。

裸机编程示例图

在这里插入图片描述
在这里插入图片描述
- 核心流程:
ldr 指令:CPU 使用 ldr(load register)指令,从存储器或外设的寄存器读取数据到寄存器。
str 指令:CPU 使用 str(store register)指令,将寄存器的数据写回存储器或外设的寄存器中。
- 硬件,分为两部分 核心板+外设板
- 核心板:能让CPU正常运行的最小系统,主要组件:CPU,flash,RAM,电源,串口,晶振
- 外设板: 外设板通常包含了许多外部设备,这些设备通过各种接口与核心板的 CPU 连接,用于扩展系统的功能。例如:扬声器、SD卡接口、ADC、HDMI
- 核心板和外设板通过接口(如 GPIO、I2C、SPI、UART 等)进行连接和通信。

操作硬件一般步骤(以 LED 为例)

  1. 从电路板上找到该硬件
    • 找到你想控制的硬件设备。例如:LED2
    • 记下该设备的标号(如 LED2),方便后续在电路图和芯片手册中查找相关信息
  2. 打开电路图,找到该硬件的连接信息
    • 打开硬件的 电路原理图,找到 LED2 相关的部分
    • 在电路图中查找 LED2 的位置,并查看该 LED 是如何连接到 CPU 的管脚上。例如,你可能会发现 LED2 连接到 CPU 的 GPX2_7 引脚
  3. 阅读芯片手册 (datasheet),找到相关控制寄存器
    • 打开对应的 芯片手册 (datasheet),找到涉及控制 GPX2_7 引脚的章节,通常是在 GPIO(通用输入输出)控制器章节中
    • GPX2_7 表示 GPIO X 组的第 7 个引脚,你需要找到该引脚的寄存器配置方式
    寄存器的相关配置:
    GPX2CON(引脚配置寄存器):配置该引脚的模式(输入/输出)。
    寄存器地址:0x11000C40。
    操作说明:设置 [31:28] 位为 0001,表示将 GPX2_7 设置为输出模式。
    GPX2DAT(数据寄存器):用于控制引脚的高低电平(控制开/关)。
    寄存器地址:0x11000C44。
    操作说明:[7] 位控制 GPX2_7 的输出状态。设置该位为 1 则为高电平(LED 点亮),设置为 0 则为低电平(LED 熄灭)。
    
    • 编写代码,进行编程和测试
      • 代码在start.s中写,通过Makefile文件编译,取bin文件烧录进去开发板即可
      • 在这里插入图片描述
.text
@@@ 通过 ARM 裸机编程直接控制硬件,具体任务是通过按键控制 LED 的亮灭,即当按键按下时点亮 LED,松开按键时熄灭 LED。
@@ 配置 GPX2_7 为输出模式
ldr r0,=0x11000c40        @ 加载 GPX2CON 地址
ldr r2,[r0]               @ 读取 GPX2CON 内容到 r2
bic r2,r2,#0xF0000000     @ 清除 GPX2_7 的控制位
orr r2,r2,#0x10000000     @ 设置 GPX2_7 为输出模式 (0001)
str r2,[r0]               @ 将修改后的值写回 GPX2CON@@ 配置 K2 为输入模式 (GPX1_1)
ldr r0,=0x11000c20        @ 加载 GPX1CON 地址
ldr r2,[r0]               @ 读取 GPX1CON 内容到 r2
bic r2,r2,#0xF0           @ 清除 GPX1_1 的控制位
str r2,[r0]               @ 直接清除后默认是输入模式 (0000)loop:ldr r0,=0x11000c24    @ 加载 GPX1DAT 地址 (检测按键输入)ldr r2,[r0]           @ 读取 GPX1DATand r2,r2,#2          @ 检查 GPX1_1 状态,检查按键是否按下cmp r2,#0bleq  light_led_fun    @ 如果按下按键,调用点亮 LED 函数blne  off_led_fun      @ 如果没有按下,调用熄灭 LED 函数bl sdelay             @ 延时b loop                @ 继续循环@@ 点亮 LED
light_led_fun:ldr r0,=0x11000c44    @ 加载 GPX2DAT 地址ldr r2,[r0]           @ 读取 GPX2DATorr r2,r2,#0x80       @ 设置 GPX2_7 为高电平,点亮 LEDstr r2,[r0]           @ 写回 GPX2DATmov pc,lr             @ 返回@@ 熄灭 LED
off_led_fun:ldr r0,=0x11000c44    @ 加载 GPX2DAT 地址ldr r2,[r0]           @ 读取 GPX2DATbic r2,r2,#0x80       @ 清除 GPX2_7 高电平,熄灭 LEDstr r2,[r0]           @ 写回 GPX2DATmov pc,lr             @ 返回@@ 延时函数
sdelay:ldr r10,=0xFFFF       @ 加载一个较大的值,用于延时
delay_loop:sub r10,r10,#1        @ 自减cmp r10,#0            @ 检查是否达到 0bgt delay_loop        @ 如果没有到 0,继续延时mov pc,lr             @ 返回
.end

-

GPIO章节

  1. 概述
  • GPIO(General Purpose Input/Output,通用输入输出端口) 是嵌入式系统中用于控制和管理芯片外部管脚的模块。
  • GPIO 模块允许用户根据需要将芯片的管脚配置为输入或输出,并通过这些管脚与外部设备进行交互(例如,控制 LED、读取传感器信号等)。
  • 多功能引脚:GPIO 引脚可以配置为多个功能,不仅限于通用输入输出,还可以配置为串口、USB、I2C、SPI 等特定功能。
  1. GPIO 管脚的分组管理
  • 为了有效管理芯片上众多管脚,GPIO 管脚通常按组分类,不同组的 GPIO 管脚通过寄存器进行独立控制。例如:
    GPA:组 A 管脚,如 GPA_0, GPA_1。
    GPB:组 B 管脚。
    GPX1, GPX2:组 X 的第 1 和第 2 组管脚。
    GPD:组 D 管脚,依次类推。
  • 每组 GPIO 通常包含 16 个引脚,可以独立配置为输入或输出,每组都有自己的控制寄存器。
  1. GPIO 的基本功能
  • 输出功能
  • 输入功能
  1. GPIO的其他功能
  • 串口(UART):用于串行通信。
  • USB 接口:用于外设通信。
  • I2C/SPI 接口:用于与传感器或其他外部设备的同步通信。
  • 中断引脚:部分 GPIO 可以配置为中断引脚,当管脚电平变化时触发中断信号,通知处理器做出响应。
  1. 使用示例在资源文件里面,开发板为fs4412

串口通信

串口(Serial)是一种常见的通信方式,它通过串行传输数据,即一次一位地发送或接收数据。相比之下,并口(Parallel)一次可以发送多个数据位,但由于并口需要更多的引脚,所以在嵌入式系统中较少使用。

基本概念

  • 串口控制器:负责管理串口通信的硬件模块,控制数据的发送与接收。
  • 串行通信:数据按位顺序依次传输,通常用于设备之间的数据交换,常见于嵌入式系统、传感器等应用。
  • 并行通信:数据一次性发送多位数据,虽然速度快,但需要更多的引脚和布线,较少在现代嵌入式系统中使用。
缺点
  • 速度较慢:相比并行通信,串行通信速度较慢,因为它一次只能传输一位数据。
  • 数据量较小:串口通信一次发送的数据量较小,通常是字节级别的数据。
  • 误码率问题:由于信号在传输过程中可能受到噪声、干扰等影响,导致数据误码。
  • 同步问题:串行通信需要发送方和接收方的时钟保持同步,如果不同步,可能会导致数据丢失。

串口通信代码编写流程

  1. 找到板子上的串口硬件
    • 在电路板上找到串口接口硬件,例如 COM2 或 CON7,这些端口通常连接到处理器的串行通信接口上,用于发送和接收数据
  2. 在电路图中查找对应的 GPIO 引脚
    • 根据硬件端口的编号(如 COM2/CON7),打开电路原理图,查找该端口连接到芯片的哪些 GPIO 管脚。
      例如:在电路图中查找到 COM2 连接到芯片的 GPA1_0 和 GPA1_1 引脚。
  3. 将 GPIO 配置为串口模式
    • 打开芯片的 datasheet(数据手册),查找 GPIO 章节,找到对应的管脚配置寄存器,并将这些 GPIO 管脚配置为串口模式。
    • 例如:对于 GPA1_0 和 GPA1_1,通过配置寄存器将其设置为串口的 TX(发送)和 RX(接收)引脚。
GPA1CON = 0x11400020;        // GPA1CON 寄存器地址
GPA1CON &= ~(0xFF);          // 清除 GPA1_0 和 GPA1_1 的原有配置
GPA1CON |= (0x22);           // 配置 GPA1_0 和 GPA1_1 为串口模式 (0x2)
  1. 配置串口寄存器
    • 在 UART 串口章节 中,找到串口控制器的相关寄存器,配置波特率、停止位、数据位等参数。
    • 配置串口寄存器的步骤
      • ULCON2:配置数据宽度、停止位、校验方式。
      • UCON2:配置接收和发送的模式(轮询模式或中断模式)。
      • UTRSTAT2:用于检测发送和接收状态,判断是否可以继续发送或接收数据。
      • UTXH2 和 URXH2:发送和接收数据的寄存器。
      • UBRDIV2 和 UFRACVAL2:配置波特率。
  2. 波特率计算
    • 根据波特率和系统时钟(SCLK_UART),需要配置波特率寄存器 UBRDIV2 和 UFRACVAL2。
    • 波特率公式
      D I V _ V A L = ( S C L K _ U A R T / ( 波特率 × 16 ) ) – 1 DIV\_VAL = (SCLK\_UART / (波特率 × 16)) – 1 DIV_VAL=(SCLK_UART/(波特率×16))–1
      例如:波特率为 115200 bps,SCLK_UART 为 100 MHz
DIV_VAL = (100000000 / (115200 × 16)) - 1 = 54.253 - 1 = 53.253
UBRDIVn = 53  // 整数部分
UFRACVALn = 4 // 小数部分(0.253 × 16 = 4)
串口与PC通信代码的编写
  1. 编写main.c
//嵌入式系统中发送 "hello world" 字符串
/*## GPIO章节  中将管脚配置为串口模式 GPA1CON  0x11400020		[3:0]=0x2   [7:4]=0x2## UART串口章节, 配置寄存器			波特率 停止位 数据值 校验位   ..... ULCON2	0x13820000		[1:0] = 0x3		8bit data width[2] = 0			1 stop bit[5:3]=000    	no parity[6] = 0			no IRUCON2	0x13820004		[1:0]=0x01		poll for recv [3:2]=0x01		poll for sendUTRSTAT2	0x13820010	x=[0]   1,接收buf有了数据   0-nodatax=[1]   ,1 可以继续发送了	0-数据还在发送中UTXH2	0x13820020		[7:0]=data;URXH2	0x13820024		data=[7:0];UBRDIV2		0x13820028		53UFRACVAL2 	0x1382002C		4
*/// GPIO 和 UART 寄存器的地址定义
#define	GPA1CON  	*(volatile long*)0x11400020	// GPA1CON GPIO 引脚控制寄存器
#define	ULCON2		*(volatile long*)0x13820000	// ULCON2 串口控制寄存器,设置数据宽度、校验位等
#define	UCON2		*(volatile long*)0x13820004	// UCON2 串口控制寄存器,设置发送和接收模式
#define	UTRSTAT2	*(volatile long*)0x13820010	// UTRSTAT2 串口状态寄存器,检查是否可以发送和接收
#define	UTXH2		*(volatile long*)0x13820020	// UTXH2 串口发送数据寄存器
#define	URXH2		*(volatile long*)0x13820024	// URXH2 串口接收数据寄存器#define	UBRDIV2		*(volatile long*)0x13820028	// 波特率整数部分寄存器
#define	UFRACVAL2 	*(volatile long*)0x1382002C	// 波特率小数部分寄存器// 初始化 UART 串口
void uart_init(void)
{// 配置 GPA1_0 和 GPA1_1 为串口模式GPA1CON = GPA1CON & ~0xF;        // 清除 GPA1_0 的原有配置GPA1CON = GPA1CON | (1<<1);      // 设置 GPA1_0 为 UART TX (串口发送)GPA1CON = GPA1CON & ~(0xF<<4);   // 清除 GPA1_1 的原有配置GPA1CON = GPA1CON | (1<<5);      // 设置 GPA1_1 为 UART RX (串口接收)// 配置 ULCON2,设置为 8 位数据宽度、1 位停止位,无校验ULCON2 = ULCON2 | 0x3;           // 设置数据宽度为 8 位ULCON2 = ULCON2 & ~(1<<2);       // 设置 1 个停止位ULCON2 = ULCON2 &  ~(7<<3);      // 禁用校验位 (no parity)ULCON2 = ULCON2 &  ~(1<<6);      // 禁用红外模式 (no IR)// 配置 UCON2,设置为轮询模式UCON2 &= ~(3);                   // 清除原有配置UCON2 |= 1<<0;                   // 设置为轮询接收模式UCON2 &= ~(3<<2);                // 清除原有发送配置UCON2 |= 1<<2;                   // 设置为轮询发送模式// 设置波特率为 115200UBRDIV2 = 53;                    // 设置波特率的整数部分UFRACVAL2 = 4;                   // 设置波特率的小数部分
}// 发送单个字符
void putc(char ch)
{// 等待直到发送缓冲区为空while(  ( UTRSTAT2 & (1<<1)  ) == 0 );// 向发送寄存器中写入数据UTXH2 = ch;
}// 发送字符串
void puts(char *s)
{int i = 0;// 循环发送字符串中的每个字符while(s[i]) {putc(s[i]);  // 发送单个字符i++;}
}// 简单的延时函数,延时 ms 毫秒
void mysleep(int ms)
{while(ms--) {int num = 0x1FFF/2;  // 简单的延时循环while(num--);}
}// 主函数
void main(void)
{uart_init();  // 初始化 UART/*在 Linux 系统中,\n 表示回车和换行在 Windows 系统中,\n 表示换行,\r 表示回到行首*/while(1) {puts("hello world\r\n");  // 通过串口发送 "hello world" 字符串并换行mysleep(200);             // 延时 200 毫秒}return;
}
  1. 编写start.S
  • 目的是为嵌入式系统中的主程序(如串口代码)提供一个基本的运行环境。
  • 启动代码的作用是对处理器和堆栈等进行初始化,并最终跳转到主程序的入口点。
.global  delay1s                @ 全局定义 delay1s 延时函数
.text                           @ 定义文本段(代码段)
.global _start                  @ 全局定义 _start 入口函数_start:                         @ 程序入口点,程序从此开始执行b      reset                @ 跳转到 reset 处(复位处理)ldr    pc, _undefined_instruction  @ 加载 undefined_instruction 异常处理器的地址ldr    pc, _software_interrupt    @ 加载 software_interrupt 异常处理器的地址ldr    pc, _prefetch_abort        @ 加载 prefetch_abort 异常处理器的地址ldr    pc, _data_abort            @ 加载 data_abort 异常处理器的地址ldr    pc, _not_used              @ 加载未使用异常的处理器地址ldr    pc, _irq                   @ 加载中断请求 (IRQ) 异常处理器的地址ldr    pc, _fiq                   @ 加载快速中断 (FIQ) 异常处理器的地址_undefined_instruction: .word _undefined_instruction  @ 定义未定义指令异常处理器的地址
_software_interrupt:    .word _software_interrupt     @ 定义软件中断异常处理器的地址
_prefetch_abort:        .word _prefetch_abort         @ 定义指令预取异常处理器的地址
_data_abort:            .word _data_abort             @ 定义数据访问异常处理器的地址
_not_used:              .word _not_used               @ 定义未使用异常处理器的地址
_irq:                   .word _irq                    @ 定义 IRQ 中断异常处理器的地址
_fiq:                   .word _fiq                    @ 定义 FIQ 快速中断异常处理器的地址reset:ldr    r0,=0x40008000        @ 设置异常向量表基地址为 0x40008000mcr    p15, 0, r0, c12, c0, 0  @ 写入 Vector Base Address Register (VBAR),重新定位异常向量表init_stack:ldr    r0,=stacktop          @ 将栈顶地址加载到 r0/******** svc 模式栈 ********/mov    sp,r0                 @ 将栈顶指针加载到 sp,svc 模式的栈sub    r0,#128*4             @ 减少 512 字节空间用于 IRQ 模式栈/******** irq 模式栈 ********/msr    cpsr,#0xd2            @ 切换到 IRQ 模式(CPSR 位设置)mov    sp,r0                 @ 设置 IRQ 模式的栈指针sub    r0,#128*4             @ 减少 512 字节空间用于 FIQ 模式栈/******** fiq 模式栈 ********/msr    cpsr,#0xd1            @ 切换到 FIQ 模式mov    sp,r0                 @ 设置 FIQ 模式的栈指针sub    r0,#0                 @ FIQ 模式使用 0 字节的栈空间/******** abort 模式栈 ********/msr    cpsr,#0xd7            @ 切换到 Abort 模式mov    sp,r0                 @ 设置 Abort 模式的栈指针sub    r0,#0                 @ Abort 模式使用 0 字节的栈空间/******** undefined 模式栈 ********/msr    cpsr,#0xdb            @ 切换到 Undefined 模式mov    sp,r0                 @ 设置 Undefined 模式的栈指针sub    r0,#0                 @ Undefined 模式使用 0 字节的栈空间/******** sys 和 usr 模式栈 ********/msr    cpsr,#0x10            @ 切换到用户模式 (SYS/USR)mov    sp,r0                 @ 设置用户模式的栈指针@ 为用户模式分配 1024 字节的栈空间b      main                  @ 跳转到 main 函数,进入主程序delay1s:                         @ 定义 1 秒延时函数ldr    r4,=0x1ffffff         @ 将一个大数加载到 r4,用作计数器
delay1s_loop:sub    r4,r4,#1              @ 每次循环 r4 减 1cmp    r4,#0                 @ 检查 r4 是否为 0bne    delay1s_loop          @ 如果 r4 不为 0,继续循环mov    pc,lr                 @ 返回调用函数.align 4                         @ 地址对齐/****  swi_interrupt handler  ****/ 
@ 异常处理中断的处理器可以在此处实现.data                            @ 定义数据段stack:.space 4*512                 @ 为栈分配 512 字节的空间
stacktop:.word stack+4*512            @ 定义栈顶指针的初始位置.end                             @ 程序结束标志
  1. 编写 Makefile
    定义编译链接规则和清除不必要的东西
    用于编译 ARM 裸机程序。它使用了 arm-none-linux-gnueabi 工具链来编译汇编和 C 代码,链接生成 ELF 可执行文件,并生成二进制文件。
# 目标名称 all 是默认目标,当执行 'make' 时会首先执行该目标中的指令
all:# 使用 arm-none-linux-gnueabi-gcc 编译 start.S 文件为目标文件 start.o# -fno-builtin: 禁用 GCC 的内置函数,避免使用标准库函数# -nostdinc: 不使用标准头文件,适合裸机编程# -c: 仅编译为目标文件,不进行链接arm-none-linux-gnueabi-gcc -fno-builtin -nostdinc -c -o start.o start.S# 使用 arm-none-linux-gnueabi-gcc 编译 main.c 文件为目标文件 main.o# 这里同样不使用内置函数和标准头文件,适合裸机编程arm-none-linux-gnueabi-gcc -fno-builtin -nostdinc -c -o main.o main.c# 链接 start.o 和 main.o 目标文件,生成 main.elf 可执行文件# -Tmap.lds: 使用自定义的链接脚本 map.lds 定义内存布局# main.elf: 最终生成的可执行文件arm-none-linux-gnueabi-ld start.o main.o -Tmap.lds -o main.elf# 使用 arm-none-linux-gnueabi-objcopy 将 ELF 文件转换为纯二进制文件 main.bin# -O binary: 指定输出格式为二进制文件arm-none-linux-gnueabi-objcopy -O binary main.elf main.bin# 使用 arm-none-linux-gnueabi-objdump 反汇编 ELF 文件,生成汇编代码文件 main.dis# -D: 反汇编整个 ELF 文件# 反汇编的输出被保存到 main.dis 文件中,便于阅读arm-none-linux-gnueabi-objdump -D main.elf > main.dis# clean 目标用于清理生成的中间文件和最终文件
clean:# 删除所有备份文件、目标文件、ELF 文件、二进制文件和反汇编文件# -f: 忽略文件不存在的错误# *.bak: 删除所有扩展名为 .bak 的备份文件# *.o: 删除所有目标文件# main.elf: 删除生成的 ELF 可执行文件# main.bin: 删除生成的二进制文件# main.dis: 删除生成的反汇编文件rm -rf *.bak start.o main.o main.elf main.bin main.dis
  1. 加入map.lds
    • map.lds 链接脚本 定义了程序在内存中的布局,指定了代码段、数据段和未初始化数据段的起始地址和内容来源。
    • 这份脚本非常适用于嵌入式系统开发,特别是当你需要精确控制程序的各个部分在内存中的位置时。
    • 代码如下
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") 
# 指定生成文件的输出格式为 "elf32-littlearm",适用于 32 位 ARM 的小端格式
# "elf32-littlearm" 出现在三个位置,分别对应目标文件、二进制文件和目标机器的 ELF 格式OUTPUT_ARCH(arm)
# 指定目标架构为 ARM,确保链接器知道处理的是 ARM 处理器的指令集ENTRY(_start)
# 指定程序的入口地址为 _start,这是程序执行的起始点,通常定义在汇编文件 start.S 中SECTIONS
{. = 0x40008000;  # 设置程序在内存中的起始地址为 0x40008000,通常是裸机编程中设置的 RAM 基地址# 所有代码段、数据段都从这个地址开始布局. = ALIGN(4);# 将当前地址对齐到 4 字节边界,以确保对齐要求满足 ARM 处理器的要求# 对齐有助于提高内存访问效率,避免非对齐访问引发错误.text      :{start.o(.text)# 将 start.o 文件中的 .text 段(代码段)加载到当前地址处*(.text)# 加载所有目标文件中的 .text 段(代码段),确保代码在合适的内存位置}. = ALIGN(4);# 再次对齐到 4 字节边界,以确保段之间的正确对齐.data : { *(.data) }# 将所有目标文件中的 .data 段(数据段)加载到当前地址# .data 段通常包含已初始化的全局变量和静态变量. = ALIGN(4);# 再次对齐到 4 字节边界,确保 bss 段的正确对齐.bss :{ *(.bss) }# 将所有目标文件中的 .bss 段(未初始化的全局变量和静态变量)加载到当前地址# .bss 段通常不占用文件大小,它只分配未初始化数据所需的空间
}
  1. make生成bin(二进制文件)烧录进入开发板即可,成功时会发送hello world 给PC
    在这里插入图片描述

相关文章:

ARM知识点三和串口代码的编写流程

ARM的一些常见问题 ARM 体系结构的主要特点是什么&#xff1f; 精简指令集 (RISC)&#xff1a;ARM 采用 RISC 结构&#xff0c;指令集较小且简单&#xff0c;执行效率高。相比于复杂指令集 (CISC)&#xff0c;RISC 更强调每条指令的执行速度。低功耗设计&#xff1a;ARM 处理…...

【unity踩坑】打开vs2022没有文字联想/杂项文件

unity打开vs2022没有文字联想 修改外置编辑器安装unity开发插件vs编辑器显示杂项文件 修改外置编辑器安装unity开发插件 参考 在unity项目里选择Edit-> Preferences->External Tools然后更换编辑器 在vs工具界面添加unity游戏开发选项。 重新打开还是有问题&#xff…...

WebGoat JAVA反序列化漏洞源码分析

目录 InsecureDeserializationTask.java 代码分析 反序列化漏洞知识补充 VulnerableTaskHolder类分析 poc 编写 WebGoat 靶场地址&#xff1a;GitHub - WebGoat/WebGoat: WebGoat is a deliberately insecure application 这里就不介绍怎么搭建了&#xff0c;可以参考其他…...

大数据-161 Apache Kylin 构建Cube 按照日期、区域、产品、渠道 与 Cube 优化

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…...

uni-app使用v-show编译成微信小程序的问题

问题 在uni-app使用v-show语法编译成微信小程序会有一个问题 当我们设置成v-show"false" 在Hbuilder X里面确实没有显示 然后运行到 微信开发程序里面 发现显示了出来&#xff0c;说明设置的 v-show"false"没有起作用 解决办法 首先去uniapp官网查看v…...

充电宝租赁管理系统网站毕业设计SpringBootSSM框架开发

目录 1. 概述 2. 技术选择与介绍 3. 系统设计 4. 功能实现 5. 需求分析 1. 概述 充电宝租赁管理系统网站是一个既实用又具有挑战性的项目。 随着移动设备的普及和人们日常生活对电力的持续依赖&#xff0c;充电宝租赁服务已成为现代都市生活中的一项重要便利设施。它不仅为…...

喜讯!迈威通信TSN产品通过“时间敏感网络(TSN)产业链名录计划”评测,各项指标名列前茅

TSN技术&#xff0c;作为推动企业网络化与智能化转型的关键力量&#xff0c;已成为工业网络迈向下一代演进的共识方向&#xff0c;正加速重构工业网络的技术架构与产业生态。为响应这一趋势&#xff0c;工业互联网产业联盟携手中国信息通信研究院及50余家产学研用单位&#xff…...

国产工具链GCKontrol-GCAir助力控制律开发快速验证

前言 随着航空领域技术的不断发展&#xff0c;飞机的飞行品质评估和优化成为了航空领域的一个重要任务&#xff0c;为了确保飞行器在各种复杂条件下的稳定性&#xff0c;控制律设计过程中的模型和数据验证需要大量仿真和测试。 本文将探讨基于世冠科技的国产软件工具链GCKont…...

嵌入式开发:STM32 硬件 CRC 使用

测试平台&#xff1a;STM32G474系列 STM32硬件的CRC不占用MCU的资源&#xff0c;计算速度快。由于硬件CRC需要配置一些选项&#xff0c;配置不对就会导致计算结果错误&#xff0c;导致使用上没有软件计算CRC方便。但硬件CRC更快的速度在一些有时间资源要求的场合还是非…...

基于STM32的智能家居语音控制系统:集成LD3320、ESP8266设计流程

一、项目概述 项目目标和用途 近年来&#xff0c;智能家居产品逐渐成为家庭生活中不可或缺的一部分。为了提升家庭生活的便捷性和舒适度&#xff0c;本项目旨在设计一款基于STM32F407VGT6&#xff08;Cortex-M4内核&#xff09;微控制器的多功能智能家居语音控制系统。该系统…...

【docker】要将容器中的 livox_to_pointcloud2 文件夹复制到宿主机上

复制文件夹 使用 docker cp 命令从容器复制文件夹到宿主机&#xff1a; docker cp <container_id_or_name>:/ws_livox/src/livox_to_pointcloud2 /path/to/host/folder sudo docker cp dandong_orin_docker:/ws_livox/src/livox_to_pointcloud2 /home...

网络编程(17)——asio多线程模型IOThreadPool

十七、day17 之前我们介绍了IOServicePool的方式&#xff0c;一个IOServicePool开启n个线程和n个iocontext&#xff0c;每个线程内独立运行iocontext, 各个iocontext监听各自绑定的socket是否就绪&#xff0c;如果就绪就在各自线程里触发回调函数。为避免线程安全问题&#xf…...

【rust/egui/android】在android中使用egui库

文章目录 说在前面AndroidStudio安装编译安装运行问题 说在前面 操作系统&#xff1a;windows11java版本&#xff1a;23android sdk版本&#xff1a;35android ndk版本&#xff1a;22rust版本&#xff1a; AndroidStudio安装 安装AndroidStudio是为了安装sdk、ndk&#xff0c;…...

Git---Git打标签

打标签 像其他版本控制系统&#xff08;VCS&#xff09;一样&#xff0c;Git 可以给仓库历史中的某一个提交打上标签&#xff0c;以示重要。 比较有代表性的是人们会使用这个功能来标记发布结点&#xff08; v1.0 、 v2.0 等等&#xff09;。 在本节中&#xff0c;你将会学习如…...

深入理解Transformer的笔记记录(精简版本)---- Transformer

自注意力机制开启大规模预训练时代 1 从机器翻译模型举例 1.1把编码器和解码器联合起来看待的话,则整个流程就是(如下图从左至右所示): 1.首先,从编码器输入的句子会先经过一个自注意力层(即self-attention),它会帮助编码器在对每个单词编码时关注输入句子中的的其他单…...

Ubuntu 更换内核版本

更换内核脚本 这里以更换 5.15.0-88-generic 版本内核为例 cat kernel.sh#!/bin/bashapt install linux-image-5.15.0-88-generic # Ubuntu内核切换脚本# 检查是否具有root权限 if [[ $(id -u) -ne 0 ]]; thenecho "请以root身份运行此脚本。"exit 1 fi# 检查系统是…...

博士找高校教职避坑指南:史上最全的避坑秘籍

在学术的海洋中遨游多年&#xff0c;博士们终于要踏上寻找高校教职的征程。这不仅是职业生涯的新起点&#xff0c;更是一场充满未知与挑战的冒险。今天&#xff0c;就让我们来聊聊那些在寻找高校教职时需要避开的坑&#xff0c;希望能为你的求职之路保驾护航。 1. 薪资结构&am…...

Study-Oracle-11-ORALCE19C-ADG集群搭建

一路走来,所有遇到的人,帮助过我的、伤害过我的都是朋友,没有一个是敌人。 一、ORACLE--ADG VS ORACLE--DG的区别 1、DG是Oracle数据库的一种灾难恢复和数据保护解决方案,它通过在主数据库和一个或多个备用数据库之间实时复制数据,提供了数据的冗余备份和故障切换功能。…...

【C++】map详解(键值对的概念,与multimap的不同)

目录 00.引言 set 和 map 的区别 键值对的概念 01.map容器 主要特性 常用操作 主要用途 02.multimap容器 特性 常用操作 用途 00.引言 set 和 map 的区别 set 和 map 都是C标准模板库&#xff08;STL&#xff09;中的容器&#xff0c;它们的区别如下&#xff1a;…...

私域电商新纪元:消费增值模式引领百万业绩飞跃

各位朋友&#xff0c;我是吴军&#xff0c;专注于带领大家深入探索私域电商领域的非凡魅力与潜在机会。 今天&#xff0c;我想与大家分享一个鼓舞人心的真实故事。在短短的一个月内&#xff0c;我们的合作伙伴实现了业绩的飞跃&#xff0c;突破百万大关&#xff0c;并且用户活跃…...

AAA Mysql与redis的主从复制原理

一 &#xff1a;Mysql主从复制 重要的两个日志文件&#xff1a;bin log 和 relay log bin log&#xff1a;二进制日志&#xff08;binnary log&#xff09;以事件形式记录了对MySQL数据库执行更改的所有操作。 relay log&#xff1a;用来保存从节点I/O线程接受的bin log日志…...

结合大语言模型的机械臂抓取操作学习

一、 大语言模型的机械臂抓取操作关键步骤 介绍如何基于大语言模型实现机械臂在PyBullet环境中的抓取操作&#xff0c;涵盖机器人运动学、坐标系转换、抓取候选位姿生成、开放词汇检测以及大语言模型代码生成等模块。 1. 机器人正逆运动学基本概念 正运动学: 已知机器人的关节…...

数据结构-二叉树_堆

一. 树的概念 树在我们的日常生活中随处可见&#xff0c;人们将生活中的树转换成存放数据的树形结构&#xff0c;就成了数据结构中的“树”。 如上图所示&#xff0c;自然界中的树有树根&#xff0c;有树枝&#xff0c;有树叶&#xff0c;当我们将其转换成树形结构时&#xf…...

Vscode+Pycharm+Vue.js+WEUI+django火锅(三)理解Vue

新创建的Vue项目里面很多文件&#xff0c;对于新手&#xff0c;老老实实做一下了解。 1.框架逻辑 框架的逻辑都是相通的&#xff0c;花点时间理一下就清晰了。 2.文件目录及文件 创建好的vue项目下&#xff0c;主要的文件和文件夹要先认识一下&#xff0c;并与框架逻辑对应起…...

溯变:守护天使 | OPENAIGC开发者大赛企业组优秀作品

在第二届拯救者杯OPENAIGC开发者大赛中&#xff0c;涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到&#xff0c;我们特意开设了优秀作品报道专栏&#xff0c;旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者&#xff0c;希望能带给…...

android中byte[] buf没有结束符,new String(buf)会不会出错?

答案是&#xff1a;不会 看例子&#xff1a; 这和c是不一样的&#xff0c;不需要特别的在字符串后面添加一个\0结束....

鸿蒙harmonyos next flutter混合开发之开发plugin(获取操作系统版本号)

创建Plugin为my_plugin flutter create --org com.example --templateplugin --platformsandroid,ios,ohos my_plugin 创建Application为my_application flutter create --org com.example my_application flutter_application引用flutter_plugin&#xff0c;在pubspec.yam…...

介绍一款开源的 Modern GUI PySide6 / PyQt6的使用

首先附上大神的开源地址&#xff08;自行克隆吧&#xff09;&#xff1a; https://github.com/Wanderson-Magalhaes/Modern_GUI_PyDracula_PySide6_or_PyQt6 步骤一&#xff1a;安装PySide6库 pip install PySide6 步骤二&#xff1a;运行main文件 python main.py 就得…...

【大模型】AI数据基础设施的对象存储

官网地址&#xff1a; MinIO | S3 Compatible Storage for AI Github地址&#xff1a; ​​​​​https://github.com/minio/minio 企业级&#xff0c;并对AI准备就绪的分布式对象存储&#xff08;一般拿来存模型文件&#xff09; 部署步骤参考&#xff1a; minio安装部署及…...

【前端工程解耦】使用事件中心实现系统解耦,注册,触发,删除事件

前言 事件中心提供了一种灵活且可扩展的方式来管理事件和处理函数之间的关系&#xff0c;同时保持它们之间的解耦&#xff0c;可以降低系统耦合度&#xff0c;将视图和逻辑拆分出来&#xff0c;还是那句话&#xff0c;如果一个中间件解决不了问题&#xff0c;那就再加一个 废话…...