本文主要涉及以下几个问题
1:显示技术要求
2:整体设计思想
3:设计方案及整体硬件框图
4:图片灰度算法原理
5:设计状态机
6:modelsim仿真分析
7:核心代码(顶层模块的)
以下是基于FPGA的LED全彩图形显示控制器设计的1核心内容,因为静态驱动相较于动态扫描驱动方式所用的驱动芯片会大大增加,LED屏的成本也会随之升高,动态扫描要提高亮度增加扫描频率就可以很好达到,所以这种全静态驱动的LED已经很少见到。本文是围绕静态驱动进行展开。由于刚接触设计,文中必然会有错误或者不足有待改进之处诚请读者指出。
1:显示技术要求
要求显示为三基色的16M色全彩图片。屏幕的驱动方式为静态,静态相对于动态扫描方式最大的优点是显示整体亮度要比动态扫描高。所谓静态就是等到8位中的某一位数据全部打到驱动芯片上再拉低锁存信号进行显示,这样的另一个好处是色彩显示准确。而动态扫描一般常见的有1/2,1/4,1/8,1/16扫描。扫描分为行扫描和列扫描,最常见的是行扫描,拿1/4行扫描来说,对于256级灰度来说,就是在显示某一位时不同与静态驱动那样是等所有数据传到驱动芯片的相应位置再显示,而是先传1/4屏幕的数据进行显示然后再显示下一个1/4的区域以此类推直到显示完整个屏幕完成一个扫描周期。屏幕刷新率为120Hz,屏幕刷新率是指在在一秒时间内能显示的图片帧数,120Hz就是在1秒时间里能显示120幅图片,对于一副图片来说就是一秒钟连续显示120次
2:整体设计思想
整个设计围绕如何存数据,如何读数据,如何控制数据传输,何时让数据在屏幕上显示以及显示时长。由于FPGA内部有一小块ROM空间,为了系统的简化和节省开发成本,不需要单片机完成数据更新通讯,直接将图片或者文字信息通过软件生成mif文件然后通过Quartus软件中的ROM IP核,再将mif文件导入其中最后只需例化就可以完成数据存储。读数据和传数据是控制器设计也是设计的核心内容,其中包含pwm灰度算法的编写,整个硬件设计是用QuartusⅡ13.0的VerilogHDL语言开发。
3:设计方案及整体硬件框图
下图3-1是整体硬件框图。首先要在上位机用一些专用的图片处理或者字膜提取软件将图片和字膜信息生成mif文件,然后PC上位机要完成数据往FPGA上存储,这一步是完成上位机与下位机之间的通讯。写控制模块在这一步已经由ROM IP核实现。在例化ROM IP核基础上ROM的读端口时钟,使能,读使能信号已经定义好,需要在驱动模块中控制读使能信号以及灰度算法的实现。地址为8位宽,读取后按照从高位到低位的顺序分成8路通过FPGA引到LED显示屏的数据接收端口。
图1 硬件系统框图
4:图片灰度算法原理
灰度是按照数据位的不同,让其显示不同位数据时显示亮度有所区别,假如一个LED屏的一个子像素点是由8位数据控制,那一颗LED灯珠也就是一个子像素点有2的8次方亮度组合即256级灰度,从0到255一共有256种亮度。由于采用的是稳压直流驱动LED灯发光,当LED点亮时对于每一颗LED子像素来说其流过的电流已经固定,LED发光强度也已确定,但是实际人眼感受的光强还应和发光时间有关。用极限的思想去想一下,假如灯发光的时间接近于0那人眼是感受不到的。而PWM灰度算法正是应用这种思想,让不同位数据点亮时间不同,达到人眼感受不同光强的效果。如果外部驱动芯片的时钟为C,那一个时钟周期为1/C记做t,对于8位数据静态驱动来说,传完第0位所有数据需要时间为nt,传完某一位,对于动态扫描为了提高刷新率,在亮灭时间内进行下一次数据的传输,有效利用空闲时间,一位数据的亮灭时间应为nt的整数倍,静态驱动则不需要nt,最少可以为一个时钟周期。如果传完第0位数据后让其显示nt/2时间再灭nt/2,接着传第1位数据然后让其显示nt时间,第2位数据显示2nt,以此类推,到第7位显示27nt时间。
本次设计所用到的外部驱动芯片时钟为15MHz,t为1/15000000s每一位共有512bit数据,每一比特数据控制一个子像素点。由于采用静态驱动,等到512bit数据打完所有子像素点同时亮。地址为8位宽,共需要64个地址,传完一位数据共需要64个内部读ROM的时钟周期,即64t。让内部读ROM时钟与外部串行传输时钟一致,数据读完立刻传输。设计要求是屏幕显示刷新为120Hz,算出第0位数据显示时间理论为468个时钟周期,这样显示刷新能保证在120Hz以上。第一位显示时间为第0位的2倍,下面高位以此类推。这样就完成了图片灰度的功能。
5:设计状态机
FPGA有大量的逻辑门可以使用,可以完成相应的控制功能。先在上位机用QuartusⅡ软件用Verilog HDL进行硬件语言的逻辑描述。整个设计是要把存储在ROM中的一副图片数据读出并显示。由于是256级灰度的全彩图片所以按照数据位的高低依次从第一位一直到第八位每一位这里记做一帧,等一帧数据全部传输到驱动芯片上后拉低读使能信号,不让其继续读数据,同时拉低驱动芯片的锁存信号让数据在屏幕上进行显示,显示一段时间再次拉高读使能信号和锁存信号。进行下一位数据的读取这样循环八次一副图片才显示完,再回到初始化进行下一幅图片显示。
逻辑仿真又叫做前仿真,仿真时不用考虑门延时,是对设计的逻辑进行验证,看输入输出波形是否符合设计预期。在灰度算法中最低位数据显示时间为468个时钟周期,时钟为15MHz,理论显示时间为468×(1/15000000)s,即31200ns,如图5、6-1仿真波形中两个光标的时间间距为最低位数据传输完后锁存信号拉低的时间长度是31199.28ns,与理论几乎没有误差。
图6-1最低位数据显示时间
如图6-2位第二位数据显示时间。理论上为显示时间为上一位数据显示时间的2倍为62400,如图5-2两个光标的时间间距为62400与理论没有误差。
图6-2 第二位数据显示时间
如图6-3是第8位显示时间为3993600ns为第一位显示时间的128倍,与理论值无误差。由逻辑仿真得到灰度算法正确。
图6-3第8位数据显示时长
如图6-4是一副图片8帧数据全部打完后的时间约为0.008s,这样1s内可显示125帧图像。屏幕刷新率为125Hz,符合设计要求最低的120Hz。
6-4 第一个显示周期结束
门级仿真是考虑实际综合布线后的数据传输和计算的物理延时,也叫做后仿真。如图6-5是测试所用的数据,这里应该注意让地址63左右几个地址的数据最好不要一样,因为在读取63地址数据后要停止读取数据开始在屏幕上显示,如果让这几个地址数据一样,那么在第63附近的地址里里读出的数据是一样的,这样在分析波形验证是否能准确读出数据时候不能一眼看出来最后一个地址的数据是地址63的还是地址64里的,对波形分析造成干扰,如果想要进行确定还需要数时钟或者计算读ROM的时间来计算时钟。让其数据不一样可避免这个问题,减少分析波形的工作量。
6-5 后仿真测试所用数据
如图6-5门级仿真第64与第65周期数据分别为8’h40,8’h01,分别是地址63和地址64里的数据。可以看到在第65个时钟周期时数据确实是读出了。但是在拉低锁存信号时数据实际电路中没有传入外部的驱动芯片内。第一位的数据传输正确。
6-6 门级仿真
如果按照第64个时钟周期结束后立马同时拉低读ROM使能信号和外部驱动芯片的锁存信号这样在显示第一位时是没有问题的,但是到了下一位的第一个时钟如图5-6可以看到数据保持在上一位最后一个时钟周期时的状态。假如在第64个时钟周期拉低读ROM使能信号那么这一个数据将会在错误地进入下一位数据的首地址,这样导致的后果是每一个位数据的最后一个地址实际控制的是16个子像素而不是原本的8个。这样会对地址控制数据位置的确定带来麻烦而且对于图片数据的调整更加复杂。这样在灰度算法中应该让第一位读完拉低“rden”的时钟计数应该小于65而非64。
6-7第二位数据门级仿真
在图6-6中可以看到在数据由h3f到h40变化中出现一个大概1/20时钟周期的毛刺。这是由于组合逻辑太多的缘故,这带来的影响是本来不亮的部分区域会发出很微弱的光,但不影响整体显示。
7:核心代码(顶层模块的)
//*******************************************//
//----------------main code------------------//
//----------------顶层模块--------------------//
module led_display(
input sys_clk,
input sys_rst,
output [7:0] data,
output led_en_top,
output led_lat_top,
output clk_o,
output led_clk,
output read
//output [9:0]addr
);
// wire define
//wire led_clk; //PLL得到分频时钟
wire locked; //PLL输出稳定信号标志,为高说明已经稳定
wire rst_n_w; //内部复位信号
//wire clk_o;
//wire [7:0] data;
assign rst_n_w = sys_rst && locked;//待内部的pll锁相环分频信号输出稳定后停止内部复位,即整个FPGA芯片可以正常工作
//*****************例化模块******************//
//--------------PLL锁相环例化----------------//
led_pll u_led_pll(
.areset (~sys_rst),
.inclk0 (sys_clk),
.c0 (led_clk),
.c1 (clk_o),
.locked (locked)
);
//---------------led模块例化---------------//
led u_led(
.led_clk_w (led_clk),
.sys_rst_n (rst_n_w),
.data_rgb (data),
.led_en (led_en_top),
.led_lat (led_lat_top),
//.clk_o (clk_o)
.rom_read_en (read)
//.adr (addr)
);
endmodule