先看图。这是整套系统的数据流全景,每个模块下方都标了对应章节,你可以随时跳回去复习:
从图里能读出三件事:
五个模块分布在三个时钟域里,域与域之间不能直接连线,全靠一套「三板斧」衔接:
| 时钟域 | 频率 | 属于哪些模块 | 跨域手段 |
|---|---|---|---|
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 / 握手 |
所谓「三板斧」,就是前面几章反复出现的三个套路:
d0 → d1 → d2 三级寄存器,消除亚稳态;这三招贯穿了 frame_read_write 的写/读状态机、sd_card_bmp 的 write_finish_pulse、bmp_read 和 video_timing_data 的请求-应答。把它们记牢,你就能独立设计任何多时钟域的 FPGA 系统。
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(第五章)。一个像素走过五关,跨了三个时钟域,握了三次手。这才是「理解官方例程」该有的颗粒度。
理论归理论,板子到手后按这个顺序做,能少走很多弯路:
driver\dl-cable 或板卡资料包 下载器驱动\win8_10_64)+ CH340 串口驱动;lab_ex4_tf 工程,确认器件选的是 EG4S20BG256;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" |
生成方法任选其一:
magick input.png -resize 640x480 -type TrueColor BMP3:out.bmp。图片放在 TF 卡根目录即可,文件名无所谓(扫描是按扇区找、不靠文件名),最多认前 4 张(SCAN_TARGET_COUNT = 4)。
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 拉高了,再往下查。