与上一次写学习总结相隔时间有点久,因为最近比较忙,一直干一些有的没的杂活,学习时间较少,而且第一次用Xilinx的FPGA不熟悉vivado开发环境和流程。以往的学习都是用Altera的芯片,实验室有了一块闲置的正点原子达芬奇开发板,刚好用来学习Xilinx开发流程。
目录
一、实验目的
实验内容为LED显示,跟着正点原子LED彩条实验教程学习,但毕竟已经有了一点基础,就将实验稍做改进。具体为:rs232协议串口传入RGB888的彩色图;将R、G、B三通道三个字节的数据组成一个24位像素的数据,传入LCD驱动模块;另外设置一个LCD背光控制模块,PWM调光设置4个等级,由按键控制输出。
二、内容
下图是正点原子实验的各模块视图
下图是此次实验修改的各模块视图:
各模块功能如下:
uart_rx:串口接收模块,输出为并行8位数据以及一个数据标志信号;
bit_wide_conver:将串口接收模块输出的8位数据转换成24位RBG888数据,并带有数据标志信号。
rd_in:读lcd ID,将其输出到config_gen模块;
config_gen:根据输入的LCD ID产生后续lcd_pic、lcd_driver模块的配置信号,包括:时钟、 行同步信号时钟数、行后沿时钟数、行像素显示时钟数、行后沿时钟数以及场的各信息。与正点原子不同的是,将时钟、显示参数单独设置为一个模块。
lcd_pic:图像数据生成模块,将输入的RGB888缓存到ram ip核中,然后根据lcd_driver模块输入的显示坐标信息,将RGB888图像与彩条像素数据整合,使得输出的一帧图像串口输入的图像在LCD的中央。
lcd_driver:驱动LCD,采用的模式与VGA一样,只是多了一个使能信号、驱动时钟。
key_filter:按键消抖模块,每次按一下按键产生一个时钟的低电平。
bl_ctrl:控制lcd屏亮度,PWM调光设置4个等级,由按键控制输出,按一次按键转换一次亮度(循环)。
三、相关程序
串口输入的RBG888图像数据由MATLAB生成,R、G、B通道数据分开传送。代码为
clc;
clear;
close all;
RGB24 = imread("C:\Users\zxy\Pictures\1.png");
R = uint32(RGB24(:,:,1));
G = uint32(RGB24(:,:,2));
B = uint32(RGB24(:,:,3));
r = R';
g = G';
b = B';
a = r(101);%用于测试正确写入文件数据的顺序
%测试发现MATMAB数组顺序索引时,按列输出,所以先转置
fid=fopen('rgb111.txt','w+');
for i=1:10000
fprintf(fid,'%02x ',r(i));
fprintf(fid,'%02x ',g(i));
fprintf(fid,'%02x ',b(i));
end
bit_wide_conver代码如下所示,主要思想就是对串口传入的数据计数,计数最大值为2。计数器为0时,输入数据赋予中间24位数据最高的8位;为1时,数据赋予输出数据中间8位;为2时,赋予低8位。一个像素的赋值后,将中间变量24位数据赋予输出的RGB888 24位输出数据,并且输出一个数据标志信号。
module bit_wide_conver (
input sys_clk,
input sys_rst_n,
input [7:0] pi_data,
input pi_flag,
output reg po_flag,
output reg [23:0] po_data
);
reg [1:0] cnt_byte;
reg [23:0] data;
reg pi_flag_reg;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) cnt_byte <= 2'd0;
else if(pi_flag == 1'b1 && cnt_byte == 2'd2) cnt_byte <= 2'd0;
else if(pi_flag == 1'b1) cnt_byte <= cnt_byte + 1'b1;
else cnt_byte <= cnt_byte;
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) data <= 24'd0;
else if(pi_flag == 1'b1) begin
case(cnt_byte)
2'd0:data[23:16] <= pi_data;
2'd1:data[15:8] <= pi_data;
2'd2:data[7:0] <= pi_data;
default:data <= 24'hffffff;
endcase
end
else data <= data;
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) pi_flag_reg <= 1'b0;
else pi_flag_reg <= pi_flag;
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) po_data <= 24'd0;
else if(pi_flag_reg == 1 && cnt_byte == 2'd0)
po_data <= data;
else po_data <= po_data;
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) po_flag <= 1'b0;
else if(pi_flag_reg == 1 && cnt_byte == 2'd0)
po_flag <= 1'b1;
else po_flag <= 1'b0;
end
endmodule
其余模块代码虽说是本人一个一个字母敲上的,但都只在野火以及正点原子教程的思想修改而来,没什么原创性。
PS:有一说一,个人感觉野火讲的课程比正点原子好得多,只是野火Xilinx教程资料不足。
四、结果
最终PWM调光方案
五、遇到的问题
(1)配置锁相环时一直报错,出错原因显示什么什么反馈,对FPGA原理不清楚就不会通透,报错原因根本就理解不了,得加强对FPGA。后面改成自动就好了。
(2)锁相环生成时钟正确,可是config_gen选择时钟输出时,时钟周期却错误,仔细观察发现输出时钟上升沿总是与50M系统时钟同步(33.3M输出时钟),这没道理。锁相环又设置半天没啥改变,突然脑子一激灵意识到选择输出是用的always语言,有问题,将锁相环生成时钟寄存了,导致输出时钟与系统时钟同步,应该变为always@(*)。
(3)串口发送数据位宽8位,一开始认为调试助手会将txt文件中16进制自动识别输出。例如acbc 11aa,会自动分成ac、bc、11、aa输出,然而并不是,所以MATLAB中将各通道依次写在txt文件中(之前是合成24位数据写入)。
(4)MATLAB报错打开文件过多,第一次遇见这问题,开始以为电脑太垃圾内存不够,自己看看发现原因是将fopen写在循环内部。我真是呆逼。
(5)关于串口发送数据,最先的想法是根据rs232的原理一次直接发送24位数据,这是可行的。打开串口调试软件发现根本选择不了一次发送24位。修改为一次发送8位数据,再将数据转换位24位。
(6)PWM调光,占空比设置为1/4、2/4、3/4、1屏幕亮度改变不明显,后面设置为1/8,2/8,3/8,4/8,1改变也不大。最终方案为0,4/16,7/16,8/16,1。此外,发现供电功率也会对调光效果产生影响,usb供电的效果比电源适配器供电的效果好。
六、总结
(1)对一些基本的知识掌握不够,比如FPGA原理、Xilinx的芯片详细等等。
(2)对Verilog掌握深度不够,后续要回顾相关书籍。相比初次学习Verilog语言,现今有更深的知识深度,回顾学习应该有新的收获。
(3)对Xilinx的开发流程有了一定了解,好耶!后续目标是摄像头采集图像数据到LCD or 显示器上显示。