FPGA实现8080接口驱动TFL LCD屏(ILI9486芯片)
来源:互联网 发布:暗黑3装备数据库 编辑:程序博客网 时间:2024/05/29 19:20
1.简介
1.1 ILI9486
实际驱动LCD的芯片,最大可驱动320*480的LCD屏,自带GRAM,作为FPGA的协处理器,FPGA利用8080接口向GRAM中写数据,ILI9486负责把GRAM的数据显示在屏幕上。成功驱动的最关键一步也就是配置它的工作方式,也就是根据它的芯片手册初始化,芯片手册可以百度到。
手册很重要!手册很重要!手册很重要!
基本会碰到的坑在手册里都有提及,之前没有成功的主要原因就是没有发现里面提及的在芯片软复位后,需要等待至少5ms才能读取其他指令,且软复位后进入睡眠模式,并默认关闭显示功能,退出睡眠模式后又需等待5ms才能读取其他指令
1.2 8080接口
使用一种接口协议最重要的就是要知道它的时序,下面附上利用8080接口操作ILI9486的时序图
写时序(命令、数据)
读时序(数据)
CSX:片选线
RESX:硬件复位线
D/CX:指令数据选择线 (低为指令,高为数据)
WRX:写命令线
RDX:读命令线
D[17:0]:数据线
所有控制线皆为低有效
可以看到,在8080接口中,在正常的驱动过程中,WRX充当了时钟的作用,在上升沿读取数据,所以它的速度决定了写RAM的速度(不是屏幕绘制速度)。
这是具体时序的时间约束,比较重要的有最小读写周期、最小数据建立和保持时间,决定了FPGA的驱动时钟频率,另外注意到DCX没有最小时间要求,所以它的信号可以和读写信号在同一时钟周期变化
2.代码实现
项目实现按钮切换显示三原色和背光pwm调节
顶层模块
主要实现按钮检测和子模块调用
module LCD_Top( input wire clk, //用来调试的按钮 input wire btn_pwm_on, input wire btn_light_up, input wire btn_light_down, input wire btn_rst, input wire btn_swap, //控制信号线 output wire cs, output wire rs, output wire wr, output wire rd, output wire lcd_rst_n, //数据线 output wire [15:0] data, //pwm调光 output wire lcd_pwm, output wire pwm_stat ); reg btn_ltup; reg btn_ltdn; reg btn_swp; reg btn_pwmon; reg is_pwm=0; reg [4:0] red=0; reg [5:0] green=0; reg [4:0] blue=0; reg [3:0] bright=0; reg [2:0] color=0; //使用xilinx开发板,检测按钮下降沿 always@ (posedge CLK_50M) begin if({btn_ltup,btn_light_up} == 2'b10) if(bright < 4'd15) bright <= bright + 1'd1; if({btn_ltdn,btn_light_down} == 2'b10) if(bright > 1) bright <= bright - 1'd1; if({btn_swp,btn_swap} == 2'b10) if(color == 2'd2) color <= 0; else color <= color + 1'd1; if({btn_pwmon,btn_pwm_on} == 2'b10) is_pwm <= ~is_pwm; btn_ltup <= btn_light_up; btn_ltdn <= btn_light_down; btn_swp <= btn_swap; btn_pwmon <= btn_pwm_on; end always@ (posedge CLK_5M) begin case(color) 0: begin red = 5'b11111; green = 0; blue = 0; end 1: begin red = 0; green = 6'b111111; blue = 0; end 2: begin red = 0; green = 0; blue = 5'b11111; end endcase end assign pwm_stat = is_pwm; assign lcd_rst_n = 1; //时钟分频IP核 clk_wizard clkw (// Clock in ports .CLK_IN1(clk), // IN 50Mhz // Clock out ports .CLK_50M(CLK_50M), // OUT .CLK_5![这里写图片描述](http://img.blog.csdn.net/20170919110910711?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMTQ1Mjc0Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)M(CLK_5M), // OUT .CLK_20M(CLK_20M), // OUT // Status and control signals .RESET(~btn_rst) ); //背光控制模块 LCD_BL_Control bl_ctl ( .clk_5M(CLK_5M), .isPWM(is_pwm), .brt(bright), .pwm(lcd_pwm) ); //初始化及显示模块 LCD_Config cfg ( .clk(CLK_20M), .rst_n(btn_rst), .data_in({red,green,blue}), .cs(cs), .rs(rs), .wr(wr), .rd(rd), .data_out(data) );endmodule
背光控制模块
根据输入亮度生成pwm波
输入时钟5MHz,能产生略低于20KHz的pwm信号
module LCD_BL_Control( input wire clk_5M, input wire isPWM, //pwm状态指示灯 input wire [3:0] brt, //亮度 output wire pwm //pwm信号 ); parameter unit = 'd16; parameter total = 'd256; reg [18:0]cnt = 0; reg r_pwm = 0; always@ (posedge clk_5M) begin if(cnt == total-1) cnt <= 0; else cnt <= cnt +1'd1; end always@ (posedge clk_5M) begin if(cnt < unit*brt-1) r_pwm <= 1; else r_pwm <= 0; end assign pwm = isPWM?r_pwm:1'b1; //根据是否选择pwm模式输出pwm波或高点平endmodule
配置及显示模块
为了偷懒用一个巨长的状态机操作ILI9486,其实不太利于扩展,之后如果想显示图片或者搭配摄像头最好能分立模块再搭配FIFO
输入20MHz时钟,每两个时钟完成一次完整读或写操作,所以写数据的频率为10MHz,写满320*480的RAM的频率大概为65Hz。也就是说屏幕的刷新率不能高于65Hz,在后面初始化配置参数时需要用到。
其实ILI9486的配置选项很多,这里只配置了我认为最基础的,各位看官可以按需配置。
module LCD_Config( input wire clk, input wire rst_n, input wire [15:0] data_in, output reg cs, output reg rs, output reg wr, output reg rd, output reg [15:0] data_out ); parameter total_px = 18'd153600; parameter total_dl = 13'd5120; reg [5:0] cnt=0; reg [12:0] delay=0; reg [17:0] px_cnt=0; always@ (posedge clk) begin if(~rst_n) begin cnt <= 0; delay <= 0; px_cnt <= 0; end rd <= 1; cs <= 0; case(cnt) //cmd//sw reset 0: begin rs <= 0; wr <= 0; data_out[7:0] <= 8'h1; cnt <= cnt + 1'd1; end 1: begin wr <= 1'd1; cnt <= cnt + 1'd1; end //delay ~5ms 2: begin if(delay == total_dl - 1'd1) begin delay <= 13'd0; cnt <= cnt + 1'd1; end else delay <= delay + 1'd1; end //cmd//Sleep OUT 3: begin rs <= 0; wr <= 0; data_out[7:0] <= 8'h11; cnt <= cnt + 1'd1; end 4: begin wr <= 1'd1; cnt <= cnt + 1'd1; end //delay ~5ms 5: begin if(delay == total_dl - 1'd1) begin delay <= 13'd0; cnt <= cnt + 1'd1; end else delay <= delay + 1'd1; end //cmd//Normal Display Mode ON 6: begin rs <= 0; wr <= 0; data_out[7:0] <= 8'h13; cnt <= cnt + 1'd1; end 7: begin wr <= 1'd1; cnt <= cnt + 1'd1; end //cmd//Display ON 8: begin rs <= 0; wr <= 0; data_out[7:0] <= 8'h29; cnt <= cnt + 1'd1; end 9: begin wr <= 1'd1; cnt <= cnt + 1'd1; end //cmd//Column Address Set 10: begin rs <= 0; wr <= 0; data_out[7:0] <= 8'h2A; cnt <= cnt + 1'd1; end 11: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 12: //arg//sc[15:8] begin rs <= 1; wr <= 0; data_out[7:0] <= 8'h0; cnt <= cnt + 1'd1; end 13: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 14: //arg//sc[7:0] begin rs <= 1; wr <= 0; data_out[7:0] <= 8'h0; cnt <= cnt + 1'd1; end 15: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 16: //arg//ec[15:8] begin rs <= 1; wr <= 0; data_out[7:0] <= 8'h1; cnt <= cnt + 1'd1; end 17: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 18: //arg//ec[7:0] begin rs <= 1; wr <= 0; data_out[7:0] <= 8'h3F; cnt <= cnt + 1'd1; end 19: begin wr <= 1'd1; cnt <= cnt + 1'd1; end //cmd//Page Address Set 20: begin rs <= 0; wr <= 0; data_out[7:0] <= 8'h2B; cnt <= cnt + 1'd1; end 21: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 22: //arg//sp[15:8] begin rs <= 1; wr <= 0; data_out[7:0] <= 8'h0; cnt <= cnt + 1'd1; end 23: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 24: //arg//sp[7:0] begin rs <= 1; wr <= 0; data_out[7:0] <= 8'h0; cnt <= cnt + 1'd1; end 25: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 26: //arg//ep[15:8] begin rs <= 1; wr <= 0; data_out[7:0] <= 8'h1; cnt <= cnt + 1'd1; end 27: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 28: //arg//ec[7:0] begin rs <= 1; wr <= 0; data_out[7:0] <= 8'hDF; cnt <= cnt + 1'd1; end 29: begin wr <= 1'd1; cnt <= cnt + 1'd1; end //cmd//Memory Access Contrl 30: begin rs <= 0; wr <= 0; data_out[7:0] <= 8'h36; cnt <= cnt + 1'd1; end 31: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 32: //arg begin rs <= 1; wr <= 0; data_out[7:0] <= 8'h0; cnt <= cnt + 1'd1; end 33: begin wr <= 1'd1; cnt <= cnt + 1'd1; end //cmd//Interface Pixel Format 34: begin rs <= 0; wr <= 0; data_out[7:0] <= 8'h3A; cnt <= cnt + 1'd1; end 35: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 36: //arg begin rs <= 1; wr <= 0; data_out[7:0] <= 8'b0101_x_101; cnt <= cnt + 1'd1; end 37: begin wr <= 1'd1; cnt <= cnt + 1'd1; end //cmd//Frame Rate Control 38: begin rs <= 0; wr <= 0; data_out[7:0] <= 8'hB1; cnt <= cnt + 1'd1; end 39: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 40: //arg begin rs <= 1; wr <= 0; data_out[7:0] <= 8'hB0; cnt <= cnt + 1'd1; end 41: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 42: //arg begin rs <= 1; wr <= 0; data_out[7:0] <= 8'h11; cnt <= cnt + 1'd1; end 43: begin wr <= 1'd1; cnt <= cnt + 1'd1; end //cmd//Display Function Control 44: begin rs <= 0; wr <= 0; data_out[7:0] <= 8'hB6; cnt <= cnt + 1'd1; end 45: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 46: //arg1 begin rs <= 1; wr <= 0; data_out[7:0] <= 8'h0; cnt <= cnt + 1'd1; end 47: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 48: //arg2 begin rs <= 1; wr <= 0; data_out[7:0] <= 8'h1; cnt <= cnt + 1'd1; end 49: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 50: //arg3 begin rs <= 1; wr <= 0; data_out[7:0] <= 8'h3B; cnt <= cnt + 1'd1; end 51: begin wr <= 1'd1; cnt <= cnt + 1'd1; end //cmd//Memory Write 52: begin rs <= 0; wr <= 0; data_out[7:0] <= 8'h2C; cnt <= cnt + 1'd1; end 53: begin wr <= 1'd1; cnt <= cnt + 1'd1; end 54: //arg begin rs <= 1; wr <= 0; data_out <= data_in; cnt <= cnt + 1'd1; end 55: begin wr <= 1'd1; //memory is full if(px_cnt == total_px - 1'd1) begin px_cnt <= 0; cnt <= 6'd52; end else begin px_cnt <= px_cnt + 1'd1; cnt <= 6'd54; end end endcase endendmodule
3.实际效果
- FPGA实现8080接口驱动TFL LCD屏(ILI9486芯片)
- s3c2416 3.5寸屏ili9486 BT035H驱动
- LCD驱动芯片大全
- 【传感器】HT1621 LCD驱动芯片
- linux LCD驱动(四) --- 驱动实现
- LCD驱动芯片1335控制器C51源程
- LCD驱动芯片SED1335控制器C51源程序
- FPGA驱动PCI9054芯片,VHDL,Xinlinx XC4VLX100
- 用FPGA驱动DA芯片TLV5618
- OV系列CMOS图像传感芯片的接口时序的FPGA实现
- LCD驱动接口函数(转)
- S3C6410驱动I80接口LCD
- S3C6410驱动I80接口LCD
- LCD-Framebuffer驱动实现
- LCD驱动的实现
- FPGA基础知识1(FPGA芯片结构)
- S3C2440A驱动RGB接口TFT LCD的研究(转载)
- S5PC100芯片的linux-lcd驱动移植(基于2.6.35.13内核)
- URLHTTPConnect的get和post请求_Teacher的,哈哈
- jz2440 插入U盘 挂载
- NodeMCU文档中文翻译 6 MQTT模块
- test
- 如何正确理解ABBYY FineReader中的模式和语言
- FPGA实现8080接口驱动TFL LCD屏(ILI9486芯片)
- java不确定参数个数方法例子
- Kotlin-->范围选择进度条, 双向SeekBar
- 分库分表的工具
- 虚拟机 安装VMware Tools
- JS中new操作符与函数返回值return
- Redis乐观锁控制事务
- JavaWeb 通过ios上传图片旋转问题
- Android 九宫格抽奖