Day9下午
工作目标:通过按键控制,让需要打印的信息在窗口里打印出来
打印的工作是在 PS 端完成的,读取的数据是 DDR 给的
首先要把 UART 的窗口打开,其次是加一个按键的输入控制
基于上午的 GPIO 工程,在 ZYNQ 的配置这里

再加一个 GPIO,作为信号输入端
由于这部分是按键 → GPIO 端口 → HI 总线 → PS 端,所以选 Input 就行了
按键这里只有一个,改成 1

如果这个时候直接自动连线,那该模块输出端会产生一个端口
正合我意,图省事就可以直接自动连线了

HI 总线的好处就在于,他可以很方便地跟多个外设进行连接,解决大多数的握手信号传递
这里还要加一个约束,gpio_rtl_tri_i 是因为 GPIO 不知用户要输入/输出,所以自动会加上一个 tri_i 后缀

导出,直到 vitis 这一步
他有点小 BUG,要再手动开一下才行,当然,他自动开了当然好


算个数
咱们打印二值化的区域,是$112\times112=12544$像素,
虽然二值化了,但是存到 DDR 内存还是 RGB565 的格式,每个像素16位对应16个 bit,一字节 8bit,所以字节数为$12544\times2=25088$
RGB565:每个像素 16 位,5 位红色,6 位绿色,5 位蓝色
让串口打印的时候,不是以 16 位打印,而是 ASCll 码打印,所以是 8 位数字传递一个字节,再$\times 2 = 50176$个字节
最后比特数为$\times 8 = 401,408$
而从之前的端口数据可以知道,每秒大概传输 10 万比特,但是需要传输 40 万比特,所以大概需要 4 秒

这4秒内是不希望 DDR 里面的东西被改写,以防内存被替换
所以在每次打印的时候,想办法把堆积 MA 写通道给关掉,这个的代码让 AI 写会存在一些 BUG,他一次写不对
关注写通道部分代码:
第一句 0x1 代表打开,后面是对寄存器配置了保存视频数据的起始地址

这边调个时间等一下,他就可以保证显示了

手搓到这个地步,可以用 AI 改写 main.c 了
我有一个vivado19.2版本写的ov5640-VDMA-DDR-VDMA-VGA工程,通过ZYDN板子实现,以下是能运行的main.c代码 #include <stdio.h> #include "xil_io.h" #include "xparameters.h" #include "xil_types.h" #include "xil_cache.h" #include "sleep.h" #define VDMA_BASEADDR XPAR_AXI_VDMA_0_BASEADDR #define VDMA_FRAME0 0x01000000 //视频帧缓冲区使用的内存起始地址 #define STRIDE 1024 #define HSIZE 1024 #define VSIZE 720 int main() { // VDMA写通道配置 Xil_Out32(VDMA_BASEADDR+0x30, 0x1); Xil_Out32(VDMA_BASEADDR+0xAC, VDMA_FRAME0); Xil_Out32(VDMA_BASEADDR+0xA8, STRIDE*2); Xil_Out32(VDMA_BASEADDR+0xA4, HSIZE*2); Xil_Out32(VDMA_BASEADDR+0xA0, VSIZE); Xil_Out32(VDMA_BASEADDR, 0x1); //开启VDMA Xil_Out32(VDMA_BASEADDR+0x5c, VDMA_FRAME0); // 帧缓冲区基地址 Xil_Out32(VDMA_BASEADDR+0x58, STRIDE*2); // 步长(字节数,16位像素=2字节/像素) Xil_Out32(VDMA_BASEADDR+0x54, HSIZE*2); // 水平尺寸(字节数) Xil_Out32(VDMA_BASEADDR+0x50, VSIZE); // 垂直尺寸(行数) usleep(8000000);//等八秒 Xil_Out32(VDMA_BASEADDR+0x30, 0x0);//把写通道关了 usleep(8000000); Xil_Out32(VDMA_BASEADDR+0x30, 0x1);//再打开 Xil_Out32(VDMA_BASEADDR+0xAC, VDMA_FRAME0); Xil_Out32(VDMA_BASEADDR+0xA8, STRIDE*2); Xil_Out32(VDMA_BASEADDR+0xA4, HSIZE*2); Xil_Out32(VDMA_BASEADDR+0xA0, VSIZE); return 0; } 上述代码可以实现开启摄像头并通过VGA显示画面,8s后画面暂停(写通道关闭),再过8S后写通道重启,画面恢复 随后我通过AXI-GPIO添加了一个按键(按下高电平),分配的地址如下(只需要读基地址就好,GPIO通道无需初始化) /*Definitions for peripheral AXLGPIO_1 */ #define XPAR_AXI_GPIO_1_BASEADDR Ox41210000 #define XPAR_AXI_GPIO_1_HIGHADDR Ox4121FFFF #define XPAR_AXI_GPIO_1_DEVICE_ID 1 #define XPAR_AXI_GPIO_1_INTERRUPT_PRESENT O #define XPAR_AXI_GPIO_1_LIS_DUAL 0 我们想通过按键实现拍照功能,PL端已经搭建好,只要修改main.c文件。我希望按下按钮后(PS端进行消抖),关闭VDMA写通道,随后将画面的456-567列,304-415行这112*112区域的RGB565数据通过UART打印,随后恢复VDMA写通道及相关配置,打印函数直接使用printf,且不额外打印任何信息,每次打印完数据记得删除cache缓存
#include <stdio.h> #include "xil_io.h" #include "xparameters.h" #include "xil_types.h" #include "xil_cache.h" #include "sleep.h" #define VDMA_BASEADDR XPAR_AXI_VDMA_0_BASEADDR #define VDMA_FRAME0 0x01000000 #define STRIDE 1024 #define HSIZE 1024 #define VSIZE 720 #define GPIO_BASEADDR XPAR_AXI_GPIO_1_BASEADDR #define KEY_PIN 0 // 按键连到 GPIO 通道1的bit0 #define DEBOUNCE_US 20000 // 消抖延时 20ms // 拍照区域(112x112) #define COL_START 456 #define COL_END 567 // 包含 #define ROW_START 304 #define ROW_END 415 // 包含 #define WIDTH 112 // (COL_END - COL_START + 1) #define HEIGHT 112 // 恢复 VDMA 写通道(和初始化相同) void restore_vdma_write(void) { Xil_Out32(VDMA_BASEADDR + 0x30, 0x1); Xil_Out32(VDMA_BASEADDR + 0xAC, VDMA_FRAME0); Xil_Out32(VDMA_BASEADDR + 0xA8, STRIDE * 2); Xil_Out32(VDMA_BASEADDR + 0xA4, HSIZE * 2); Xil_Out32(VDMA_BASEADDR + 0xA0, VSIZE); } int main() { // -------- 1. 初始化 VDMA(与原代码一致) -------- Xil_Out32(VDMA_BASEADDR + 0x30, 0x1); Xil_Out32(VDMA_BASEADDR + 0xAC, VDMA_FRAME0); Xil_Out32(VDMA_BASEADDR + 0xA8, STRIDE * 2); Xil_Out32(VDMA_BASEADDR + 0xA4, HSIZE * 2); Xil_Out32(VDMA_BASEADDR + 0xA0, VSIZE); Xil_Out32(VDMA_BASEADDR, 0x1); // 全局使能 Xil_Out32(VDMA_BASEADDR + 0x5c, VDMA_FRAME0); Xil_Out32(VDMA_BASEADDR + 0x58, STRIDE * 2); Xil_Out32(VDMA_BASEADDR + 0x54, HSIZE * 2); Xil_Out32(VDMA_BASEADDR + 0x50, VSIZE); // -------- 2. 配置 GPIO 方向(输入) -------- // 方向寄存器偏移 0x04,bit0=0 表示输入 Xil_Out32(GPIO_BASEADDR + 0x0004, 0x00000000); int last_state = 0; while (1) { u32 gpio_val = Xil_In32(GPIO_BASEADDR); int current = (gpio_val & (1 << KEY_PIN)) ? 1 : 0; // 检测上升沿(按下) if (current == 1 && last_state == 0) { usleep(DEBOUNCE_US); gpio_val = Xil_In32(GPIO_BASEADDR); if (gpio_val & (1 << KEY_PIN)) { // ---------- 有效按键:执行拍照 ---------- // 关闭 VDMA 写通道 Xil_Out32(VDMA_BASEADDR + 0x30, 0x0); usleep(1000); // 等待停止 // 使需要读取的区域 Cache 失效(强制从 DDR 重新加载) u32 start_addr = VDMA_FRAME0 + ROW_START * STRIDE + COL_START * 2; u32 end_addr = VDMA_FRAME0 + ROW_END * STRIDE + COL_END * 2 + 2; u32 region_len = end_addr - start_addr; Xil_DCacheInvalidateRange(start_addr, region_len); // 读取并打印 112×112 像素 for (int row = ROW_START; row <= ROW_END; row++) { u32 row_addr = VDMA_FRAME0 + row * STRIDE + COL_START * 2; for (int col = COL_START; col <= COL_END; col++) { unsigned short pixel = *(volatile unsigned short *)(row_addr + (col - COL_START) * 2); printf("%04X", pixel); if (col < COL_END) printf(" "); } printf("\n"); } // 恢复 VDMA 写通道 restore_vdma_write(); } } last_state = current; usleep(10000); // 轮询间隔 10ms } return 0; }

浙公网安备 33010602011771号