之前记录过串口模块、SDRAM模块、VGA和TFT屏模块的开发,把他们结合起来就能做出串口传图工程了。
很多人一直没做好这个工程,很大的原因是 SDRAM 控制器没有写好,所以采用了ROM或RAM来做缓存,最终显示的图片分辨率非常小。如果SDRAM控制器搞好了,那就可以传大图了,我在之前的博客已经记录了 SDRAM 控制器的开发过程,那时候最后也进行了传图,但是没有记录上。这次做图像处理系列工程,专门整理一下传图的整个过程和注意事项。
一、RGB332格式
串口一次传输 8bit 的数据,所以最开始设计时,我想的也是一个串口 8bit 数据就代表一个 8bit 的像素,这样用之前的串口模块来接收就可以了,非常简洁。
1、生成 RGB332 的 Hex 文本(学自V3学院FPGA教程)
日常见到的图片大多是 24bit 的,称为 RGB888,而我们这次要用 8bit 代表一个像素值,则要使用 RGB332。此外还需要将一张图片里所有的像素值转化为 Hex 文本,串口那边才能识别。这个过程看起来很复杂,实际上用 MATLAB 软件很容易就可以实现。
(1)网上找到一张喜欢的图片,将分辨率调整为 VGA 或 TFT 显示的分辨率,例如我找到一张美女的图片,命名为 woman.jpg。很多软件进行图片分辨率调整,最后要么是图片变形,要么是简单的裁剪,如果是分辨率大的图,按分辨率裁剪就只能看到图片的一小部分了。我采用的调整工具是光影魔术手,可以指定分辨率的纵横比,调整框可以继续调大调小,最大限度的获得原图的有效部分,而图片本身也不会变形。
(2)打开MATLAB,新建 .m 文件,将刚刚的图片和 .m 文件放在同一个文件夹,输入如下程序。程序都写了注释,还是比较好理解的,bitshift函数是移位函数,正数为左移,负数为右移,不懂的可以在MATLAB软件中查看具体用法。
1 %-------------------------------------------------------------------------- 2 %-- 图片数据转换:1个像素转换成1个 8bit hex 数据 3 %-------------------------------------------------------------------------- 4 clear all; 5 RGB24 = imread('woman.jpg'); %读取图片文件 6 7 R = bitshift(RGB24(1:end,1:end,1),-5); %取R高3位,{5'b0,R[7:5]} 8 G = bitshift(RGB24(1:end,1:end,2),-5); %取G高3位,{5'd0,G[7:5]} 9 B = bitshift(RGB24(1:end,1:end,3),-6); %取B高2位,{6'd0,B[7:6]} 10 rgb332 = bitshift(R,5) + bitshift(G,2) + B; %拼接{R[7:5],G[7:5],B[7:6]} 11 12 fid=fopen('rgb332.txt','w+'); %打开文件 13 fprintf(fid,'%02x ',rgb332'); %将字符打印到txt文件中
(3)点击运行,MATLAB就在文件夹里新建并打印了好了 hex 文件,这就是我们待会串口要用到的图像数据,其格式为RGB332,一个8bit hex 字符代表一个 RGB332 像素。
2、搭建工程
如上是我本次搭建的模块,如果基础扎实,这些都算比较简单的,总而言之就是“串口接收 - SDRAM缓存 - TFT屏显示”,如果有摄像头,那串口部分换成摄像头也是可以的。
3、RGB332转RGB565
如果VGA或TFT屏的图像接口也是8位的,那直接发送后就可以看到图像了。如果是16位的,那串口接收到RGB332的像素数据后还得进行 RGB332 转 RGB565 的操作,这样才能和屏幕像素格式相匹配。
转换的方式非常简单,不够的低位直接补0,或者不够的低位继续填充原像素值的低位,就算是转RGB888也是一样的思想。这块代码比较简单,我直接写在了顶层的top模块,如下所示:
assign wr_data = {uart_data[7:5],uart_data[6:5], //SDRAM写数据 uart_data[4:2],uart_data[4:2], uart_data[1:0],uart_data[1:0],uart_data[0]}; assign wr_en = uart_data_vld; //SDRAM写使能
4、实验效果
实际的效果是肉眼可见图片慢慢的加载,直到加载完成。加载速度受串口传输速度的影响,尽可能把串口传输速度提高,如比特率设置为115200,虽然还是比较慢,但可以忍受。
二、RGB565格式
上面RGB332的传输方式,导致图片质量不好,因此改为直接传输RGB565格式,看看效果是否会变好。
1、生成 RGB565 的 Hex 文本(学自V3学院FPGA教程)
(1)网上找到一张喜欢的图片,将分辨率调整为 VGA 或 TFT 显示的分辨率。
(2)打开MATLAB,新建 .m 文件,将刚刚的图片和 .m 文件放在同一个文件夹,输入如下程序。bitshift函数是移位函数,bitand函数是位与函数,把数字转化为二进制来看就能想通了。
1 %-------------------------------------------------------------------------- 2 %-- 图片数据转换:1个像素转换成2个 8bit hex 数据 3 %-------------------------------------------------------------------------- 4 clear all; 5 RGB24 = imread('woman.jpg'); %读取图片文件 6 7 fid = fopen('rgb565.txt','w+'); %打开文件 8 [ROW,COL,N] = size(RGB24); %获得图片尺寸[高度,长度,维度] 9 10 for i = 1:ROW 11 for j = 1:COL 12 RG = bitand(RGB24(i,j,1),248) + bitshift(RGB24(i,j,2),-5); %{R[7:3],3'd0} + {5'd0,G[7:5]} 13 G = bitand(RGB24(i,j,2),28); %{3'd0,G[4:2],2'd0} 14 GB = bitshift( G,3) + bitshift(RGB24(i,j,3),-3); %{G[4:2],5'd0} + {3'd0,B[7:3]} 15 fprintf(fid,'%02x %02x ',RG,GB);%将字符打印到txt文件中 16 end 17 end
(3)点击运行,MATLAB就在文件夹里新建并打印了好了 hex 文件,这就是我们待会串口要用到的图像数据,其格式为RGB565,两个 8bit hex 字符代表一个 RGB565 像素,第一个字符是R5G3,第二个字符是G3B5。
2、搭建工程
和上面是一样的,串口模块变为了16bit,因为我在串口模块内部增加了 8bit 转 16bit 数据的操作。
3、8bit转16bit
串口接收后的 2 个数据代表 1 个 RGB565 像素,因此我们要把串口的一个个 8bit 数据进行拼接,还原为 16bit RGB565 像素,这一块代码用到时序逻辑,我写在了串口模块里面。
always @(posedge clk or negedge rst_n) begin if(!rst_n) byte_cnt <= 'd0; else if(add_byte_cnt) begin if(end_byte_cnt) byte_cnt <= 0; else byte_cnt <= byte_cnt + 1'b1; end end assign add_byte_cnt = rx_data_vld; assign end_byte_cnt = add_byte_cnt && byte_cnt== 2-1; always @(posedge clk or negedge rst_n) begin if(!rst_n) dout <= 0; else if(add_byte_cnt) dout <= {dout[7:0],rx_data}; end always @(posedge clk or negedge rst_n) begin if(!rst_n) dout_vld <= 1'b0; else if (end_byte_cnt) dout_vld <= 1'b1; else dout_vld <= 1'b0; end
4、实验效果
可以看到,图片效果比上面好太多了,几乎与电脑上的原图无异。既是如此,那以后做图像处理就用这个版本的工程吧!
参考资料:
[1]V3学院FPGA教程
[2]小梅哥FPGA教程
[3]正点原子FPGA教程