Sobel边沿检测

前言

        Sobel边沿检测是一种用于图像处理的边缘检测技术。它通过计算图像的梯度来识别图像中的边缘。Sobel算子是一种常用的边缘检测滤波器,它利用两个卷积核来分别检测水平和垂直方向的边缘。Sobel边沿检测在很多图像处理应用中都很有用,比如图像分割、目标检测等。它的优点是计算简单且效果较好,但在噪声较多的图像中可能需要结合其他方法进行处理。

正文

一、Sobel边沿检测

        1.项目需求

        通过RS232将原始图片的灰度图像数据发送到FPGA,FPGA实验Sobel算法对图像进行处理,将处理完的数据通过VGA接口在640*480显示器上显示。

        2.技术介绍

        边缘检测,针对的是灰度图像,顾名思义,检测图像的边缘,是针对图像像素点的一种计算,目的是标识数字图像中灰度变化明显的点,图像的边缘检测,在保留了图像的重要结构信息的同时,剔除了可以认为不相关的信息,大幅度减少了数据量,便于图像的传输和处理。基于查找,零穿越的方法,Sobel算法准确率较低,效率高,基于Sobel算子。

        3.顶层架构

        4.端口描述

clk_in时钟(50Mhz)
rst_n_in系统复位信号
rxRS232数据接收
[7:0]data显示颜色数据
hx行同步信号
vx场同步信号
txRS232数据发送

二、代码验证

数据生成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时显示白色,由此提取出图像边缘。

数据提取成功,数据对比正确,实验成功。

参考资料

边缘检测

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张明阳.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值