前言
Sobel边沿检测是一种用于图像处理的边缘检测技术。它通过计算图像的梯度来识别图像中的边缘。Sobel算子是一种常用的边缘检测滤波器,它利用两个卷积核来分别检测水平和垂直方向的边缘。Sobel边沿检测在很多图像处理应用中都很有用,比如图像分割、目标检测等。它的优点是计算简单且效果较好,但在噪声较多的图像中可能需要结合其他方法进行处理。
正文
一、Sobel边沿检测
1.项目需求
通过RS232将原始图片的灰度图像数据发送到FPGA,FPGA实验Sobel算法对图像进行处理,将处理完的数据通过VGA接口在640*480显示器上显示。
2.技术介绍
边缘检测,针对的是灰度图像,顾名思义,检测图像的边缘,是针对图像像素点的一种计算,目的是标识数字图像中灰度变化明显的点,图像的边缘检测,在保留了图像的重要结构信息的同时,剔除了可以认为不相关的信息,大幅度减少了数据量,便于图像的传输和处理。基于查找,零穿越的方法,Sobel算法准确率较低,效率高,基于Sobel算子。
3.顶层架构
4.端口描述
clk_in | 时钟(50Mhz) |
rst_n_in | 系统复位信号 |
rx | RS232数据接收 |
[7:0]data | 显示颜色数据 |
hx | 行同步信号 |
vx | 场同步信号 |
tx | RS232数据发送 |
二、代码验证
数据生成MATLAB
clear %清零命令窗口
clc %清理工作区
RGB = imread('abc.bmp');%图片输入
%100*100*3
red = RGB(:,:, 1);%红色分量提取
figure;%创建窗口
imshow(red);
[height,width,z] = size(red);
% rgb矩阵初始化
data = zeros( 1 ,height*width);
% 转换为8位数据
for i = 1:height
for j = 1:width
data((i-1)*width + j ) = bitshift(red(i,j),-5);%红色数据右移动5位
end
end
fid = fopen ('data.txt', 'w+');%新建文件
for i = 1:height*width
fprintf(fid,'%02x ',data(i));
end
fclose(fid);
sobel算法模块
module sobel_ctrl1(
input clk ,
input rst_n ,
input [7:0] data ,//rs232传输到FPGA的数据
input flag ,
output reg[7:0]data_out ,//求和结果
output reg out_flag //输出使能
);
parameter l_max = 'd99,//列数
h_max = 'd99;//行数
parameter Th = 8'b000_011_00; //灰度阈值
parameter black = 8'b000_000_00,//颜色
white = 8'b111_111_11;
reg [9:0]l_cnt ;//列计数器
reg [9:0]h_cnt ;//行计数器
reg fifo1_en ;//fifo_1写使能
reg [7:0]fifo1_data;//fifo_1写数据
reg fifo2_en ;//fifo_2写使能
reg [7:0]fifo2_data;//fifo_2写数据
reg rd_en;//读使能信号
wire [7:0]data1; //fifo1读出数据
wire [7:0]data2; //fifo2读出数据
reg add_en;//求和标志信号
reg data_flag;//fifo1的写使能信号
reg [7:0]cnt_rd;//寄存计数器
reg [7:0]data1_r;
reg [7:0]data2_r;
reg [7:0]data_r;
reg rd_en_reg;
reg re_en_reg1;
reg [7:0]a3;//计算矩阵
reg [7:0]b3;//计算矩阵
reg [7:0]c3;//计算矩阵
reg [7:0]a2;//计算矩阵
reg [7:0]b2;//计算矩阵
reg [7:0]c2;//计算矩阵
reg [7:0]a1;//计算矩阵
reg [7:0]b1;//计算矩阵
reg [7:0]c1;//计算矩阵
reg gx_gy_flag;//计算标志信号
reg [8:0]gx;//计算
reg [8:0]gy;//计算
reg gxy_flag;//输出有效
reg [7:0]gxy;//计算结果
reg com_flag;//比较
always@(posedge clk,negedge rst_n)//列计数器驱动
begin
if(rst_n == 0)
l_cnt <= 10'd0;
else
if((l_cnt == l_max)&&(flag == 1'b1))
l_cnt <= 10'd0;
else
if(flag == 1'b1)
l_cnt <= l_cnt + 10'd1;
else
l_cnt <= l_cnt;
end
always@(posedge clk,negedge rst_n)//行计数器驱动
begin
if(rst_n == 0)
h_cnt <= 10'd0;
else
if((h_cnt == h_max)&&(l_cnt == l_max)&&(flag == 1'b1))
h_cnt <= 10'd0;
else
if((l_cnt == l_max)&&(flag == 1'b1))
h_cnt <= h_cnt + 10'd1;
else
h_cnt <= h_cnt;
end
always@(posedge clk,negedge rst_n)//fifo_1写使能
begin
if(rst_n == 0)
fifo1_en <= 1'b0;
else
if((h_cnt == 0)&&(flag == 1'b1))
fifo1_en <= 1'b1;
else
fifo1_en <= data_flag;
end
always@(posedge clk,negedge rst_n)//fifo_1写数据
begin
if(rst_n == 0)
fifo1_data <= 8'd0;
else
if((h_cnt == 0)&&(flag == 1'b1))
fifo1_data <= data;
else
if(data_flag == 1'b1)
fifo1_data <= data2;
else
fifo1_data <= fifo1_data;
end
always@(posedge clk,negedge rst_n)//fifo_2写使能
begin
if(rst_n == 0)
fifo2_en <= 1'b0;
else
if((h_cnt >= 8'd1)&&(h_cnt <= h_max - 8'd1)&&(flag == 1'b1))
fifo2_en <= 1'b1;
else
fifo2_en <= 1'b0;
end
always@(posedge clk,negedge rst_n)//fifo_2写数据
begin
if(rst_n == 0)
fifo2_data <= 8'd0;
else
if((h_cnt >= 8'd1)&&(h_cnt <= h_max - 8'd1)&&(flag == 1'b1))
fifo2_data <= data;
else
fifo2_data <= fifo2_data;
end
always@(posedge clk,negedge rst_n)//fifo读写使能(共用)
begin
if(rst_n == 0)
rd_en <= 1'b0;
else
if((h_cnt >= 8'd2)&&(flag <= h_max)&&(flag == 1'b1))
rd_en <= 1'b1;
else
rd_en <= 1'b0;
end
always@(posedge clk,negedge rst_n)//fifo1的写使能信号
begin
if(rst_n == 0)
data_flag <= 1'b0;
else
if((fifo2_en == 1'b1)&&(rd_en == 1'b1))
data_flag <= 1'b1;
else
data_flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)//寄存计数器
begin
if(rst_n == 0)
cnt_rd <= 8'd0;
else
if((cnt_rd == l_max)&&(rd_en == 1'b1))
cnt_rd <= 8'd0;
else
if((rd_en == 1'b1))
cnt_rd <= cnt_rd + 8'd1;
else
cnt_rd <= cnt_rd;
end
always@(posedge clk,negedge rst_n)//数据寄存
begin
if(rst_n == 0)
begin
data1_r <= 8'd0;
data2_r <= 8'd0;
data_r <= 8'd0;
end
else
if(rd_en_reg == 1'b1)
begin
data1_r <= data1;
data2_r <= data2;
data_r <= data;
end
else
begin
data1_r <= data1_r;
data2_r <= data2_r;
data_r <= data_r ;
end
end
always@(posedge clk,negedge rst_n)//数据寄存读使能信号
begin
if(rst_n == 0)
rd_en_reg <= 1'b0;
else
if(rd_en == 1'b1)
rd_en_reg <= 1'b1;
else
rd_en_reg <= 1'b0;
end
always@(posedge clk,negedge rst_n)//数据寄存读使能信号打排
begin
if(rst_n == 0)
re_en_reg1 <= 1'b0;
else
if(rd_en_reg == 1'b1)
re_en_reg1 <= 1'b1;
else
re_en_reg1 <= 1'b0;
end
always@(posedge clk,negedge rst_n)//计算矩阵赋值
begin
if(rst_n == 0)
begin
a3 <= 8'd0;
b3 <= 8'd0;
c3 <= 8'd0;
a2 <= 8'd0;
b2 <= 8'd0;
c2 <= 8'd0;
a1 <= 8'd0;
b1 <= 8'd0;
c1 <= 8'd0;
end
else
begin
a3 <= data1_r;
b3 <= data2_r;
c3 <= data_r;
a2 <= a3;
b2 <= b3;
c2 <= c3;
a1 <= a2;
b1 <= b2;
c1 <= c2;
end
end
always@(posedge clk,negedge rst_n)//计算使能信号
begin
if(rst_n == 0)
gx_gy_flag <= 1'b0;
else
if((re_en_reg1 == 1'b1)&&((cnt_rd >= 8'd3)||(cnt_rd == 8'd0)))
gx_gy_flag <= 1'b1;
else
gx_gy_flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)//计算准备
begin
if(rst_n == 0)
gx <= 9'b0;
else
if(gx_gy_flag == 1'b1 )
gx <= (a3-a1)+((b3-b1)<<1)+(c3-c1);
else
gx <= gx;
end
always@(posedge clk,negedge rst_n)//计算准备
begin
if(rst_n == 0)
gy <= 9'b0;
else
if(gx_gy_flag == 1'b1 )
gy <= (a1-c1)+((a2-c2)<<1)+(a3-c3);
else
gy <= gy;
end
always@(posedge clk,negedge rst_n)//计算标志信号
begin
if(rst_n == 0)
gxy_flag <= 1'b0;
else
if(gx_gy_flag == 1'b1)
gxy_flag <= 1'b1;
else
gxy_flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)//计算标志信号
begin
if(rst_n == 0)
gxy <= 8'b0;
else
if((gx[8] == 1'b1)&&(gy[8] == 1'b1)&&(gxy_flag == 1'b1))//--
gxy <= (~gx[7:0] +1'b1) + (~gy[7:0] + 1'b1);
else
if((gx[8] == 1'b1)&&(gy[8] == 1'b0)&&(gxy_flag == 1'b1))//-+
gxy <= (~gx[7:0] +1'b1) + gy[7:0];
else
if((gx[8] == 1'b0)&&(gy[8] == 1'b1)&&(gxy_flag == 1'b1))//+-
gxy <= gx[7:0] + (~gy[7:0] + 1'b1);
else
if((gx[8] == 1'b0)&&(gy[8] == 1'b0)&&(gxy_flag == 1'b1))//++
gxy <= gx[7:0] + gy[7:0];
else
gxy <= gxy;
end
always@(posedge clk,negedge rst_n)//比较信号
begin
if(rst_n == 0)
com_flag <= 1'b0;
else
if(gxy_flag == 1'b1)
com_flag <= 1'b1;
else
com_flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)//结果输出
begin
if(rst_n == 0)
data_out <= 8'd0;
else
if((com_flag == 1'b1)&&(gxy > Th))
data_out <= black;
else
if(com_flag == 1'b1)
data_out <= white;
else
data_out <= data_out;
end
always@(posedge clk,negedge rst_n)//结果输出使能
begin
if(rst_n == 0)
out_flag <= 1'b0;
else
if(com_flag == 1'b1)
out_flag <= 1'b1;
else
out_flag <= 1'b0;
end
fifo_sobel fifo1_inst (
.clock ( clk ),
.data ( fifo1_data ),
.rdreq ( rd_en ),
.wrreq ( fifo1_en ),
.q ( data1 )
);
fifo_sobel fifo2_inst (
.clock ( clk ),
.data ( fifo2_data ),
.rdreq ( rd_en ),
.wrreq ( fifo2_en ),
.q ( data2 )
);
endmodule
RS232数据接收
module uart_rx(
input clk ,
input rst_n ,
input rx ,
output reg[7:0]po_data , //接收到的数据
output reg po_flag //数据输出有效
);
parameter uart_btl ='d9600 ;//串口波特率
parameter clk_shuj ='d50_000_000 ;//时钟频率
parameter cnt_max =clk_shuj/uart_btl;
reg reg1,reg2,reg3 ;//打排延迟周期,消除亚稳态
reg flag ;//uart工作信号(uart工作时在工作状态切换后产生一个时钟周期高电平)
reg en ;//uart工作使能标志信号(uart工作时在工作状态切换后的下一个时钟周期开始一直拉高,直到检测到停止信号)
reg [15:0] cnt ;//每比特数据持续时钟周期计数器
reg [3 :0] bit_cnt ;//数据个数计数器
reg bit_flag ;//每bit数据接收有效信号
reg [7 :0] rx_data ;//存储输入数据
reg rx_flag ;//输入数据并位结束信号
always@(posedge clk,negedge rst_n)//数据打排1
begin
if(rst_n == 0)
reg1 <= 1'b1;
else
reg1 <= rx;
end
always@(posedge clk,negedge rst_n)//数据打排2
begin
if(rst_n == 0)
reg2 <= 1'b1;
else
reg2 <= reg1;
end
always@(posedge clk,negedge rst_n)//数据打排3
begin
if(rst_n == 0)
reg3 <= 1'b1;
else
reg3 <= reg2;
end
always@(posedge clk,negedge rst_n)//uart工作信号赋值
begin
if(rst_n == 0)
flag <= 1'b0;
else
if((reg2 == 1'b0)&&(reg3 == 1'b1)&&(en == 1'b0))
flag <= 1'b1;
else
flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)//uart工作使能标志信号赋值
begin
if(rst_n == 0)
en <= 1'b0;
else
if(flag == 1'b1)
en <= 1'b1;
else
if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))
en <= 1'b0;
else
en <= en;
end
always@(posedge clk,negedge rst_n)//每比特数据持续时钟周期计数器驱动逻辑
begin
if(rst_n == 0)
cnt <= 16'd0;
else
if((cnt == cnt_max - 1)||(en == 1'b0))
cnt <= 16'd0;
else
cnt = cnt + 1'b1;
end
always@(posedge clk,negedge rst_n)//每比特数据持续时钟周期计数器驱动逻辑
begin
if(rst_n == 0)
bit_flag <= 1'b0;
else
if(cnt == cnt_max/2 - 1)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)//数据个数计数器驱动逻辑
begin
if(rst_n == 0)
bit_cnt <= 4'd0;
else
if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))
bit_cnt <= 4'd0;
else
if(bit_flag == 1'b1)
bit_cnt <= bit_cnt + 1'b1;
else
bit_cnt <= bit_cnt;
end
always@(posedge clk,negedge rst_n)//接收数据并位
begin
if(rst_n == 0)
rx_data <= 8'd0;
else
if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
rx_data <= {reg3,rx_data[7:1]};
end
always@(posedge clk,negedge rst_n)//输入数据并位结束信号
begin
if(rst_n == 0)
rx_flag <= 1'b0;
else
if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))
rx_flag <= 1'b1;
else
rx_flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)//输出数据传递
begin
if(rst_n == 0)
po_data <= 8'd0;
else
if(rx_flag == 1'b1)
po_data <= rx_data;
else
po_data <= po_data;
end
always@(posedge clk,negedge rst_n)//输出使能
begin
if(rst_n == 0)
po_flag <= 1'b0;
else
po_flag <= rx_flag;
end
endmodule
RS232数据发送
module uart_tx(
input clk ,
input rst_n ,
input [7:0] pi_data ,
input pi_flag ,
output reg tx
);
parameter uart_btl ='d9600 ;//串口波特率
parameter clk_shuj ='d50_000_000 ;//时钟频率
parameter cnt_max =clk_shuj/uart_btl;
reg en ;
reg [15:0] cnt ;//每bit数据传输完成计数器
reg flag ;//
reg [3 :0] bit_cnt ;//bit计数器
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
en <= 1'b0;
else
if(pi_flag == 1'b1)
en <= 1'b1;
else
if((bit_cnt == 4'd9)&&(flag == 1'b1))
en <= 1'b0;
else
en <= en;
end
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt <= 16'd0;
else
if((en == 1'b0)||(cnt == cnt_max - 1'b1))
cnt <= 16'd0;
else
if(en == 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
end
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
flag <= 1'b0;
else
if(cnt == 16'd1)
flag <= 1'b1;
else
flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
bit_cnt <= 4'd0;
else
if((bit_cnt == 4'd9)&&(flag == 1'b1))
bit_cnt <= 4'd0;
else
if((en == 1'b1)&&(flag == 1'b1))
bit_cnt <= bit_cnt + 1'b1;
else
bit_cnt <= bit_cnt;
end
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
tx <= 1'b1;
else
if(flag == 1'b1)
case(bit_cnt)
0: tx <= 1'b0;
1: tx <= pi_data[0];
2: tx <= pi_data[1];
3: tx <= pi_data[2];
4: tx <= pi_data[3];
5: tx <= pi_data[4];
6: tx <= pi_data[5];
7: tx <= pi_data[6];
8: tx <= pi_data[7];
9: tx <= 1'b1;
default :tx <= 1'b1;
endcase
end
endmodule
VGA模块顶层
module vga_top(
input clk_ram ,
input clk ,
input rst_n ,
input[7:0] po_data ,
input po_flag ,
output vga_hs ,
output vga_vs ,
output[7:0] vga_rgb
);
wire volid_en;//数据输出有效
wire [7:0]vga_rgb_t;//显示数据
wire [9:0]vga_h;//x
wire [9:0]vga_v;//y
vga_ctrl vga_ctrl(
.clk (clk ),
.rst_n (rst_n ),
.in_rgb (vga_rgb_t ),
.volid_en ( ),
.vga_hs (vga_hs ),
.vga_vs (vga_vs ),
.vga_h (vga_h ),//坐标
.vga_v (vga_v ),//坐标
.vga_rgb (vga_rgb )
);
vga_data vga_data(
.clk_ram (clk_ram ),
.clk (clk ),
.rst_n (rst_n ),
.pix_x (vga_h ),
.pix_y (vga_v ),
.po_data (po_data ), //接收到的数据
.po_flag (po_flag ), //数据输出有效
.vga_rgb_t (vga_rgb_t )
);
endmodule
VGA数据生成
module vga_data(
input clk_ram ,
input clk ,
input rst_n ,
input[9:0] pix_x ,
input[9:0] pix_y ,
input[7:0] po_data , //接收到的数据
input po_flag , //数据输出有效
output [7:0] vga_rgb_t
);
parameter show_h = 10'd110,//定位点X坐标
show_v = 10'd100;//定位点Y坐标
parameter show_chan = 10'd256,//图片长度
show_kuan = 10'd32;//图片宽度
parameter show_1_rgb = 8'b000_000_00,//背景
show_0_rgb = 8'b111_100_00;//图片颜色
parameter pot_h = 10'd120,//定位点X坐标
pot_v = 10'd100;//定位点Y坐标
parameter chan = 10'd100,//图片长度
kuan = 10'd100;//图片宽度
reg [255:0] char [31:0];//存储字符数据
wire[9:0] char_x;
wire[9:0] char_y;
reg [07:0]vga_rgb;
wire[07:0]rom_data;//存储RAM数据
reg [13:0]addr;//ip核读地址
reg [13:0]wier;//ip核写地址
wire rd_en ;//ip核读使能
assign char_x =((pix_x >= show_h)&&(pix_x < (show_h + show_chan)))&&((pix_y >= show_v)&&(pix_y <(show_v + show_kuan)))?(pix_x - show_h):10'h3ff;
assign char_y =((pix_x >= show_h)&&(pix_x < (show_h + show_chan)))&&((pix_y >= show_v)&&(pix_y <(show_v + show_kuan)))?(pix_y - show_v):10'h3ff;
always@(posedge clk)
begin
char[0 ] <= 256'h0000000000000000000000000001000000200000000100000020000000010000;
char[1 ] <= 256'h0020000000010000003FFFFFFFFF0000003FFFFFFFFF0000003FFFFFFFFF0000;
char[2 ] <= 256'h003FFFFFFFFF0000003000180001000000300018000100000030001800010000;
char[3 ] <= 256'h0030001800010000003000180000000000300018000000000030001800000000;
char[4 ] <= 256'h003000180000000000300018000000000030003C000000000030007E00000000;
char[5 ] <= 256'h003803FFE00000000038000000000000003C000000000000003E000000000000;
char[6 ] <= 256'h003F8000000000000003C0000000000000006000000000000000000000000000;
char[7 ] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[8 ] <= 256'h0000000000000000000000000001000000200000000100000020000000010000;
char[9 ] <= 256'h0020000000010000003FFFFFFFFF0000003FFFFFFFFF0000003FFFFFFFFF0000;
char[10 ] <= 256'h003FFFFFFFFF0000003000060001000000300006000100000030000600010000;
char[11 ] <= 256'h0030000600010000003000060000000000300006000000000030000600000000;
char[12 ] <= 256'h00300006000000000038000E000000000038000E00000000001C001C00000000;
char[13 ] <= 256'h001E003C00000000000F80F800000000000FFFF8000000000007FFF000000000;
char[14 ] <= 256'h0001FFC00000000000007F000000000000000000000000000000000000000000;
char[15 ] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[16 ] <= 256'h00000000000000000000000000000000000000FFE000000000000FFFFE000000;
char[17 ] <= 256'h00007FFFFF8000000000FFFFFFE000000003FE001FF800000007E00000FC0000;
char[18 ] <= 256'h000F0000003C0000001E0000000E0000001C0000000600000038000000070000;
char[19 ] <= 256'h0030000000030000003000000003000000300000000300000030000100030000;
char[20 ] <= 256'h00300001000300000018000100060000001C0001800E0000001E0001FFFC0000;
char[21 ] <= 256'h001F8001FFF80000001FF001FFF8000000003801FFF800000000000180000000;
char[22 ] <= 256'h0000000100000000000000010000000000000001000000000000000000000000;
char[23 ] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[24 ] <= 256'h00000000000100000000000000010000000000000003000000000000001F0000;
char[25 ] <= 256'h0000000001FF0000000000001FE3000000000001FC0100000000001FC0010000;
char[26 ] <= 256'h000001FCC000000000001FC0C00000000001FC00C0000000001FC000C0000000;
char[27 ] <= 256'h003E0000C0000000007F0000C0000000007FF000C0000000003FFF00C0000000;
char[28 ] <= 256'h0001FFF0C000000000001FFFC0000000000001FFFC0100000000001FFFC10000;
char[29 ] <= 256'h00000001FFFF0000000000000FFF00000000000000FF000000000000000F0000;
char[30 ] <= 256'h0000000000030000000000000001000000000000000100000000000000000000;
char[31 ] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
vga_rgb <= show_1_rgb;
else
if( ((pix_x >= show_h - 1'b1)&&(pix_x < (show_h + show_chan - 1'b1)))
&& ((pix_y >= show_v )&&(pix_y <(show_v + show_kuan)))
&& (char[char_y][10'd255 - char_x] == 1'b1))
vga_rgb <= show_0_rgb;
else
vga_rgb <= show_1_rgb;
end
assign rd_en = (((pix_x >= pot_h)&&(pix_x < pot_h + chan))&&((pix_y >= pot_v)&&(pix_y <pot_v + kuan)))? 1'b1:1'b0;//ip核图片显示区域
always @(posedge clk,negedge rst_n)//读地址
begin
if(rst_n == 0)
addr <= 14'd0;
else
if(addr == 14'd9999)
addr <= 14'd0;
else
if(rd_en == 1'b1)
addr <= addr + 14'd1;
else
addr <= addr;
end
always @(posedge clk_ram,negedge rst_n)//写地址
begin
if(rst_n == 0)
wier <= 14'd0;
else
if((wier == 14'd9999)&&(po_flag == 1'b1))
wier <= 14'd0;
else
if(po_flag == 1'b1)
wier <= wier + 14'd1;
else
wier <= wier;
end
ram_vga ram_vga_inst (
.data ( po_data ),
.inclock ( clk_ram ),
.outclock ( clk ),
.rdaddress ( addr ),
.wraddress ( wier ),
.wren ( ~po_flag ),
.q ( rom_data )
);
assign vga_rgb_t = (rd_en)?rom_data:vga_rgb;
endmodule
VGA驱动模块
module vga_ctrl(
input clk ,
input rst_n ,
input[7:0] in_rgb ,
output volid_en ,
output vga_hs ,
output vga_vs ,
output[9:0] vga_h ,//坐标
output[9:0] vga_v ,//坐标
output[7:0] vga_rgb
);
reg [9:0] cnt_hs;//列计数器
reg [9:0] cnt_vs;//行计数器
wire hs_en;//列有效显示区域
wire vs_en;//行有效显示区域
//wire volid_en;//有效显示区域
parameter hs_sync = 10'd96 ,//同步
hs_bask = 10'd40 ,//后沿
hs_left = 10'd8 ,//左边
hs_vali = 10'd640 ,//有效
hs_righ = 10'd8 ,//右边
hs_fpon = 10'd8 ,//前沿
hs_tota = 10'd800 ;//总共
parameter vs_sync = 10'd2 ,//同步
vs_bask = 10'd25 ,//后沿
vs_left = 10'd8 ,//上边
vs_vali = 10'd480 ,//有效
vs_righ = 10'd8 ,//底边
vs_fpon = 10'd2 ,//前沿
vs_tota = 10'd525 ;//总共
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt_hs <= 10'd0;
else
if(cnt_hs < hs_tota - 10'd1)
cnt_hs <= cnt_hs + 10'd1;
else
cnt_hs <= 10'd0;
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt_vs <= 10'd0;
else
if(cnt_hs == hs_tota - 10'd1)
if(cnt_vs < vs_tota - 10'd1)
cnt_vs <= cnt_vs + 10'd1;
else
cnt_vs <= 10'd0;
else
cnt_vs <= cnt_vs;
end
assign vga_hs = (cnt_hs < hs_sync)?1'b0:1'b1;//行同步信号赋值
assign vga_h = (volid_en == 1'b1)?(cnt_hs - (hs_sync + hs_bask + hs_left)):10'd0;//行坐标(Y)
assign vga_vs = (cnt_vs < vs_sync)?1'b0:1'b1;//场同步信号赋值
assign vga_v = (volid_en == 1'b1)?(cnt_vs - (vs_sync + vs_bask + vs_left)):10'd0;//列坐标(X)
assign hs_en = ((cnt_hs >= hs_sync + hs_bask + hs_left )&&(cnt_hs < hs_sync + hs_bask + hs_left + hs_vali))?1'b1:1'b0;
assign vs_en = ((cnt_vs >= vs_sync + vs_bask + vs_left )&&(cnt_vs < vs_sync + vs_bask + vs_left + vs_vali))?1'b1:1'b0;
assign volid_en = hs_en & vs_en;//显示区域
assign vga_rgb = (volid_en == 1'b1)?in_rgb:8'd0;
endmodule
顶层连线
module sobel_vga_top (
input clk_in ,
input rst_n_in ,
input rx ,
output [7:0] data ,
output hx ,
output vx ,
output tx
);
wire clk;//25Mhz
wire rst_n;//复位信号
wire clk_ram;//50Mhz
wire[7:0]po_data;//接收到的数据
wire po_flag;//数据输出有效
wire[7:0]data_out;//边沿数据
wire out_flag;
pll_25 pll_25_inst (
.areset ( ~rst_n_in ),
.inclk0 ( clk_in ),
.c0 ( clk_ram),
.c1 ( clk ),
.locked ( rst_n )
);
uart_rx uart_rx(
.clk (clk_ram ),
.rst_n (rst_n ),
.rx (rx ),
.po_data (po_data ), //接收到的数据
.po_flag (po_flag ) //数据输出有效
);
sobel_ctrl1 sobel_ctrl1(
.clk (clk ),
.rst_n (rst_n ),
.data (po_data ),
.flag (po_flag ),
.data_out(data_out),
.out_flag(out_flag)
);
vga_top vga_top(
.clk_ram (clk_ram ),
.clk (clk ),
.rst_n (rst_n ),
.po_data (data_out),
.po_flag (out_flag),
.vga_hs (hx ),
.vga_vs (vx ),
.vga_rgb (data )
);
uart_tx uart_tx(
.clk (clk_ram ),
.rst_n (rst_n ),
.pi_data (data_out),
.pi_flag (out_flag),
.tx (tx )
);
endmodule
仿真模块
`timescale 1ns/1ps
module sobel_vga_top_tb;
reg clk ;
reg rst_n ;
reg rx ;
reg [7:0] data_a[9999:0];//数据存储器
wire tx ;
wire [7:0] data ;
wire hx ;
wire vx ;
sobel_vga_top fifo_add_top(
.clk_in (clk ),
.rst_n_in(rst_n ),
.rx (rx ),
.data (data ),
.hx (hx ),
.vx (vx ),
.tx (tx )
);
initial
$readmemh("E:/FPGA_exer/Sobel_vga_0906/doc/data.txt",data_a);//调用目标位置文件
initial clk = 1'b1;
always #10 clk = ~clk;
initial begin
rst_n = 1'b0;
rx = 1'b1;
#40
rst_n = 1'b1;
#200
re_byte();
#100000
$stop;
end
//璧嬪€煎嚱鏁
task rx_bit(input [7:0]data);
integer i;
for(i = 0;i < 10; i = i + 1)//寰幆9娆
begin
case(i)
0: rx <= 1'b0;
1: rx <= data[0];
2: rx <= data[1];
3: rx <= data[2];
4: rx <= data[3];
5: rx <= data[4];
6: rx <= data[5];
7: rx <= data[6];
8: rx <= data[7];
9: rx <= 1'b1;
endcase
#(5208*20);//姣忔寤舵椂
end
endtask
//defparam fifo_add_top.uart_rx.clk_shuj = 50_000;
task re_byte();
integer j;
for(j = 0;j < 10000;j = j + 1)
rx_bit(data_a[j]);//浼犻€鈥斺€锛涓暟鎹
endtask
endmodule
三、仿真验证
进行仿真,运行仿真代码,可以看到数据输出完整。
data_out在0使显示黑色,在255时显示白色,由此提取出图像边缘。
数据提取成功,数据对比正确,实验成功。
参考资料
边缘检测