Sobel边沿检测

news2024/11/15 1:38:35

前言

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

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

参考资料

边缘检测

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2111586.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot 集成 PDFBox 实现电子签章

Apache PDFBox 是一个开源的 Java 库&#xff0c;用于处理 PDF 文档。它提供了一系列强大的功能&#xff0c;包括创建、渲染、拆分、合并、加密、解密 PDF 文件&#xff0c;以及从 PDF 中提取文本和元数据等。PDFBox 支持 PDF 1.7 标准&#xff0c;并且兼容大多数现代 PDF 格式…

为何iPhone 16系列的发布对苹果至关重要?

即将发布的iPhone 16系列对苹果来说将是至关重要的时刻&#xff0c;特别是在快速发展的AI智能手机市场背景下。随着Android制造商在集成先进AI功能方面领先一步&#xff0c;苹果正处于一个关键的转折点——赶上竞争对手不仅仅是选择&#xff0c;而是必须完成的任务。 AI竞赛&am…

java设计模式(行为型模式:状态模式、观察者模式、中介者模式、迭代器模式、访问者模式、备忘录模式、解释器模式)

6&#xff0c;行为型模式 6.5 状态模式 6.5.1 概述 【例】通过按钮来控制一个电梯的状态&#xff0c;一个电梯有开门状态&#xff0c;关门状态&#xff0c;停止状态&#xff0c;运行状态。每一种状态改变&#xff0c;都有可能要根据其他状态来更新处理。例如&#xff0c;如果…

SpringCloud集成ELK

1、添加依赖 <dependency><groupId>net.logstash.logback</groupId><artifactId>logstash-logback-encoder</artifactId><version>6.1</version> </dependency>2、在logback-spring.xml中添加配置信息&#xff08;logback-sp…

Ubuntu 22.04 如何关闭防火墙

在Ubuntu 22.04中&#xff0c;默认的防火墙管理工具仍然是ufw&#xff08;Uncomplicated Firewall&#xff09;。您可以使用以下命令来关闭防火墙&#xff1a; 关闭防火墙&#xff1a; sudo ufw disable查看防火墙状态&#xff1a; sudo ufw status如果需要重新开启防火墙&…

WebShell流量特征检测_冰蝎篇

什么是一句话木马&#xff1f; 1、定义 顾名思义就是执行恶意指令的木马&#xff0c;通过技术手段上传到指定服务器并可以正常访问&#xff0c;将我们需要服务器执行的命令上传并执行 2、特点 短小精悍&#xff0c;功能强大&#xff0c;隐蔽性非常好 3、举例 php一句话木…

结合Python与GUI实现比赛预测与游戏数据分析

在现代软件开发中&#xff0c;用户界面设计和数据处理紧密结合&#xff0c;以提升用户体验和功能性。本篇博客将基于Python代码和相关数据分析进行讨论&#xff0c;尤其是如何通过PyQt5等图形界面库实现交互式功能。同时&#xff0c;我们将探讨如何通过嵌入式预测模型为用户提供…

Java项目: 基于SpringBoot+mybatis+maven+mysql图书馆管理系统(含源码+数据库+任务书+答辩PPT+毕业论文)

一、项目简介 本项目是一套基于SpringBootmybatismavenmysql图书馆管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操…

MongoDB创建用户教程

1、连接到 MongoDB 首先&#xff0c;打开你的终端并使用 mongosh 命令连接到 MongoDB 服务器&#xff1a; mongosh --host <hostname> --port <port> 说明&#xff1a; mongosh&#xff1a;启动 MongoDB Shell 命令行工具。 --host <hostname>&#xff1a…

前端URL链接转迅雷下载链接

一、前言 普通的url是无法拉起迅雷下载的&#xff0c;这个时候需要把url转成迅雷能识别的链接。 二、url转迅雷链接 首先就是在普通url前面加上 AA&#xff0c;尾部加上 ZZ&#xff0c;然后将拼接后的url转成base64&#xff0c;JavaScript提供了转成base64的函数&#xff08…

leetcode 1651 Hopper公司查询3(postgresql)

需求 Table: Drivers -------------------- | Column Name | Type | -------------------- | driver_id | int | | join_date | date | -------------------- driver_id是该表的主键。 该表的每一行均包含驾驶员的ID以及他们加入Hopper公司的日期。 Table: Rides ---------…

Python面向对象(14成员成员修饰符)

目录 一.成员 1.变量 2.方法 3.属性 二.成员修饰符 一.成员 1.变量 实例变量&#xff0c;属于对象&#xff0c;每个对象中各自维护自己的数据。 类变量&#xff0c;属于类&#xff0c;可以被所有对象共享&#xff0c;一般用于给对象提供公共数据&#xff08;类似于全局变…

[机器学习]线性回归算法

1 线性回归简介 概念&#xff1a;利用回归方程&#xff08;函数&#xff09;对一个或多个自变量&#xff08;特征值&#xff09;和因变量&#xff08;目标值&#xff09;之间关系进行建模的一种分析方式。 weight&#xff1a;权重&#xff1b;bias 偏置 线性回归分类&#xf…

代码随想录算法训练营_day37

题目信息 518. 零钱兑换 II 题目链接: https://leetcode.cn/problems/coin-change-ii/description/题目描述: 给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑…

pgrx在docker中问题无法解决

前面写了一系列pgrx开发结合docker postgres,但是当程序写的很大的时候,发现本地安装没有问题,docker里面可能有libc6 2.33 报错以及下面错误: 但是,在本地安装的pg中,没有这问题: 至于为什么,我查了很多,比如docker版本问题blablabla,都不重要了,按照所有建议的方法都没有解决…

示波器知识 | 什么是示波器?模拟和数字示波器区别?

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 1、什么是示波器&#xff1f; 示波器是一种电子测试和测量仪器&#xff0c;以 X-Y 图的形式以图形方式显示电信号。在这里&…

【LeetCode】:面试题 16.05. 阶乘尾数

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;C课程学习 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 好久没有写文章了&#xff0c;今天碰见了一道有趣的题目&#xff0c;写下来分享一下。 &#x1f3c6;1.问题描…

智能新纪元:GPT-Next引领的AI革命及其跨领域应用

GPT-Next&#xff1a;性能的百倍提升 在当今这个科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;无疑是最具活力和变革性的领域之一。最近&#xff0c;OpenAI在KDDI峰会上宣布了一项激动人心的消息&#xff1a;他们即将推出名为“GPT-Next”的新一代语言模…

【Matplotlib】利用Python进行绘图!(python数据分析与可视化)

文章开始前打个小广告——分享一份Python学习大礼包&#xff08;激活码安装包、Python web开发&#xff0c;Python爬虫&#xff0c;Python数据分析&#xff0c;人工智能、自动化办公等学习教程&#xff09;点击领取&#xff0c;100%免费&#xff01; 【Matplotlib】 教程&…

MySQL(CRUD)

MySQL mysql -u root -ply MySQL的三层结构 1.安装MySQL数据库本质就是在主机安装一个数据库管理系统(DBMS),这个管理程序可以管理多个数据库. 2.一个数据库中可以创建多个表,以保存数据 SQL语句分类 1.DDL:数据定义语句[create 表,库] 2.DML:数据操作语句[增加insert,修改…