OV5640图像采集(一)VGA显示
vga控制器模块
1 引言
项目的背景是采集无人车间现场的工件图像并送往控制间pc端处理,最终实现缺陷检测。项目包括图像采集模块,数据传输模块,上位机,缺陷检测算法等四个部分。其中,图像采集模块又分为摄像头配置及数据采集,输入缓存FIFO,SDRAM读写控制器,输出缓存DIDO,VGA/HDMI显示等部分,共大大小小18个模块,之后我将陆续进行更新讲解每一个子模块。
在图像采集模块中第一部分要介绍的是VGA显示模块,同时为了更好的兼容性,我们也设置了VGA转HDMI模块供调用。关于VGA的原理教程有很多珠玉在前这里就不进行赘述了。我们只说明一点,就是为了更好的显示车间内图像采集的情况,我们希望能够实时显示画面,因此加入了显示模块,同时在显示的图像上添加了一个LOGO图片。这个LOGO的加入有如下几种添加方式:
1.摄像头输出数据我们是无法编辑的,但可以选择在存入输入缓存FIFO前将图标与摄像头输出的数据进行融合
2.可以选择在SDRAM从输入缓存FIFO中读数据时融合,当然这样是很繁琐的
3.在从SDRAM写入数据至输出缓存FIFO时进行融合,原理与2类似
4.在VGA从输出缓存FIFO读出数据后,对数据进行融合和显示
5.保留VGA数据不变,我们在VGA转HDMI模块中对数据进行编辑
在此处,为了不让LOGO干扰到我们后续的缺陷检测我们不选择123方案,为了保证两种显示方案都能显示LOGO我们不选5,因此最终敲定的是方案4。
2 实现
2.1 LOGO数据生成
首先,我们需要将LOGO的图像数据预先存入ROM中,因此需要生成.coe文件,而此次图标的大小为222x98,位宽16bit,这么大的数据量我们不可能手动生成,在这里我们借用了野火的程序,可以将图片自动转化为coe文件
clear;
clc;
img = imread('image.bmp'); %读取图片
% 使用size函数计算图片矩阵三个维度的大小
% 第一维为图片的高度,第二维为图片的宽度,第三维为图片维度
[height,width,z]=size(img); % 100*100*3
red = img(:,:,1); % 提取红色分量,数据类型为uint8
green = img(:,:,2); % 提取绿色分量,数据类型为uint8
blue = img(:,:,3); % 提取蓝色分量,数据类型为uint8
% 使用reshape函数将各个分量重组成一个一维矩阵
%为了避免溢出,将uint8类型的数据扩大为uint32类型
r = uint32(reshape(red' , 1 ,height*width));
g = uint32(reshape(green' , 1 ,height*width));
b = uint32(reshape(blue' , 1 ,height*width));
% 初始化要写入.coe文件中的RGB颜色矩阵
rgb=zeros(1,height*width);
% 导入的图片为24bit真彩色图片,每个像素占用24bit,RGB888
% 将RGB888转换为RGB565
% 红色分量右移3位取出高5位,左移11位作为ROM中RGB数据的第15bit到第11bit
% 绿色分量右移2位取出高6位,左移5位作为ROM中RGB数据的第10bit到第5bit
% 蓝色分量右移3位取出高5位,左移0位作为ROM中RGB数据的第4bit到第0bit
for i = 1:height*width
rgb(i) = bitshift(bitshift(r(i),-3),11)+ bitshift(bitshift(g(i),-2),5)+ bitshift(bitshift(b(i),-3),0);
end
fid = fopen('image.coe', 'w+'); %创建COE文件
fprintf(fid, 'memory_initialization_radix=16;\n'); %表明数据进制为16进制
fprintf(fid, 'memory_initialization_vector=\n');
% m = size(img); %获取图片尺寸,m(1)为高,m(2)为宽
% for i = 1:m(1)
% for j = 1:m(2)
% % 将RGB数据写在一起
% fprintf(fid, '%02X%02X%02X,\n', img(i,j,1), ... % R
% img(i,j,2), ... % G
% img(i,j,3)); % B
% end
% end
for i = 1:height*width
fprintf(fid,'%04X,\n',rgb(i));
end
fseek(fid, -2, 1); % 将最后一个逗号用分号覆盖
fprintf(fid, ';');
fclose(fid); %关闭文件
2.2 生成ROM的IP
得到了.coe文件后,我们就可以在VIVADO工程中生成ROM的P核了,配置的参数如下:

图1 IP核配置页1

图2 IP核配置页2

图3 IP核配置页3

图4 IP核配置页4
如图2,此处我们选用了带有使能的ROM,因此后续工程需要生成时序对齐的使能和地址信号;同时图4中我们注意到,输出数据的latency是两个clock,我们需要把使能信号打两派和数据对齐。
2.3 verilog编写
还有一点需要注意的是,我们在屏幕的左上角保留了50/20个像素的空间,使LOGO显示位置更恰当;同时,去除了空白部分(即值为16'h f7bf的部分,这个值可以在.coe文件中查看),使得显示效果更好,最终模块全部的代码如下:
//**************************************************************************
// *** 名称 : vga_ctrl.v
// *** 作者 : 吃豆熊
// *** 日期 : 2021-11-22
// *** 描述 : vga时序控制模块,驱动vga将输入模块的彩色像素的信息扫描至显示器上
//**************************************************************************
// *** 版本 : 2022-4-1修改为 V2.0
// *** 修订 : 添加了logo显示
//**************************************************************************
module vga_ctrl
//========================< 参数 >==========================================
#(
parameter H_SYNC = 12'd40 , //行同步
H_BACK = 12'd220 , //行时序后沿
H_LEFT = 12'd0 , //行时序左边框
H_VALID = 12'd1280, //行有效数据
H_RIGHT = 12'd0 , //行时序右边框
H_FRONT = 12'd110 , //行时序前沿
H_TOTAL = 12'd1650, //行扫描周期
parameter V_SYNC = 12'd5 , //场同步
V_BACK = 12'd20 , //场时序后沿
V_TOP = 12'd0 , //场时序左边框
V_VALID = 12'd720 , //场有效数据
V_BOTTOM = 12'd0 , //场时序右边框
V_FRONT = 12'd5 , //场时序前沿
V_TOTAL = 12'd750 //场扫描周期
)
//========================< 端口 >==========================================
(
input wire vga_clk,
input wire sys_rst_n,
input wire [15:0] pix_data, //数据输入
output reg pix_data_req,
output wire hsync, //行同步信号
output wire vsync, //场同步信号
output reg [15:0] rgb, //输出数据
output wire VGA_BLK //输出有效信号
);
//========================< 定义 >==========================================
parameter PIC_LEN = 8'd222; //图像长度
parameter PIC_HGT = 7'd98; //图像宽度
parameter PIC_SIZE = 15'd21756; //图像大小
parameter PIC_LSPACE = 6'd50; //图像左侧预留宽度
parameter PIC_HSPACE = 5'd20; //图像上方预留宽度
//========================< 信号 >==========================================
//vga控制信号
reg [11:0] hsync_cnt; //行同步信号计数器
reg [11:0] vsync_cnt; //行同步信号计数器
reg rgb_vld; //图像显示有效信号
//lolo数据切换辅助信号
wire rd_en; //rom数据提前读使能
reg rd_en_reg; //rom数据提前读使能打牌
reg data_sel; //logo/摄像头数据选择信号
wire [15:0] logo_data; //logo数据生成
reg [14:0] rom_addr; //rom数据读地址
//==========================================================================
//== 行同步信号控制
//==========================================================================
always @(posedge vga_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
hsync_cnt <= 12'b0;
end
else if (hsync_cnt == H_TOTAL - 1'b1) begin
hsync_cnt <= 12'b0;
end
else begin
hsync_cnt <= hsync_cnt + 1'b1;
end
end
assign hsync = ((hsync_cnt <= H_SYNC - 1'b1)&&(sys_rst_n == 1'b1)) ? 1'b1 : 1'b0;
//==========================================================================
//== 场同步信号控制
//==========================================================================
always @(posedge vga_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
vsync_cnt <= 12'b0;
end
else if (vsync_cnt == V_TOTAL - 1'b1) begin
vsync_cnt <= 12'b0;
end
else if (hsync_cnt == H_TOTAL - 1'b1) begin
vsync_cnt <= vsync_cnt + 1'b1;
end
end
assign vsync = ((vsync_cnt <= V_SYNC - 1'b1)&&(sys_rst_n == 1'b1)) ? 1'b1 : 1'b0;
//==========================================================================
//== 生成图像有效区域
//==========================================================================
always @(posedge vga_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
rgb_vld <= 1'b0;
end
else if ((hsync_cnt >= H_SYNC+H_BACK+H_LEFT-1'd1)&&(hsync_cnt <= H_TOTAL-H_RIGHT-H_FRONT-2'd2)&&
(vsync_cnt >= V_SYNC+V_BACK+V_TOP)&&(vsync_cnt <= V_SYNC+V_BACK+V_TOP+V_VALID-1'b1)) begin
rgb_vld <= 1'b1;
end
else begin
rgb_vld <= 1'b0;
end
end
assign VGA_BLK = rgb_vld;
//==========================================================================
//== 准备显示图像的请求信号
//==========================================================================
always @(posedge vga_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
pix_data_req <= 1'b0;
end
else if ((hsync_cnt >= H_SYNC+H_BACK+H_LEFT-2'd2)&&(hsync_cnt <= H_TOTAL-H_RIGHT-H_FRONT-2'd3)&&
(vsync_cnt >= V_SYNC+V_BACK+V_TOP)&&(vsync_cnt <= V_SYNC+V_BACK+V_TOP+V_VALID-1'b1)) begin
pix_data_req <= 1'b1;
end
else begin
pix_data_req <= 1'b0;
end
end
//==========================================================================
//== 图片数据载入
//==========================================================================
//提前读取rom数据
assign rd_en = ((hsync_cnt > H_SYNC+H_BACK+H_LEFT-1'b1+6'd50)&&(hsync_cnt <= H_SYNC+H_BACK+H_LEFT+PIC_LEN-1'b1+6'd50)&&
(vsync_cnt > V_SYNC+V_BACK+V_TOP+5'd20)&&(vsync_cnt <= V_SYNC+V_BACK+V_TOP+PIC_HGT+5'd20));
//logo/摄像头数据切换
always @(posedge vga_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
rd_en_reg <= 1'b0;
data_sel <= 1'b0;
end
else begin
rd_en_reg <= rd_en;
data_sel <= rd_en_reg;
end
end
//rom读地址信号生成
always @(posedge vga_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
rom_addr <= 15'd0;
end
else if (rom_addr == (PIC_SIZE - 1'b1)) begin
rom_addr <= 15'd0;
end
else if (rd_en == 1'b1) begin
rom_addr <= rom_addr + 1'b1;
end
end
//rom模块例化
rom_16x21756 ins_rom_16x21756
(
.clka (vga_clk ), // input wire clka
.ena (rd_en ), // input wire ena
.addra (rom_addr ), // input wire [14 : 0] addra
.douta (logo_data ) // output wire [15 : 0] douta
);
//==========================================================================
//== 有效数据写入
//==========================================================================
always @(*) begin
if (rgb_vld == 1'b0) begin
rgb <= 16'd0;
end
else if ((data_sel == 1'b1) && (logo_data != 16'hf7bf)) begin
rgb <= logo_data;
end
else begin
rgb <= pix_data;
end
end
endmodule
3 展示
由于模块非常简单,而且在V1.0版本已经进行过了仿真,在这里我们不再进行仿真,而是直接展示实际的视频效果
---
原创教程,转载请注明出处吃豆熊-异步复位同步释放
参考资料:野火FPGA开发教程
OV5640图像采集(一)VGA显示的更多相关文章
- VmodCAM图像采集 VGA显示
先上图 总体框图 效果图 效果不是很好,因为暂时用的是zedboard自带的VGA,其只能RGB只有3*3*3的彩色度 VmodCAM原理图 VmodCAM的zedboard管脚约束见:http:// ...
- VGA显示原理
VGA显示是图像处理的基础,是一开始广泛使用的显示器,大部分机器采用VGA接口驱动,所以后来的显示器需要用VGA-xxx转接口来匹配. 用FPGA来驱动VGA,并不适用于显示动态(如手机显示,GUI) ...
- zedboard VmodCAM 图像采集 HDMI输出显示
本文叙述zedboard VmodCAM 图像采集 HDMI输出显示 参考: 1.color space详解资料 http://download.csdn.net/detail/xiabodan/ ...
- vga显示彩条
vga显示驱动程序可分为扫描行列和行列同步两个部分 //注意:只有在有效区域内给vga赋值才会有颜色变化 assign vga_b = isready ? vga_s[:] :'d0; assign ...
- 基于FPGA的VGA显示静态图片
终于熬到暑假了,记过三四周的突击带考试,终于为我的大二画上了一个完整的句号,接下来终于可以静心去做自己想做的事情了,前一阵子报了一个线上培训班,学学Sobel边缘检测,之前一直在学习图像处理,但是因为 ...
- 基于FPGA的Uart接收图像数据至VGA显示
系统框图 前面我们设计了基于FPGA的静态图片显示,接下来我们来做做基于FPGA的动态图片显示,本实验内容为:由PC端上位机软件通过串口发送一幅图像数据至FPGA,FPGA内部将图像数据存储,最后扫描 ...
- 纠错:基于FPGA串口发送彩色图片数据至VGA显示
今天这篇文章是要修改之前的一个错误,前面我写过一篇基于FPGA的串口发送图片数据至VGA显示的文章,最后是显示成功了,但是显示的效果图,看起来确实灰度图,当时我默认我使用的MATLAB代码将图片数据转 ...
- 基于FPGA驱动VGA显示图片的小问题
学习VGA显示图片的过程中,遇到了一个小问题,我在显示屏上开了一个60x60的框,放了一张图片进去显示,但是最终的结果如下图所示. 出现了一个竖黑边,看了看代码,分析了一下逻辑没问题,然而看这个显示那 ...
- FPGA驱动VGA显示静态图片
一 .前言 本文设计思想采用明德扬至简设计法.VGA是最常见的视频显示接口,时序也较为简单.本文从利用显示屏通过VGA方式显示测试图案及静态图片着手带大家接触图像显示应用,算是为后续VGA显示摄像头采 ...
随机推荐
- 学习SpringMVC必知必会(7)~springmvc的数据校验、表单标签、文件上传和下载
输入校验是 Web 开发任务之一,在 SpringMVC 中有两种方式可以实现,分别是使用 Spring 自带的验证 框架和使用 JSR 303 实现, 也称之为 spring-validator 和 ...
- Ubuntu一键安装LAMP,LNMP
参考: https://blog.csdn.net/xueyingqi/article/details/50674078 https://www.cnblogs.com/pengzheng/p/363 ...
- jinja2.exceptions.TemplateNotFound 报错处理
一.检查模板文件夹是否正确. 遇到这个问题,首先需要检查你的模板文件夹命名是否规范,Flask默认会在工程下寻找templates文件夹,这个是默认配置,不能写成template或者其他名字.当然,如 ...
- java反射和动态代理实现与原理详细分析
关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理. 一.代理模式 代理模式是常用的java设计模式, ...
- SpringBoot中的日志使用:
SpringBoot中的日志使用(一) 一:日志简介: 常用的日志接口 commons-logging/slf4j 日志框架:log4j/logback/log4j2 日志接口屏蔽了日志框架的底层实现 ...
- 谈一谈 Kafka 的再均衡?
在Kafka中,当有新消费者加入或者订阅的topic数发生变化时,会触发Rebalance(再均衡:在同一个消费者组当中,分区的所有权从一个消费者转移到另外一个消费者)机制,Rebalance顾名思义 ...
- JVM-learning
JVM是什么?? Java Virtual Mechine JRE(JavaRuntimeEnvironment,Java运行环境),也就是Java平台.所有的Java 程序都要在JRE下才能运行. ...
- 学习Nginx(二)
Nginx支持四层代理 http://nginx.org/en/docs/stream/ngx_stream_core_module.html 该ngx_stream_core_module模块自1. ...
- 【Azure Developer】使用PowerShell Where-Object方法过滤多维ArrayList时候,遇见的诡异问题 -- 当查找结果只有一个对象时,返回结果修改了对象结构,把多维变为一维
问题描述 编写PowerShell脚本,以多维(3维)数组中第二维度的值进行过滤,并打印出结果 #三维数组源数据 "A", "11", "Cheng ...
- STM32 之 HAL库(固件库)
1 STM32的三种开发方式 通常新手在入门STM32的时候,首先都要先选择一种要用的开发方式,不同的开发方式会导致你编程的架构是完全不一样的.一般大多数都会选用标准库和HAL库,而极少部分人会通过直 ...