1.看原理图,需要用到的引脚

1.设置某一个引脚为输入、输出引脚(GPIO),要怎么做?


1)看原理图,D7为例子,这个led接到了GPIOE13,所以第一步 在芯片手册,搜GPIOE13。
得到,要使用E14这个引脚复用成通用输入输出功能(GPIOE13),应该选择候选功能0。(可以看到功能0对应GPIOE13)需要的信号名(GPIOE13)在功能0那格


2)候选功能寄存器GPIOxALTFN

搜候选功能寄存器GPIOxALTFN,发现GPIOx[13]使用GPIOxALTFN0进行配置,它的物理地址是0xC001E020


[27:26]=00


3)输入配置寄存器GPIOxOUTENB

搜输入输入配置寄存器GPIOxOUTENB,得到GPIOEOUTENB的物理地址是0xC001E004,设置[13]=1,输出。


4)搜数据寄存器GPIOxOUT,得到GPIOEOUT的物理地址是0xC001E000,设置[13]=0/1,输出高低电平。


5)(uboot默认是关闭看门狗寄存器,保险起见自己关一下)搜看门狗配置寄存器WTCON,得到其物理地址0xC0019000,设置[5]=0,关闭看门狗定时器。

代码

1.编写ARM 汇编代码(初始化代码)

#define WTCON 0xC0019000
#define GPIOxALTFN0 0xC001E020
#define GPIOEOUTENB 0xC001E004
#define GPIOEOUT 0xC001E000
//宏定义部分,定义寄存器的物理地址

.global _start
//声明_start为全局符号,是程序的入口地址(链接脚本指定的入口)
_start:
//程序执行的起点,嵌入式裸机程序必须从这里开始执行
			//关闭看门狗
			LDR R0,=WTCON
			LDR R1,[R0]  //R1得到0xC0019000下的内容/值
			BIC R1,R1, #(1<<5)  //第五位清零,关闭看门狗定时器
			//BIC 目标寄存器, 源寄存器, 掩码
			//1 << 5
			//1      = 0000 0001
			//1 << 5 = 0010 0000   (第5位变成1)
//BIC规则:
//掩码是 1 的位 → 结果变 0
//掩码是 0 的位 → 结果保持不变
			//  1111 1111    (原 R1)
			//& 1101 1111    (~00100000,掩码取反)
			//= 1101 1111    (新 R1)
			STR R1,[R0]
			//STR数据存储指令
			//R1:源寄存器(要写入的数据来自这里)
			//[R0]:R0 里面存的是内存地址
			// 把 R1 的值,写入到 R0 指向的内存地址中
			
			//候选功能0
			LDR R0,=GPIOxALTFN0
			LDR R1,[R0]  //R1得到0xC0019000下的内容/值
			BIC R1,R1, #(3<<26)  //[27:26]=00
			//3即0000 0011 
			STR R1,[R0]
			
			//输入输出使能寄存器
			LDR R0,=GPIOEOUTENB
			LDR R1,[R0]  //R1得到0xC0019000下的内容/值
			ORR R1,R1, #(1<<13)  //[13]=1 输入出
			//ORR = Bitwise OR(按位或)
			//按位或运算规则:1 | 任何数 = 1,0 | 数 = 数本身
			//第 13 位:无论原来是什么,强制变成 1
			STR R1,[R0]
loop:
//loop: 是汇编里的「标签(Label)」
//作用 = 给这一行代码起个名字,方便跳转到这里执行

			//数据寄存器
			LDR R0,=GPIOEOUT
			LDR R1,[R0]  //R1得到0xC0019000下的内容/值
			ORR R1,R1, #(1<<13)  //[13]=1,输出高电平,led灭
			STR R1,[R0]
			
			MOV R0,#0xff00000
			MOV R1,#0x50000			
			bl delay //bl跳转的同时保存返回地址
			
			//数据寄存器
			LDR R0,=GPIOEOUT
			LDR R1,[R0]  //R1得到0xC0019000下的内容/值
			BIC R1,R1, #(1<<13)  //[13]=0,输出低电平,led亮
			STR R1,[R0]	
			
			MOV R0,#0xff00000
			MOV R1,#0x50000			
			bl delay //bl跳转的同时保存返回地址
			//bl = Branch with Link 中文:带链接的跳转
			//跳过去执行,同时把 “回来的路” 存在 LR 里,保证函数执行完能正确返回到主程序
			//b (Branch):跳转。让 PC 指针跳到目标地址。
			//l (Link):链接。把 下一条指令的地址(返回地址)保存到 LR 寄存器 (R14) 中。

			b loop //跳转
			//执行到 B loop
			//跳回 loop: 那一行
			//一直反复,永远出不去
			
			


  • 系统启动完,不能让程序跑飞
  • 必须停在一个死循环里等待
  • 所以裸机启动代码最后一定是:
loop:
    B loop

指令 全称 作用
LDR Load Register 从内存加载数据到寄存器(支持加载地址 / 内存值)
STR Store Register 将寄存器数据写入内存
BIC Bit Clear 按位清除,将指定位清零
ORR Bitwise OR 按位或运算,规则:1 | 任何数 = 10 | 数 = 数本身
LDR R0, =label 伪指令 加载 label 对应的地址到寄存器(ARM 汇编常用技巧)
指令 英文 方向 意思
LDR R1, [R0] Load 内存 → 寄存器
STR R1, [R0] Store 寄存器 → 内存

register:

  1. 名词 (n.):(计算机 / 嵌入式)寄存

  2. 动词 (v.):登记;注册;

“store” 最常见的意思是储存、存放,也可指商店、店铺

bl = Branch with Link 中文:带链接的跳转

2.裸机程序的makefile文件(跑在硬件上的makefile文件)

大小写m都可以,大写M和小写m同时存在优先执行小写的

ledSC.bin:LedSC.o delay.o #目标:依赖
#ledSC.bin:最终目标文件,是可以直接烧录到 ARM 芯片的纯二进制裸机程序
#ledSC.o delay.o:依赖文件,由汇编文件ledSC.S和 C 文件delay.c编译生成的目标文件
	arm-linux-ld -Ttext 0x40000000 -o ledSC.elf $^
#arm-linux-ld(链接工具):ARM 架构的链接器,把多个.o目标文件合并成可执行文件
#-T:指定代码段,指定text代码段放在0x40000000的地址上,(指定裸机程序的链接地址)
#内存开始的地方是0x40000000
#-o 指定输出文件的名字,.elf 表示可执行链接文件
#$^:Makefile 自动变量,代表所有依赖文件(即ledSC.o delay.o)
	arm-linux-objcopy -O binary ledSC.elf ledSC.bin
#arm-linux-objcopy:二进制格式转换工具
#-O binary:(是大写O)指定输出格式为纯二进制(binary),剥离 ELF 头、段信息、调试信息
#把可执行链接文件,转换成可以直接烧录到芯片的裸机二进制文件ledSC.bin
	arm-linux-objdump -D ledSC.elf > ledSC_elf.dis
#arm-linux-objdump:反汇编工具
#-D:显示文件的所有汇编信息
#把可执行链接文件转换成汇编文件
%.o:%.S
#写明依赖关系,.O文件依赖.S文件
	arm-linux-gcc -o $@ $< -c -nostdlib
#arm-linux-gcc:ARM 架构的交叉编译器
# -o:指定生成文件
# $@是自动变量,代表目标文件,这里指LedSC.o
# $<:自动变量,代表第一个依赖文件
#-c:只编译不连接,生成.o目标文件,一连接就生成可执行文件
# -nostdlib:不链接(使用)标准 C 库函数(裸机程序没有操作系统,没有文件的概念,不能用标准库)
%.o:%.c
	arm-linux-gcc -o $@ $< -c -nostdlib
clean:
	rm *.o *.elf *.bin *.dis -f
# clean:伪目标,用于清理编译生成的中间文件

3.执行编译,生成ledSC.bin

make clean # 清理所有编译产物

make # 执行编译,生成ledSC.bin

复制到共享目录下

| 命令                              | 目标地址          | 说明                  |
| ----------------------------   | -------------         | ------------------- |
| `loadb`                          | `0x4800_0000`  |  默认地址(U-Boot 环境变量设置) |
| `loadb 0x40000000 ledSc.bin` | `0x4000_0000` | 显式指定 DDR 起始地址       |
 

  • 0x4000_0000 是 DDR3 内存的物理起始地址

  • 开发板通常配备 1GB DDR(范围:0x4000_0000 ~ 0x7FFF_FFFF

是否需要加载到 `0x40000000` 取决于程序本身的链接地址,而不是一个固定规则。

加载地址 = 链接地址(或符合重定位要求)

程序在哪里运行,取决于编译/链接时指定的地址,U-Boot 只是负责把数据放到正确的位置。

## 常见情况对比

| 程序类型 | 典型加载地址 | 原因 |
|---------|------------|------|
| **裸机程序** (如 `ledSc.bin`) | `0x40000000` 或链接指定地址 | 直接跑在 DDR,无操作系统搬运 |
| **Linux zImage/uImage** | `0x48000000` 或 `0x60000000` | 避免覆盖 U-Boot,内核自解压/重定位 |
| **设备树 dtb** | 内核附近或指定地址 | U-Boot 通过 `bootm` 传递给内核 |
| **U-Boot 自身** | `0x43C00000` 等 | 由 BL2 从 eMMC/SD 加载到特定地址 |

---

GEC6818 的具体例子
DDR 内存范围: 0x40000000 ~ 0x7FFFFFFF (1GB)

常用加载区域:
├─ 0x40000000 : 裸机程序、简单测试代码
├─ 0x48000000 : U-Boot 默认 loadaddr,Linux 内核常用
├─ 0x49000000 : 设备树 dtb
├─ 0x4A000000 : ramdisk、其他数据
└─ ...        : 根据实际需要分配
```

---

## 怎么知道该用哪个地址?

### 1. 看链接脚本(裸机程序)
```ld
/* ledSc.lds */
SECTIONS {
    . = 0x40000000;    /* ← 链接基址 */
    .text : { *(.text*) }
    .data : { *(.data*) }
}
```
→ 必须加载到 `0x40000000`

### 2. 看 U-Boot 环境变量
```bash
GEC6818# printenv
loadaddr=0x48000000    # ← 默认加载地址
fdtaddr=0x49000000
```
→ 加载内核等可用默认值

### 3. 看编译时的指定
```bash
arm-linux-gnueabihf-ld -Ttext 0x41000000 ...
```
→ 按编译时指定的地址加载

---

## 总结

| 问题 | 答案 |
| 裸机程序为什么常用 `0x40000000`? | 因为链接脚本指定了 DDR 起始地址,简单直接 |
| 加载地址错误会怎样? | 程序跑飞、崩溃、或执行异常 |

**关键是:加载地址要与程序的链接地址匹配,或者程序自身具备重定位能力。**

最后:

go 0x40000000   # 跳转到该地址执行

完整编译流程(从源码到烧录)

ledSC.S → arm-linux-gcc → ledSC.o
delay.c → arm-linux-gcc → delay.o
ledSC.o + delay.o → arm-linux-ld → ledSC.elf
ledSC.elf → arm-linux-objcopy → ledSC.bin(可烧录)
ledSC.elf → arm-linux-objdump → ledSC_elf.dis(反汇编调试)

开发板网络设置和支持uboot命令

连接后输入reboot,在倒计时时不断按空格键进入下载模式,在没有进入系统的情况下运行裸机程序

默认的是不支持loadb通过串口传程序文件

我们重新烧录uboot使其支持网络和文件传输

接上OTG,输入fastroot

打开设备管理器,显示如下是已连接

运行。bat文件

查看网络

设置网关为虚拟机的ip(虚拟机为服务器),ip在同一网段并且不能和windows和ubanto冲突(133)Windows10 ubanto12

修改完保存

修改完后重启(reboot或复位按键)

重启后检查

检测环境变量设置的对不对

Logo

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

更多推荐