2. 开发环境搭建:交叉编译工具链(ARM GCC)、JTAG调试器(OpenOCD)、目标板连接与启动

好,咱们正式开始动手了。这一章我带你搭环境。说实话,嵌入式开发有一半的坑都出在环境上。工具链版本不对、调试器连不上、板子起不来——这些我都经历过。所以这一章我会把每个步骤掰开揉碎了讲,你跟着走一遍,基本不会出问题。

2.1 交叉编译工具链:ARM GCC

先说说为什么需要交叉编译。你想想看,你的电脑是x86架构,但Exynos芯片是ARM架构。你不能直接在电脑上编译一个x86的程序扔到板子上跑——它不认识。所以我们需要一套工具,在x86电脑上生成ARM能执行的机器码。这就是交叉编译工具链。

我个人习惯用arm-none-eabi-gcc。这个工具链是专门针对裸机或RTOS开发的,没有Linux系统层的依赖。如果你做的是Linux内核驱动,那得用arm-linux-gnueabihf-gcc。这里我以裸机开发为例,因为底层驱动调试时,裸机环境更可控。

安装步骤

我建议直接从ARM官网下载预编译的二进制包。别自己从源码编译,太折腾了,而且容易踩坑。

# 下载ARM GCC 10.3-2021.07版本(我测试过,稳定)
wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.07/gcc-arm-none-eabi-10.3-2021.07-x86_64-linux.tar.bz2

# 解压到 /opt 目录
sudo tar -xjf gcc-arm-none-eabi-10.3-2021.07-x86_64-linux.tar.bz2 -C /opt/

# 添加环境变量
echo 'export PATH=$PATH:/opt/gcc-arm-none-eabi-10.3-2021.07/bin' >> ~/.bashrc
source ~/.bashrc

# 验证安装
arm-none-eabi-gcc --version

重要: 输出中应该看到 "arm-none-eabi-gcc (GNU Arm Embedded Toolchain 10.3-2021.07)" 这样的字样。如果版本不对,后面编译出来的代码可能会有奇怪的问题。

我曾经遇到过一件事:用9.2版本编译的启动代码,在Exynos4412上死活跑不起来。折腾了两天,最后换成10.3版本就好了。嗯,有时候工具链的版本就是这么玄学。所以建议你跟我用同一个版本。

测试编译

写一个最简单的C程序,验证工具链能不能正常工作。

// test.c
void main(void) {
    while(1);
}
arm-none-eabi-gcc -c -mcpu=cortex-a9 -mthumb test.c -o test.o
arm-none-eabi-objdump -d test.o

如果能看到ARM汇编指令输出,说明工具链没问题。这里注意-mcpu=cortex-a9参数——Exynos4412用的是Cortex-A9内核,这个参数不能搞错。

2.2 JTAG调试器:OpenOCD

工具链搞定了,接下来是调试器。JTAG是硬件调试的命脉。没有它,你只能靠printf和LED灯来猜问题——那效率太低了。

我推荐用OpenOCD(Open On-Chip Debugger)。它是开源的,支持各种调试适配器,比如J-Link、ST-Link、FT2232等。对于Exynos芯片,我一般用J-Link或者FT2232H。

安装OpenOCD

别用apt-get装,那个版本太老了。自己从源码编译,能拿到最新的功能和bug修复。

# 安装依赖
sudo apt-get install libtool autoconf automake pkg-config libusb-1.0-0-dev

# 下载OpenOCD 0.12.0(我测试过,稳定)
git clone https://git.code.sf.net/p/openocd/code openocd
cd openocd
git checkout v0.12.0

# 配置,启用J-Link和FTDI支持
./bootstrap
./configure --enable-jlink --enable-ftdi --enable-sysfsgpio

# 编译安装
make -j4
sudo make install

# 验证
openocd --version

注意: 如果你用的是J-Link,记得安装J-Link的驱动。OpenOCD需要libjaylink支持。如果编译时提示找不到libjaylink,先装一下:sudo apt-get install libjaylink-dev

配置OpenOCD脚本

OpenOCD需要知道你的目标芯片是什么、调试接口怎么连。对于Exynos4412,我写了一个配置文件,你直接拿去用。

# exynos4412.cfg
source [find interface/jlink.cfg]
source [find target/exynos4412.cfg]

# 设置JTAG时钟频率
adapter speed 1000

# 初始化
init
targets

这里adapter speed 1000表示JTAG时钟设为1MHz。别设太高,我试过5MHz,结果信号不稳定,经常断连。1MHz是个稳妥的选择。

连接调试器

硬件连接很简单:

  • JTAG接口:Exynos4412开发板上一般有20针的JTAG插座。J-Link的20针排线直接插上去就行。
  • 接线顺序:注意方向,别插反了。板子上一般有三角标记,对应排线的红线。
  • 供电:调试器本身不供电,板子需要单独上电。

启动OpenOCD:

sudo openocd -f exynos4412.cfg

如果看到类似这样的输出,说明连接成功:

Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 1000 kHz
Info : J-Link ARM-OB STM32 compiled Dec 13 2021 10:15:26
Info : JTAG tap: exynos4412.cpu tap/device found: 0x4ba00477

小技巧: 如果提示 "JTAG tap: ... not found",先检查硬件连接。我遇到过好几次,都是排线松了或者方向插反了。另外,有些开发板的JTAG引脚需要跳线帽短接,别忘了看原理图。

2.3 目标板连接与启动

环境搭好了,调试器也连上了。接下来让板子跑起来。这一步其实最考验耐心——我第一次搞Exynos4412的时候,光启动就折腾了一整天。

启动模式选择

Exynos4412支持多种启动模式:

启动模式 OM[5:0]引脚配置 说明
eMMC启动 0b000011 从板载eMMC启动,最常用
SD卡启动 0b000100 从SD卡启动,调试阶段常用
USB启动 0b000101 通过USB下载程序,裸机调试用
UART启动 0b000110 串口下载,速度慢,不推荐

我个人习惯用SD卡启动。为什么呢?因为eMMC启动需要先把固件烧进去,万一烧错了,板子就变砖了。SD卡启动就灵活得多,换张卡就行。

生成启动镜像

Exynos4412的启动流程是这样的:iROM(内部ROM)先执行,然后加载BL1(一级引导加载器),BL1再加载BL2,最后跳转到你的程序。听起来复杂,但我们可以用mkblimage工具来生成正确的镜像格式。

# 假设你已经编译好了裸机程序 bl2.bin
# 生成带BL1头的完整镜像
mkblimage -t exynos4412 -b bl2.bin -o u-boot.bin

# 烧录到SD卡(假设SD卡设备是 /dev/sdb)
sudo dd if=u-boot.bin of=/dev/sdb bs=512 seek=1
sync

关键点: seek=1表示跳过第一个扇区。Exynos4412的iROM会从SD卡的第二个扇区开始读取BL1。这个细节搞错了,板子绝对起不来。我当年就是在这里卡了一整天。

用GDB调试启动过程

OpenOCD启动后,会监听GDB连接。我们可以用GDB来单步调试启动代码。

# 启动GDB
arm-none-eabi-gdb

# 在GDB中连接OpenOCD
(gdb) target remote localhost:3333

# 复位并停在第一条指令
(gdb) monitor reset halt

# 加载程序
(gdb) load bl2.elf

# 设置断点
(gdb) b main

# 运行
(gdb) c

当程序停在main函数时,你就可以单步执行、查看寄存器、检查内存了。这才是真正的底层调试。

调试技巧: 我建议你在启动代码的每个关键步骤都加一个GPIO翻转。比如初始化时钟后翻转一次,初始化DDR后翻转一次。用示波器看GPIO波形,能快速定位问题出在哪一步。这比盯着GDB输出直观多了。

2.4 常见问题与避坑指南

最后,我总结几个我踩过的坑,你遇到了可以直接翻到这里查。

  • OpenOCD连不上:先检查USB权限。把当前用户加入plugdev组:sudo usermod -a -G plugdev $USER。然后重新插拔调试器。
  • 板子启动后没反应:检查SD卡烧录位置。用hexdump查看SD卡第二个扇区有没有数据:sudo hexdump -C /dev/sdb -n 512 -s 512。如果全是0,说明烧录位置错了。
  • GDB加载程序失败:检查链接脚本。Exynos4412的DDR基地址是0x40000000,如果你的程序链接地址不对,加载后执行会直接崩溃。
  • JTAG信号不稳定:降低时钟频率。从1MHz降到500kHz试试。另外,排线不要太长,超过20cm就容易出问题。

嗯,环境搭建这部分就到这里。你把这些步骤走一遍,应该能顺利让板子跑起来。下一章我们开始写真正的底层驱动——GPIO和UART。到时候你会用到今天搭好的这套环境。