I2C协议驱动OLED的Verilog代码

以下代码I2C速度取为400K,所用开发板为24M主频,故需要分频。所选OLED为4线制,I2C协议,0.96吋。由于其功耗较低,完全可以用FPGA的IO脚供电,故在顶层文件里设置Vcc和Gnd,作为OLED的供电引脚。

以下代码在第一排显示21个字符A,第二排按照1Hz的频率循环0-9计数,同时完成ACK功能。

//顶层文件
//OLED I2C驱动,测试成功,第一排显示21个字符A,第二排按照1Hz的频率0-9计数,ACK测试成功
 module I2C_top(     
          input CLK_24M,     //24MHz
          input CLK_1K,     //低频更新数据,此处取1KHz
          input CLK_1,     //1Hz时钟          
          input RST,
          output SCL,
          inout  SDA,
          output READY,
          output ACK1,
          output ACK2,
          output ACK3,          
          output Vcc,
          output Gnd
    
      );
      
     parameter CMD = 8'h00;  //命令
     parameter DAT = 8'h40;     //数据 

     reg [4:0]cnt;  //计数器,用来分频

     reg [23:0]Data;
     reg [7:0]reg_data;
     reg EN;
     reg CLK_400K;
     reg [3:0] state; //状态计数器
     reg [7:0] count; //用于数据处理的计数器
     reg [4:0] m;  //m:0~8 
     reg [8:0] n;  //n:0~128
     reg [7:0] x; //x坐标
     reg [5:0] number; //显示数字索引
     reg [3:0] num; //显示数字
     reg [7:0] number_data;     //数字字库
     I2C_COM u1(.CLK_400K(CLK_400K),
               .RST(RST),
               .Data(Data),
               .EN(EN),
               .RDY(READY),
               .ack1(ACK1),
               .ack2(ACK2),
               .ack3(ACK3),
               .SCL(SCL),
               .SDA(SDA));
assign Vcc=1'b1;
assign Gnd=1'b0;

always@(posedge CLK_1)   //1Hz计数器,用于显示
   if(num<9)                 
         num<=num+1'b1;
   else num<=0;

//产生I2C控制时钟400KHz    
always@(posedge CLK_24M or negedge RST)   
begin
   if(!RST) begin
        CLK_400K<=0;
        cnt<=0;
   end
   else if(cnt<29)                  //分频 24000/400/2-1=30-1=29
         cnt<=cnt+1'b1;
   else begin
         CLK_400K<=!CLK_400K;
         cnt<=0;
   end
end

//I2C寄存器配置    
always@(posedge CLK_1K or negedge RST)    
begin
   if(!RST) begin   //
        count<=0;     
        EN<=0;
        state<=0;
        m<=0;
        n<=0;
        x<=0;
      end
  else  if (count<100) count<=count+1;   //延时
  else  if (count<129)                   //初始化
          case (state)
            0:if (READY) begin Data<={8'h78,CMD,reg_data};EN<=1;state<=1; end  //OLED地址:0x78
            1:if (READY) begin EN<=0; state<=2; end     
            2:begin count<=count+1; state<=0; end         
            default: state<=0;
          endcase
  else  if (count==129)    //清屏操作
                     case(state)
                     0:if (m<8) begin
                          Data<={8'h78,CMD,8'hB0+m};//   
                          EN<=1;
                          state<=1;
                        end
                         else begin
                           EN<=0;
                           state<=0;
                           count<=count+1;
                         end
                     1:if(READY) begin    //I2C发送结束 
                             EN<=0;
                             state<=2;
                        end
                     2:begin
                        Data<={8'h78,CMD,8'h00};//
                        EN<=1;
                        state<=3;
                      end
                     3:if(READY) begin    //I2C发送结束 
                             EN<=0;
                             state<=4;
                        end
                     4:begin
                        Data<={8'h78,CMD,8'h10};// 
                        EN<=1;
                        state<=5;
                      end
                     5:if(READY) begin    //I2C发送结束 
                         EN<=0;
                         state<=6;
                        end
                     6:if (n<128) begin
                           Data<={8'h78,DAT,8'h00};//I2C DeviceAddress:0x78   
                           EN<=1;
                           state<=7;
                           end
                          else begin
                            n<=0;
                            m<=m+1;
                           state<=0;
                           end
                     7:if (READY) begin    //I2C发送结束 
                             EN<=0;
                             n<=n+1;
                             state<=6;
                        end
                    
                    default:state<=0;
                endcase
   else   if (count==130)  case (state) //y位置
            0:if (READY) begin Data<={8'h78,CMD,reg_data};EN<=1;state<=1; end  //OLED地址:0x78
            1:if (READY) begin EN<=0; state<=2; end     
            2:begin count<=count+1; state<=0; end         
            default: state<=0;
          endcase
   else   if (count==131)  case (state) //x位置
            0:if (READY) begin Data<={8'h78,CMD,reg_data+x};EN<=1;state<=1; end  //OLED地址:0x78
            1:if (READY) begin EN<=0; state<=2; end     
            2:begin count<=count+1; state<=0; end         
            default: state<=0;
          endcase 
   else    if (count==132)  
           case (state) //写位置命令
            0:if (READY) begin Data<={8'h78,CMD,reg_data};EN<=1;state<=1; end  //OLED地址:0x78
            1:if (READY) begin EN<=0; state<=2; end     
            2:begin count<=count+1; state<=0; end         
            default: state<=0;
          endcase
   else  if (count<139)  case (state)//写字母A
            0:if (READY) begin Data<={8'h78,DAT,reg_data};EN<=1;state<=1; end  //OLED地址:0x78
            1:if (READY) begin EN<=0;state<=2; end     
            2:begin count<=count+1; state<=0; end         
            default: state<=0;
          endcase 
   
   else if (count==139) //第一排显示21个A
         if (x<120)   begin  //x范围是0-127,此处x起始坐标取为1。第21个字符开始于121。
             count<=133 ;//重复写
             x<=x+6;    //字库是6*8,所以每次+6      
             end             
           else 
             count<=140;

  else   if (count==140)  case (state) //y位置
            0:if (READY) begin Data<={8'h78,CMD,reg_data};EN<=1;state<=1; end  //OLED地址:0x78
            1:if (READY) begin EN<=0; state<=2; end     
            2:begin count<=count+1; state<=0; end         
            default: state<=0;
          endcase
   else   if (count==141)  case (state) //x位置
            0:if (READY) begin Data<={8'h78,CMD,reg_data};EN<=1;state<=1; end  //OLED地址:0x78
            1:if (READY) begin EN<=0; state<=2; end     
            2:begin count<=count+1; state<=0; end         
            default: state<=0;
          endcase 
   else    if (count==142)  
           case (state) //写位置命令
            0:if (READY) begin Data<={8'h78,CMD,reg_data};EN<=1;state<=1; end  //OLED地址:0x78
            1:if (READY) begin EN<=0; state<=2; end     
            2:begin count<=count+1; state<=0; end         
            default: state<=0;
          endcase  
    else      //显示可变数字0-9
              case (state)
                 0:begin number<=num*6;state<=1; end
                 1:if (READY) begin  Data<={8'h78,DAT,number_data};EN<=1;state<=2; end  //OLED地址:0x78
                 2:if (READY) begin EN<=0;state<=3; number<=number+1; end     
                 3:begin Data<={8'h78,DAT,number_data};EN<=1;state<=4; end 
                 4:if (READY) begin EN<=0;state<=5; number<=number+1;end                 
                 5:begin Data<={8'h78,DAT,number_data};EN<=1;state<=6; end 
                 6:if (READY) begin EN<=0;state<=7; number<=number+1; end                  
                 7:begin Data<={8'h78,DAT,number_data};EN<=1;state<=8; end 
                 8:if (READY) begin EN<=0;state<=9; number<=number+1; end 
                 9:begin Data<={8'h78,DAT,number_data};EN<=1;state<=10; end 
                 10:if (READY) begin EN<=0;state<=11;number<=number+1; end 
                 11:begin Data<={8'h78,DAT,number_data};EN<=1;state<=12; end 
                 12:if (READY) begin EN<=0;state<=0; count<=140; end                                                    
                default: state<=0;
               endcase 
             
  end    
            
//I2C需要配置的寄存器值              
always@(count)   
    case(count-100)
     0:reg_data<=8'hAE;// 100~128条为初始化命令
     1:reg_data<=8'h00;//  
     2:reg_data<=8'h10;//
     3:reg_data<=8'h40;// 
     4:reg_data<=8'hB0;// 
     5:reg_data<=8'h81;//
     6:reg_data<=8'hFF;//
     7:reg_data<=8'hA1;//
     8:reg_data<=8'hC8;//
     9:reg_data<=8'hA6;//
     10:reg_data<=8'hA8;//
     11:reg_data<=8'h3F;//
     12:reg_data<=8'hD3;//
     13:reg_data<=8'h00;//
     14:reg_data<=8'hD5;//
     15:reg_data<=8'h80;//
     16:reg_data<=8'hD9;//
     17:reg_data<=8'hF1;//
     18:reg_data<=8'hDA;//
     19:reg_data<=8'h12;//
     20:reg_data<=8'hDB;//
     21:reg_data<=8'h40;//
     22:reg_data<=8'h20;//
     23:reg_data<=8'h02;//
     24:reg_data<=8'h8D;//
     25:reg_data<=8'h14;//                         
     26:reg_data<=8'hA4;//
     27:reg_data<=8'hA6;//
     28:reg_data<=8'hAF;//
     30:reg_data<=8'hB0; //字符位置原点(y)
     31:reg_data<=8'h01; //字符位置01点(x)
     32:reg_data<=8'h10; //写命令
     33:reg_data<=8'h00; //写A数据(字库)
     34:reg_data<=8'h7C;
     35:reg_data<=8'h12;
     36:reg_data<=8'h11;
     37:reg_data<=8'h12;
     38:reg_data<=8'h7C;     
     40:reg_data<=8'hB1; //字符位置(y) 第二排
     41:reg_data<=8'h0D; //字符位置(x)0x3D  x&0x0f
     42:reg_data<=8'h13; //写命令 ((x&0xf0)>>4)|0x10     
    default:reg_data<=8'hzz;
    endcase 
//字库译码            
always@(number)   
    case(number)
     0:number_data<=8'h00; //写0数据(字库)
     1:number_data<=8'h3E;
     2:number_data<=8'h51;
     3:number_data<=8'h49;
     4:number_data<=8'h45;
     5:number_data<=8'h3E;
     6:number_data<=8'h00; //写1数据(字库)
     7:number_data<=8'h00;
     8:number_data<=8'h42;
     9:number_data<=8'h7F;
     10:number_data<=8'h40;
     11:number_data<=8'h00;     
     12:number_data<=8'h00; //写2数据(字库)
     13:number_data<=8'h42;
     14:number_data<=8'h61;
     15:number_data<=8'h51;
     16:number_data<=8'h49;
     17:number_data<=8'h46; 
     18:number_data<=8'h00; //写3数据(字库)
     19:number_data<=8'h21;
     20:number_data<=8'h41;
     21:number_data<=8'h45;
     22:number_data<=8'h4B;
     23:number_data<=8'h31; 
     24:number_data<=8'h00; //写4数据(字库)
     25:number_data<=8'h18;
     26:number_data<=8'h14;
     27:number_data<=8'h12;
     28:number_data<=8'h7F;
     29:number_data<=8'h10; 
     30:number_data<=8'h00; //写5数据(字库)
     31:number_data<=8'h27;
     32:number_data<=8'h45;
     33:number_data<=8'h45;
     34:number_data<=8'h45;
     35:number_data<=8'h39; 
     36:number_data<=8'h00; //写6数据(字库)
     37:number_data<=8'h3C;
     38:number_data<=8'h4A;
     39:number_data<=8'h49;
     40:number_data<=8'h49;
     41:number_data<=8'h30; 
     42:number_data<=8'h00; //写7数据(字库)
     43:number_data<=8'h01;
     44:number_data<=8'h71;
     45:number_data<=8'h09;
     46:number_data<=8'h05;
     47:number_data<=8'h03; 
     48:number_data<=8'h00; //写8数据(字库)
     49:number_data<=8'h36;
     50:number_data<=8'h49;
     51:number_data<=8'h49;
     52:number_data<=8'h49;
     53:number_data<=8'h36; 
     54:number_data<=8'h00; //写9数据(字库)
     55:number_data<=8'h06;
     56:number_data<=8'h49;
     57:number_data<=8'h49;
     58:number_data<=8'h29;
     59:number_data<=8'h1E; 
    default:reg_data<=8'hzz;      
   endcase      
 endmodule
  //SCL,SDA数据传输时序代码(I2C写控制代码)
`timescale 1ns / 1ps
module I2C_COM(CLK_400K,          //I2C控制接口传输所需时钟
               RST,          //重置,0有效
               Data,          //SDA接口传输的24位数据
               EN,             //使能,1有效
               RDY,           //传输准备标志,1有效
	           ack1,            //ACK应答
	           ack2,            //ACK应答
	           ack3,            //ACK应答
               SCL,          //FPGA与I2C时钟接口
               SDA);         //FPGA与I2C数据接口
    input  [23:0]Data;
    input  RST;
    input  CLK_400K;
    input  EN;
    output RDY;
    output SCL;
    inout  SDA;
    output ack1,ack2,ack3;
    reg  [6:0] count;  //I2C时间片计数器
    reg  sclk;
    reg  ack1,ack2,ack3;
    reg  RDY;
    reg  SCL;
    reg  isOut;      //SDA输出标示
    wire SDA;
    reg  sdia;   //寄存器,用于进行数据读写
    assign SDA = isOut ? sdia : 1'bz;        //SDA数据输出选择,1输出,0高阻(接收从机响应或数据)
	 
   always@(posedge CLK_400K or  negedge RST)
    begin
       if(!RST)             //初始状态,SCK,SDA全部为高
       begin
          RDY<=1;
          SCL<=1;
          sdia<=1;
          ack1<=0;
          ack2<=0;
          ack3<=0;
          isOut<=1;
       end
       else if (!EN) begin count<=0; ack1<=0;ack2<=0;ack3<=0;RDY<=1;isOut<=1; end
         else  case(count)
             0,1:begin RDY<=0;SCL<=1;sdia<=1;
               ack1<=0;
               ack2<=0;
               ack3<=0;
               isOut<=1;             
               count<=count+1; 
              end  //2个时钟,确保释放总线
             2:begin sdia<=0;count<=count+1; end                  //起始
             3:begin SCL<=0;count<=count+1; end                 //
             4:begin sdia<=Data[23]; count<=count+1; end         //SCL=0采集数据
	   	     5:begin SCL<=1; count<=count+1; end                //SCL上升沿传数据
		     6:begin SCL<=0;count<=count+1; end
		     7:begin sdia<=Data[22];count<=count+1; end
             8:begin SCL<=1;count<=count+1; end
		     9:begin SCL<=0;count<=count+1; end
		     10:begin sdia<=Data[21];count<=count+1; end
		     11:begin SCL<=1;count<=count+1; end
		     12:begin SCL<=0;count<=count+1; end
		     13:begin sdia<=Data[20];count<=count+1; end
		     14:begin SCL<=1;count<=count+1; end
		     15:begin SCL<=0;count<=count+1; end
		     16:begin sdia<=Data[19];count<=count+1; end
		     17:begin SCL<=1;count<=count+1; end
			 18:begin SCL<=0;count<=count+1; end
			 19:begin sdia<=Data[18];count<=count+1; end
			 20:begin SCL<=1;count<=count+1; end
			 21:begin SCL<=0;count<=count+1; end
			 22:begin sdia<=Data[17];count<=count+1; end
			 23:begin SCL<=1;count<=count+1; end
			 24:begin SCL<=0;count<=count+1; end
			 25:begin sdia<=Data[16];count<=count+1; end
			 26:begin SCL<=1; count<=count+1;end    //SCL期间不允许改变数据
			 27:begin SCL<=0; count<=count+1;isOut<=0;end	 //高阻
	         28:begin SCL<=1;count<=count+1; end
			 29:begin ack1<=!SDA;count<=count+1; end    
             30:begin SCL<=0; count<=count+1; isOut<=1;end
			 31:begin sdia<=Data[15];   count<=count+1; end
			 32:begin SCL<=1;count<=count+1; end                 //SCL上升沿传数据
		     33:begin SCL<=0;count<=count+1; end
			 34:begin sdia<=Data[14];count<=count+1; end
             35:begin SCL<=1;count<=count+1; end
			 36:begin SCL<=0;count<=count+1; end
			 37:begin sdia<=Data[13];count<=count+1; end
			 38:begin SCL<=1;count<=count+1; end
			 39:begin SCL<=0;count<=count+1; end
			 40:begin sdia<=Data[12];count<=count+1; end
			 41:begin SCL<=1;count<=count+1; end
			 42:begin SCL<=0;count<=count+1; end
			 43:begin sdia<=Data[11];count<=count+1; end
			 44:begin SCL<=1;count<=count+1; end
			 45:begin SCL<=0;count<=count+1; end
			 46:begin sdia<=Data[10];count<=count+1; end
			 47:begin SCL<=1;count<=count+1; end
			 48:begin SCL<=0;count<=count+1; end
			 49:begin sdia<=Data[9];count<=count+1; end
			 50:begin SCL<=1;count<=count+1; end
			 51:begin SCL<=0;count<=count+1; end
			 52:begin sdia<=Data[8];count<=count+1; end
			 53:begin SCL<=1;count<=count+1; end
			 54:begin SCL<=0;count<=count+1;isOut<=0; end //高阻
			 55:begin SCL<=1;count<=count+1; end
			 56:begin ack2<=!SDA;count<=count+1; end    //读ack
             57:begin SCL<=0;count<=count+1;isOut<=1; end	 
			 58:begin sdia<=Data[7];count<=count+1; end
			 59:begin SCL<=1;  count<=count+1; end               //SCL上升沿传数据
		     60:begin SCL<=0;count<=count+1; end
			 61:begin sdia<=Data[6];count<=count+1; end
             62:begin SCL<=1;count<=count+1; end
			 63:begin SCL<=0;count<=count+1; end
			 64:begin sdia<=Data[5];count<=count+1; end
			 65:begin SCL<=1;count<=count+1; end
			 66:begin SCL<=0;count<=count+1; end
			 67:begin sdia<=Data[4];count<=count+1; end
			 68:begin SCL<=1;count<=count+1; end
			 69:begin SCL<=0;count<=count+1; end
			 70:begin sdia<=Data[3];count<=count+1; end
			 71:begin SCL<=1;count<=count+1; end
			 72:begin SCL<=0;count<=count+1; end
			 73:begin sdia<=Data[2];count<=count+1; end
			 74:begin SCL<=1;count<=count+1; end
			 75:begin SCL<=0;count<=count+1; end
			 76:begin sdia<=Data[1];count<=count+1; end
			 77:begin SCL<=1;count<=count+1; end
			 78:begin SCL<=0;count<=count+1; end
			 79:begin sdia<=Data[0];count<=count+1; end
			 80:begin SCL<=1;count<=count+1; end
			 81:begin SCL<=0;count<=count+1;isOut<=0; end  //高阻
		     82:begin SCL<=1;count<=count+1; end
			 83:begin ack3<=!SDA;  count<=count+1; end  //读ack
             84:begin SCL<=0;	count<=count+1;isOut<=1; end			 
			 85:begin sdia<=0;count<=count+1; end
			 86:begin SCL<=1;count<=count+1; end
			 87:begin sdia<=1;     count<=count+1; end //停止,释放总线
			 88:begin RDY<=1; end
		    default:begin RDY<=0;SCL<=1;sdia<=1; end   //释放总线,待机
	      endcase
  end
endmodule

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
引用\[1\]中的代码片段是一个Verilog代码,用于实现IIC总线的读写操作。其中,通过assign语句将iic_sda赋值为sda_dir ? sda_r : 1’bz,用于控制SDA数据线的输入输出方向。另外,通过always块中的逻辑判断,根据iic_wr_en_pos和iic_ack的状态来确定iicwr_req的值,用于表示IIC写操作的请求。类似地,通过另一个always块来确定iicrd_req的值,用于表示IIC读操作的请求。最后,通过assign语句将iic_busy赋值为iicwr_req || iicrd_req,用于表示IIC总线的忙闲状态。 引用\[2\]中的代码片段是一个Verilog模块,用于处理双向IO口。其中,通过assign语句将IO_data赋值为Control ? R_data_out : 1'bz,用于控制双向IO口的输入输出方向。同时,通过assign语句将I_data_in赋值为IO_data,用于获取双向IO口的输入数据。 引用\[3\]中的代码片段是一个顶层模块,用于实例化之前提到的iic_send模块,并连接到IIC总线的时钟线和数据线上。其中,通过wire声明了一个W_done_flag信号,用于表示IIC操作的完成状态。通过实例化iic_send模块,并将相应的输入输出信号连接到顶层模块的输入输出端口上。 综上所述,这些代码片段是用Verilog语言实现IIC总线的读写操作的。 #### 引用[.reference_title] - *1* [verilog 实现 IIC](https://blog.csdn.net/u010942671/article/details/69666139)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [【IIC】IIC总线原理与Verilog实现](https://blog.csdn.net/m0_52840978/article/details/122398039)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [IIC总线的原理与Verilog实现](https://blog.csdn.net/qq_38695100/article/details/119153048)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SDAU2005

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值