FPGA视频处理系统
时间:2022-09-26 14:00:01
视频图像处理作为一种重要的现代技术,已广泛应用于通信、航空航天、遥感遥测、生物医学、军事、信息安全等领域。视频图像处理,特别是高分辨率图像实时处理的实现技术,对相关领域的发展具有深远的意义。
随着科学技术的进步,对视频图像采集和处理的应用需求也在增加。传统的采集和处理主要依赖于传统的采集和处理PC机器等专用集成电路板卡由于功能单一、体积大、处理速度有限,逐渐无法满足实际要求。由于FPGA技术发展迅速,计算能力不断提高,计算速度越来越快,成本也在下降。目前FPGA从军事到民用,从航空航天到生产生活,技术已广泛应用于各个领域。FPGA。
因此,本设计的视频采集与处理系统采用双处理系统:专用芯片和可编程逻辑器件FPGA,完成图像采集和图像压缩。选择了适合当前行业实际需——DCT算法。FPGA上VHDL模块化和可重用的程序功能可以提高设计效率,从而获得可靠和优化的设计。
2.1 FPGA开发与应用
随着FPGA设计越来越复杂,最重要的硬件描述语言是VHDL和Verilog HDL。从EDA根据技术的发展趋势,直接采用C语言设计FPGA它将是一个发展方向,现在已经出现了FPGA在5~10年内,C语言很可能会逐渐成为继续VHDL和Verilog然后大规模设计FPGA的又一种手段。
通信是FPGA传统领域。随着微细化的进步,芯片面积缩小,价格迅速下降,市场发展加快。同时,由于FPGA灵活方便,不仅性能、速度、连接优势,又能缩短上市时间,因此应用领域不断扩大。现在,许多用户已经开始使用一些批量生产的消费电子产品FPGA,如DVD、TV、游戏设备、空调、PDA、数字视频移动网络、无线局域网等。
2.1.1 FPGA系统结构
FPGA也就是说,现场可编程门阵列的通用结构由逻辑功能块排列成阵列,这些逻辑功能块由可编程的互连资源连接,实现不同的设计。
FPGA静态存储器通常由三个可编程电路和一个用于存储编程数据SRAM组成。这三种可编程电路是:可编程逻辑块(CLB, Configurable Logic Block)、输入∕输出模块(IOB, I/O BLOCK)和互连资源(IR, Interconnect Resource)。可编程逻辑块CLB它是实现逻辑功能的基本单元,通常在整个芯片中定期排列阵列;可编程输入∕输出模块(IOB)主要完成芯片上的逻辑和外部包装脚的接口,通常排列在芯片周围;可编程连接资源包括各种长度的连接段和一些可编程连接开关CLB之间活CLB、IOB之间,以及IOB连接起来,形成特定功能的电路。
(1) 可编程逻辑块CLB
CLB是FPGA主要部件。它主要由逻辑函数发生器、触发器、数据选择器等电路组成。CLB三个逻辑函数发生器分别是G、F和H,相应的输出是G`、F`和H`。G有四个输入变量,和;F还有四个输入变量,和。这两个逻辑函数发生器完全独立,可以实现四个输入变量的任意组合逻辑函数。逻辑函数发生器H有三个输入信号:前两个函数发生器的输出G`和F`,另一个输入信号来自信号变换电路的输出。该函数发生器可以实现三个输入变量的各种组合函数。这三个函数发生器可以实现多达9个变量的逻辑函数。
CLB有许多不同规格的数据选择器(4选1,2选1等)CLB内部数据选择器编程,逻辑函数发生器G、F与H的输出可以连接到CLB内部触发器或直接连接到CLB输出端X或Y,并用于选择触发器激励输入信号、时钟有效边缘、时钟使能信号和输出信号。编程信息提供这些数据选择器的地址控制信号,以实现所需的电路结构。
CLB逻辑函数发生器F和G都是搜索表结构,工作原理类似ROM。F和G的输入等效于ROM搜索地址码ROM组合逻辑函数可以输出中的地址表。另一方面,逻辑函数发生器F和G也可作为器件内的高速RAM由信号变换电路控制的小型可读写存储器。
- 输入∕输出模块(IOB)
IOB设备引脚与内部逻辑阵列之间的连接。主要由输入触发器、输入缓冲器和输出触发∕由锁定器和输出缓冲器组成。
每个IOB可配置为输入、输出或双向引脚I/O功能。当IOB当控制引脚被定义为输入时,通过引脚的输入信号首先输入输入缓冲器。缓冲器的输出分为两种方式:一种方式可以直接送到MUX,另一条路径延迟几次ns(或不延迟)送到输入通路D触发器,然后送到数据选择器。通过编程向数据选择器发送不同的控制信息CLB阵列的I1和I来自输入缓冲器或触发器。
IOB两个输出端MOS管道,它们的网格极其可编程,使其极其可编程MOS管道分别通过上拉电阻或下拉电阻连接或截止VCC、地线或不连接,以提高输出波形和负载能力。
(3)可编程互联网资源IR
可编程互联网资源IR可以将FPGA内部的CLB和CLB之间、CLB和IOB它们相互连接,形成具有各种复杂功能的系统。IR通过自动布线实现各种电路连接,主要由多个金属线段组成。
根据相对长度,长度分为单长线、双长线和长线。
单长度线连接结构,CLB输入和输出分别连接到相邻的单长度线,然后连接到开关矩阵。开关矩阵可以通过编程控制CLB与其他CLB或IOB连在一起。
双长度线连接结构,双长度线金属线段的长度是单位长度线金属线段的两倍,通过两个CLB之后,这些金属线段才与可编程的开关矩阵相连接。因此,通过双长度线可使两个相隔(非相邻)的CLB连接起来。
长线连接结构,由长线网络组成的金属网络,充满了阵列的所有长度和宽度,每个长线中间有一个可编程的分离开关,使长线分为两个独立的连接连接,每个连接只有阵列宽度或高度的一半。CLB输入可由相邻的任何长线驱动,输出可由三态缓冲器驱动。
单长度线盒的双长度线提供了相邻的CLB快速连接和复杂连接的灵活性,但传输信号增加了一个延迟,而不是通过可编程开关矩阵。FPGA内部延迟与设备结构和逻辑布线有关,其信号传输延迟不确定。长期不通过可编程开关矩阵,信号延迟时间小,长期用于传输高风扇和关键信号。
2.1.2 FPGA的结构特点
FPGA芯片是小批量系统提高系统集成度和可靠性的最佳选择之一,其主要特点是:
(1) 采用FPGA设计ASIC,设计周期短,开发成本低,风险低,无需投影生产;
(2) FPGA可以做其他全定制或半定制ASIC电路中试样片;
(3)FPGA触发器内部丰富I∕O引脚;
(4)FPGA采用高速CHMOS工艺,低功耗,可与CMOS、TTL电平兼容。
下面以Altera公司的FLEX∕ACEX系列FPGA与Xilinx公司的FPGA比较结构有以下区别:
(1)结构不同。
Altera FPGA: 嵌入式阵列块EAB、逻辑阵列块LAB、FastTrack、I∕O单元;
Xilinx FPGA: CLB(Configurable Logic Block)、IOB、布线资源(ICR)。
(2)RAM不同的使用。
Altera FPGA: EAB可用做大型RAM,LE也可以构成中搜表RAM;
Xilinx FPGA: 没有专门的RAM,需要时可由CLB中的LUT构成。
(3)基本单元不同。
Altera FPGA: LE(8个LE组成一个LAB,一个LE带触发器);
< style="margin-left:0cm;"> Xilinx FPGA: CLB(一个CLB带两个触发器)。(4)布线不同
Altera FPGA: FastTrack,局部互连,进位链和级联链;
Xilinx FPGA: 单长线、双长线、长线。
(5)时钟的同步方式不同。
Altera FPGA: 使用时钟锁定和时钟自举方法;
Xilinx FPGA: 有一个时钟网用于时钟同步,在Virtex器件中使用DLL(Delay-locked-loop)技术,缩短了信号输出时间,提高了I∕O速度。
(6)另外,Xilinx FPGA中有专门的乘法器。
2.1.3 FPGA的编程工艺
FPGA的功能由逻辑结构的配置数决定。工作时,这些配置数据存放在片内的SRAM或熔丝图上。基于SRAM的FPGA器件,在工作前需要从芯片外部加载配置数据,配置数据可以存储在片外的EPROM或其他存储体上。用户可以控制加载过程,在现场修改器件的逻辑功能,即所谓的现场编程。
用户可以根据不同的配置模式,采用不同的编码方式。加电时,FPGA芯片将EPROM中数据读入片内编程RAM中,配置完成后,FPGA进入工作状态。掉电后,FPGA恢复成白片,内部逻辑关系消失,因此,FPGA能够反复使用。FPGA的编程无需专用的FPGA编程器,只需用通用的EPROM、PROM编程即可。当需要修改FPGA功能时,只需要换一片EPROM即可。这样,同一片FPGA,不同的编程数据,可以产生不同的电路功能。因此,FPGA的使用非常灵活。
FPGA有多种配置模式:并行主题模式为一片FPGA加一片EPROM的方式;主从模式可以支持一片PROM编程多片FPGA;串行模式可以采用串行PROM编程FPGA;外设模式可以将FPGA作为微处理器的外设,有微处理器对其进行编译。
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
USE ieee.std_logic_unsigned.all;
ENTITY videocpt IS
PORT
(
cdao : out std_logic_vector(7 downto 0); -- 验证视频数据输出口
cps : out std_logic; -- 视频数据输出像素 Pixel SYN Signal
cfs : out std_logic; -- 视频数据输出帧
caclk: out std_logic;
cclk : in std_logic; --主时钟 24M
cdi : in std_logic_vector(7 downto 0); --视频数据输入口
pclk : in std_logic; -- 采集时钟 4M
ccs : in std_logic; -- 视频信号选择
avf : in std_logic; --有效场同步帧
avh : in std_logic; --有效行同步帧
vd : in std_logic --场同步信号
);
END videocpt;
ARCHITECTURE a OF videocpt IS
constant StartWidPix : integer range 0 to 1023 := 0; --起始宽度像素常量
constant StartHiPix : integer range 0 to 288 := 0; --起始高度像素常量
constant EndWidPix : integer range 0 to 1023 := 704; --末端宽度像素常量
constant EndHiPix : integer range 0 to 288 := 287; --末端高度像素常量
signal pclk1 : std_logic := '0';
signal demodata : std_logic_vector(7 downto 0) := "01111001";
signal readline : std_logic := '0';
signal read : std_logic := '0';
signal done : std_logic := '0';
BEGIN
process (cclk)
variable ccount : integer range 0 to 3 :=0;
begin
if cclk='1' and cclk'event then
if ccount < 3 then
ccount := ccount+1; caclk <= '1';
else
ccount := 0; caclk <= '0';
end if;
end if;
end process;
process (cclk, ccs )
variable HPCnt : integer range 0 to 288 := 0;
variable WPCnt : integer range 0 to 1023 := 0;
variable avh1 : std_logic; -- avh延迟1个周期
variable vd1 : std_logic; -- vd延迟1个周期
variable frame : boolean; -- 搜索起始帧
begin
if ccs = '1' then
read <='0';
frame := FALSE;
HPCnt:=0;
WPCnt:=0;
done <='0';
elsif cclk='1' and cclk'event then
pclk1<=pclk;
if vd1='1' and vd='0' and done='0' then
done<='1';
frame :=TRUE;
HPCnt:=0;
WPCnt:=0;
end if;
if frame and avh1='0' and avh='1' then
if HPCnt < 288 then
HPCnt:=HPCnt+1;
end if;
WPCnt:=0;
end if;
if frame and avh='1' and avf='1' then
readline<='1';
else
readline<='0';
end if;
if(readline='1' and pclk='0'and pclk1='1')then
WPCnt:=WPCnt+1;
end if;
--此处如果用if frame and HPCnt>3-HiPix and HPCnt<=HiPix and avh='1' and avf = '1' then 就会出现毛刺
if frame and HPCnt>StartHiPix and HPCnt<=EndHiPix and WPCnt>=StartWidPix and WPCnt if avh='1' and avf = '1' then read <='1'; demodata<=cdi; else read<='0'; end if; else read <='0'; end if; vd1:=vd; avh1:=avh; end if; end process; cdao <= demodata WHEN read = '1' ELSE (OTHERS=>'1') ; cps <= not pclk1 WHEN read = '1' ELSE '1' ; cfs <= '1'; END a; C语言 #include "DataType.h" void ffdct(LINT8 *mcubyte, LINT32 *array) {
LINT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; LINT32 tmp10, tmp11, tmp12, tmp13; LINT32 z1, z2, z3, z4, z5, z11, z13; LINT32* dataptr; LINT8* datain; int ctr; datain = mcubyte; dataptr = array; for(ctr=0;ctr<64;ctr++) {
dataptr[ctr]=datain[ctr]-128; } /* 第一部分,对行进行计算 */ for (ctr = 7; ctr >= 0; ctr--) {
tmp0 = dataptr[0] + dataptr[7]; tmp7 = dataptr[0] - dataptr[7]; tmp1 = dataptr[1] + dataptr[6]; tmp6 = dataptr[1] - dataptr[6]; tmp2 = dataptr[2] + dataptr[5]; tmp5 = dataptr[2] - dataptr[5]; tmp3 = dataptr[3] + dataptr[4]; tmp4 = dataptr[3] - dataptr[4]; /* 对偶数项进行运算 */ tmp10 = tmp0 + tmp3; /* 相位2 */ tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp1 - tmp2; dataptr[0] = tmp10 + tmp11; /*相位3 */ dataptr[4] = tmp10 - tmp11; z1 = (tmp12 + tmp13) * (46341); /* c4 / 0.707106781 */ z1 = z1 >> 16; dataptr[2] = tmp13 + z1; /* 相位 5 */ dataptr[6] = tmp13 - z1; /* 对奇数项进行计算 */ tmp10 = tmp4 + tmp5; /* 相位2 */ tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; z5 = (tmp10 - tmp12) * (25080); /* c6 /0.382683433 */ z5 = z5 >> 16; z2 = tmp10*(35468); z2 = z2 >> 16; z2 = z2 + z5; z4 = tmp12 * (85627); z4 = z4 >> 16; z4 = z4 + z5; z3 = tmp11 * (46341); /* c4 / 0.707106781*/ z3 = z3 >> 16; z11 = tmp7 + z3; /* 相位 5 */ z13 = tmp7 - z3; dataptr[5] = z13 + z2; /* 相位 6 */ dataptr[3] = z13 - z2; dataptr[1] = z11 + z4; dataptr[7] = z11 - z4; dataptr += 8; /* 将指针指向下一行 */ } /* 第二部分,对列进行计算 */ dataptr = array; for (ctr = 7; ctr >= 0; ctr--) {
tmp0 = dataptr[0] + dataptr[56]; tmp7 = dataptr[0] - dataptr[56]; tmp1 = dataptr[8] + dataptr[48]; tmp6 = dataptr[8] - dataptr[48]; tmp2 = dataptr[16] + dataptr[40]; tmp5 = dataptr[16] - dataptr[40]; tmp3 = dataptr[24] + dataptr[32]; tmp4 = dataptr[24] - dataptr[32]; /* 对偶数项进行运算 */ tmp10 = tmp0 + tmp3; /* 相位 2 */ tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp1 - tmp2; dataptr[0] = tmp10 + tmp11; /* 相位3 */ dataptr[32] = tmp10 - tmp11; z1 = (tmp12 + tmp13) * (46341); /* c4 / 0.707106781 */ z1 = z1 >> 16; dataptr[16] = tmp13 + z1; /* 相位5 */ dataptr[48] = tmp13 - z1; /* 对奇数项进行计算 */ tmp10 = tmp4 + tmp5; /* phase 2 */ tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; z5 = (tmp10 - tmp12) * (25080); /* c6 /0.382683433 */ z5 = z5 >> 16; z2 = tmp10*(35468); z2 = z2 >> 16; z2 = z2 + z5; z4 = tmp12 * (85627); z4 = z4 >> 16; z4 = z4 + z5; z3 = tmp11 * (46341); /* c4 / 0.707106781*/ z3 = z3 >> 16; z11 = tmp7 + z3; /* 相位5 */ z13 = tmp7 - z3; dataptr[40] = z13 + z2; /* 相位6 */ dataptr[24] = z13 - z2; dataptr[8] = z11 + z4; dataptr[56] = z11 - z4; ++dataptr; /* 将指针指向下一列 */ } dataptr = array; for(ctr=0;ctr<64;ctr++) dataptr[ctr]=dataptr[ctr]>>3;} #include "DataType.h" #include "tables.h" #include "dct.h" #include "flash.h" #define DEBUG #ifdef DEBUG #include "serial.h" #endif #define CIFW 352 #define CIFH 288 LINT8 cha_y[CIFW][CIFH]; LINT8 cha_u[CIFW/2][CIFH/2]; LINT8 cha_v[CIFW/2][CIFH/2]; LINT8 ix=0; LINT8 iy=0; LINT32 ydc=0,udc=0,vdc=0; LINT32 accoder[16][11],acbit[16][11]; LINT32 accoder2[16][11],acbit2[16][11]; ///文件部分/ #define MAXSIZE 262114 //256K typedef struct{
LINT8 buffer[MAXSIZE]; LINT32 fp,lp,CurrentPosition,length; LINT8 CurrentData; LINT8 CurrentBits; }FILEBUF; FILEBUF infile; //输入数据,长度为bits void PutDatatoJpegFile(LINT32 data,LINT8 bits,FILEBUF *buf) {
LINT8 CurrentFreeBits=0; while (bits!=0) {
if (buf->CurrentBits+bits<8) {
buf->CurrentData=(buf->CurrentData< buf->CurrentBits+=bits; bits=0; } else {
CurrentFreeBits=8-buf->CurrentBits; bits=bits-CurrentFreeBits; buf->CurrentData=(buf->CurrentData< buf->buffer[buf->CurrentPosition]=buf->CurrentData; buf->CurrentPosition++; buf->length++; //如果data=0xFF, 加0 if (buf->CurrentData==0xff) {
buf->buffer[buf->CurrentPosition]=0; buf->CurrentPosition++; buf->length++; } buf->CurrentData=0; buf->CurrentBits=0; data=data&mask[bits]; } } } ///Huffman编码 / typedef struct {
LINT32 maxcode[16]; LINT32 mincode[16]; LINT8 ml; unsigned valptr[16]; }DHUFF; typedef struct {
LINT8 huffval[256]; }XHUFF; DHUFF dhuff,dhuffdc,dhuff2,dhuffdc2; XHUFF xhuff,xhuffdc,xhuff2,xhuffdc2; void len2huff(LINT8 *lengthtab,DHUFF *dhuff,XHUFF *xhuff,LINT8 *val) {
int i,l,k,base=0; dhuff->ml=16; for(i=0;i {
dhuff->mincode[i]=0; dhuff->maxcode[i]=0; dhuff->valptr[i]=-1; } for(i=0;i<256;i++) xhuff->huffval[i]=0; i=0; base=0; for(l=0;l {
if(lengthtab[l]!=0) {
dhuff->mincode[l]=base; dhuff->valptr[l]=i; } for(k=0;k {
xhuff->huffval[i]=val[i]; i++; base++; } if(lengthtab[l]!=0)dhuff->maxcode[l]=base-1; base=base*2; } } void iztransfer(LINT32 *array) {
LINT32 temp[64],n,x,y; for(n=0;n<64;n++) {
x=iztab[2*n]; y=iztab[2*n+1]; temp[n]=array[x*8+y]; } for(n=0;n<64;n++) array[n]=temp[n]; } //量化 void qdata(LINT32 * in,LINT32 * out,LINT8 level) {
int i; for(i=0;i<64;i++) {
if(in[i]>0) out[i]=in[i]/agraytab[level][i]; else out[i]=in[i]/agraytab[level][i]; } } void quvdata(LINT32 * in,LINT32 * out,LINT8 level) {
int i; for(i=0;i<64;i++) {
if(in[i]>0) out[i]=in[i]/acolortab[level][i]; else out[i]=in[i]/acolortab[level][i]; } } //从SRAM 0xC0100000读数据到 cha_y[CIFW][CIFH]; //cha_u[CIFW/2][CIFH/2]; cha_v[CIFW/2][CIFH/2]; #ifdef DEBUG void put_num8(unsigned char i) {
put_char(((((i>>4) & 0x0f) + '0')> '9' )? ((i>>4) & 0x0f) +'0'+7 : ((i>>4) & 0x0f) +'0' ); put_char((((i & 0x0f) + '0')> '9' )? (i & 0x0f) +'0'+7 : (i & 0x0f) +'0' ); } static void put_num32(int i) {
put_num8((unsigned char)(( i>>24) & 0xff)); put_num8( (unsigned char) (i>>16 & 0xff)); put_num8((unsigned char)(( i>>8) & 0xff)); put_num8( (unsigned char) (i & 0xff)); } #endif void readfile(void) {
int i,count=0; for (i=0;i {
//取决于图像的帧格式 cha_y[(i*2/6%CIFW)][(i*2/6/CIFW)*2]=RAM_BYTE(i); cha_y[(i*2/6%CIFW)+1][(i*2/6/CIFW)*2]=RAM_BYTE(i+1); cha_y[(i*2/6%CIFW)][(i*2/6/CIFW)*2+1]=RAM_BYTE(i+2); cha_y[(i*2/6%CIFW)+1][(i*2/6/CIFW)*2+1]=RAM_BYTE(i+3); cha_u[i/6%(CIFW/2)][i/6/(CIFW/2)]=RAM_BYTE(i+4); cha_v[i/6%(CIFW/2)][i/6/(CIFW/2)]=RAM_BYTE(i+5); count++; } } //写编码文件到串行口 void writefile(FILEBUF *buf) {
int i; for (i=0;i<=buf->length;i++) {