第二章:MTK8678平台概述
好,咱们直接进入正题。这一章我会带你快速了解MTK8678这颗芯片到底长什么样,有什么家底,以及我们做底层开发时最常打交道的那些外设接口。嗯,这部分内容偏“硬”,但别急,我会结合我实际踩过的坑来讲,保证你听完心里有数。
2.1 芯片特性介绍
MTK8678,说白了就是一颗面向高端物联网和边缘计算场景的SoC。我最早接触它是在一个工业网关项目上,客户要求低功耗、高算力,还要支持多种存储。当时选型时对比了好几款,最后发现这颗芯片的性价比确实能打。
它的核心特性,我列几个关键点:
- CPU架构:采用ARM Cortex-A76大核 + Cortex-A55小核的big.LITTLE架构。大核主频能跑到2.2GHz,小核也有1.8GHz。我个人习惯把实时性要求高的任务绑在小核上,省电又稳定。
- GPU与NPU:集成Mali-G57 GPU,支持OpenGL ES 3.2和Vulkan。另外还带一个独立的NPU,算力大概4.8 TOPS。我在项目中用NPU做过轻量级的人脸检测,效果还不错。
- 内存支持:支持LPDDR4X,最高频率3200MHz。这里要注意,DDR的布线在PCB设计阶段就得提前规划,否则跑高频时容易出时序问题。
- 存储接口:同时支持eMMC 5.1、NAND Flash(SLC/MLC)、NOR Flash。这个灵活性在同类芯片里不多见,后面我会详细讲。
- 功耗控制:支持动态电压频率调整(DVFS),待机功耗可以做到几十微瓦级别。我曾经在一个电池供电的设备上,靠这个特性把续航从3天拉到了7天。
重要提示:MTK8678的启动流程是典型的“多级引导”。从BootROM开始,依次加载Preloader、U-Boot,最后才是Linux内核。我们做Bootloader定制,主要就是改Preloader和U-Boot这两级。
2.2 硬件架构框图
讲架构之前,我先画个简单的框图在你脑子里。你想象一下:
芯片最中心是Cortex-A76/A55集群,通过CCI-550互联总线连接各个模块。总线左边挂着DDR控制器和存储控制器(负责eMMC、NAND、NOR)。总线右边挂着显示子系统(GPU、DPU、DSI)。下面则是外设总线,挂载了UART、SPI、I2C、USB、Ethernet等接口。
嗯,这里有个关键点:BootROM是固化在芯片内部的一块只读存储器,上电后CPU第一条指令就从这里取。它负责初始化最基本的硬件,然后从你指定的启动介质(比如eMMC或NAND)里加载下一级代码。
个人经验:我建议你在看芯片手册时,重点关注“Memory Map”章节。它告诉你每个外设的基地址,比如UART0的基地址是0x11002000。这个地址在写驱动时天天要用,记牢了能省不少翻手册的时间。
2.3 关键外设接口
做底层开发,说白了就是跟这些外设打交道。下面我挑几个Bootloader阶段最常用的接口来讲。
2.3.1 UART(串口)
UART是我们调试的“眼睛”。没有它,你连芯片有没有跑起来都不知道。MTK8678内部有4个UART控制器,其中UART0默认被BootROM用作调试输出。
配置UART时,我一般关注这几个寄存器:
- UART_THR:发送保持寄存器,写数据就是往这里写。
- UART_RBR:接收缓冲寄存器,读数据从这里读。
- UART_LCR:线路控制寄存器,设置数据位、停止位、校验位。
- UART_DLL/DLH:分频寄存器,用来设置波特率。
举个例子,初始化UART0的代码片段大概长这样:
// 设置波特率115200,8N1
#define UART0_BASE 0x11002000
void uart_init(void) {
// 设置分频系数
*(volatile uint32_t*)(UART0_BASE + 0x00) = 0x0D; // DLL = 13
*(volatile uint32_t*)(UART0_BASE + 0x04) = 0x00; // DLH = 0
// 配置8位数据,1位停止,无校验
*(volatile uint32_t*)(UART0_BASE + 0x0C) = 0x03; // LCR = 0x03
// 使能FIFO
*(volatile uint32_t*)(UART0_BASE + 0x08) = 0x01; // FCR = 0x01
}
避坑指南:我曾经在调试时发现UART输出全是乱码,查了半天发现是晶振频率配错了。MTK8678的UART时钟源可以来自26MHz或13MHz,你得根据实际硬件设计来设置分频系数。否则波特率算出来就是错的。
2.3.2 SPI(串行外设接口)
SPI在Bootloader里主要用来连接NOR Flash或者一些传感器。MTK8678有3个SPI控制器,支持主模式和从模式,最高速率能到50MHz。
SPI的时序其实不复杂,就是四根线:SCK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。但有个细节容易忽略——时钟极性(CPOL)和时钟相位(CPHA)。不同的Flash芯片要求的模式不一样,你得看数据手册。
我记得有一次,项目上用了一颗新款的NOR Flash,读ID总是返回0xFF。后来用示波器一抓,发现是CPHA设置反了。调整之后,一次就通了。
2.3.3 NAND/NOR Flash
这两种Flash在MTK8678上都能用,但区别很大:
| 特性 | NOR Flash | NAND Flash |
|---|---|---|
| 读取速度 | 快,支持XIP(片上执行) | 慢,需要先读到RAM |
| 写入速度 | 慢 | 快 |
| 容量 | 小(通常64MB以下) | 大(可达几GB) |
| 坏块管理 | 不需要 | 必须要有 |
| 典型用途 | 存放Bootloader、关键配置 | 存放文件系统、大容量数据 |
我个人习惯把Preloader和U-Boot放在NOR Flash里,因为支持XIP,上电就能直接执行,不用等拷贝。而Linux内核和根文件系统则放在NAND或eMMC里,容量大,成本低。
小技巧:如果你用NAND Flash,一定要在Bootloader里实现坏块管理。否则写到坏块上,系统可能直接挂掉。MTK的BMT(Bad Block Management Table)机制就是干这个的,建议直接用。
2.3.4 eMMC
eMMC其实是把NAND Flash和控制器封装在一起了,对外暴露的是MMC接口。MTK8678支持eMMC 5.1,理论速度能到400MB/s。
在Bootloader里操作eMMC,主要就是发送MMC命令。比如:
- CMD0:复位卡
- CMD1:查询卡的工作电压
- CMD2:获取卡的CID(识别码)
- CMD3:设置卡的相对地址(RCA)
- CMD6:切换卡的工作模式(比如切换到HS200)
- CMD17/CMD18:读单个/多个块
- CMD24/CMD25:写单个/多个块
嗯,这里要注意:eMMC的初始化时序是有严格要求的。比如上电后要先等一段时间(至少74个时钟周期),然后才能发CMD0。我见过有人把延时写短了,结果eMMC死活不响应。
另外,eMMC的分区表也很重要。它内部有Boot分区、RPMB分区、用户数据分区。Bootloader通常放在Boot分区里,这样芯片上电后会自动从那里加载。
总结一下:MTK8678这颗芯片,接口丰富,灵活性高。做Bootloader移植时,你主要就是跟UART(调试)、SPI(NOR Flash)、NAND控制器(NAND Flash)、MMC控制器(eMMC)这四个模块打交道。把它们的初始化流程和寄存器配置搞熟,后面就顺了。
好,这一章就到这里。下一章我会带你手把手搭建开发环境,包括交叉编译工具链的配置、源码目录结构的解析,以及如何编译出第一个能跑的Preloader。到时候咱们直接上代码,不玩虚的。