一设计功能

计算得到的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. Solution -「LOJ #6029」「雅礼集训 2017」市场

    \(\mathcal{Description}\)   Link.   维护序列 \(\lang a_n\rang\),支持 \(q\) 次如下操作: 区间加法: 区间下取整除法: 区间求最小值: 区 ...

  2. HashTable源码学习

    一.介绍 1.HashMap和HashTable的区别 1.相同点 二者都实现了Map接口. 底层都是哈西表 2.不同点 Hashtable继承自Dictionary类,而HashMap继承自Abst ...

  3. 基于myscript.js的web手写板(支持中文识别)

    网上的手写板模板不少,但是支持中文识别的却不多,而且基本上都收费的,毕竟别人的中文库凭什么免费提供给你(说好的开源呢?说好的开源呢? ←_←) 好了,进入主题,myscript.js,在官网其实我并没 ...

  4. 【FAQ】接入HMS Core应用内支付服务过程中一些常见问题总结

    HMS Core应用内支付服务(In-App Purchases,IAP)为应用提供便捷的应用内支付体验和简便的接入流程.开发者的应用集成IAP SDK后,调用IAP SDK接口,启动IAP收银台,即 ...

  5. 学习Spring5必知必会(6)~Spring DAO

    一.Spring 对持久层技术的支持 Spring DAO 1.模板类: 2.基类: 二.spring JDBC [JDBCTemplate 模板类] 1.案例:使用jdbc 完成crud操作 (1) ...

  6. bi数据可视化平台带来的企业变化

    相信现在互联网的发展,大家有目共睹,在互联网的快速发展下,所产生的数据已经成为庞然大物,各行各业都在进行数据化转型,大数据分析也就成了香饽饽,bi数据可视化平台能够让数据可视化,通过数据分析可以业务人 ...

  7. Html简单标签

    学习html <h1> 标题标签</h1> 标题标签 <p>段落标签</p> 段落标签 换行标签</br> 换行标签 水平线标签</h ...

  8. 在 Linux 下确认 NTP 是否同步的方法

    NTP 意即网络时间协议Network Time Protocol,它通过网络同步计算机系统之间的时钟.NTP 服务器可以使组织中的所有服务器保持同步,以准确时间执行基于时间的作业.NTP 客户端会将 ...

  9. 在不受支持的 Mac 上安装 macOS Monterey 12(OpenCore Patcher)

    一.介绍 本文通用于 macOS Big Sur 和 macOS Monterey,也可以视作笔者 早期文章 的升级版. 这一章节将介绍 macOS Monterey 的系统要求和不受支持的 Mac ...

  10. docker学习笔记(5)——docker场景问题汇总(docker权限问题、docker文件目录、查看docker历史日志文件)

    参考资料: 1.博客1:https://www.jianshu.com/p/d645e2ed4bb1 使用docker的时候有一些常见问题: docker权限问题:一般输入docker命令前面都要加s ...