Day9下午

工作目标:通过按键控制,让需要打印的信息在窗口里打印出来

打印的工作是在 PS 端完成的,读取的数据是 DDR 给的

首先要把 UART 的窗口打开,其次是加一个按键的输入控制


基于上午的 GPIO 工程,在 ZYNQ 的配置这里

image

 再加一个 GPIO,作为信号输入端

由于这部分是按键 → GPIO 端口 → HI 总线 → PS 端,所以选 Input 就行了

按键这里只有一个,改成 1

image

 如果这个时候直接自动连线,那该模块输出端会产生一个端口

正合我意,图省事就可以直接自动连线了

image

HI 总线的好处就在于,他可以很方便地跟多个外设进行连接,解决大多数的握手信号传递


 

这里还要加一个约束,gpio_rtl_tri_i 是因为 GPIO 不知用户要输入/输出,所以自动会加上一个 tri_i 后缀

image

 导出,直到 vitis 这一步

他有点小 BUG,要再手动开一下才行,当然,他自动开了当然好

image

image


 

算个数 

咱们打印二值化的区域,是$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 秒

image

 这4秒内是不希望 DDR 里面的东西被改写,以防内存被替换

所以在每次打印的时候,想办法把堆积 MA 写通道给关掉,这个的代码让 AI 写会存在一些 BUG,他一次写不对

关注写通道部分代码:

第一句 0x1 代表打开,后面是对寄存器配置了保存视频数据的起始地址

image

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

image

手搓到这个地步,可以用 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;
}
给出的代码

 

posted @ 2026-07-31 11:45  little_sheep_xiaoen  阅读(9)  评论(0)    收藏  举报