基于EBAZ4205礦板的圖像處理:11閾值系數可調的圖像局部閾值二值化
先看效果
還是一樣拿我的pynq當模特,然后用usb——HDMI采集卡把輸出圖像采集到電腦上。
注意看右邊mobelxtem中的通過串口調節的參數,
我這里是實現了閾值系數可調的局部閾值二值化,
局部閾值二值化系數binary_threshold = 128時的效果
局部閾值二值化系數binary_threshold =73時的效果,個人感覺這個時候效果最好
局部閾值二值化系數binary_threshold = 188時的效果
graph_threshold 不用管,那是為了后續的膨脹和腐蝕算法準備的。
算法講解
圖像局部閾值二值化,是一種能夠自適應地提取圖像局部二值化閾值的方法,他在圖像明暗變化不一致的場景下,仍能獲得很好的效果。
這種自適應地提取局部(即卷積的滑動窗口)閾值的提取方式有最大類間差,中值提取,均值提取等。
均值提取指的是當一個滑動窗口的中心點像素的像素值大于那個滑動窗口的均值時,將該像素點的像素值置為255,否則置為0。
然后,我在上面均值提取的基礎上,在這個“均值”上加了一個調整系數,讓一個像素點的最終二值化輸出的結果既受到他周圍的像素點的影響,也受到我的閾值系數的影響,以此我們就能動態調整圖像的二值化效果。
算法的FPGA部署
部署流程如上圖所示,用兩個ram緩存兩行數據,然后和第三行實時數據一起通過打三拍地形式,以一種步長為1的滑動窗口的形式輸出,將數據矩陣輸入到局部閾值二值化模塊中通過滑動窗口的均值和局部閾值系數,來對滑動窗口中心點像素進行判斷,對滑動窗口中心點進行二值化處理。
項目講解
block design跟上一篇有一個不同之處在于我添加了一個通過axi lite總線讀寫pl端reg的ip核,我在這個系列的第10篇博客里詳細講了怎么創建這個IP核。只需要按照那篇博客創建就可以了。
項目block design 如下
我的AXI Lite讀寫PLreg的模塊,我把四個reg都引出了,方便以后開發。
項目代碼
視頻流處理模塊
//作者:搶公主的大魔王
//日期:24.5.15
module video_processor((* X_INTERFACE_IGNORE = "true" *) input frame_clk, //cmos 像素時鐘(* X_INTERFACE_IGNORE = "true" *) input frame_rst_n, //預處理圖像(* X_INTERFACE_IGNORE = "true" *) input pre_vsync, //預處理圖像場同步信號(* X_INTERFACE_IGNORE = "true" *) input [23:0] pre_data, //預處理圖像數據(* X_INTERFACE_IGNORE = "true" *) input pre_href, //預處理圖像數據有效信號(* X_INTERFACE_IGNORE = "true" *) input pre_frame_ce, //預處理圖像時鐘使能信號//閾值控制(* X_INTERFACE_IGNORE = "true" *) input [7:0 ] loc_bin_thresh_coefficient, //來自PS端的局部二值化閾值系數//處理后圖像(* X_INTERFACE_IGNORE = "true" *) output pos_vsync, //處理后圖像場同步信號(* X_INTERFACE_IGNORE = "true" *) output [23:0] pos_data, //處理后圖像數據(* X_INTERFACE_IGNORE = "true" *) output pos_href, //處理后圖像數據有效信號(* X_INTERFACE_IGNORE = "true" *) output pos_frame_ce //處理后圖像時鐘使能信號
);//wire define
wire [7:0] gray_data ;
wire gray_vsync;
wire gray_frame_ce;
wire gray_href;//*****************************************************
//** main code
//*****************************************************
//rgb轉ycbcr模塊
rgb2gray u_rgb2gray(.cmos_frame_clk (frame_clk ),.cmos_rstn (frame_rst_n ),//同步復位.cmos_frame_vsync (pre_vsync ),.cmos_frame_data (pre_data ),.cmos_frame_href (pre_href ),.cmos_frame_ce (pre_frame_ce ),.dataout_frame_vsync(gray_vsync ),.dataout_frame_data (gray_data ),.dataout_frame_href (gray_href ),.dataout_frame_ce (gray_frame_ce )
);
//wire define
wire matrix_frame_vsync;
wire matrix_frame_href;
wire matrix_frame_ce;
wire [7:0] matrix_p11; //3X3 矩陣數據
wire [7:0] matrix_p12;
wire [7:0] matrix_p13;
wire [7:0] matrix_p21;
wire [7:0] matrix_p22;
wire [7:0] matrix_p23;
wire [7:0] matrix_p31;
wire [7:0] matrix_p32;
wire [7:0] matrix_p33;
// wire [7:0] mid_value ;
// wire [7:0] pos_img_Y;//*****************************************************
//** main code
//*****************************************************// assign pos_img_Y = pos_gray_href ? mid_value : 8'd0;
// assign pos_pixel_data = {pos_img_Y,pos_img_Y,pos_img_Y};VIP_matrix_generate_3x3_8bit u_VIP_matrix_generate_3x3_8bit(.clk (frame_clk ), .rst_n (frame_rst_n ),.per_frame_vsync (gray_vsync ),.per_frame_href (gray_href ), .per_frame_ce (gray_frame_ce ),.per_img_Y (gray_data ),//輸出3x3矩陣.matrix_frame_vsync (matrix_frame_vsync ),.matrix_frame_href (matrix_frame_href ),.matrix_frame_ce (matrix_frame_ce ),.matrix_p11 (matrix_p11), .matrix_p12 (matrix_p12), .matrix_p13 (matrix_p13),.matrix_p21 (matrix_p21), .matrix_p22 (matrix_p22), .matrix_p23 (matrix_p23),.matrix_p31 (matrix_p31), .matrix_p32 (matrix_p32), .matrix_p33 (matrix_p33)
);
region_binary u_region_binary(.clk (frame_clk ),.rst_n (frame_rst_n ),.matrix_img_vsync (matrix_frame_vsync ),.matrix_img_href (matrix_frame_href ),.matrix_frame_ce (matrix_frame_ce ),.loc_bin_thresh_coefficient (loc_bin_thresh_coefficient ),.matrix_p11 (matrix_p11 ),.matrix_p12 (matrix_p12 ),.matrix_p13 (matrix_p13 ),.matrix_p21 (matrix_p21 ),.matrix_p22 (matrix_p22 ),.matrix_p23 (matrix_p23 ),.matrix_p31 (matrix_p31 ),.matrix_p32 (matrix_p32 ),.matrix_p33 (matrix_p33 ),.dataout_vsync (pos_vsync ), // processed Image data vsync valid signal.dataout_href (pos_href ), // processed Image data href vaild signal.dataout_gray (pos_data ), // processed Image brightness output.dataout_frame_ce (pos_frame_ce ));
endmodule
rgb565轉gray模塊
`timescale 1ns / 1ps
//作者:搶公主的大魔王
module rgb2gray((* X_INTERFACE_IGNORE = "true" *) input cmos_frame_vsync,
(* X_INTERFACE_IGNORE = "true" *) input [23:0] cmos_frame_data,
(* X_INTERFACE_IGNORE = "true" *) input cmos_frame_href,(* X_INTERFACE_IGNORE = "true" *) input cmos_frame_clk,
(* X_INTERFACE_IGNORE = "true" *) input cmos_rstn,//同步復位
(* X_INTERFACE_IGNORE = "true" *) input cmos_frame_ce,(* X_INTERFACE_IGNORE = "true" *) output dataout_frame_vsync,
(* X_INTERFACE_IGNORE = "true" *) output [7:0] dataout_frame_data,
// (* X_INTERFACE_IGNORE = "true" *) output [23:0] dataout_frame_data,
(* X_INTERFACE_IGNORE = "true" *) output dataout_frame_href,
(* X_INTERFACE_IGNORE = "true" *) output dataout_frame_ce);// Y = 0.299R +0.587G + 0.114B// Y = (77 *R + 150*G + 29 *B)>>8reg [15:0] r_gray1;reg [15:0] g_gray1;reg [15:0] b_gray1;reg [15:0] y1;reg [7:0] y2;reg [2:0] dataout_frame_vsync_r;reg [2:0] dataout_frame_href_r;reg [2:0] dataout_frame_ce_r;always@(posedge cmos_frame_clk)beginif(!cmos_rstn)beginr_gray1 <= 8'h00;g_gray1 <= 8'h00;b_gray1 <= 8'h00;endelse beginr_gray1 <= cmos_frame_data[23:16] * 8'd77 ;g_gray1 <= cmos_frame_data[15:8] * 8'd150;b_gray1 <= cmos_frame_data[7:0] * 8'd29 ;endendalways@(posedge cmos_frame_clk)beginif(!cmos_rstn)beginy1 <= 16'h0000;endelse beginy1 <= r_gray1 + g_gray1 + b_gray1;endendalways@(posedge cmos_frame_clk)beginif(!cmos_rstn)beginy2 <= 8'h0000;endelse beginy2 <= y1[15:8];endendalways@(posedge cmos_frame_clk)beginif(!cmos_rstn)begindataout_frame_ce_r <= 3'b000;dataout_frame_vsync_r <= 3'b000;dataout_frame_href_r <= 3'b000;endelse begindataout_frame_ce_r <= {dataout_frame_ce_r[1:0] ,cmos_frame_ce};dataout_frame_vsync_r <= {dataout_frame_vsync_r[1:0] ,cmos_frame_vsync};dataout_frame_href_r <= {dataout_frame_href_r[1:0] ,cmos_frame_href};endend// assign dataout_frame_data = {y2,y2,y2};assign dataout_frame_data = y2;assign dataout_frame_ce = dataout_frame_ce_r[2];assign dataout_frame_vsync = dataout_frame_vsync_r[2];assign dataout_frame_href = dataout_frame_href_r[2];endmodule
卷積矩陣數據生成模塊
這個模塊和下面的行數據緩沖輸出模塊不是我寫的,然后注意下面的行數據緩沖輸出模塊,有一個地方要改,我會說明。
//****************************************Copyright (c)***********************************//
//技術支持:www.openedv.com
//淘寶店鋪:http://openedv.taobao.com
//關注微信公眾平臺微信號:"正點原子",免費獲取FPGA & STM32資料。
//版權所有,盜版必究。
//Copyright(C) 正點原子 2018-2028
//All rights reserved
//----------------------------------------------------------------------------------------
// File name: VIP_matrix_generate_3x3_8bit
// Last modified Date: 2019/7/19 10:55:56
// Last Version: V1.0
// Descriptions: VIP_matrix_generate_3x3_8bit
//----------------------------------------------------------------------------------------
// Created by: 正點原子
// Created date: 2019/7/19 10:55:56
// Version: V1.0
// Descriptions: The original version
//----------------------------------------------------------------------------------------
//****************************************************************************************//module VIP_matrix_generate_3x3_8bit
(input clk, input rst_n, //準備要進行處理的圖像數據input per_frame_vsync,input per_frame_href,input per_frame_ce,//frame_ceinput [7:0] per_img_Y,//矩陣化后的圖像數據和控制信號output matrix_frame_vsync,output matrix_frame_href,output matrix_frame_ce,output reg [7:0] matrix_p11, output reg [7:0] matrix_p12,output reg [7:0] matrix_p13,output reg [7:0] matrix_p21, output reg [7:0] matrix_p22, output reg [7:0] matrix_p23,output reg [7:0] matrix_p31, output reg [7:0] matrix_p32, output reg [7:0] matrix_p33
);//wire define
wire [7:0] row1_data; //第一行數據
wire [7:0] row2_data; //第二行數據
wire read_frame_href ;
wire read_frame_ce;//reg define
reg [7:0] row3_data; //第三行數據,即當前正在接受的數據
reg [1:0] per_frame_vsync_r;
reg [1:0] per_frame_href_r;
reg [1:0] per_frame_ce_r;//*****************************************************
//** main code
//*****************************************************assign read_frame_href = per_frame_href_r[0] ;
assign read_frame_ce = per_frame_ce_r[0];
assign matrix_frame_vsync = per_frame_vsync_r[1];//源數據打兩拍輸出,得到結果
assign matrix_frame_href = per_frame_href_r[1] ;//源數據打兩拍輸出,得到結果
assign matrix_frame_ce = per_frame_ce_r[1];//源數據打兩拍輸出,得到結果//打一拍時,第一行第一個點數據寫入,且讀出前一行和前前一行的數據,//第二排時數據穩定,將數據發完外部
//當前數據放在第3行
always@(posedge clk or negedge rst_n) beginif(!rst_n)row3_data <= 0;else begin if(per_frame_ce)row3_data <= per_img_Y ;elserow3_data <= row3_data ;end
end//用于存儲列數據的RAM
line_shift_RAM_8bit u_Line_Shift_RAM_8Bit
(.clock (clk),.clken (per_frame_ce),.per_frame_href (per_frame_href),.shiftin (per_img_Y), //當前行的數據.taps0x (row2_data), //前一行的數據.taps1x (row1_data) //前前一行的數據
);//將同步信號延遲兩拍,用于同步化處理
always@(posedge clk or negedge rst_n) beginif(!rst_n) begin per_frame_vsync_r <= 0;per_frame_href_r <= 0;per_frame_ce_r <= 0;endelse begin per_frame_vsync_r <= { per_frame_vsync_r[0], per_frame_vsync };per_frame_href_r <= { per_frame_href_r[0], per_frame_href };per_frame_ce_r <= { per_frame_ce_r[0], per_frame_ce };end
end//在同步處理后的控制信號下,輸出圖像矩陣
always@(posedge clk or negedge rst_n) beginif(!rst_n) begin {matrix_p11, matrix_p12, matrix_p13} <= 24'h0;{matrix_p21, matrix_p22, matrix_p23} <= 24'h0;{matrix_p31, matrix_p32, matrix_p33} <= 24'h0;endelse if(read_frame_href) beginif(read_frame_ce) begin {matrix_p11, matrix_p12, matrix_p13} <= {matrix_p12, matrix_p13, row1_data};{matrix_p21, matrix_p22, matrix_p23} <= {matrix_p22, matrix_p23, row2_data};{matrix_p31, matrix_p32, matrix_p33} <= {matrix_p32, matrix_p33, row3_data};endelse begin {matrix_p11, matrix_p12, matrix_p13} <= {matrix_p11, matrix_p12, matrix_p13};{matrix_p21, matrix_p22, matrix_p23} <= {matrix_p21, matrix_p22, matrix_p23};{matrix_p31, matrix_p32, matrix_p33} <= {matrix_p31, matrix_p32, matrix_p33};end endelse begin {matrix_p11, matrix_p12, matrix_p13} <= 24'h0;{matrix_p21, matrix_p22, matrix_p23} <= 24'h0;{matrix_p31, matrix_p32, matrix_p33} <= 24'h0;end
endendmodule
行數據緩沖輸出模塊
一定要注意正點原子的開源代碼和免費教程里,例化的1024x8bit,如果你向我一樣視頻流的寬度大于1024就不行了,要改長度。我這里例化的就是2048x8bit的ram。
//****************************************Copyright (c)***********************************//
//技術支持:www.openedv.com
//淘寶店鋪:http://openedv.taobao.com
//關注微信公眾平臺微信號:"正點原子",免費獲取FPGA & STM32資料。
//版權所有,盜版必究。
//Copyright(C) 正點原子 2018-2028
//All rights reserved
//----------------------------------------------------------------------------------------
// File name: line_shift_RAM_8bit
// Last modified Date: 2019/7/19 10:55:56
// Last Version: V1.0
// Descriptions: line_shift_RAM_8bit
//----------------------------------------------------------------------------------------
// Created by: 正點原子
// Created date: 2019/7/19 10:55:56
// Version: V1.0
// Descriptions: The original version
// 兩個偽雙端口RAM用于存儲 舊兩行的數據。
// 在新一行的數據到來時,在延遲打拍的時序下,
// 先將RAM中的舊一行數據讀出,再將新一行數據寫入到RAM。
//----------------------------------------------------------------------------------------
//****************************************************************************************//module line_shift_RAM_8bit(input clock,input clken,input per_frame_href,input [7:0] shiftin, //當前行的數據output [7:0] taps0x, //前一行的數據output [7:0] taps1x //前前一行的數據
);//reg define
reg [2:0] clken_dly;
reg [10:0] ram_rd_addr;
reg [10:0] ram_rd_addr_d0;
reg [10:0] ram_rd_addr_d1;
reg [7:0] shiftin_d0;
reg [7:0] shiftin_d1;
reg [7:0] shiftin_d2;
reg [7:0] taps0x_d0;//*****************************************************
//** main code
//*****************************************************//在數據到來時,RAM的讀地址累加
always@(posedge clock)beginif(per_frame_href)if(clken)ram_rd_addr <= ram_rd_addr + 1 ;elseram_rd_addr <= ram_rd_addr ;elseram_rd_addr <= 0 ;
end//對時鐘延遲3拍
always@(posedge clock) beginclken_dly <= { clken_dly[1:0] , clken };
end//將RAM地址延遲2拍
always@(posedge clock ) beginram_rd_addr_d0 <= ram_rd_addr;ram_rd_addr_d1 <= ram_rd_addr_d0;
end//輸入數據延遲3拍送入RAM
always@(posedge clock)beginshiftin_d0 <= shiftin;shiftin_d1 <= shiftin_d0;shiftin_d2 <= shiftin_d1;
end//用于存儲前一行圖像的RAM
blk_mem_gen_0 u_ram_2048x8_0(.clka (clock),.wea (clken_dly[2]),.addra (ram_rd_addr_d1), //在延遲的第三個時鐘周期,當前行的數據寫入RAM0.dina (shiftin_d2),.clkb (clock),.addrb (ram_rd_addr), .doutb (taps0x) //延遲一個時鐘周期,輸出RAM0中前一行圖像的數據
);//寄存前一行圖像的數據
always@(posedge clock)begintaps0x_d0 <= taps0x;
end//用于存儲前前一行圖像的RAM
blk_mem_gen_0 u_ram_2048x8_1(.clka (clock),.wea (clken_dly[1]),.addra (ram_rd_addr_d0),.dina (taps0x_d0), //在延遲的第二個時鐘周期,將前一行圖像的數據寫入RAM1.clkb (clock),.addrb (ram_rd_addr),.doutb (taps1x) //延遲一個時鐘周期,輸出RAM1中前前一行圖像的數據
);endmodule
局部閾值二值化模塊
`timescale 1ns / 1ps
//作者:搶公主的大魔王
//日期:24.5.15module region_binary((* X_INTERFACE_IGNORE = "true" *) input wire clk ,(* X_INTERFACE_IGNORE = "true" *) input wire rst_n ,(* X_INTERFACE_IGNORE = "true" *) input wire matrix_img_vsync,(* X_INTERFACE_IGNORE = "true" *) input wire matrix_img_href,(* X_INTERFACE_IGNORE = "true" *) input wire [7:0] loc_bin_thresh_coefficient, //來自PS端的閾值控制(* X_INTERFACE_IGNORE = "true" *) input wire [7:0] matrix_p11,(* X_INTERFACE_IGNORE = "true" *) input wire [7:0] matrix_p12,(* X_INTERFACE_IGNORE = "true" *) input wire [7:0] matrix_p13,(* X_INTERFACE_IGNORE = "true" *) input wire [7:0] matrix_p21,(* X_INTERFACE_IGNORE = "true" *) input wire [7:0] matrix_p22,(* X_INTERFACE_IGNORE = "true" *) input wire [7:0] matrix_p23,(* X_INTERFACE_IGNORE = "true" *) input wire [7:0] matrix_p31,(* X_INTERFACE_IGNORE = "true" *) input wire [7:0] matrix_p32,(* X_INTERFACE_IGNORE = "true" *) input wire [7:0] matrix_p33,(* X_INTERFACE_IGNORE = "true" *) input wire matrix_frame_ce,(* X_INTERFACE_IGNORE = "true" *) output reg dataout_vsync , // processed Image data vsync valid signal(* X_INTERFACE_IGNORE = "true" *) output reg dataout_href , // processed Image data href vaild signal(* X_INTERFACE_IGNORE = "true" *) output reg [23:0] dataout_gray , // processed Image brightness output(* X_INTERFACE_IGNORE = "true" *) output reg dataout_frame_ce);
reg [10:0] data_sum1;
reg [10:0] data_sum2;
reg [10:0] data_sum3;always @(posedge clk)
begindata_sum1 <= matrix_p11 + matrix_p12 + matrix_p13;data_sum2 <= matrix_p21 + matrix_p22 + matrix_p23;data_sum3 <= matrix_p31 + matrix_p32 + matrix_p33;endreg [12:0] data_sum;always @(posedge clk)
begindata_sum <= data_sum1 + data_sum2 + data_sum3;
end//----------------------------------------------------------------------
// thresh = floor(sum/9 *0.9) ==> floor(sum*603980 >> 24)
// 1677722/16777216;
// sum *9 /90
// sum /10
reg [31:0] mult_result;
reg [24:0] loc_bin_thresh_mult_coefficient;//0~16777216
always @(posedge clk)
beginloc_bin_thresh_mult_coefficient <= {loc_bin_thresh_coefficient,16'd0}; //1.8
endalways @(posedge clk)
begin// mult_result <= data_sum*21'd1677722;mult_result <= data_sum*loc_bin_thresh_mult_coefficient; //1.8
endwire [7:0] thresh;
assign thresh = mult_result[31:24];//----------------------------------------------------------------------
// lag 3 clocks signal sync
reg [2:0] matrix_img_vsync_r;
reg [2:0] matrix_img_href_r;
reg [2:0] dataout_frame_ce_r;always @(posedge clk or negedge rst_n)
beginif(!rst_n)beginmatrix_img_vsync_r <= 3'b0;matrix_img_href_r <= 3'b0;dataout_frame_ce_r <= 3'b0;endelsebeginmatrix_img_vsync_r <= {matrix_img_vsync_r[1:0],matrix_img_vsync};matrix_img_href_r <= {matrix_img_href_r[1:0],matrix_img_href};dataout_frame_ce_r <= {dataout_frame_ce_r[1:0],matrix_frame_ce};end
endreg [7:0] matrix_p22_r [0:2];always @(posedge clk)
beginmatrix_p22_r[0] <= matrix_p22;matrix_p22_r[1] <= matrix_p22_r[0];matrix_p22_r[2] <= matrix_p22_r[1];
end//----------------------------------------------------------------------
// result output
always @(posedge clk or negedge rst_n)
beginif(!rst_n)dataout_gray <= {8'd255,8'd255,8'd255};else if(matrix_p22_r[2] < thresh)dataout_gray <= 24'd0;elsedataout_gray <= {8'd255,8'd255,8'd255};
endalways @(posedge clk or negedge rst_n)
beginif(!rst_n)begindataout_vsync <= 1'b0;dataout_href <= 1'b0;dataout_frame_ce <= 1'b0; endelsebegindataout_vsync <= matrix_img_vsync_r[2];dataout_href <= matrix_img_href_r[2];dataout_frame_ce <= dataout_frame_ce_r[2];end
endendmodule
vitis代碼
//作者:搶公主的大魔王
//功能:局部閾值二值化系數可調的圖像二值化
//日期:24.5.15
//版本:1v0
//聯系方式:2376635586@qq.com
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xil_types.h"
#include "xil_cache.h"
#include "xparameters.h"
#include "xgpiops.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xplatform_info.h"
#include "xaxivdma.h"
#include "xaxivdma_i.h"
#include "display_ctrl_hdmi/display_ctrl.h"
#include "vdma_api/vdma_api.h"
#include "emio_sccb_cfg/emio_sccb_cfg.h"
#include "ov5640/ov5640_init.h"
#include "sleep.h"
#include "xuartps.h"
#include "string.h"//宏定義
#define DYNCLK_BASEADDR XPAR_AXI_DYNCLK_0_BASEADDR //動態時鐘基地址
#define VDMA_ID XPAR_AXIVDMA_0_DEVICE_ID //VDMA器件ID
#define DISP_VTC_ID XPAR_VTC_0_DEVICE_ID //VTC器件ID
#define UART_DEVICE_ID XPAR_PS7_UART_0_DEVICE_ID //串口設備ID
#define UART_INT_IRQ_ID XPAR_XUARTPS_0_INTR //串口中斷ID
#define THRESHOLD_BASEADDR XPAR_AXICTRLTHRESHOLD_0_S00_AXI_BASEADDR#define EMIO_SCL_NUM 54
#define EMIO_SDA_NUM 55
#define KEY1 56 //T19
#define KEY2 57 //P19
#define KEY3 58 //U20
#define KEY4 59 //U19
#define KEY5 60 //V20
#define LED1 61 //H18
#define LED2 62 //K17
#define LED3 63 //E19#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
XGpioPs Gpio;
#define GPIO_BANK XGPIOPS_BANK0 /* Bank 0 of the GPIO Device */
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define GPIO_INTERRUPT_ID XPAR_XGPIOPS_0_INTR//全局變量
//frame buffer的起始地址
unsigned int const frame_buffer_addr = (XPAR_PS7_DDR_0_S_AXI_BASEADDR+ 0x1000000);
u8 binary_threshold = 128;
u8 graph_threshold = 8;XAxiVdma vdma;
DisplayCtrl dispCtrl;
VideoMode vd_mode;static XScuGic Intc; //中斷控制器驅動程序實例
static XUartPs Uart_Ps; //串口驅動程序實例#define Uart0_Rec_Len 128 //定義USART1最大接收字節數
u8 Uart0_Rx_Buf[Uart0_Rec_Len]; //接收緩沖,最大USART_REC_LEN個字節.
//接收狀態
//bit15, 接收完成標志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字節數目
u16 Uart0_Rx_Sta=0; //接收狀態標記//UART初始化函數
int uart_init(XUartPs* uart_ps)
{int status;XUartPs_Config *uart_cfg;uart_cfg = XUartPs_LookupConfig(UART_DEVICE_ID);if (NULL == uart_cfg)return XST_FAILURE;status = XUartPs_CfgInitialize(uart_ps, uart_cfg, uart_cfg->BaseAddress);if (status != XST_SUCCESS)return XST_FAILURE;//UART設備自檢status = XUartPs_SelfTest(uart_ps);if (status != XST_SUCCESS)return XST_FAILURE;//設置工作模式:正常模式XUartPs_SetOperMode(uart_ps, XUARTPS_OPER_MODE_NORMAL);//設置波特率:115200XUartPs_SetBaudRate(uart_ps,115200);//設置RxFIFO的中斷觸發等級XUartPs_SetFifoThreshold(uart_ps, 1);return XST_SUCCESS;
}void uart_intr_handler(void *call_back_ref)
{XUartPs *uart_instance_ptr = (XUartPs *) call_back_ref;u32 rec_data = 0 ;u32 isr_status ; //中斷狀態標志//讀取中斷ID寄存器,判斷觸發的是哪種中斷isr_status = XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress,XUARTPS_IMR_OFFSET);isr_status &= XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress,XUARTPS_ISR_OFFSET);//判斷中斷標志位RxFIFO是否觸發if (isr_status & (u32)XUARTPS_IXR_RXOVR){rec_data = XUartPs_RecvByte(XPAR_PS7_UART_0_BASEADDR);//清除中斷標志XUartPs_WriteReg(uart_instance_ptr->Config.BaseAddress,XUARTPS_ISR_OFFSET, XUARTPS_IXR_RXOVR) ;}XUartPs_SendByte(XPAR_PS7_UART_0_BASEADDR,rec_data);if((Uart0_Rx_Sta&0x8000)==0)//接收未完成{if(Uart0_Rx_Sta&0x4000)//接收到了0x0d{if(rec_data!=0x0a)Uart0_Rx_Sta=0;//接收錯誤,重新開始else Uart0_Rx_Sta|=0x8000; //接收完成了}else//還沒收到0X0D{if(rec_data==0x0d)Uart0_Rx_Sta|=0x4000;else{Uart0_Rx_Buf[Uart0_Rx_Sta&0X3FFF]=rec_data ; //將收到的數據放入數組Uart0_Rx_Sta++; //數據長度計數加1if(Uart0_Rx_Sta>(Uart0_Rec_Len-1))Uart0_Rx_Sta=0;//接收數據錯誤,重新開始接收}}}}static void IntrHandler(void *CallBackRef, u32 Bank, u32 Status)
{XGpioPs *Gpio_cb = (XGpioPs *)CallBackRef;if (XGpioPs_IntrGetStatusPin(Gpio_cb, KEY1)){//binary_threshold++;//Xil_Out32(THRESHOLD_BASEADDR, binary_threshold);//xil_printf("The threshold has been changed\n\rThe threshold now is %d\n\r",binary_threshold);XGpioPs_IntrClearPin(Gpio_cb, KEY1);}else if (XGpioPs_IntrGetStatusPin(Gpio_cb, KEY4)){//binary_threshold--;//Xil_Out32(THRESHOLD_BASEADDR, binary_threshold);//xil_printf("The threshold has been changed\n\rThe threshold now is %d\n\r",binary_threshold);XGpioPs_IntrClearPin(Gpio_cb, KEY4);}else if (XGpioPs_IntrGetStatusPin(Gpio_cb, KEY2)){//binary_threshold = binary_threshold+10;//Xil_Out32(THRESHOLD_BASEADDR, binary_threshold);//xil_printf("The threshold has been changed\n\rThe threshold now is %d\n\r",binary_threshold);XGpioPs_IntrClearPin(Gpio_cb, KEY2);}else if (XGpioPs_IntrGetStatusPin(Gpio_cb, KEY5)){//binary_threshold = binary_threshold-10;//Xil_Out32(THRESHOLD_BASEADDR, binary_threshold);//xil_printf("The threshold has been changed\n\rThe threshold now is %d\n\r",binary_threshold);XGpioPs_IntrClearPin(Gpio_cb, KEY5);}else if (XGpioPs_IntrGetStatusPin(Gpio_cb, KEY3)){//binary_threshold = 128;//Xil_Out32(THRESHOLD_BASEADDR, binary_threshold);//xil_printf("The threshold has been reset\n\rThe threshold now is %d\n\r",binary_threshold);XGpioPs_IntrClearPin(Gpio_cb, KEY3);}XGpioPs_WritePin(&Gpio, LED1, !XGpioPs_ReadPin(&Gpio, LED1));
}//串口中斷初始化void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio,u16 GpioIntrId, XUartPs *uart_ps){XScuGic_Config *IntcConfig;IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);//初始化中斷控制器XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,IntcConfig->CpuBaseAddress);Xil_ExceptionInit();//設置并打開中斷異常處理功能Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,GicInstancePtr);Xil_ExceptionEnable();//同Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);XScuGic_Connect(GicInstancePtr, UART_INT_IRQ_ID,(Xil_ExceptionHandler) uart_intr_handler,(void *) uart_ps);//為中斷設置中斷處理函數XUartPs_SetInterruptMask(uart_ps, XUARTPS_IXR_RXOVR); //設置UART的中斷觸發方式XScuGic_Enable(GicInstancePtr, UART_INT_IRQ_ID);//使能GIC中的串口中斷XScuGic_Connect(GicInstancePtr, GpioIntrId,(Xil_ExceptionHandler)IntrHandler,(void *)Gpio);//為按鍵中斷設置中斷處理函數XGpioPs_SetIntrTypePin(Gpio, KEY1, XGPIOPS_IRQ_TYPE_EDGE_FALLING);XGpioPs_SetIntrTypePin(Gpio, KEY2, XGPIOPS_IRQ_TYPE_EDGE_FALLING);//設置按鍵的中斷方式XGpioPs_SetIntrTypePin(Gpio, KEY3, XGPIOPS_IRQ_TYPE_EDGE_FALLING);XGpioPs_SetIntrTypePin(Gpio, KEY4, XGPIOPS_IRQ_TYPE_EDGE_FALLING);XGpioPs_SetIntrTypePin(Gpio, KEY5, XGPIOPS_IRQ_TYPE_EDGE_FALLING);XGpioPs_IntrEnablePin(Gpio, KEY1);XGpioPs_IntrEnablePin(Gpio, KEY2);XGpioPs_IntrEnablePin(Gpio, KEY3);XGpioPs_IntrEnablePin(Gpio, KEY4);XGpioPs_IntrEnablePin(Gpio, KEY5);XScuGic_Enable(GicInstancePtr, GpioIntrId);//使能按鍵中斷
}void Gpio_Init(void){XGpioPs_Config *ConfigPtr;ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);XGpioPs_SetDirectionPin(&Gpio, LED1, 1);XGpioPs_SetOutputEnablePin(&Gpio, LED1, 1);XGpioPs_WritePin(&Gpio, LED1, 0);XGpioPs_SetDirectionPin(&Gpio, LED2, 1);XGpioPs_SetOutputEnablePin(&Gpio, LED2, 1);XGpioPs_WritePin(&Gpio, LED2, 0);XGpioPs_SetDirectionPin(&Gpio, LED3, 1);XGpioPs_SetOutputEnablePin(&Gpio, LED3, 1);XGpioPs_WritePin(&Gpio, LED3, 0);XGpioPs_SetDirectionPin(&Gpio, KEY1, 0);XGpioPs_SetDirectionPin(&Gpio, KEY2, 0);XGpioPs_SetDirectionPin(&Gpio, KEY3, 0);XGpioPs_SetDirectionPin(&Gpio, KEY4, 0);XGpioPs_SetDirectionPin(&Gpio, KEY5, 0);}int main(void)
{u32 status;u16 cmos_h_pixel; //ov5640 DVP 輸出水平像素點數u16 cmos_v_pixel; //ov5640 DVP 輸出垂直像素點數u16 total_h_pixel; //ov5640 水平總像素大小u16 total_v_pixel; //ov5640 垂直總像素大小cmos_h_pixel = 1280;cmos_v_pixel = 720;total_h_pixel = 2570;total_v_pixel = 980;emio_init();//控制hdmi的emio初始化//xil_printf("Uart and Key is init successful! \r\n");//xil_printf("ov5640 is initing! \r\n");status = ov5640_init( cmos_h_pixel, //初始化ov5640cmos_v_pixel,total_h_pixel,total_v_pixel);//設置OV5640輸出分辨率為1280*720 PCLK = 72Mhzif(status == 0);//xil_printf("OV5640 init successful!\r\n");elsexil_printf("OV5640 detected failed!\r\n");xil_printf("Uart and OV5640 is init successful! \r\nInput any to run!\r\n");sleep(1);vd_mode = VMODE_1280x720;//配置VDMArun_vdma_frame_buffer(&vdma, VDMA_ID, vd_mode.width, vd_mode.height,frame_buffer_addr,0,0,BOTH);//初始化Display controllerDisplayInitialize(&dispCtrl, DISP_VTC_ID, DYNCLK_BASEADDR);//設置VideoModeDisplaySetMode(&dispCtrl, &vd_mode);DisplayStart(&dispCtrl);Gpio_Init();//按鍵和led的初始化uart_init(&Uart_Ps); //串口初始化SetupInterruptSystem(&Intc, &Gpio, GPIO_INTERRUPT_ID,&Uart_Ps);while(1){if(Uart0_Rx_Sta&0xC000){ //如果標志位是0xC000表示收到數據串完成,可以處理。if((Uart0_Rx_Sta&0x3FFF)==0){ //單獨的回車鍵再顯示一次歡迎詞printf("1y--add binary threshold 1n--sub binary threshold \r\n");printf("2y--add graph threshold 2n--sub graph threshold \r\n");printf("3s--show all the threshold \r\n");printf("please input cmd \r\n");}else if((Uart0_Rx_Sta&0x3FFF)==2 && Uart0_Rx_Buf[0]=='1' && Uart0_Rx_Buf[1]=='y'){ //判斷數據是不是2個,第一個數據是不是“1”,第二個是不是“y”binary_threshold = binary_threshold + 5;Xil_Out32(THRESHOLD_BASEADDR, binary_threshold);printf("1y -- add binary threshold to %d\r\n",binary_threshold);}else if((Uart0_Rx_Sta&0x3FFF)==2 && Uart0_Rx_Buf[0]=='1' && Uart0_Rx_Buf[1]=='n'){binary_threshold = binary_threshold - 5;Xil_Out32(THRESHOLD_BASEADDR, binary_threshold);printf("1n -- sub binary threshold to %d\r\n",binary_threshold);}else if((Uart0_Rx_Sta&0x3FFF)==2 && Uart0_Rx_Buf[0]=='2' && Uart0_Rx_Buf[1]=='y'){graph_threshold = graph_threshold + 1;Xil_Out32(THRESHOLD_BASEADDR+4, graph_threshold);printf("2y -- add graph threshold to %d\r\n",graph_threshold);}else if((Uart0_Rx_Sta&0x3FFF)==2 && Uart0_Rx_Buf[0]=='2' && Uart0_Rx_Buf[1]=='n'){graph_threshold = graph_threshold - 1;Xil_Out32(THRESHOLD_BASEADDR+4, graph_threshold);printf("2n -- sub graph threshold to %d\r\n",graph_threshold);}else if((Uart0_Rx_Sta&0x3FFF)==2 && Uart0_Rx_Buf[0]=='3' && Uart0_Rx_Buf[1]=='s'){printf("3s--show all the threshold\r\nbinary threshold is %d \r\ngraph threshold is %d \r\n",binary_threshold,graph_threshold);}else{ //如果以上都不是,即是錯誤的指令。printf("cmd error\r\n");}Uart0_Rx_Sta=0; //將串口數據標志位清0}}return 0;
}
代碼中使用UART接受中斷,接受用戶通過串口發送的數據,用戶可以通過串口動態調整局部閾值二值化系數。