这一章在干嘛?
前五章是「钻进每个模块看细节」,这一章是「站到山顶看全貌」——一张架构图把五个模块的职责、三个时钟域、四条跨域握手连成整体;然后落到地面,告诉你板子到手后从装驱动、准备 SD 卡到下载上板、按图排错的完整动作。学到这里,你对这套官方例程的理解就不再是零散的知识点,而是一张能随时调用的地图。
一张图看懂整个系统
先看图。这是整套系统的数据流全景,每个模块下方都标了对应章节,你可以随时跳回去复习:
HDMI 多媒体播放系统数据流全景图(写路径蓝色、读路径橙色)
从图里能读出三件事:
- 数据从左流到右:TF 卡读图 → BMP 解析 → 索引控制 → 双缓冲仲裁 → 帧缓存 → 时序对齐 → HDMI 上屏;
- 写读两条路径:左边半段是「写路径」(事件驱动,有新图才动),右边半段是「读路径」(时钟驱动,每帧必刷);
- SDRAM 是交汇点:写路径把像素「存进去」,读路径把像素「取出来」,中间用双缓冲 BUF0/BUF1 隔离,读写永不打架。
三个时钟域与跨域三板斧
五个模块分布在三个时钟域里,域与域之间不能直接连线,全靠一套「三板斧」衔接:
| 时钟域 | 频率 | 属于哪些模块 | 跨域手段 |
|---|---|---|---|
sd_card_clk | 100 MHz | bmp_read、sd_card_bmp | 写 FIFO / 握手 / toggle |
ext_mem_clk | 100 MHz | frame_read_write 状态机、SDRAM | 写 FIFO + 读 FIFO |
video_clk | 25.175 MHz | video_timing_data、video_delay、HDMI | 读 FIFO / 握手 |
所谓「三板斧」,就是前面几章反复出现的三个套路:
- 异步 FIFO——不同频率之间传数据,用格雷码指针 + 双口 RAM 隔离;
- 三级同步——单比特跨域信号先过
d0 → d1 → d2三级寄存器,消除亚稳态; - 跳变握手——跨域传「完成」这种脉冲,不用电平用 toggle 翻转,接收端异或取边沿。
这三招贯穿了 frame_read_write 的写/读状态机、sd_card_bmp 的 write_finish_pulse、bmp_read 和 video_timing_data 的请求-应答。把它们记牢,你就能独立设计任何多时钟域的 FPGA 系统。
一个检验标准
现在如果让你随手画出这套系统的跨域信号有哪些,你应该能数出:写数据(写 FIFO)、读数据(读 FIFO)、
write_req/write_req_ack、read_req/read_req_ack、write_finish_toggle。能数全,说明五章没白学。
一个像素的完整旅程
把视角缩到最小——跟踪一个像素从诞生到上屏,五章的知识就串成了一条线:
bmp_read从 SD 卡读出一串字节,跳过 54 字节文件头,每 3 个字节(BGR)拼成一个 24bit 像素(第四章);sd_card_bmp把这 24bit 扩成 32bit,按next_buf_lut决定写进哪块缓冲,发出write_req(第三、四章);frame_read_write的写状态机应答握手,把像素经写 FIFO 跨到mem_clk域,突发写进 SDRAM 的 BUF0 或 BUF1,写完翻转write_finish_toggle(第二章);sd_card_bmp同步到 toggle、异或取边沿,配合「源送完」双确认,翻转disp_buf_idx把这块缓冲切给显示(第三章);video_timing_data在场同步边沿发起read_req,读状态机把整帧从 SDRAM 吸进读 FIFO,经video_delay对齐时序,像素终于赶上hs/vs/de的节拍,被推上 HDMI(第五章)。
一个像素走过五关,跨了三个时钟域,握了三次手。这才是「理解官方例程」该有的颗粒度。
板子到手后的实操清单
理论归理论,板子到手后按这个顺序做,能少走很多弯路:
- 装驱动:USB-JTAG 下载器驱动(TD 安装目录
driver\dl-cable或板卡资料包下载器驱动\win8_10_64)+ CH340 串口驱动; - 连线:下载器接 JTAG 口、板子上电、HDMI 线接显示器、TF 卡插卡座;
- 备卡:TF 卡格式化成 FAT32,放入 640×480 的 24bit BMP 图片(最多前 4 张,见下一节);
- 开工程:用 TD 打开
lab_ex4_tf工程,确认器件选的是EG4S20BG256; - 生成位流:跑综合 → 布局布线 → 生成 bitstream,注意看时序报告有没有违例;
- 下载:选对下载器型号,下载后按复位,观察现象。
器件千万别选错
EG4S20 赛道用的是
EG4S20BG256。器件型号选错,管脚约束、时钟资源全都对不上,综合能过下载必失败。打开工程第一件事就是核对器件。
准备一张能被识别的图
官方例程对 BMP 的校验很严格(第四章的 header_match),图不符合就永远显示不出来。准备图片时盯住这四条:
| 要求 | 值 | 为什么 |
|---|---|---|
| 分辨率 | 640 × 480 | bmp_width/bmp_height 写死 640/480,不符直接跳过 |
| 位深 | 24bit | bit_count == 24,8bit/16bit 都不行 |
| 压缩 | 无 | compression == 0,RLE 压缩的 BMP 解析不了 |
| 格式 | 标准 BMP | 54 字节头,魔数 "BM" |
生成方法任选其一:
- Windows 画图「另存为 → 24 位位图」;
- Photoshop「存储为 BMP,24 位,Windows」;
- ImageMagick 一行命令:
magick input.png -resize 640x480 -type TrueColor BMP3:out.bmp。
图片放在 TF 卡根目录即可,文件名无所谓(扫描是按扇区找、不靠文件名),最多认前 4 张(SCAN_TARGET_COUNT = 4)。
多图轮播
放 2~4 张合格 BMP,上电后
key1(下一张)/key2(自动播放)就能切换。只有一张图时key2无效——代码里自动播放要求img_found_count > 1。
排错地图:黑屏 / 花屏 / 色错
上板遇到问题,按现象对号入座:
| 现象 | 最可能的原因 | 先查这里 |
|---|---|---|
| 完全黑屏 | 首图没加载成功,display_valid 一直为 0 | TF 卡识别(sd_init_done)、BMP 格式、卡是否 FAT32 |
| 花屏 / 撕裂 | 双缓冲没生效,读写冲突 | write_buf_idx/disp_buf_idx 是否真的错开、write_finish_toggle 是否同步 |
| 红蓝互换(蓝脸) | BGR/RGB 顺序搞反 | 第四章的 bmp_byte_idx 拼像素顺序 |
| 上下颠倒 | 垂直翻转配置错 | WRITE_V_FLIP 参数(第二章的地址重映射) |
| 不识别卡 | SPI 初始化失败 | 卡容量、FAT32 格式、SD_nCS 管脚电平 |
| 只显示第一张、切不动 | 按键消抖/扫描没跑完 | scan_done、img_found_count、按键管脚 |
排查时善用模块里现成的 state_code(sd_card_bmp 输出到数码管的那个 4bit 信号)——它把当前状态机状态暴露出来,上板一读就知道卡在哪个状态。
黑屏先别慌
黑屏 ≠ 程序挂了。按第五章讲的,首图提交前
display_valid = 0,屏幕本来就是黑的。先确认 TF 卡里有合格 BMP、sd_init_done拉高了,再往下查。
三个时钟域分别叫什么、频率多少?
sd_card_clk 100MHz(写侧)、ext_mem_clk 100MHz(SDRAM/状态机)、video_clk 25.175MHz(读侧/HDMI)
跨时钟域的「三板斧」是哪三招?
异步 FIFO、三级同步、跳变(toggle)握手
一个像素从 SD 卡到上屏要跨几次时钟域、握几次手?
跨三个时钟域,写侧 write_req/ack、读侧 read_req/ack、帧完成 toggle 共三次握手
官方例程能识别什么样的 BMP?
640×480、24bit、无压缩、标准 54 字节头、魔数 “BM”
上板后完全黑屏,最先应该排查哪两件事?
TF 卡是否 FAT32、卡里是否有合格 BMP;以及 sd_init_done 是否拉高(卡识别成功)