ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

20_基于FPGA驱动LCD12864显示图片

2022-02-20 17:33:33  阅读:276  来源: 互联网

标签:cnt 20 FPGA state lcd12864 LCD12864 wr end data


20_基于FPGA驱动LCD12864显示图片

实验原理

LCD12864图片显示原理

LCD12864的驱动原理和时序可以参考上一节"LCD12864显示字符",LCD12864显示图片的关键点就是要弄懂一个问题:LCD12864中每一像素点要如何去点亮或者是熄灭。

LCD12864的显示屏简图如下:

    屏幕分为上下半屏,分别为128*32个像素点。而上下分别的控制是通过选择地址进行控制上下半屏的显示。

将屏幕分为:64行*128列,显示顺序为从左上到右下。

显示的驱动框架如下:

分频模块:

将50M的时钟信号进行简单的16分频,输出大概1.5M时钟信号,给显示控制模块。

/******************时钟分频**********************/

always @(posedge clk or negedge rst)

begin

if(!rst)

         div_cnt <= 15'd0;

else

if(div_cnt==15'h4000)

begin

div_cnt <= 15'd0;

clk_div<=~clk_div;

end

else

    div_cnt <= div_cnt+ 1'b1;

end

显示控制状态机:

    主要分为三个部分:

  1. LCD12864初始化,设置显示模式。
  2. ROM地址的生成
  3. LCD12864行列地址的控制

只要解决了这三个问题,LCD12864的显示控制就基本解决了。下面我们一一讲解:

    在显示过程中,行数有64行,每一行有128列,这里我们将这128列分为16个部分,每一部分为8列,这样128*64就变成了16*64,这样显示的最小元素就从一个像素点变成了1*8个像素点。这样就总共需要16*64=1024个数据单元,每个数据单元的位宽为8。这样我们就可以很方便读出数据进行显示了。这里需要创建一个深度为1024,位宽为8的ROM,然后将我们要显示的图片数据保存在这个ROM中,然后通过控制ROM的地址,将显示屏上的每一个元素的对应数据读出送到数据线上。

    在知道了数据从哪里来到数据到哪里去之后,接下来就是要解决一个重要问题:ROM的寻址。这里将显示屏分为:行(X)和列(Y)。该工程中采用一个地址计数器进行地址的计数,由于显示屏分为上下半屏,各个屏显示的数据量为512,所以这里就可以用该地址计数器的最高位来控制上下半屏的驱动,当最高位为0时,控制上半屏,为1时控制下半屏。

在设置LCD122864的寄存器的时候将DDRAM的地址计数器(AC)设置为自动增加,这样在每写如一个元素数据之后,LCD12864的行地址就会自动加一,显示的位置就会跳到下一个,就不需要不断往LCD12864里送地址了,只需在每写入一行后,切换列地址。

对应代码段如下:

/****************设定图形显示区X轴地址*****************/

wr_x_addr_1:                     

        begin

        next_state <= wr_x_addr_2;

            lcd12864_en <= 1'b1;

    lcd12864_data <= {4'd8,cnt[9],3'd0};//上半屏:0x80;下半屏:0x88

        end

 

wr_x_addr_2:

        begin

            next_state <= wr_data_1;

    lcd12864_data <= {4'd8,cnt[9],3'd0};

end

 

    同时,将地址计数器送入ROM的地址线,这样就可以对ROM进行寻址。等等,这里只是控制好了X地址,而Y地址的控制是通过每完成显示一行,即送出了16个数据块,Y的地址便加一,然后更新到LCD12864上,由于Y地址是逢16进一的,所以就可以用地址计数器的第8位~第4位对LCD进行列控制。

对应代码段如下:

/********************写图片数据到LCD显存***********************/

wr_y_addr_1:                     //设定图形显示区Y轴地址

        begin

        next_state <= wr_y_addr_2;

    lcd12864_data <= {3'b100,cnt[8:4]};//设置列地址(y)

            lcd12864_en <= 1'b1;

        end

 

wr_y_addr_2:

        begin

            next_state <= wr_x_addr_1;

    lcd12864_data <= {3'b100,cnt[8:4]};

end

    在写完64行之后(其实就是一帧)整个显示屏就驱动完成了,最后再转到初始状态不断刷新屏幕。

对应代码段如下:

/****************写数据到图形显示区*****************/

wr_data_1:                     

        begin

            next_state <= wr_data_2;                  

    lcd12864_rs <= 1'b1;

            lcd12864_en <= 1'b1;

    lcd12864_data <= data;

end

      

wr_data_2://写数据

        begin

            lcd12864_rs<=1'b1;

            lcd12864_data <= data;

            if(cnt[3:0] == 4'hf)//写完一行数据(16个)

            begin

                if(cnt[9:4] == 7'h3f)//写完一屏数据(64行)                    

                    next_state <= idle;//写完一屏的数据后重新跳转到初始状态

                else

                    next_state <= wr_y_addr_1;//每写完一行数据,重新写入行地址

            end

            else

                next_state <= wr_data_1;//每写完一行数据,重新写入行地址,地址会自动加一

end

图片取模与MIF文件的生成

1、打开Image2Lcd取模软件,

  1. 打开要显示的图片,图片的大小是128*64像素,可以选择实验提供的两张图片:

3、设置取模的参数:

4、设置完成之后就点击保存:

5、接下来就用BmpToMif.exe软件将.bin文件转换为.Mif文件

打开刚刚生成的.bin文件,设置字长为8

6、点击生成MIF文件

打开MIF文件,查看数据是否正确:

 

硬件原理图

LCD12864的接口与LCD1602接口进行复用,所以使用时需要将PSB置高,作为背光电源,滑动变阻器用来设置背光灯的亮度。

 

实验代码

/********************************版权声明**************************************

** 大西瓜团队

**

**----------------------------文件信息--------------------------

** 文件名称: lcd12864.v

** 创建日期:

** 功能描述:实现LCD显示功能

** 硬件平台:大西瓜第三代开发板,http://daxiguafpga.taobao.com

** 版权声明:本代码属个人知识产权,本代码仅供交流学习.

**---------------------------修改文件的相关信息----------------

** 修改人:

** 修改日期:    

** 修改内容:

*******************************************************************************/

module LCD12864(clk,rst,lcd12864_rs,lcd12864_rw,lcd12864_en,lcd12864_data,psb);

input         clk;         //系统时钟

input        rst;         //复位信号

output         lcd12864_rs;             //1:数据模式;0:指令模式

output         lcd12864_rw;             //1:读操作;0:写操作

output        lcd12864_en;             //使能信号,写操作时在下降沿将数据送出;读操作时保持高电平

output psb;

output[7:0]    lcd12864_data;         //LCD数据总线

 

reg         lcd12864_rs;

reg      lcd12864_en;

reg[7:0] lcd12864_data;

 

reg[3:0]    state;              //状态机

reg[3:0]    next_state;

reg[14:0]     div_cnt;      //分频计数器

reg[9:0]    cnt;             //写操作计数器

reg         cnt_rst;         //写操作计数器复位信号

wire[7:0] data;             //要显示的数据

 

reg clk_div;                 //分频时钟

/********************状态机参数*********************/

parameter     idle      = 4'b0000,

            setbase_1 = 4'b0001,

            setmode_1 = 4'b0010,

            setcurs_1 = 4'b0111,

            setexte_1 = 4'b0100,

            setexte_2 = 4'b1100,

            wr_y_addr_1 = 4'b1101,

            wr_y_addr_2 = 4'b1111,

            wr_x_addr_1 = 4'b1110,

            wr_x_addr_2 = 4'b1010,    

            wr_data_1 = 4'b1011,

            wr_data_2 = 4'b1001;             

            

assign lcd12864_rw = 1'b0;            //对LCD始终为写操作

assign psb=1'b1; //开背光灯

/******************时钟分频**********************/

always @(posedge clk or negedge rst)

begin

if(!rst)

         div_cnt <= 15'd0;

else

if(div_cnt==15'h4000)

begin

div_cnt <= 15'd0;

clk_div<=~clk_div;

end

else

    div_cnt <= div_cnt+ 1'b1;

end

/**************状态机转向*********/

always @(posedge clk_div or negedge rst)

begin

if(!rst)

         state <= idle;

else

    state <= next_state;

end

/***************************************************************/

always @(posedge clk_div)

begin

        if(cnt_rst)//ROM寻址复位,表示已写入一帧数据

            cnt <= 10'd0;        

        else if(state == wr_data_2)//每写入一字节数据,地址加一

            cnt <= cnt + 1'b1;

end

/**********************状态机逻辑*******************************/

always @(state or cnt or data)

begin

    lcd12864_rs <= 1'b0;

    lcd12864_en <= 1'b0;

    cnt_rst <= 1'b0;

    lcd12864_data <= 8'h0;

case(state)

        idle:

        begin

            next_state <= setbase_1;

        end

/************************初使化LCD*****************************/

/******************设置为基本指令操作***************/    

setbase_1:                     

        begin

            next_state <= setmode_1;

        lcd12864_data <= 8'h30;

            lcd12864_en <= 1'b1;

end

          

/*************设定DDRAM 的地址计数器(AC)自动增加********************/

        setmode_1:                     

        begin             

         next_state <= setcurs_1;

lcd12864_data <= 8'h06;

            lcd12864_en <= 1'b1;

end

 

/**************开显示,关光标,不闪*******************/

setcurs_1:                          

        begin

            next_state <= setexte_1;

        lcd12864_data <= 8'h0c;

            lcd12864_en <= 1'b1;

         end

/**************扩充指令操作*******************/

setexte_1:                     

        begin

            next_state<=setexte_2;

        lcd12864_data <= 8'h36;

            lcd12864_en <= 1'b1;

end

          

setexte_2:

        begin

next_state <= wr_y_addr_1;

            lcd12864_data <= 8'h36;

            cnt_rst <= 1'b1;//图片数据帧复位标志

    end

 

/********************写图片数据到LCD显存***********************/

wr_y_addr_1:                     //设定图形显示区Y轴地址

        begin

        next_state <= wr_y_addr_2;

    lcd12864_data <= {3'b100,cnt[8:4]};//设置列地址(y)

            lcd12864_en <= 1'b1;

        end

 

wr_y_addr_2:

        begin

            next_state <= wr_x_addr_1;

    lcd12864_data <= {3'b100,cnt[8:4]};

end

/****************设定图形显示区X轴地址*****************/

wr_x_addr_1:                     

        begin

        next_state <= wr_x_addr_2;

            lcd12864_en <= 1'b1;

    lcd12864_data <= {4'd8,cnt[9],3'd0};//上半屏:0x80;下半屏:0x88

        end

 

wr_x_addr_2:

        begin

            next_state <= wr_data_1;

    lcd12864_data <= {4'd8,cnt[9],3'd0};

end

/****************写数据到图形显示区*****************/

wr_data_1:                     

        begin

            next_state <= wr_data_2;                  

    lcd12864_rs <= 1'b1;

            lcd12864_en <= 1'b1;

    lcd12864_data <= data;

end

      

wr_data_2://写数据

        begin

            lcd12864_rs<=1'b1;

            lcd12864_data <= data;

            if(cnt[3:0] == 4'hf)//写完一行数据(16个)

            begin

                if(cnt[9:4] == 7'h3f)//写完一屏数据(64行)                    

                    next_state <= idle;//写完一屏的数据后重新跳转到初始状态

                else

                    next_state <= wr_y_addr_1;//每写完一行数据,重新写入行地址

            end

            else

                next_state <= wr_data_1;//每写完一行数据,重新写入行地址,地址会自动加一

end

 

    default:     next_state<=idle;//跳转到初始状态

    endcase

end

/***************************************************************/

/******************调用ROM(图片数据)*****************************/

rom    imagerom(.address (cnt),.clock (clk),.q (data));

endmodule

 

实验操作

实验效果

 

 

 

大西瓜FPGA-->https://daxiguafpga.taobao.com

配套开发板:https://item.taobao.com/item.htm?spm=a1z10.1-c.w4004-24211932856.3.489d7241aCjspB&id=633897209972

博客资料、代码、图片、文字等属大西瓜FPGA所有,切勿用于商业! 若引用资料、代码、图片、文字等等请注明出处,谢谢!

   

每日推送不同科技解读,原创深耕解读当下科技,敬请关注微信公众号"科乎"。

标签:cnt,20,FPGA,state,lcd12864,LCD12864,wr,end,data
来源: https://www.cnblogs.com/logic3/p/15915930.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有