原创 基于apb3总线的lcd驱动

2009-1-11 13:11 2738 2 2 分类: FPGA/CPLD

 


 


在参加比赛时做的


也是第一次用modelsim详细做仿真


 


内容是基于apb3片内系统总线的lcd驱动电路


rtl代码:


// LCD_Driver.v


//功能简述:在1602液晶模块上显示字符串,其中第一行显示“Welcome To ZLG”,
//                                        在第二行显示“www.zlgmcu.com”。
//液晶模块为TC1602A,相关特性请参考其数据手册


module LCD_Driver(clk_LCD,rst,LCD_EN,RS,RW,DB8,
                    pclk,
                    paddr,
                    pwdata,
                    pwrite,
                    psel,
                    penable,
                   
                    pready,
                    data_end);
                   
input   clk_LCD,rst;        //rst为全局复位信号(高电平有效)


output  LCD_EN,RS,RW;
//apb3片内总线的接口信号
input [4:0] paddr;
input [7:0] pwdata;
input pwrite,psel,penable,pclk;
output reg pready;
//数据缓冲区已经完成写任务
output reg data_end;


//LCD_EN为LCD模块的使能信号(下降沿触发)
//RS=0时为写指令;RS=1时为写数据
//RW=0时对LCD模块执行写操作;RW=1时对LCD模块执行读操作


output  [7:0] DB8;          //8位指令或数据总线


reg     [7:0] DB8;
reg[255:0] buffer;     //液晶显示的数据缓存(以供32字节,之用31:4字节,后3:0字节不用)
reg     RS,LCD_EN_Sel;
reg     [3:0] disp_count;
reg     [3:0] state;


reg   sent_data;
reg     en,mod;
reg[7:0] one_data,one_addr;


//解决不同时钟问题
reg[1:0] count;
always @(posedge pclk or posedge rst)
begin
   if(rst)
 count<=2'b00;
   else
 count<={count[0],disp_count[0]};
end
assign kk=(count[0]!=count[1]);


 


parameter   Clear_Lcd = 4'b0000,                            //清屏并光标复位
            Set_Disp_Mode = 4'b0001,                        //设置显示模式:8位2行5x7点阵  
            Disp_On = 4'b0010,                              //显示器开、光标不显示、光标不允许闪烁
            Shift_Down = 4'b0011,                           //文字不动,光标自动右移
            Write_Addr = 4'b0100,                           //写入显示起始地址
            Write_Data_First = 4'b0101,                     //写入第一行显示的数据
            Write_Data_Second = 4'b0110,                    //写入第二行显示的数据
      one_write_addr=4'b0111,
      one_write_data=4'b1000,
            Idel = 4'b0111;                                 //空闲状态



assign  RW = 1'b0;                     //RW=0时对LCD模块执行写操作
assign  LCD_EN = LCD_EN_Sel ? clk_LCD : 1'b0;


///写数据缓冲区
always@(posedge pclk or posedge rst)
begin
    if(rst)
        begin
        pready<=1'b0;
     buffer<="Welcome To ZLG www.zlgmcu.com";
        end
    else
        begin
        pready<=1'b0;
   if(sent_data&kk)
  buffer<=(buffer<<8);
      if(psel&(!penable))
            begin
            {buffer[paddr*8+7],buffer[paddr*8+6],buffer[paddr*8+5],buffer[paddr*8+4],buffer[paddr*8+3],buffer[paddr*8+2],buffer[paddr*8+1],buffer[paddr*8]}<=pwdata;//利用apb3总线写入缓存
            pready<=1'b1;
            end
        end
end


 


always @(posedge pclk or posedge rst)
    if(rst)
 begin
    en<=1'b0;
    mod<=1'b1;
    one_data<=8'b0000_0000;
    one_addr<=8'b0000_0000;
 end
    else
 begin
    if(psel&(!penable))
       begin
 case(paddr)
 5'b0000://apb3的00000单元用于控制字
  begin
  en<=pwdata[0];
  mod<=pwdata[1];
  end
 5'b00001://apb3的00001单元用于只写一个单元时的数据
  one_data<=pwdata;
 5'b00010://apb3的00010单元用于只写一个单元时的地 
  one_addr<=pwdata;
 default:
  begin
  en<=en;
  mod<=mod;
  one_data<=one_data;
  one_addr<=one_addr;
  end
 endcase
 end
 end


//通过LCD_EN_Sel信号来控制LCD_EN的开启与关闭


always @(posedge clk_LCD or posedge rst)
begin
   if(rst)
      begin
          state <= Clear_Lcd;                               //复位:清屏并光标复位  
          RS <= 1'b0;                                       //复位:RS=0时为写指令;                      
          DB8 <= 8'b0;                                      //复位:使DB8总线输出全0
          LCD_EN_Sel <= 1'b1;                               //复位:开启夜晶使能信号
          disp_count <= 4'b0;
      end
   else
      case(state)                                           //初始化LCD模块
      Clear_Lcd:
             begin
                state <= Set_Disp_Mode;
                DB8 <= 8'b00000001;                         //清屏并光标复位  
             end
      Set_Disp_Mode:
             begin
                state <= Disp_On;
                DB8 <= 8'b00111000;                         //设置显示模式:8位2行5x8点阵        
             end
      Disp_On:
             begin
                state <= Shift_Down;
                DB8 <= 8'b00001100;                         //显示器开、光标不显示、光标不允许闪烁   
             end
      Shift_Down:
            begin
    DB8 <= 8'b00000110;                        //文字不动,光标自动右移
    if(mod)//如果mod为一,则全页刷新
                 state <= Write_Addr;
                else//否则,只刷新一个显示单元
     state<=one_write_addr;                           
            end
      Write_Addr:
            begin
                DB8 <= 8'b10000001;                         //写入第一行显示起始地址:第一行第二个位置   
                state <= Write_Data_First;
    sent_data<=1'b1;
            end
      Write_Data_First:                                     //写第一行数据
            begin
     if(disp_count==13)
      sent_data<=1'b0;
                if(disp_count == 14)                        //disp_count等于14时表示第一行数据已写完
                    begin
                        DB8 <= 8'b11000001;                 //送入写第二行的指令
                        RS <= 1'b0;
                        disp_count <= 4'b0;
                        state <= Write_Data_Second;         //写完第一行进入写第二
      sent_data<=1'b1;
                    end
                else
                    begin
                        RS <= 1'b1;                         //RS=1表示写数据
                        disp_count <= disp_count + 1'b1;
                        state <= Write_Data_First;
      DB8<=buffer[255:248];
                    end
            end
      Write_Data_Second:                                    //写第二行数据
            begin
                if(disp_count == 14)
                    begin
                        LCD_EN_Sel <= 1'b0;
                        RS <= 1'b0;
                        disp_count <= 4'b0;
                        state <= Idel;                      //写完进入空闲状态
      sent_data<=1'b0;
                    end
                else
                    begin
                        RS <= 1'b1;
                        disp_count <= disp_count + 1'b1;
                        state <= Write_Data_Second;
      DB8<=buffer[255:248];
                    end             
            end
 one_write_addr:
  begin
    DB8 <= one_addr;                         //写入地址  
                state <= one_write_data;
  end
 one_write_data:
  begin
    RS <= 1'b1;
    state<=Idel;
    DB8<=one_data;
  end
      Idel:    
            begin
                RS <= 1'b0;
    if(en)
   state<= Shift_Down;
            end
      default:  state <= Clear_Lcd;                         //若state为其他值,则将state置为Clear_Lcd
      endcase
end


endmodule


 


测试文件:


`timescale  1 ns /  1 ps
module testbench();



//////////////////////////////////////////////////////////
reg clk_LCD;
reg rst;
reg pclk;
reg pwrite;
reg    psel;
reg    penable;


reg[4:0]  paddr;
reg[7:0]           pwdata;
//////////////////////////////////////////////////////////
wire LCD_EN;
wire RS;
wire RW;
wire pready;
wire data_end;
wire[7:0] DB8;
////////////////////////////////////////////////////////
 top_lcd_drive uu1(
   .clk_LCD(clk_LCD),
   .rst(rst),
   .LCD_EN(LCD_EN),
   .RS(RS),
   .RW(RW),
   .DB8(DB8),
                    .pclk(pclk),
                    .paddr(paddr),
                    .pwdata(pwdata),
                    .pwrite(pwrite),
                    .psel(psel),
                    .penable(penable),
                   
                    .pready(pready),
                    .data_end(data_end)
   );


initial
begin
 clk_LCD=1'b0;
 rst=1'b1;
 pclk=1'b0;
 pwrite=1'b0;
     psel=1'b0;
     penable=1'b0;
 paddr=5'b00000;
       pwdata="8"'b0000_0000;
 
 # 300 rst="1"'b0;
 # 4000
 //利用单独写数据的模式显示
 cpu_write(5'b00010,8'b10000010); //写地址
 # 10 cpu_write(5'b00001,"o");//写数据
 # 10 cpu_write(5'b00000,8'b0000_0001);//写控制字


 //利用全页改变的模式显示
 # 150 cpu_write(5'b00111,"o");//改写一个数据
    # 10 cpu_write(5'b01000,"l");
    # 10 cpu_write(5'b01001,"d");
    # 10 cpu_write(5'b01010,"l");
    # 10 cpu_write(5'b01011,"e");
    # 10 cpu_write(5'b01100,"e");
 # 10 cpu_write(5'b00000,8'b0000_0011);//写控制字


    # 4000 $stop;
end
//clock
always
begin
# 50 clk_LCD=~clk_LCD;
end


always
begin
# 5 pclk=~pclk;
end


 


task cpu_write;
input [4:0] address;
input [7:0] data;
begin
  // $display("CPU Write %04x = %04x",address,data);
  @(negedge pclk); 
  pwrite  = 1'b0;
  @(negedge pclk); 
  penable  = 1'b0;
  psel  = 1'b1;
  paddr= address;
  pwdata = data; 
  pwrite  = 1'b1;
  @(negedge pclk); 
  penable = 1'b1;
  @(negedge pclk); 
  pwrite  = 1'b0;
  penable  = 1'b0;
  psel = 1'b0;
end
endtask



endmodule


 

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
2
关闭 站长推荐上一条 /3 下一条