RTC时钟实验

RTC时钟实验

本节的实验任务是通过领航者Zynq开发板上的PCF8563实时时钟芯片,在 RGB LCD 液晶屏上来显示时间。

PCF8563 简介

​ PCF8563 是 PHILIPS 公司推出的一款工业级多功能时钟/日历芯片,具有报警功能、定时器功能、时钟输出功能以及中断输出功能,能完成各种复杂的定时服务。其内部功能模块的框图如下图所示:

PCF8563 有 16 个可寻址的 8 位寄存器, 但不是所有位都有用到。前两个寄存器(内存地址 00H、01H)用作控制寄存器和状态寄存器(CONTROL_STATUS);内存地址 02H~08H 用作 TIME 计时器(秒~年计时器); 地址 09H~0CH 用于报警(ALARM)寄存器(定义报警条件); 地址 0DH 控制 CLKOUT 管脚的输出频率;地址 0EH 和 0FH 分别用于定时器控制寄存器和定时器寄存器。 PCF8563 通过 I2C 接口与 Zynq 进行通信

具体的BIT描述见PCF8563数据手册-

秒、分钟、小时、日、月、年、分钟报警、小时报警、日报警寄存器中的数据编码格式为 BCD用的是8421BCD码 ,只有星期和星期报警寄存器中的数据不以 BCD 格式编码。 BCD 码(Binary-Coded Decimal)是一种二进制的数字编码形式,用四个二进制位来表示一位十进制数(0~9) ,能够使二进制和十进制之间的转换得以快捷的进行 。

硬件设计

领航者开发板上 PCF8563 接口部分的原理图如下图所示

​ PCF8563 作为 I2C 接口的从器件与 EEPROM 等模块统一挂接在领航者开发板上的 IIC 总线上。OSCI、 OSCO 与外部 32.768KHz 的晶振相连, 为芯片提供驱动时钟; SCL 和 SDA 分别是 I2C 总线的串行时钟接口和串行数据接口。

程序设计

根据实验任务,我们可以大致规划出系统的控制流程: ZYNQ 首先通过 I2C 总线向 PCF8563 写入初始时间值,然后不断地读取时间数据,并将读到的时间数据显示到 LCD 上。 由此画出系统的功能框图如下所示:

​ 由系统框图可知, 顶层模块(rtc_lcd)例化了以下三个模块,分别是 IIC 驱动模块(iic_dri) 、PCF8563 控制模块(pcf8563_ctrl)和 LCD 字符显示模块(lcd_disp_char) 。 其中 LCD 字符显示模块例化了读取 ID 模块(rd_id)、时钟分频模块(clk_div) 、 LCD 显示模块(lcd_display)以及 LCD 驱动模块(lcd_driver)

各模块端口及信号连接如图 23.4.2 所示:

PCF8563 实时时钟控制模块(pcf8563_ctrl)通过与 IIC 驱动模块(iic_dri)进行通信来实现对 PCF8563 实时时钟数据的读取; PCF8563 实时时钟控制模块(pcf8563_ctrl)再将从 IIC 读取的时间数据送给 LCD 字符显示模块(lcd_disp_char),以进行显示。

顶层模块

module rtc_lcd(
    input                sys_clk,     //系统时钟
    input                sys_rst_n,   //系统复位

    //RGB LCD接口
    output               lcd_de,      //LCD 数据使能信号
    output               lcd_hs,      //LCD 行同步信号
    output               lcd_vs,      //LCD 场同步信号
    output               lcd_bl,      //LCD 背光控制信号
    output               lcd_clk,     //LCD 像素时钟
    inout        [23:0]  lcd_rgb,     //LCD RGB888颜色数据
    
    //RTC实时时钟
    output               iic_scl,     //RTC的时钟线scl
    inout                iic_sda      //RTC的数据线sda    
    );                                                      

//parameter define
parameter    SLAVE_ADDR = 7'b101_0001   ; //器件地址(SLAVE_ADDR)
parameter    BIT_CTRL   = 1'b0          ; //字地址位控制参数(16b/8b)
parameter    CLK_FREQ   = 26'd50_000_000; //i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter    I2C_FREQ   = 18'd250_000   ; //I2C的SCL时钟频率
parameter    TIME_INIT  = 48'h19_01_01_09_30_00;//初始时间

//wire define
wire          dri_clk   ;   //I2C操作时钟
wire          i2c_exec  ;   //I2C触发控制
wire  [15:0]  i2c_addr  ;   //I2C操作地址
wire  [ 7:0]  i2c_data_w;   //I2C写入的数据
wire          i2c_done  ;   //I2C操作结束标志
wire          i2c_ack   ;   //I2C应答标志 0:应答 1:未应答
wire          i2c_rh_wl ;   //I2C读写控制
wire  [ 7:0]  i2c_data_r;   //I2C读出的数据

wire    [7:0]  sec      ;   //秒
wire    [7:0]  min      ;   //分
wire    [7:0]  hour     ;   //时
wire    [7:0]  day      ;   //日
wire    [7:0]  mon      ;   //月
wire    [7:0]  year     ;   //年

//*****************************************************
//**                    main code
//*****************************************************

//i2c驱动模块
i2c_dri #(
    .SLAVE_ADDR  (SLAVE_ADDR),  //EEPROM从机地址
    .CLK_FREQ    (CLK_FREQ  ),  //模块输入的时钟频率
    .I2C_FREQ    (I2C_FREQ  )   //IIC_SCL的时钟频率
) u_i2c_dri(
    .clk         (sys_clk   ),  
    .rst_n       (sys_rst_n ),  
    //i2c interface
    .i2c_exec    (i2c_exec  ), 
    .bit_ctrl    (BIT_CTRL  ), 
    .i2c_rh_wl   (i2c_rh_wl ), 
    .i2c_addr    (i2c_addr  ), 
    .i2c_data_w  (i2c_data_w), 
    .i2c_data_r  (i2c_data_r), 
    .i2c_done    (i2c_done  ), 
    .i2c_ack     (i2c_ack   ), 
    .scl         (iic_scl   ), 
    .sda         (iic_sda   ), 
    //user interface
    .dri_clk     (dri_clk   )  
);

//PCF8563控制模块
pcf8563_ctrl #(
    .TIME_INIT (TIME_INIT)
   )u_pcf8563_ctrl(
    .clk         (dri_clk   ),
    .rst_n       (sys_rst_n ),
    //IIC
    .i2c_rh_wl   (i2c_rh_wl ),
    .i2c_exec    (i2c_exec  ),
    .i2c_addr    (i2c_addr  ),
    .i2c_data_w  (i2c_data_w),
    .i2c_data_r  (i2c_data_r),
    .i2c_done    (i2c_done  ),
    //时间和日期
    .sec         (sec       ),
    .min         (min       ),
    .hour        (hour      ),
    .day         (day       ),
    .mon         (mon       ),
    .year        (year      )
    );

//LCD字符显示模块
lcd_disp_char u_lcd_disp_char(
    .sys_clk     (sys_clk   ),
    .sys_rst_n   (sys_rst_n ),
    //时间和日期
    .sec         (sec       ),
    .min         (min       ),
    .hour        (hour      ),
    .day         (day       ),
    .mon         (mon       ),
    .year        (year      ),
    //RGB LCD接口
    .lcd_de      (lcd_de    ),
    .lcd_hs      (lcd_hs    ),
    .lcd_vs      (lcd_vs    ),
    .lcd_bl      (lcd_bl    ),
    .lcd_clk     (lcd_clk   ),
    .lcd_rgb     (lcd_rgb   )
    );

endmodule

IIC驱动模块
module i2c_dri
#(
 parameter   SLAVE_ADDR = 7'b1010000   ,  //EEPROM从机地址
 parameter   CLK_FREQ   = 26'd50_000_000, //模块输入的时钟频率
 parameter   I2C_FREQ   = 18'd250_000     //IIC_SCL的时钟频率
)
(                                                            
input                clk        ,    
input                rst_n      ,   
                                    
//i2c interface                      
input                i2c_exec   ,  //I2C触发执行信号
input                bit_ctrl   ,  //字地址位控制(16b/8b)
input                i2c_rh_wl  ,  //I2C读写控制信号
input        [15:0]  i2c_addr   ,  //I2C器件内地址
input        [ 7:0]  i2c_data_w ,  //I2C要写的数据
output  reg  [ 7:0]  i2c_data_r ,  //I2C读出的数据
output  reg          i2c_done   ,  //I2C一次操作完成
output  reg          i2c_ack    ,  //I2C应答标志 0:应答 1:未应答
output  reg          scl        ,  //I2C的SCL时钟信号
inout                sda        ,  //I2C的SDA信号
                                  
//user interface                   
output  reg          dri_clk       //驱动I2C操作的驱动时钟
);

//localparam define
localparam  st_idle     = 8'b0000_0001; //空闲状态
localparam  st_sladdr   = 8'b0000_0010; //发送器件地址(slave address)
localparam  st_addr16   = 8'b0000_0100; //发送16位字地址
localparam  st_addr8    = 8'b0000_1000; //发送8位字地址
localparam  st_data_wr  = 8'b0001_0000; //写数据(8 bit)
localparam  st_addr_rd  = 8'b0010_0000; //发送器件地址读
localparam  st_data_rd  = 8'b0100_0000; //读数据(8 bit)
localparam  st_stop     = 8'b1000_0000; //结束I2C操作

//reg define
reg            sda_dir   ; //I2C数据(SDA)方向控制
reg            sda_out   ; //SDA输出信号
reg            st_done   ; //状态结束
reg            wr_flag   ; //写标志
reg    [ 6:0]  cnt       ; //计数
reg    [ 7:0]  cur_state ; //状态机当前状态
reg    [ 7:0]  next_state; //状态机下一状态
reg    [15:0]  addr_t    ; //地址
reg    [ 7:0]  data_r    ; //读取的数据
reg    [ 7:0]  data_wr_t ; //I2C需写的数据的临时寄存
reg    [ 9:0]  clk_cnt   ; //分频时钟计数

//wire define
wire          sda_in     ; //SDA输入信号
wire   [8:0]  clk_divide ; //模块驱动时钟的分频系数

//*****************************************************
//**                    main code
//*****************************************************

//SDA控制
assign  sda     = sda_dir ?  sda_out : 1'bz;     //SDA数据输出或高阻
assign  sda_in  = sda ;                          //SDA数据输入
assign  clk_divide = (CLK_FREQ/I2C_FREQ) >> 2'd2;//模块驱动时钟的分频系数

//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
   dri_clk <=  1'b0;
   clk_cnt <= 10'd0;
end
else if(clk_cnt == clk_divide[8:1] - 1'd1) begin
   clk_cnt <= 10'd0;
   dri_clk <= ~dri_clk;
end
else
   clk_cnt <= clk_cnt + 1'b1;
end

//(三段式状态机)同步时序描述状态转移
always @(posedge dri_clk or negedge rst_n) begin
if(!rst_n)
   cur_state <= st_idle;
else
   cur_state <= next_state;
end

//组合逻辑判断状态转移条件
always @(*) begin
next_state = st_idle;
case(cur_state)
   st_idle: begin                          //空闲状态
      if(i2c_exec) begin
          next_state = st_sladdr;
      end
      else
          next_state = st_idle;
   end
   st_sladdr: begin
       if(st_done) begin
           if(bit_ctrl)                    //判断是16位还是8位字地址
              next_state = st_addr16;
           else
              next_state = st_addr8 ;
       end
       else
           next_state = st_sladdr;
   end
   st_addr16: begin                        //写16位字地址
       if(st_done) begin
           next_state = st_addr8;
       end
       else begin
           next_state = st_addr16;
       end
   end
   st_addr8: begin                         //8位字地址
       if(st_done) begin
           if(wr_flag==1'b0)               //读写判断
               next_state = st_data_wr;
           else
               next_state = st_addr_rd;
       end
       else begin
           next_state = st_addr8;
       end
   end
   st_data_wr: begin                       //写数据(8 bit)
       if(st_done)
           next_state = st_stop;
       else
           next_state = st_data_wr;
   end
   st_addr_rd: begin                       //写地址以进行读数据
       if(st_done) begin
           next_state = st_data_rd;
       end
       else begin
           next_state = st_addr_rd;
       end
   end
   st_data_rd: begin                       //读取数据(8 bit)
       if(st_done)
           next_state = st_stop;
       else
           next_state = st_data_rd;
   end
   st_stop: begin                          //结束I2C操作
       if(st_done)
           next_state = st_idle;
       else
           next_state = st_stop ;
   end
   default: next_state= st_idle;
endcase
end

//时序电路描述状态输出
always @(posedge dri_clk or negedge rst_n) begin
//复位初始化
if(!rst_n) begin
   scl       <= 1'b1;
   sda_out   <= 1'b1;
   sda_dir   <= 1'b1;                          
   i2c_done  <= 1'b0;                          
   i2c_ack   <= 1'b0;                          
   cnt       <= 1'b0;                          
   st_done   <= 1'b0;                          
   data_r    <= 1'b0;                          
   i2c_data_r<= 1'b0;                          
   wr_flag   <= 1'b0;                          
   addr_t    <= 1'b0;                          
   data_wr_t <= 1'b0;                          
end                                              
else begin                                       
   st_done <= 1'b0 ;                            
   cnt     <= cnt +1'b1 ;                       
   case(cur_state)                              
        st_idle: begin                          //空闲状态
           scl     <= 1'b1;                     
           sda_out <= 1'b1;                     
           sda_dir <= 1'b1;                     
           i2c_done<= 1'b0;                     
           cnt     <= 7'b0;               
           if(i2c_exec) begin                   
               wr_flag   <= i2c_rh_wl ;         
               addr_t    <= i2c_addr  ;         
               data_wr_t <= i2c_data_w;  
               i2c_ack <= 1'b0;                      
           end                                  
       end                                      
       st_sladdr: begin                         //写地址(器件地址和字地址)
           case(cnt)                            
               7'd1 : sda_out <= 1'b0;          //开始I2C
               7'd3 : scl <= 1'b0;              
               7'd4 : sda_out <= SLAVE_ADDR[6]; //传送器件地址
               7'd5 : scl <= 1'b1;              
               7'd7 : scl <= 1'b0;              
               7'd8 : sda_out <= SLAVE_ADDR[5]; 
               7'd9 : scl <= 1'b1;              
               7'd11: scl <= 1'b0;              
               7'd12: sda_out <= SLAVE_ADDR[4]; 
               7'd13: scl <= 1'b1;              
               7'd15: scl <= 1'b0;              
               7'd16: sda_out <= SLAVE_ADDR[3]; 
               7'd17: scl <= 1'b1;              
               7'd19: scl <= 1'b0;              
               7'd20: sda_out <= SLAVE_ADDR[2]; 
               7'd21: scl <= 1'b1;              
               7'd23: scl <= 1'b0;              
               7'd24: sda_out <= SLAVE_ADDR[1]; 
               7'd25: scl <= 1'b1;              
               7'd27: scl <= 1'b0;              
               7'd28: sda_out <= SLAVE_ADDR[0]; 
               7'd29: scl <= 1'b1;              
               7'd31: scl <= 1'b0;              
               7'd32: sda_out <= 1'b0;          //0:写
               7'd33: scl <= 1'b1;              
               7'd35: scl <= 1'b0;              
               7'd36: begin                     
                   sda_dir <= 1'b0;             
                   sda_out <= 1'b1;                         
               end                              
               7'd37: scl     <= 1'b1;            
               7'd38: begin                     //从机应答 
                   st_done <= 1'b1;
                   if(sda_in == 1'b1)           //高电平表示未应答
                       i2c_ack <= 1'b1;         //拉高应答标志位     
               end                                          
               7'd39: begin                     
                   scl <= 1'b0;                 
                   cnt <= 1'b0;                 
               end                              
               default :  ;                     
           endcase                              
       end                                      
       st_addr16: begin                         
           case(cnt)                            
               7'd0 : begin                     
                   sda_dir <= 1'b1 ;            
                   sda_out <= addr_t[15];       //传送字地址
               end                              
               7'd1 : scl <= 1'b1;              
               7'd3 : scl <= 1'b0;              
               7'd4 : sda_out <= addr_t[14];    
               7'd5 : scl <= 1'b1;              
               7'd7 : scl <= 1'b0;              
               7'd8 : sda_out <= addr_t[13];    
               7'd9 : scl <= 1'b1;              
               7'd11: scl <= 1'b0;              
               7'd12: sda_out <= addr_t[12];    
               7'd13: scl <= 1'b1;              
               7'd15: scl <= 1'b0;              
               7'd16: sda_out <= addr_t[11];    
               7'd17: scl <= 1'b1;              
               7'd19: scl <= 1'b0;              
               7'd20: sda_out <= addr_t[10];    
               7'd21: scl <= 1'b1;              
               7'd23: scl <= 1'b0;              
               7'd24: sda_out <= addr_t[9];     
               7'd25: scl <= 1'b1;              
               7'd27: scl <= 1'b0;              
               7'd28: sda_out <= addr_t[8];     
               7'd29: scl <= 1'b1;              
               7'd31: scl <= 1'b0;              
               7'd32: begin                     
                   sda_dir <= 1'b0;             
                   sda_out <= 1'b1;   
               end                              
               7'd33: scl  <= 1'b1;             
               7'd34: begin                     //从机应答
                   st_done <= 1'b1;     
                   if(sda_in == 1'b1)           //高电平表示未应答
                       i2c_ack <= 1'b1;         //拉高应答标志位    
               end        
               7'd35: begin                     
                   scl <= 1'b0;                 
                   cnt <= 1'b0;                 
               end                              
               default :  ;                     
           endcase                              
       end                                      
       st_addr8: begin                          
           case(cnt)                            
               7'd0: begin                      
                  sda_dir <= 1'b1 ;             
                  sda_out <= addr_t[7];         //字地址
               end                              
               7'd1 : scl <= 1'b1;              
               7'd3 : scl <= 1'b0;              
               7'd4 : sda_out <= addr_t[6];     
               7'd5 : scl <= 1'b1;              
               7'd7 : scl <= 1'b0;              
               7'd8 : sda_out <= addr_t[5];     
               7'd9 : scl <= 1'b1;              
               7'd11: scl <= 1'b0;              
               7'd12: sda_out <= addr_t[4];     
               7'd13: scl <= 1'b1;              
               7'd15: scl <= 1'b0;              
               7'd16: sda_out <= addr_t[3];     
               7'd17: scl <= 1'b1;              
               7'd19: scl <= 1'b0;              
               7'd20: sda_out <= addr_t[2];     
               7'd21: scl <= 1'b1;              
               7'd23: scl <= 1'b0;              
               7'd24: sda_out <= addr_t[1];     
               7'd25: scl <= 1'b1;              
               7'd27: scl <= 1'b0;              
               7'd28: sda_out <= addr_t[0];     
               7'd29: scl <= 1'b1;              
               7'd31: scl <= 1'b0;              
               7'd32: begin                     
                   sda_dir <= 1'b0;         
                   sda_out <= 1'b1;                    
               end                              
               7'd33: scl     <= 1'b1;          
               7'd34: begin                     //从机应答
                   st_done <= 1'b1;     
                   if(sda_in == 1'b1)           //高电平表示未应答
                       i2c_ack <= 1'b1;         //拉高应答标志位    
               end   
               7'd35: begin                     
                   scl <= 1'b0;                 
                   cnt <= 1'b0;                 
               end                              
               default :  ;                     
           endcase                              
       end                                      
       st_data_wr: begin                        //写数据(8 bit)
           case(cnt)                            
               7'd0: begin                      
                   sda_out <= data_wr_t[7];     //I2C写8位数据
                   sda_dir <= 1'b1;             
               end                              
               7'd1 : scl <= 1'b1;              
               7'd3 : scl <= 1'b0;              
               7'd4 : sda_out <= data_wr_t[6];  
               7'd5 : scl <= 1'b1;              
               7'd7 : scl <= 1'b0;              
               7'd8 : sda_out <= data_wr_t[5];  
               7'd9 : scl <= 1'b1;              
               7'd11: scl <= 1'b0;              
               7'd12: sda_out <= data_wr_t[4];  
               7'd13: scl <= 1'b1;              
               7'd15: scl <= 1'b0;              
               7'd16: sda_out <= data_wr_t[3];  
               7'd17: scl <= 1'b1;              
               7'd19: scl <= 1'b0;              
               7'd20: sda_out <= data_wr_t[2];  
               7'd21: scl <= 1'b1;              
               7'd23: scl <= 1'b0;              
               7'd24: sda_out <= data_wr_t[1];  
               7'd25: scl <= 1'b1;              
               7'd27: scl <= 1'b0;              
               7'd28: sda_out <= data_wr_t[0];  
               7'd29: scl <= 1'b1;              
               7'd31: scl <= 1'b0;              
               7'd32: begin                     
                   sda_dir <= 1'b0;           
                   sda_out <= 1'b1;                              
               end                              
               7'd33: scl <= 1'b1;              
               7'd34: begin                     //从机应答
                   st_done <= 1'b1;     
                   if(sda_in == 1'b1)           //高电平表示未应答
                       i2c_ack <= 1'b1;         //拉高应答标志位    
               end          
               7'd35: begin                     
                   scl  <= 1'b0;                
                   cnt  <= 1'b0;                
               end                              
               default  :  ;                    
           endcase                              
       end                                      
       st_addr_rd: begin                        //写地址以进行读数据
           case(cnt)                            
               7'd0 : begin                     
                   sda_dir <= 1'b1;             
                   sda_out <= 1'b1;             
               end                              
               7'd1 : scl <= 1'b1;              
               7'd2 : sda_out <= 1'b0;          //重新开始
               7'd3 : scl <= 1'b0;              
               7'd4 : sda_out <= SLAVE_ADDR[6]; //传送器件地址
               7'd5 : scl <= 1'b1;              
               7'd7 : scl <= 1'b0;              
               7'd8 : sda_out <= SLAVE_ADDR[5]; 
               7'd9 : scl <= 1'b1;              
               7'd11: scl <= 1'b0;              
               7'd12: sda_out <= SLAVE_ADDR[4]; 
               7'd13: scl <= 1'b1;              
               7'd15: scl <= 1'b0;              
               7'd16: sda_out <= SLAVE_ADDR[3]; 
               7'd17: scl <= 1'b1;              
               7'd19: scl <= 1'b0;              
               7'd20: sda_out <= SLAVE_ADDR[2]; 
               7'd21: scl <= 1'b1;              
               7'd23: scl <= 1'b0;              
               7'd24: sda_out <= SLAVE_ADDR[1]; 
               7'd25: scl <= 1'b1;              
               7'd27: scl <= 1'b0;              
               7'd28: sda_out <= SLAVE_ADDR[0]; 
               7'd29: scl <= 1'b1;              
               7'd31: scl <= 1'b0;              
               7'd32: sda_out <= 1'b1;          //1:读
               7'd33: scl <= 1'b1;              
               7'd35: scl <= 1'b0;              
               7'd36: begin                     
                   sda_dir <= 1'b0;            
                   sda_out <= 1'b1;                    
               end
               7'd37: scl     <= 1'b1;
               7'd38: begin                     //从机应答
                   st_done <= 1'b1;     
                   if(sda_in == 1'b1)           //高电平表示未应答
                       i2c_ack <= 1'b1;         //拉高应答标志位    
               end   
               7'd39: begin
                   scl <= 1'b0;
                   cnt <= 1'b0;
               end
               default : ;
           endcase
       end
       st_data_rd: begin                        //读取数据(8 bit)
           case(cnt)
               7'd0: sda_dir <= 1'b0;
               7'd1: begin
                   data_r[7] <= sda_in;
                   scl       <= 1'b1;
               end
               7'd3: scl  <= 1'b0;
               7'd5: begin
                   data_r[6] <= sda_in ;
                   scl       <= 1'b1   ;
               end
               7'd7: scl  <= 1'b0;
               7'd9: begin
                   data_r[5] <= sda_in;
                   scl       <= 1'b1  ;
               end
               7'd11: scl  <= 1'b0;
               7'd13: begin
                   data_r[4] <= sda_in;
                   scl       <= 1'b1  ;
               end
               7'd15: scl  <= 1'b0;
               7'd17: begin
                   data_r[3] <= sda_in;
                   scl       <= 1'b1  ;
               end
               7'd19: scl  <= 1'b0;
               7'd21: begin
                   data_r[2] <= sda_in;
                   scl       <= 1'b1  ;
               end
               7'd23: scl  <= 1'b0;
               7'd25: begin
                   data_r[1] <= sda_in;
                   scl       <= 1'b1  ;
               end
               7'd27: scl  <= 1'b0;
               7'd29: begin
                   data_r[0] <= sda_in;
                   scl       <= 1'b1  ;
               end
               7'd31: scl  <= 1'b0;
               7'd32: begin
                   sda_dir <= 1'b1;             
                   sda_out <= 1'b1;
               end
               7'd33: scl     <= 1'b1;
               7'd34: st_done <= 1'b1;          //非应答
               7'd35: begin
                   scl <= 1'b0;
                   cnt <= 1'b0;
                   i2c_data_r <= data_r;
               end
               default  :  ;
           endcase
       end
       st_stop: begin                           //结束I2C操作
           case(cnt)
               7'd0: begin
                   sda_dir <= 1'b1;             //结束I2C
                   sda_out <= 1'b0;
               end
               7'd1 : scl     <= 1'b1;
               7'd3 : sda_out <= 1'b1;
               7'd15: st_done <= 1'b1;
               7'd16: begin
                   cnt      <= 1'b0;
                   i2c_done <= 1'b1;            //向上层模块传递I2C结束信号
               end
               default  : ;
           endcase
       end
   endcase
end
end

endmodule
实时时钟控制模块
module  lcd_disp_char(
input              sys_clk  ,
input              sys_rst_n,
//时间和日期
input       [7:0]  sec      ,  // 秒
input       [7:0]  min      ,  // 分
input       [7:0]  hour     ,  // 时
input       [7:0]  day      ,  // 日
input       [7:0]  mon      ,  // 月
input       [7:0]  year     ,  // 年   

//RGB LCD接口 
output             lcd_hs   ,  //LCD 行同步信号
output             lcd_vs   ,  //LCD 场同步信号
output             lcd_de   ,  //LCD 数据输入使能
inout      [23:0]  lcd_rgb  ,  //LCD RGB565颜色数据
output             lcd_bl   ,  //LCD 背光控制信号
output             lcd_clk     //LCD 采样时钟
);

//wire define    
wire  [15:0]  lcd_id    ;    //LCD屏ID
wire          lcd_pclk  ;    //LCD像素时钟
         
wire  [10:0]  pixel_xpos;    //当前像素点横坐标
wire  [10:0]  pixel_ypos;    //当前像素点纵坐标
wire  [10:0]  h_disp    ;    //LCD屏水平分辨率
wire  [10:0]  v_disp    ;    //LCD屏垂直分辨率
wire  [23:0]  pixel_data;    //像素数据
wire  [23:0]  lcd_rgb_o ;    //输出的像素数据
wire  [23:0]  lcd_rgb_i ;    //输入的像素数据

//*****************************************************
//**                    main code
//*****************************************************

//像素数据方向切换
assign lcd_rgb = lcd_de ?  lcd_rgb_o :  {24{1'bz}};
assign lcd_rgb_i = lcd_rgb;

//读LCD ID模块
rd_id u_rd_id(
.clk          (sys_clk  ),
.rst_n        (sys_rst_n),
.lcd_rgb      (lcd_rgb_i),
.lcd_id       (lcd_id   )
);    

//时钟分频模块    
clk_div u_clk_div(
.clk          (sys_clk  ),
.rst_n        (sys_rst_n),
.lcd_id       (lcd_id   ),
.lcd_pclk     (lcd_pclk )
);    

//LCD显示模块    
lcd_display u_lcd_display(
.lcd_pclk    (lcd_pclk  ),
.rst_n       (sys_rst_n ),
//时间和日期
.sec         (sec       ),
.min         (min       ),
.hour        (hour      ),
.day         (day       ),
.mon         (mon       ),
.year        (year      ),
//像素点坐标
.pixel_xpos  (pixel_xpos),
.pixel_ypos  (pixel_ypos),
.pixel_data  (pixel_data)
);    

//LCD驱动模块
lcd_driver u_lcd_driver(
.lcd_pclk      (lcd_pclk  ),
.rst_n         (sys_rst_n ),

.lcd_id        (lcd_id    ),
.pixel_data    (pixel_data),
.pixel_xpos    (pixel_xpos),
.pixel_ypos    (pixel_ypos),
.h_disp        (h_disp    ),
.v_disp        (v_disp    ),

.lcd_de        (lcd_de    ),
.lcd_hs        (lcd_hs    ),
.lcd_vs        (lcd_vs    ),
.lcd_bl        (lcd_bl    ),
.lcd_clk       (lcd_clk   ),
.lcd_rgb       (lcd_rgb_o )
);

endmodule

RGB LCD显示日期和时间
module  lcd_disp_char(
input              sys_clk  ,
input              sys_rst_n,
//时间和日期
input       [7:0]  sec      ,  // 秒
input       [7:0]  min      ,  // 分
input       [7:0]  hour     ,  // 时
input       [7:0]  day      ,  // 日
input       [7:0]  mon      ,  // 月
input       [7:0]  year     ,  // 年   

//RGB LCD接口 
output             lcd_hs   ,  //LCD 行同步信号
output             lcd_vs   ,  //LCD 场同步信号
output             lcd_de   ,  //LCD 数据输入使能
inout      [23:0]  lcd_rgb  ,  //LCD RGB565颜色数据
output             lcd_bl   ,  //LCD 背光控制信号
output             lcd_clk     //LCD 采样时钟
);

//wire define    
wire  [15:0]  lcd_id    ;    //LCD屏ID
wire          lcd_pclk  ;    //LCD像素时钟
         
wire  [10:0]  pixel_xpos;    //当前像素点横坐标
wire  [10:0]  pixel_ypos;    //当前像素点纵坐标
wire  [10:0]  h_disp    ;    //LCD屏水平分辨率
wire  [10:0]  v_disp    ;    //LCD屏垂直分辨率
wire  [23:0]  pixel_data;    //像素数据
wire  [23:0]  lcd_rgb_o ;    //输出的像素数据
wire  [23:0]  lcd_rgb_i ;    //输入的像素数据

//*****************************************************
//**                    main code
//*****************************************************

//像素数据方向切换
assign lcd_rgb = lcd_de ?  lcd_rgb_o :  {24{1'bz}};
assign lcd_rgb_i = lcd_rgb;

//读LCD ID模块
rd_id u_rd_id(
.clk          (sys_clk  ),
.rst_n        (sys_rst_n),
.lcd_rgb      (lcd_rgb_i),
.lcd_id       (lcd_id   )
);    

//时钟分频模块    
clk_div u_clk_div(
.clk          (sys_clk  ),
.rst_n        (sys_rst_n),
.lcd_id       (lcd_id   ),
.lcd_pclk     (lcd_pclk )
);    

//LCD显示模块    
lcd_display u_lcd_display(
.lcd_pclk    (lcd_pclk  ),
.rst_n       (sys_rst_n ),
//时间和日期
.sec         (sec       ),
.min         (min       ),
.hour        (hour      ),
.day         (day       ),
.mon         (mon       ),
.year        (year      ),
//像素点坐标
.pixel_xpos  (pixel_xpos),
.pixel_ypos  (pixel_ypos),
.pixel_data  (pixel_data)
);    

//LCD驱动模块
lcd_driver u_lcd_driver(
.lcd_pclk      (lcd_pclk  ),
.rst_n         (sys_rst_n ),

.lcd_id        (lcd_id    ),
.pixel_data    (pixel_data),
.pixel_xpos    (pixel_xpos),
.pixel_ypos    (pixel_ypos),
.h_disp        (h_disp    ),
.v_disp        (v_disp    ),

.lcd_de        (lcd_de    ),
.lcd_hs        (lcd_hs    ),
.lcd_vs        (lcd_vs    ),
.lcd_bl        (lcd_bl    ),
.lcd_clk       (lcd_clk   ),
.lcd_rgb       (lcd_rgb_o )
);

endmodule

读取LCD ID模块
module rd_id(
 input                   clk    ,    //时钟
 input                   rst_n  ,    //复位,低电平有效
 input           [23:0]  lcd_rgb,    //RGB LCD像素数据,用于读取ID
 output   reg    [15:0]  lcd_id     //LCD屏ID
 );

//reg define
reg            rd_flag;  //读ID标志

//*****************************************************
//**                    main code
//*****************************************************

//获取LCD ID   M2:B7  M1:G7  M0:R7
always @(posedge clk or negedge rst_n) begin
 if(!rst_n) begin
     rd_flag <= 1'b0;
     lcd_id <= 16'd0;
 end    
 else begin
     if(rd_flag == 1'b0) begin
         rd_flag <= 1'b1; 
         case({lcd_rgb[7],lcd_rgb[15],lcd_rgb[23]})
             3'b000 : lcd_id <= 16'h4342;    //4.3' RGB LCD  RES:480x272
             3'b001 : lcd_id <= 16'h7084;    //7'   RGB LCD  RES:800x480
             3'b010 : lcd_id <= 16'h7016;    //7'   RGB LCD  RES:1024x600
             3'b100 : lcd_id <= 16'h4384;    //4.3' RGB LCD  RES:800x480
             3'b101 : lcd_id <= 16'h1018;    //10'  RGB LCD  RES:1280x800
             default : lcd_id <= 16'd0;
         endcase    
     end
 end    
end

endmodule

时钟分频模块
module clk_div(
 input               clk,          //50Mhz
 input               rst_n,
 input       [15:0]  lcd_id,
 output  reg         lcd_pclk
 );

reg          clk_25m;
reg          clk_12_5m;
reg          div_4_cnt;

//时钟2分频 输出25MHz时钟 
always @(posedge clk or negedge rst_n) begin
 if(!rst_n)
     clk_25m <= 1'b0;
 else 
     clk_25m <= ~clk_25m;
end

//时钟4分频 输出12.5MHz时钟 
always @(posedge clk or negedge rst_n) begin
 if(!rst_n) begin
     div_4_cnt <= 1'b0;
     clk_12_5m <= 1'b0;
 end    
 else begin
     div_4_cnt <= div_4_cnt + 1'b1;
     if(div_4_cnt == 1'b1)
         clk_12_5m <= ~clk_12_5m;
 end        
end

always @(*) begin
 case(lcd_id)
     16'h4342 : lcd_pclk = clk_12_5m;
     16'h7084 : lcd_pclk = clk_25m;       
     16'h7016 : lcd_pclk = clk;
     16'h4384 : lcd_pclk = clk_25m;
     16'h1018 : lcd_pclk = clk;
     default :  lcd_pclk = 1'b0;
 endcase      
end

endmodule

LCD驱动模块
module lcd_driver(
    input                lcd_pclk,    //时钟
    input                rst_n,       //复位,低电平有效
    input        [15:0]  lcd_id,      //LCD屏ID
    input        [23:0]  pixel_data,  //像素数据
    output       [10:0]  pixel_xpos,  //当前像素点横坐标
    output       [10:0]  pixel_ypos,  //当前像素点纵坐标   
    output  reg  [10:0]  h_disp,      //LCD屏水平分辨率
    output  reg  [10:0]  v_disp,      //LCD屏垂直分辨率   
    //RGB LCD接口
    output               lcd_de,      //LCD 数据使能信号
    output               lcd_hs,      //LCD 行同步信号
    output               lcd_vs,      //LCD 场同步信号
    output               lcd_bl,      //LCD 背光控制信号
    output               lcd_clk,     //LCD 像素时钟
    output       [23:0]  lcd_rgb      //LCD RGB888颜色数据
    );

//parameter define  
// 4.3' 480*272
parameter  H_SYNC_4342   =  11'd41;     //行同步
parameter  H_BACK_4342   =  11'd2;      //行显示后沿
parameter  H_DISP_4342   =  11'd480;    //行有效数据
parameter  H_FRONT_4342  =  11'd2;      //行显示前沿
parameter  H_TOTAL_4342  =  11'd525;    //行扫描周期
   
parameter  V_SYNC_4342   =  11'd10;     //场同步
parameter  V_BACK_4342   =  11'd2;      //场显示后沿
parameter  V_DISP_4342   =  11'd272;    //场有效数据
parameter  V_FRONT_4342  =  11'd2;      //场显示前沿
parameter  V_TOTAL_4342  =  11'd286;    //场扫描周期
   
// 7' 800*480   
parameter  H_SYNC_7084   =  11'd128;    //行同步
parameter  H_BACK_7084   =  11'd88;     //行显示后沿
parameter  H_DISP_7084   =  11'd800;    //行有效数据
parameter  H_FRONT_7084  =  11'd40;     //行显示前沿
parameter  H_TOTAL_7084  =  11'd1056;   //行扫描周期
   
parameter  V_SYNC_7084   =  11'd2;      //场同步
parameter  V_BACK_7084   =  11'd33;     //场显示后沿
parameter  V_DISP_7084   =  11'd480;    //场有效数据
parameter  V_FRONT_7084  =  11'd10;     //场显示前沿
parameter  V_TOTAL_7084  =  11'd525;    //场扫描周期       
   
// 7' 1024*600   
parameter  H_SYNC_7016   =  11'd20;     //行同步
parameter  H_BACK_7016   =  11'd140;    //行显示后沿
parameter  H_DISP_7016   =  11'd1024;   //行有效数据
parameter  H_FRONT_7016  =  11'd160;    //行显示前沿
parameter  H_TOTAL_7016  =  11'd1344;   //行扫描周期
   
parameter  V_SYNC_7016   =  11'd3;      //场同步
parameter  V_BACK_7016   =  11'd20;     //场显示后沿
parameter  V_DISP_7016   =  11'd600;    //场有效数据
parameter  V_FRONT_7016  =  11'd12;     //场显示前沿
parameter  V_TOTAL_7016  =  11'd635;    //场扫描周期
   
// 10.1' 1280*800   
parameter  H_SYNC_1018   =  11'd10;     //行同步
parameter  H_BACK_1018   =  11'd80;     //行显示后沿
parameter  H_DISP_1018   =  11'd1280;   //行有效数据
parameter  H_FRONT_1018  =  11'd70;     //行显示前沿
parameter  H_TOTAL_1018  =  11'd1440;   //行扫描周期
   
parameter  V_SYNC_1018   =  11'd3;      //场同步
parameter  V_BACK_1018   =  11'd10;     //场显示后沿
parameter  V_DISP_1018   =  11'd800;    //场有效数据
parameter  V_FRONT_1018  =  11'd10;     //场显示前沿
parameter  V_TOTAL_1018  =  11'd823;    //场扫描周期

// 4.3' 800*480   
parameter  H_SYNC_4384   =  11'd128;    //行同步
parameter  H_BACK_4384   =  11'd88;     //行显示后沿
parameter  H_DISP_4384   =  11'd800;    //行有效数据
parameter  H_FRONT_4384  =  11'd40;     //行显示前沿
parameter  H_TOTAL_4384  =  11'd1056;   //行扫描周期
   
parameter  V_SYNC_4384   =  11'd2;      //场同步
parameter  V_BACK_4384   =  11'd33;     //场显示后沿
parameter  V_DISP_4384   =  11'd480;    //场有效数据
parameter  V_FRONT_4384  =  11'd10;     //场显示前沿
parameter  V_TOTAL_4384  =  11'd525;    //场扫描周期    

//reg define
reg  [10:0] h_sync ;
reg  [10:0] h_back ;
reg  [10:0] h_total;
reg  [10:0] v_sync ;
reg  [10:0] v_back ;
reg  [10:0] v_total;
reg  [10:0] h_cnt  ;
reg  [10:0] v_cnt  ;

//wire define    
wire        lcd_en;
wire        data_req;

//*****************************************************
//**                    main code
//*****************************************************

//RGB LCD 采用DE模式时,行场同步信号需要拉高
assign  lcd_hs = 1'b1;        //LCD行同步信号
assign  lcd_vs = 1'b1;        //LCD场同步信号

assign  lcd_bl = 1'b1;        //LCD背光控制信号  
assign  lcd_clk = lcd_pclk;   //LCD像素时钟
assign  lcd_de = lcd_en;      //LCD数据有效信号

//使能RGB888数据输出
assign  lcd_en = ((h_cnt >= h_sync + h_back) && (h_cnt < h_sync + h_back + h_disp)
                  && (v_cnt >= v_sync + v_back) && (v_cnt < v_sync + v_back + v_disp)) 
                  ? 1'b1 : 1'b0;

//请求像素点颜色数据输入  
assign data_req = ((h_cnt >= h_sync + h_back - 1'b1) && (h_cnt < h_sync + h_back + h_disp - 1'b1)
                  && (v_cnt >= v_sync + v_back) && (v_cnt < v_sync + v_back + v_disp)) 
                  ? 1'b1 : 1'b0;

//像素点坐标  
assign pixel_xpos = data_req ? (h_cnt - (h_sync + h_back - 1'b1)) : 11'd0;
assign pixel_ypos = data_req ? (v_cnt - (v_sync + v_back - 1'b1)) : 11'd0;

//RGB888数据输出
assign lcd_rgb = lcd_en ? pixel_data : 24'd0;

//行场时序参数
always @(*) begin
    case(lcd_id)
        16'h4342 : begin
            h_sync  = H_SYNC_4342; 
            h_back  = H_BACK_4342; 
            h_disp  = H_DISP_4342; 
            h_total = H_TOTAL_4342;
            v_sync  = V_SYNC_4342; 
            v_back  = V_BACK_4342; 
            v_disp  = V_DISP_4342; 
            v_total = V_TOTAL_4342;            
        end
        16'h7084 : begin
            h_sync  = H_SYNC_7084; 
            h_back  = H_BACK_7084; 
            h_disp  = H_DISP_7084; 
            h_total = H_TOTAL_7084;
            v_sync  = V_SYNC_7084; 
            v_back  = V_BACK_7084; 
            v_disp  = V_DISP_7084; 
            v_total = V_TOTAL_7084;        
        end
        16'h7016 : begin
            h_sync  = H_SYNC_7016; 
            h_back  = H_BACK_7016; 
            h_disp  = H_DISP_7016; 
            h_total = H_TOTAL_7016;
            v_sync  = V_SYNC_7016; 
            v_back  = V_BACK_7016; 
            v_disp  = V_DISP_7016; 
            v_total = V_TOTAL_7016;            
        end
        16'h4384 : begin
            h_sync  = H_SYNC_4384; 
            h_back  = H_BACK_4384; 
            h_disp  = H_DISP_4384; 
            h_total = H_TOTAL_4384;
            v_sync  = V_SYNC_4384; 
            v_back  = V_BACK_4384; 
            v_disp  = V_DISP_4384; 
            v_total = V_TOTAL_4384;             
        end        
        16'h1018 : begin
            h_sync  = H_SYNC_1018; 
            h_back  = H_BACK_1018; 
            h_disp  = H_DISP_1018; 
            h_total = H_TOTAL_1018;
            v_sync  = V_SYNC_1018; 
            v_back  = V_BACK_1018; 
            v_disp  = V_DISP_1018; 
            v_total = V_TOTAL_1018;        
        end
        default : begin
            h_sync  = H_SYNC_4342; 
            h_back  = H_BACK_4342; 
            h_disp  = H_DISP_4342; 
            h_total = H_TOTAL_4342;
            v_sync  = V_SYNC_4342; 
            v_back  = V_BACK_4342; 
            v_disp  = V_DISP_4342; 
            v_total = V_TOTAL_4342;          
        end
    endcase
end

//行计数器对像素时钟计数
always@ (posedge lcd_pclk or negedge rst_n) begin
    if(!rst_n) 
        h_cnt <= 11'd0;
    else begin
        if(h_cnt == h_total - 1'b1)
            h_cnt <= 11'd0;
        else
            h_cnt <= h_cnt + 1'b1;           
    end
end

//场计数器对行计数
always@ (posedge lcd_pclk or negedge rst_n) begin
    if(!rst_n) 
        v_cnt <= 11'd0;
    else begin
        if(h_cnt == h_total - 1'b1) begin
            if(v_cnt == v_total - 1'b1)
                v_cnt <= 11'd0;
            else
                v_cnt <= v_cnt + 1'b1;    
        end
    end    
end

endmodule
RGB LCD显示模块
module lcd_display(
 input                lcd_pclk ,
 input                rst_n ,
 
 //日历数据
 input         [7:0]  sec,        //秒
 input         [7:0]  min,        //分
 input         [7:0]  hour,       //时
 input         [7:0]  day,        //日
 input         [7:0]  mon,        //月
 input         [7:0]  year,       //年
 
 //LCD数据接口
 input        [10:0]  pixel_xpos, //像素点横坐标
 input        [10:0]  pixel_ypos, //像素点纵坐标
 output  reg  [23:0]  pixel_data  //像素点数据
);

//parameter define
localparam CHAR_POS_X_1  = 11'd1;  //第1行字符区域起始点横坐标
localparam CHAR_POS_Y_1  = 11'd1;  //第1行字符区域起始点纵坐标
localparam CHAR_POS_X_2  = 11'd17; //第2行字符区域起始点横坐标
localparam CHAR_POS_Y_2  = 11'd17; //第2行字符区域起始点纵坐标
localparam CHAR_WIDTH_1  = 11'd80; //第1行字符区域的宽度,第1行共10个字符(加空格)
localparam CHAR_WIDTH_2  = 11'd64; //第2行字符区域的宽度,第2行共8个字符(加空格)
localparam CHAR_HEIGHT   = 11'd16; //单个字符的高度
localparam WHITE  = 24'hffffff;    //背景色,白色
localparam BLACK  = 24'h000000;    //字符颜色,黑色

//reg define
reg  [127:0]  char  [9:0] ;        //字符数组

//*****************************************************
//**                    main code
//*****************************************************

//字符数组初始值,用于存储字模数据(由取模软件生成,单个数字字体大小:16*16)
always @(posedge lcd_pclk ) begin
 char[0] <= 128'h00000018244242424242424224180000 ;  // "0"
 char[1] <= 128'h000000107010101010101010107C0000 ;  // "1"
 char[2] <= 128'h0000003C4242420404081020427E0000 ;  // "2"
 char[3] <= 128'h0000003C424204180402024244380000 ;  // "3"
 char[4] <= 128'h000000040C14242444447E04041E0000 ;  // "4"
 char[5] <= 128'h0000007E404040586402024244380000 ;  // "5"
 char[6] <= 128'h0000001C244040586442424224180000 ;  // "6"
 char[7] <= 128'h0000007E444408081010101010100000 ;  // "7"
 char[8] <= 128'h0000003C4242422418244242423C0000 ;  // "8"
 char[9] <= 128'h0000001824424242261A020224380000 ;  // "9"
end

//不同的区域绘制不同的像素数据
always @(posedge lcd_pclk or negedge rst_n ) begin
 if (!rst_n)  begin
     pixel_data <= BLACK;
 end
 
 //在第一行显示年的千位 固定值"2"
 else if(     (pixel_xpos >= CHAR_POS_X_1)                    
           && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1/10*1)
           && (pixel_ypos >= CHAR_POS_Y_1)                    
           && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)  ) begin
     if(char [2] [ (CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos)*8 
                      - ((pixel_xpos-CHAR_POS_X_1)%8) -1 ] )  
            pixel_data <= BLACK;         //显示字符为黑色
        else
            pixel_data <= WHITE;        //显示字符区域背景为白色
    end
    
    //在第一行显示年的百位 固定值"0"
    else if(     (pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1/10*1) 
              && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1/10*2)
              && (pixel_ypos >= CHAR_POS_Y_1)                  
              && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)  ) begin
        if(char [0] [ (CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos)*8 
                      - ((pixel_xpos-CHAR_POS_X_1)%8) -1 ]  )
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第一行显示年的十位
    else if(     (pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1/10*2) 
              && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1/10*3)
              && (pixel_ypos >= CHAR_POS_Y_1)                  
              && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)  ) begin
        if(char [year[7:4]] [ (CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos)*8 
                              - ((pixel_xpos-CHAR_POS_X_1)%8) -1 ]  )
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第一行显示年的个位
    else if(     (pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1/10*3) 
              && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1/10*4)
              && (pixel_ypos >= CHAR_POS_Y_1)                  
              && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)  ) begin
        if(char [year[3:0]] [ (CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos)*8 
                              - ((pixel_xpos-CHAR_POS_X_1)%8) -1 ]  )
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第一行显示空格
    else if(     (pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1/10*4) 
              && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1/10*5)
              && (pixel_ypos >= CHAR_POS_Y_1)                  
              && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)  ) begin
        pixel_data <= WHITE;
    end
    
    //在第一行显示月的十位
    else if(     (pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1/10*5) 
              && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1/10*6)
              && (pixel_ypos >= CHAR_POS_Y_1)                  
              && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
        if(char [mon[7:4]] [ (CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos)*8 
                             - ((pixel_xpos-CHAR_POS_X_1)%8) -1 ] )
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第一行显示月的个位
    else if(     (pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1/10*6) 
              && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1/10*7)
              && (pixel_ypos >= CHAR_POS_Y_1)                  
              && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)  ) begin
        if(char [mon[3:0]] [ (CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos)*8 
                             - ((pixel_xpos-CHAR_POS_X_1)%8) -1 ] )
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第一行显示空格
    else if(     (pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1/10*7) 
              && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1/10*8)
              && (pixel_ypos >= CHAR_POS_Y_1)                  
              && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)  ) begin
        pixel_data <= WHITE;
    end 
    
    //在第一行显示日的十位
    else if(     (pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1/10*8) 
              && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1/10*9)
              && (pixel_ypos >= CHAR_POS_Y_1)                  
              && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)  ) begin
        if(char [day[7:4]] [ (CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos)*8 
                             - ((pixel_xpos-CHAR_POS_X_1)%8) -1 ] )
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第一行显示日的个位
    else if(     (pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1/10*9) 
              && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1)
              && (pixel_ypos >= CHAR_POS_Y_1)                  
              && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)  ) begin
        if(char [day[3:0]] [ (CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos)*8 
                             - ((pixel_xpos-CHAR_POS_X_1)%8) -1 ] )
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第二行显示时的十位
    else if(     (pixel_xpos >= CHAR_POS_X_2)                  
              && (pixel_xpos <  CHAR_POS_X_2 + CHAR_WIDTH_2/8*1)
              && (pixel_ypos >= CHAR_POS_Y_2)                  
              && (pixel_ypos <  CHAR_POS_Y_2 + CHAR_HEIGHT)  ) begin
        if(char [hour[7:4]] [ (CHAR_HEIGHT+CHAR_POS_Y_2 - pixel_ypos)*8 
                              - ((pixel_xpos-CHAR_POS_X_2)%8) -1 ] )
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第二行显示时的个位
    else if(     (pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2/8*1) 
              && (pixel_xpos <  CHAR_POS_X_2 + CHAR_WIDTH_2/8*2)
              && (pixel_ypos >= CHAR_POS_Y_2)                  
              && (pixel_ypos <  CHAR_POS_Y_2 + CHAR_HEIGHT)  ) begin
        if(char [hour[3:0]] [ (CHAR_HEIGHT+CHAR_POS_Y_2 - pixel_ypos)*8 
                              - ((pixel_xpos-CHAR_POS_X_2)%8) -1 ] )
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第二行显示空格
    else if(     (pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2/8*2) 
              && (pixel_xpos <  CHAR_POS_X_2 + CHAR_WIDTH_2/8*3)
              && (pixel_ypos >= CHAR_POS_Y_2)                  
              && (pixel_ypos <  CHAR_POS_Y_2 + CHAR_HEIGHT)  ) begin
        pixel_data <= WHITE;
    end
    
    //在第二行显示分的十位
    else if(     (pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2/8*3) 
              && (pixel_xpos <  CHAR_POS_X_2 + CHAR_WIDTH_2/8*4)
              && (pixel_ypos >= CHAR_POS_Y_2)                  
              && (pixel_ypos <  CHAR_POS_Y_2 + CHAR_HEIGHT)  ) begin
        if(char [min[7:4]] [ (CHAR_HEIGHT+CHAR_POS_Y_2 - pixel_ypos)*8 
                             - ((pixel_xpos-CHAR_POS_X_2)%8) -1 ] )
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第二行显示分的个位
    else if(     (pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2/8*4) 
              && (pixel_xpos <  CHAR_POS_X_2 + CHAR_WIDTH_2/8*5)
              && (pixel_ypos >= CHAR_POS_Y_2)                  
              && (pixel_ypos <  CHAR_POS_Y_2 + CHAR_HEIGHT)  ) begin
        if(char [min[3:0]] [ (CHAR_HEIGHT+CHAR_POS_Y_2 - pixel_ypos)*8 
                             - ((pixel_xpos-CHAR_POS_X_2)%8) -1 ] )
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第二行显示空格
    else if(     (pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2/8*5) 
              && (pixel_xpos <  CHAR_POS_X_2 + CHAR_WIDTH_2/8*6)
              && (pixel_ypos >= CHAR_POS_Y_2)                  
              && (pixel_ypos <  CHAR_POS_Y_2 + CHAR_HEIGHT)  ) begin
        pixel_data <= WHITE;
    end 
    
    //在第二行显示秒的十位
    else if(     (pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2/8*6) 
              && (pixel_xpos <  CHAR_POS_X_2 + CHAR_WIDTH_2/8*7)
              && (pixel_ypos >= CHAR_POS_Y_2)                  
              && (pixel_ypos <  CHAR_POS_Y_2 + CHAR_HEIGHT)  ) begin
        if(char [sec[7:4]] [ (CHAR_HEIGHT+CHAR_POS_Y_2 - pixel_ypos)*8 
                              - ((pixel_xpos-CHAR_POS_X_2)%8) -1 ] )
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第二行显示秒的个位    
    else if(     (pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2/8*7) 
              && (pixel_xpos <  CHAR_POS_X_2 + CHAR_WIDTH_2)
              && (pixel_ypos >= CHAR_POS_Y_2)                  
              && (pixel_ypos <  CHAR_POS_Y_2 + CHAR_HEIGHT)  ) begin
        if(char [sec[3:0]] [ (CHAR_HEIGHT+CHAR_POS_Y_2 - pixel_ypos)*8 
                             - ((pixel_xpos-CHAR_POS_X_2)%8) -1 ] )
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    else begin
        pixel_data <= WHITE;    //屏幕背景为白色
    end
end

endmodule 

数字时钟显示改进

在RGB LCD显示模块中,我们是在lcd屏幕的左上角显示数字时钟,并且设置的数字大小是 8*16的大小,这个大小在显示屏上太小了(我用的是800x480分辨率的lcd),位置也很别扭。所以对其进行了一定的修改:

. 数字大小改为16*32

. 位置居中

原来的代码如下

//parameter define
localparam CHAR_POS_X_1  = 11'd1;  //第1行字符区域起始点横坐标
localparam CHAR_POS_Y_1  = 11'd1;  //第1行字符区域起始点纵坐标
localparam CHAR_POS_X_2  = 11'd17; //第2行字符区域起始点横坐标
localparam CHAR_POS_Y_2  = 11'd17; //第2行字符区域起始点纵坐标
localparam CHAR_WIDTH_1  = 11'd80; //第1行字符区域的宽度,第1行共10个字符(加空格)
localparam CHAR_WIDTH_2  = 11'd64; //第2行字符区域的宽度,第2行共8个字符(加空格)
localparam CHAR_HEIGHT   = 11'd16; //单个字符的高度
localparam WHITE  = 24'hffffff;    //背景色,白色
localparam BLACK  = 24'h000000;    //字符颜色,黑色

//reg define
reg  [127:0]  char  [9:0] ;        //字符数组

//不同的区域绘制不同的像素数据
always @(posedge lcd_pclk or negedge rst_n ) begin
    if (!rst_n)  begin
        pixel_data <= BLACK;
    end
    
    //在第一行显示年的千位 固定值"2"
    else if(     (pixel_xpos >= CHAR_POS_X_1)                    
              && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1/10*1)
              && (pixel_ypos >= CHAR_POS_Y_1)                    
              && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)  ) begin
        if(char [2] [ (CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos)*8 
                     - ((pixel_xpos-CHAR_POS_X_1)%8) -1 ] )  
            pixel_data <= BLACK;         //显示字符为黑色
        else
            pixel_data <= WHITE;        //显示字符区域背景为白色
    end

因为数字大小是8*16=128,因此数组元素的位宽是 128 位。

CHAR_WIDTH_1 = 11’d80 即8*10 ,第1行字符区域的宽度,第1行共10个字符(加空格)

生成的字模为:

//字符数组初始值,用于存储字模数据(由取模软件生成,单个数字字体大小:16*16)
always @(posedge lcd_pclk ) begin
 char[0] <= 128'h00000018244242424242424224180000 ;  // "0"
 char[1] <= 128'h000000107010101010101010107C0000 ;  // "1"
 char[2] <= 128'h0000003C4242420404081020427E0000 ;  // "2"
 char[3] <= 128'h0000003C424204180402024244380000 ;  // "3"
 char[4] <= 128'h000000040C14242444447E04041E0000 ;  // "4"
 char[5] <= 128'h0000007E404040586402024244380000 ;  // "5"
 char[6] <= 128'h0000001C244040586442424224180000 ;  // "6"
 char[7] <= 128'h0000007E444408081010101010100000 ;  // "7"
 char[8] <= 128'h0000003C4242422418244242423C0000 ;  // "8"
 char[9] <= 128'h0000001824424242261A020224380000 ;  // "9"
end

修改后的代码如下:

//parameter define
localparam CHAR_POS_X_1  = 11'd320;  //第1行字符区域起始点横坐标
localparam CHAR_POS_Y_1  = 11'd176;  //第1行字符区域起始点纵坐标
localparam CHAR_POS_X_2  = 11'd352; //第2行字符区域起始点横坐标
localparam CHAR_POS_Y_2  = 11'd214; //第2行字符区域起始点纵坐标
localparam CHAR_WIDTH_1  = 11'd160; //第1行字符区域的宽度,第1行共10个字符(加空格)
localparam CHAR_WIDTH_2  = 11'd128; //第2行字符区域的宽度,第2行共8个字符(加空格)
localparam CHAR_HEIGHT   = 11'd32; //单个字符的高度
localparam WHITE  = 24'hffffff;    //背景色,白色
localparam BLACK  = 24'h000000;    //字符颜色,黑色

//reg define
reg  [511:0]  char  [9:0] ;        //字符数组 因为字体大小是 32*16=512

//不同的区域绘制不同的像素数据
always @(posedge lcd_pclk or negedge rst_n ) begin
    if (!rst_n)  begin
        pixel_data <= BLACK;
    end
    
    //在第一行显示年的千位 固定值"2"
    else if(     (pixel_xpos >= CHAR_POS_X_1)                    
              && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1/10*1)
              && (pixel_ypos >= CHAR_POS_Y_1)                    
              && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)  ) begin
        if(char [2] [ (CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos)*16 
                     - ((pixel_xpos-CHAR_POS_X_1)%16) -1 ] )  
            pixel_data <= BLACK;         //显示字符为黑色
        else
            pixel_data <= WHITE;        //显示字符区域背景为白色
    end

生成的字模为:

需要注意的是:

对于 代码 “if(char [2] [ (CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos)*16
-((pixel_xpos-CHAR_POS_X_1)%16) -1 ] ) ”,

是将原来的8改成16。我们不难理解“16”的由来,因为在查找数组元素的时候, pixel_ypos 的每次变化代表换到下一行扫描, 一行跨过 16 个数据,所有乘以 16。这里总结一下:字符数组一行的 512 个数据从高位到低位,每 16 位代表另一行,分别对应点阵中该行从左向右的每一个像素点 。

如果不改的话会乱码如下所示:

修改最后显示结果如图:

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值