一设计功能

计算得到的dxy,再通过和阈值比较大小,输出po_sum作为VGA的输入,在显示器器上显示图像的轮廓。

二设计思路

根据前一篇博客对sobel算法的介绍,先通过FIFO的双流水线操作采集到三行三列的九个数,再得到dx  和dy,再求dx 和dy的绝对值和给Dxy,最后把Dxy 和阈值比较大小得到输出po_sum。

(一)双FIFO的流水线操做

根据FIFO的时序图:写的读写控制代码如下

注意在实际过程中加一个wr_cnt来计算数据的个数。一般先设置一个FIFOIP核,再自己写一个控制程序。根据上面的读写时序,控制程序如下

module fifo_ctrl(

input      wire              clk,

input      wire              rst_n

);

reg                              wr_en;

reg         [8:0]              wr_cnt;

reg         [7:0]              wr_data;

wire                                   full,empty;

reg                              rd_en;

reg         [1:0]              empty_dly;

reg                              read_start;

reg                [8:0]              rd_cnt;

wire              [7:0]              rd_data;

always @(posedge clk or negedge rst_n) begin

if (rst_n == 1'b0) begin

wr_en <= 1'b0;

end

else if (wr_cnt == 'd0) begin

wr_en <= 1'b1;

end

else if (wr_cnt== 'd257) begin

wr_en <= 1'b0;

end

end

always @(posedge clk or negedge rst_n) begin

if (rst_n == 1'b0) begin

// reset

wr_cnt <= 'd0;

end

else if (wr_cnt != 'd257) begin

wr_cnt <= wr_cnt + 1'b1;

end

end

always @(posedge clk or  negedge rst_n) begin

if (rst_n == 1'b0) begin

// reset

wr_data <='d0;

end

else if (wr_en == 1'b1) begin

wr_data <= wr_data + 1'b1;

end

else begin

wr_data <= 'd0;

end

end

always @(posedge clk) begin

empty_dly <= {empty_dly[0],empty};//shift reg empty_dly == 2'b10 negedge

end

always @(posedge clk or negedge rst_n) begin

if (rst_n == 1'b0) begin

// reset

read_start <= 1'b0;

end

else if (empty_dly == 2'b10) begin

read_start <= 1'b1;

end

else begin

read_start <= 1'b0;

end

end

always @(posedge clk or negedge rst_n) begin

if (rst_n == 1'b0) begin

// reset

rd_en <= 1'b0;

end

else if(rd_cnt == 256)begin

rd_en <= 1'b0;

end

else if (read_start == 1'b1) begin

rd_en <= 1'b1;

end

end

always @(posedge clk or negedge rst_n) begin

if (rst_n == 1'b0) begin

// reset

rd_cnt <= 'd0;

end

else if (rd_en == 1'b1) begin

rd_cnt <= rd_cnt + 1'b1;

end

end

asfifo_w256x8_r256x8 fifo_inst (

.wr_clk(clk), // input wr_clk

.rd_clk(clk), // input rd_clk

.din(wr_data), // input [7 : 0] din

.wr_en(wr_en), // input wr_en

.rd_en(rd_en), // input rd_en

.dout(rd_data), // output [7 : 0] dout

.full(full), // output full

.empty(empty) // output empty

);

endmodule

     1. sobel_ctrl控制模块的关键点一:双FIFO流水线操作

有了上面一个FIFO的读写控制代码基础,现在来实现怎么设计实现双FIFO的流水线操作

/根据时序图写代码时,一定得懂设计思路

//FIFO1的写使能怎么产生,写数据来源于什么

//FIFO2的写使能怎么产生,写数据来源于什么

//读信号怎么产生,读出的数据给谁,读信号有效才加

来回答上面的问题:

第一部分:写使能信号的产生

FIFO1的写使能信号:在0行时,来一个pi_flag 就拉高。对于2~84行的数据,利用wr_en1_pre1 和wr_en1_pre2进行打一拍操作,在2~84行,只要有pi_flag 就拉高wr_en1_pre2。

FIFO2的写使能怎么产生:在1~84行且来一个pi_flag 就拉高wr_en2

第二部分:写数据的产生

FIFO1:在第0行,fifo1的数据来源于串口接收端。对于2~84行的数据,FIFO1的数据来源于FIFO2的读出端数据。

FIFO2:在1~84行,FIFO2的数据来源于串口的数据(来一个pi_flag)

第三部分:读信号和读出的数据输出给谁

对于FIFO1和FIFO2的读出端数据dout1和dout2.对于2~85行的数据,在读信号的控制下,读出后直接在加标志信号flag_add控制下,将FIFO1 和FIFO2的读出端数据和串口接收端的数据加起来输出给PO_SUM

读信号rd_en,在大于第二行,且来一个pi_flag 就拉高rd_en

根据上面双FIFO流水线设计思路的要点,编写代码如下:

parameter VALUE = 10 ; //阈值

always @(posedge clk or negedge rst_n) begin

if (rst_n == 1'b0) begin

wr_en1 <= 1'b0;

end

else if (cnt_row == 'd0) begin

wr_en1 <= pi_flag;

end

else begin

wr_en1 <= wr_en1_pre1;

end

end

always @(posedge clk or negedge rst_n) begin

if (rst_n == 1'b0) begin

// reset

wr_en1_pre2 <= 1'b0;

end

else if (cnt_row >=2 && cnt_row <=198 && pi_flag == 1'b1) begin

wr_en1_pre2 <= 1'b1;

end

else begin

wr_en1_pre2 <= 1'b0;

end

end

always @(posedge clk) begin

wr_en1_pre1 <= wr_en1_pre2;

end

always @(posedge clk or negedge rst_n) begin

if (rst_n == 1'b0) begin

// reset

rd_en <= 1'b0;

end

else if (cnt_row >=2 && cnt_row <=199 && pi_flag == 1'b1) begin

rd_en <= 1'b1;

end

else begin

rd_en <= 1'b0;

end

end

always @(posedge clk or negedge rst_n) begin

if (rst_n == 1'b0) begin

// reset

data_in1 <= 'd0;

end

else if (cnt_row == 'd0) begin

data_in1 <= pi_data;

end

else  begin//if (cnt_row >=2 && cnt_row <=84)

data_in1 <= dout2;

end

end

always @(posedge clk or negedge rst_n ) begin

if (rst_n  == 1'b0) begin

// reset

wr_en2 <= 1'b0;

end

else if (cnt_row >=1 && cnt_row <=198 && pi_flag == 1'b1) begin

wr_en2 <= 1'b1;

end

else begin

wr_en2 <= 1'b0;

end

end

always @(posedge clk or negedge rst_n) begin

if (rst_n == 1'b0) begin

// reset

data_in2 <= 'd0;

end

else if ( cnt_row >=1 && cnt_row <=198) begin

data_in2 <= pi_data;

end

end

always @(posedge clk) begin

add_flag <= flag_shift;

end

2. sobel_ctrl控制模块的关键点二:怎么采集三行三列的9个数

关键是怎么采集三行三列的9个数,我的想法是,比如第零行用FIFO1存储,然后再加三个寄存器,打拍操作。每来一个串口接收标志信号就送FIFO1的数据到如寄存器1中,然后是寄存器2,再是寄存器3。

怎么采集三行三列的9个数

//注意打拍的寄存器越往后是越早的数据

//如pi_data是最新的数据,dout1_tt是前两个数据

reg flag_shift;

always @(posedge clk or negedge rst_n) begin

if (rst_n == 1'b0) begin

// reset

flag_shift <= 1'b0;

end

else begin

flag_shift <= rd_en;

end

end

//打拍操作,三个寄存器在标志信号下打一拍存一个数

always@(posedge clk)begin

if(flag_shift == 1'b1)begin

{dout1_tt,dout1_t} <= {dout1_t,dout1};

{dout2_tt,dout2_t} <= {dout2_t,dout2};

{rx_data_tt,rx_data_t}<={rx_data_t,pi_data};

end

end

3. sobel_ctrl控制模块的关键点三:怎么得到横向的DX和纵向的DY?

always @(posedge clk) begin

add_flag <= flag_shift;

end

//对这三行三列的数进行加减乘运算

//((dout2-dout2_tt)<<1)实现乘2操作

reg [7:0]dx;

always@(posedge clk or negedge rst_n)

if(rst_n==0)

dx<=0;

else if(add_flag)

dx<=(dout1-dout1_tt)+((dout2-dout2_tt)<<1)+(pi_data-rx_data_tt);

//对这三行三列的数进行加减乘运算

reg [7:0]dy;

always@(posedge clk or negedge rst_n)

if(rst_n==0)

dy<=0;

else if(add_flag)

dy<=(dout1_tt-rx_data_tt)+((dout1_t-rx_data_t)<<1)+(dout1-pi_data);

4. sobel_ctrl控制模块的关键点四:怎么得到横向的DX和纵向的DY的绝对值和?

利用原码反码,补码的关系。即最高位为符号位,1表示负数,绝对值为取反加一。0表示正数,绝对值等于本身。值得注意的是进行绝对值前,利用flag_d_pre,flag_d, flag_abs延迟三个时钟周期,打了三拍。再求绝对值的和,又利用flag_dxy打了一拍。

//对dx和dy进行绝对值求和

//补码原码反码的关系

//flag_d_pre

reg flag_d_pre;

always@(posedge clk or negedge rst_n)

if(rst_n==0)

flag_d_pre<=0;

else if(cnt_row>=2 && cnt_col>=2 && pi_flag)

flag_d_pre<=1;

else

flag_d_pre<=0;

//flag_d;

reg flag_d;

always@(posedge clk or negedge rst_n)

if(rst_n==0)

flag_d<=0;

else

flag_d<=flag_d_pre;

//falg_abs

reg flag_abs;

always@(posedge clk or negedge rst_n)

if(rst_n==0)

flag_abs<=0;

else

flag_abs<=flag_d;

//abs_dx

reg [7:0]abs_dx;

reg [7:0]abs_dy;

always@(posedge clk or negedge rst_n)

if(!rst_n)

abs_dx<=0;

else if(flag_abs & dx[7]==1'b1)

abs_dx<=(~dx)+1;

else if(flag_abs & dx[7]==1'b0)

abs_dx<=dx;

//abs_dy

always@(posedge clk or negedge rst_n)

if(!rst_n)

abs_dy<=0;

else if(flag_abs & dy[7]==1'b1)

abs_dy<=(~dy)+1;

else if(flag_abs & dy[7]==1'b0)

abs_dy<=dy;

//取了绝对值进行打拍

reg flag_dxy;

always@(posedge clk or negedge rst_n)

if(rst_n==0)

flag_dxy<=0;

else

flag_dxy<=flag_abs;

reg [7:0]dxy;

always@(posedge clk or negedge rst_n)

if(rst_n==0)

dxy<=0;

else if(flag_dxy)

dxy<=abs_dy+abs_dx;

5. sobel_ctrl控制模块的关键点五:怎么阈值比较得到边缘,大于阈值白色显示,否则黑色?

//判断相对于阈值大小输出不同的图像

reg flag_rgb;

always@(posedge clk or negedge rst_n)

if(rst_n==0)

flag_rgb<=0;

else

flag_rgb<=flag_dxy;

//rgb

always@(posedge clk or negedge rst_n)

if(!rst_n)

po_sum<=0;

else if(flag_rgb & dxy>=VALUE)

po_sum<=8'hff;

else if(flag_rgb & dxy<=VALUE)

po_sum<=8'h00;

always@(posedge clk or negedge rst_n)

if(rst_n==0)

po_flag<=0;

else

po_flag<=flag_rgb;

三,仿真验证

到这里,终于把图像边缘检测的算法关键点讲清楚了,而我的仿真是先利用80X4的数据,即用sublime的重复语句产生一个0~320的数,然后再为仿真的输入,进行波形的验证。

在sublime中利用敲出{:08b},然后再选中这个代码,再右键选择重复代码,之后在设置起始位和停止位及步长,,敲回车即可。

如0~320,1

RAM一般最大为9K,即1024X9个数据。

刚开始我写的仿真程序包含了串口发送有点复杂,所以在这里提高vga_ram模块的仿真,两者很相似,只需要改哈端口就行,设计思想相同。把上面产生的数据保存在一个txt文件里,在仿真里用mem读取这个文本,再用一个task函数把这数据赋值给我们功能模块vga_ram的输入pi_data,如程序所写。b第二个注意点,如要产生300X3的数据,不用写一个矩阵的仿真程序,直接并转串,用一个900X1的数据表示,结果是一样,还很方便。

`timescale 1ns / 1ps

module tb_vga_ram;

// Inputs

reg sclk;

reg rst_n;

reg pi_flag;

reg [7:0]pi_data;

reg [7:0] mem[899:0];

// Outputs

wire hsync ;

wire vsync  ;

wire [7:0]vga_rgb;

vga_ram  inst_vga_ram (

.clk     (sclk),

.rst_n   (rst_n),

.pi_flag (pi_flag),

.pi_data (pi_data),

.hsync   (hsync),

.vsync   (vsync),

.vga_rgb (vga_rgb)

);

// Instantiate the Unit Under Test (UUT)

initial begin

// Initialize Inputs

sclk = 0;

rst_n = 0;

pi_flag=0;

pi_data=0;

// Wait 100 ns for global reset to finish

#100;

rst_n =1;

// Add stimulus here

end

initial begin

$readmemb("./data.txt",mem);

end

always #10 sclk = ~sclk;

always #100 pi_flag = ~pi_flag;

initial begin

#200;

rx_byte();

end

task rx_byte;

integer i;

integer j;

begin

for(j=0;j<900;j=j+1)begin

for (i=0;i<900;i=i+1)begin

pi_data=mem[i];

#199;

end

end

end

endtask

endmodule

波形如下:

14图像边缘检测的sobel_ctrl控制模块的更多相关文章

  1. CSharpGL(24)用ComputeShader实现一个简单的图像边缘检测功能

    CSharpGL(24)用ComputeShader实现一个简单的图像边缘检测功能 效果图 这是红宝书里的例子,在这个例子中,下述功能全部登场,因此这个例子可作为使用Compute Shader的典型 ...

  2. 图像边缘检测--OpenCV之cvCanny函数

    图像边缘检测--OpenCV之cvCanny函数 分类: C/C++ void cvCanny( const CvArr* image, CvArr* edges, double threshold1 ...

  3. Python实现图像边缘检测算法

    title: "Python实现图像边缘检测算法" date: 2018-06-12T17:06:53+08:00 tags: ["图形学"] categori ...

  4. 图像边缘检测——几种图像边缘检测算子的学习及python 实现

    本文学习利用python学习边缘检测的滤波器,首先读入的图片代码如下: import cv2 from pylab import * saber = cv2.imread("construc ...

  5. OpenCV3入门(八)图像边缘检测

    1.边缘检测基础 图像的边缘是图像的基本特征,边缘点是灰度阶跃变化的像素点,即灰度值的导数较大或极大的地方,边缘检测是图像识别的第一步.用图像的一阶微分和二阶微分来增强图像的灰度跳变,而边缘也就是灰度 ...

  6. 图像边缘检测——Sobel算子

    边缘是图像最基本的特征,其在计算机视觉.图像分析等应用中起着重要的作用,这是因为图像的边缘包含了用于识别的有用信息,是图像分析和模式识别的主要特征提取手段. 1.何为“图像边缘”? 在图像中,“边缘” ...

  7. 数字集成电路设计-8-一个简单sobel图像边缘检测加速器的设计,实现,仿真与综合

    引言 图像视频处理等多媒体领域是FPGA应用的最主要的方面之一,边缘检测是图像处理和计算机视觉中的基本问题,所以也是最常用的,随着数据量的不断增加以及对实时性的要求,一般软件已经不能满足实际需要,这时 ...

  8. python计算机视觉2:图像边缘检测

    我是一名初学者,如果你发现文中有错误,请留言告诉我,谢谢 如果需要检测到图像里面的边缘,首先我们需要知道边缘处具有什么特征. 对于一幅灰度图像来说,边缘两边的灰度值肯定不相同,这样我们才能分辨出哪里是 ...

  9. 基于 FPGA 的图像边缘检测

    本文主要内容是实现图像的边缘检测功能 目录 mif文件的制作 调用 ip 核生成rom以及在 questasim 仿真注意问题 灰度处理 均值滤波:重点是3*3 像素阵列的生成 sobel边缘检测 图 ...

随机推荐

  1. Nacos中服务删除不了,怎么办?

    前两天遇到了一个问题,Nacos 中的永久服务删除不了,折腾了一番,最后还是顺利解决了.以下是原因分析和解决方案,建议先收藏,以备不时之需. 临时实例和持久化实例是 Nacos 1.0.0 中新增了一 ...

  2. 《PHP程序员面试笔试真题解析》——新书上线

    你好,是我--琉忆.很高兴可以跟你分享我的新书. 很高兴,在出版了PHP程序员面试笔试宝典后迎来了我的第二本书出版--<PHP程序员面试笔试真题解析>. 如果你是一个热爱PHP的程序员,刚 ...

  3. php使用CURL实现GET和POST方式请求

    CURL请求,支持GET和POST两种方式,默认为GET方式,如果传第二个参数则为POST方式请求,设置了超时时间,避免程序卡死. /** 使用curl方式实现get或post请求 @param $u ...

  4. Android SugarORM(4)

    Android Sugar ORM(4) Android Sugar ORM 数据库迁移 据官网描述, Sugar ORM的设计灵感来自与Rails(没用过, 咱也不知道是啥, 以后也许会学到吧)迁移 ...

  5. Android SugarORM(2)

    Android Sugar ORM (2) Android Sugar ORM 实体 1. 创建一个实体类 Sugar ORM在创建一个实体的时候, 仅需要使这个实体类继承于SugarRecord即可 ...

  6. Java邮件发送中的setRecipient方法使用

    ​ 一.方法setRecipient(Message.RecipientType type, Address address),是用于设置邮件的接收者. 1.有两个参数,第一个参数是接收者的类型,第二 ...

  7. 从数据源支持、支持方式等角度深入了解Smartbi与Tableau

    对数据分析来讲,数据源支持是基本功.让数据分析工具与数据保持一个通道,建立会话.用数据分析应用服务器与我们需要分析的业务数据进行连接,拿到需要的数据进行分析.Smartbi.Tableau系统给我们提 ...

  8. 从这3个方面考虑BI工具,选型一选一个准

    BI工具在很多场合都能听到,那么BI工具有什么功能呢?能给企业带来什么?好用的BI工具长什么样?今天跟着小编走近BI工具,一探究竟! 首先要了解BI工具的定义,什么是BI工具.BI工具是指利用现代数据 ...

  9. 报表软件测评来啦!Smartbi电子表格使用感受

    最近因为工作需求,需要用到一些报表工具软件,看到Smartbi比较方便,可以直接在excel中进行配置,所以安装体验了一下. 下载 Smartbi有多个版本,我主要是在excel中使用,所以下载了一个 ...

  10. strtok()出现segment fault的错误

    在写一个简易的 shell 时,需要将命令行的命令通过空格分割成一个个字符串参数,这里我使用了 strtok() 函数,然后遇到了 segment fault 的错误. 出现问题的代码如下: 终于寻找 ...