该篇博文主要对“基于UDP协议的网络摄像头的设计与实现”调试过程遇到的问题进行说明,并阐述其解决过程。
硬件平台:DIY_DE2
软件平台:Quartus II 9.0 + Nios II 9.0 + Visual Studio 2008
以VGA显示作为参照,综合调试,使C#端的视频显示正常。C#端的控件显示分辨率为320*240。
图1 初始图片
上图是初步搭建完成的网络摄像头的显示效果,观察视频图像,其具有以下特点:
(1)有一半的色彩未完全显示;
(2)显示的部分呈斜体状,有锯齿;
(3)显示的部分分辨率降低为160*120,同(1);
(4)显示的图像的起始位置并非一帧图像的开始,即图像错位。
解决过程大致如下:
(1)首先,保证Quartus II端不变的情况下,在NIOS II端不读取缓存在SRAM中的视频数据,而是直接对待传输的数组赋一个定值,则C#端应显示一个单色图像。NIOS II的代码如下,C#端的显示效果如图2所示。
NIOS II端测试代码:
for(j = 0;j < 76800;j++) { //a[j] = IORD(SRAM_16BIT_512K_0_BASE,j); //b[j] = IORD(SRAM_16BIT_512K_0_BASE,j) >> 8; a[j] = 0xff; b[j] = 0x03; }
图2 C#端显示单色图像1
上述图像能够验证UDP传输正确和C#端的显示程序正确。
(2)其次,保证NIOS II正常传输的基础上,即不做刚才的改动,在Quartus II端压缩子程序press_moudle模块中,直接设定特定数据,观察C#端显示效果,效果如图3所示。
图3 C#端显示单色图像2
这张图像与图1很相似,即有一半的色彩未正确显示(因为是单色图片,其他的特征不容易看出来)。根据图2和图3的对比,可以得知:图像数据在读写SRAM时出了问题。到底是读SRAM还是写SRAM的问题?
(3)再次,在NIOS II端,往SRAM里面写特定数据,之后再读取出来,最后通过网络传输给PC,PC端用C#显示,通过这个测试,可以发现NIOS II端读SRAM没有问题,因此,问题便可以确定为写SRAM。
(4)图像错位的问题可以定位为:开始往SRAM写图像数据时,当前数据并非一帧图像的开始,即写图像数据的条件不正确。为了能够正确的锁存住帧开始信号,采用状态机功能,关键代码如下:
always@(posedge iclk or negedge rst_n) begin if(!rst_n) test_out <= 0; else begin if((niosii_cmd) && (oVAL_VS == 2'b01)) begin test_out <= ~test_out; // if(test_out) // rgb_zuhe <= rgb_zuhe + 1; end // else // test_out <= 0; end end always@(posedge iclk or negedge rst_n) begin if(!rst_n) begin // oaddress <= 0; rd_en <= 0; rgb_zuhe <= 0; end else begin case(test_out) 0: begin oaddress <= 0; rd_en <= 0; rgb_zuhe <= 0; end 1: begin if(VGA_X_count[0] && VGA_Y_count[0] && oVAL_HS) begin // rgb_zuhe <= (rgb_zuhe + 1)%32; rgb_zuhe <= {1'b0,iR[9:5],iG[9:5],iB[9:5]}; oaddress <= oaddress + 2; if(oaddress == 18'h257fe) begin rd_en <= 1; oaddress <= 0; end end else rd_en <= 0; end endcase end end
因为test_out的反转,这里其实是隔一帧采一帧的结果。
采用状态机之后,图像错位的现象便得到解决,显示效果如图4所示。
图4 解决图像错位的显示效果
在Quartus II端的压缩子程序press_moudle中,设定图像数据为+1递增的数据,在网络抓包工具wireshark,收到的数据可以看出,数据是呈+2的形式递增的,因此可以断定,存储的时候出现了问题。
刚开始,以为SRAM存储的时候是隔一个地址存储一个数据,后分析一下,实则不然。因为在NIOS II读取缓存在SRAM中的数据的时候,是挨个地址读取的,而读出来的数据确实呈+2的关系,说明:实际写入SRAM的时候还是挨个地址写的,只是写控制器端问题。
后查阅相关资料,16位数据的存储器对应CPU时,地址线应左移一位,在系统的顶层文件中,并没有将地址线左移,因此,造成了分辨率又一次的降低了1/4,解决办法为,往SRAM存储数据时,地址线每次+2即可(在第2步代码中已有所体现)。显示效果如图5所示。
图5 调试完成分辨率降低问题的效果
(1)开始一直以为C#端绘图部分的问题,经C#端写buffer数据显示,并认真分析显示效果后,确定绘图没有问题。下面是分析过程。采用单色递增进行分析。
C#端写buffer测试代码:
for (int m = 0; m < num; m++) { if (m % 2 == 0) buffer[m] = 0; else buffer[m] = (byte)(((m - 1) * 2) % 128); }
通过C#端调试,可以得知num=1468,即734个像素,按照320*240分辨率来算,一包数据可以绘制2行94个像素点,截取细节图(图5的左上部分),如图6所示。
图6 C#端测试细节图
由图形可以看出,第一包数据刚好绘制了2行94个像素点,且3行的起始部分均正确对应,即均从纯黑色开始,说明造成图像倾斜的不是C#的问题。
(2)NIOS II测试,同样采用单色递增的方式进行验证,同样分析前2行94个像素点及以后的像素点,发现第95和96个像素点为非纯黑色,而第97个像素点才开始为纯黑色。这说明95和96像素点为其他的值,并非预期的像素值。
(3)仔细分析,发现最后2个像素点,即4个字节为一包数据的校验位。NIOS II端发送一包数据的最大值为1510-42-4=1464,前42个字节为包头,后4个字节为校验位。而C#端在绘制图像的时候是按照1468个字节来绘制的,因此,绘制的效果是,每次错位2个像素点。在C#端绘制图像的时候需要将num-4。改正后的显示效果如图7所示。
图7 显示效果
C#端控制底层发送视频数据,即实现C#接收视频数据时是从一帧图像的开头,而不是中间的某一包数据,否则,仍会造成图像的错位。但实现的过程出现一些问题。C#端发送命令,NIOS II通过中断方式获取命令时,则会出现以下两种可能的问题:
(1)首先,NIOS II一直往PC发送数据,PC向NIOS II发送命令,NIOS II能够正确收到命令数据,但是在PC不发送命令的时候,NIOS II端仍会进入中断服务函数,接收到空命令或者是未知的多余命令,疑为PC会自动发送命令?这里的PC并非C#。
(2)NIOS II等待PC命令,PC发送命令,但是PC端的命令最多只能进入一次NIOS II的中断服务函数。
这个问题的本质其实就是PC和DM9000A互相发送数据,其中,DM9000A是通过中断的方式接收PC发送下来的数据,但是DM9000A却不能正常的接收到PC发送到数据。测试好久,仍没有好的效果,难道是DM9000A中断处理机制的问题?
鉴于上述两种均不理想的情况,系统采用另外一种解决办法:
NIOS II向PC发送视频数据之前,先发送一包全是0的数据,一旦C#先解析出该包数据,便使能一个标志位,当该标志位使能后,开始绘制图像。这能够解决PC端图像错位的问题,但是不是最优的办法。因为第一包数据为0的缘故,则显示图片的顶部有些许其他的色彩。如图8所示。
图8 最终的显示效果
最后通过wireshark测的传输速度为1Mbps,也就是说基于UDP协议的网络摄像头的帧率为1帧左右,分辨率为320*240。初步验证了系统的可行性。由于视频的帧率和分辨率均太小,下步将研究一下压缩算法及在TCP传输协议上的实现。