945|7

55

帖子

0

TA的资源

一粒金砂(中级)

【国产Tang Primer 25K测评】自写IIC驱动0.96寸OLED屏 [复制链接]

  1. 靠网上的各路例程和研究时序成功写了一个OLED屏幕驱动,但是目前除了IIC那个驱动是我自己参考着写出来的,驱动oled的其他文件还是引用了其他大佬的成果。先贴一下我参考的那些例程,想真正搞明白要好好研究时序:

    FPGA实现IIC协议(二)----IIC总线的FPGA实现(单次读写驱动) - 古月居 (guyuehome.com)

    IIC总线的原理与Verilog实现 - 接口/总线/驱动 - 电子发烧友网 (elecfans.com)(这是我目前写的驱动主要借鉴的帖子,它的时序也讲得很明白,想要搞明白时序和思路可以仔细看看)

    一文解读IIC总线的FPGA实现原理及过程 - 知乎 (zhihu.com)
    IIC驱动FPGA-----oled_verilog i2c oled-CSDN博客目前使用的代码主要源于这个博主和公众号公开的代码)

  2. 写驱动要研究好时序,IIC的虽然只有两根线SDA SCL,但是IIC有一个最麻烦的地方,每次发送数据要先发送器件地址 和 寄存器地址,然后才能发送数据。想要看明白时序,建议去找个EEPROM芯片或者其他相关的IIC驱动的芯片的规格书。

  3. IIC驱动的操作流程:对应11个状态

    1. 使能SCL时钟,同时输出

    2. 输出iic启动时序

    3. 向器件写入器件地址

    4. 写入器件地址后通过SDA线判断目前IIC驱动时写操作还是读操作,默认写操作

    5. 判断一次ACK

    6. 写入寄存器地址

    7. 再判断一次ACK

    8. 写入寄存器数据

    9. 最后判断一次ACK

    10. 输出iic停止时序

    11. 关闭时钟输出,IIC驱动进入空闲状态,同时输出IIC操作完成信号

  4. 以下是IIC驱动模块代码
     

    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer: 
    // 
    // Create Date:    22:40:45 11/20/2017 
    // Design Name: 
    // Module Name:    I2C_Master 
    // Project Name: 
    // Target Devices: 
    // Tool versions: 
    // Description: 
                   /*
    	       I2C总线通信协议通用模块:SCL SDA
    	       开始信号:SCL高时,SDA拉低
    	       结束信号:SCL高时,SDA拉高
    	       SDA数据在SCL低电平时置位
    	       模块中实际默认开始信号与结束信号在SCL高电平中间产生
    	       SDA数据位改变在SCL低电平的中间产生
    	       SCL时钟频率为200kHz
    	       从机地址可调,模块既支持读也支持写,通过输入管脚控制
    		*/
    // 
    // Dependencies: 
    //
    // Revision: 
    // Revision 0.01 - File Created
    // Additional Comments: 
    //
    //
    
    module I2C_Master(
        //I/O
        input		I_Clk_in,
        input		I_Rst_n,
        output		O_SCL,
        inout		IO_SDA,
        
        input		I_Start,
        output		O_Done,
        input  [6:0] 	I_Slave_Addr,
        input		I_R_W_SET,
        input  [15:0]	I_R_W_Data,
        output [7:0] 	O_Data,
        output      	O_Error
     );
     
    
    		// I_Clk_in,
    		// I_Rst_n,
    		// O_SCL,
    		// IO_SDA,
    		// //control_sig
    		// I_Start,   //一次读/写操作开始信号
    		// O_Done,    //一次读/写操作结束信号
    
    		// I_R_W_SET, //读写控制信号,写为1,读为0
    		// I_Slave_Addr,//从机地址
    		// I_R_W_Data,//读写控制字16位I_R_W_Data[15:8]->reg_addr,I_R_W_Data[7:0]->W_data,读状态则可默认为7'b0
            //     	O_Data,    //读到的数据,当O_Done拉高时数据有效
    		// O_Error	  //检测传输错误信号,当出现从机未响应,从机不能接收数据等情况时,拉高电平		
    parameter   CLK_FREQ   = 32'd50_000_000         ; //模块输入的时钟频率
    parameter   I2C_FREQ   = 24'd100_000            ;  //IIC_SCL的时钟频率
    parameter clk_divide  = CLK_FREQ/I2C_FREQ       ;//模块驱动时钟的分频系数500,50M/100k=500,分频250为scl
    
    parameter clk_divide1 = (clk_divide >> 1'b1)    ;//模块驱动时钟的分频系数500/2=250,产生scl时钟线 249
    
    parameter clk_divide2 = (clk_divide1 >> 1'b1)-1 ;//模块驱动时钟的分频系数250/2=125-1,scl低电平中间位 124
    
    parameter clk_divide3 = (clk_divide2 >> 1'b1)   ;//模块驱动时钟的分频系数124/2=62,scl的4倍频时钟 4
    
    parameter clk_divide4 = (clk_divide1+clk_divide2)+1;//用来产生IIC总线SCL高电平最中间的标志位 375
    
    reg    [8:0]  SCL_cnt   ; //分频时钟计数
    wire    scl_h_mid;
    wire    scl_l_mid;
    wire    slc_h2l;//产生时钟下降沿
    wire    scl__o;
    // reg    [8:0]	SCL_cnt;
    reg         	SCL_En;
    
    assign scl_h_mid = (SCL_cnt==clk_divide2)? 1'b1:1'b0;//如果等于125,高电平中间
    assign scl_l_mid = (SCL_cnt==clk_divide4)? 1'b1:1'b0;//如果等于375,低电平中间
    assign scl__o    = (SCL_cnt<=clk_divide1)? 1'b1:1'b0;//如果cnt2 小于249,那么高电平,大于250,低电平
    assign scl_h2l   = (SCL_cnt==clk_divide1)?1'b1:1'b0;//如果等于250,高向低
    
    always @(posedge I_Clk_in or negedge I_Rst_n) begin
        if(!I_Rst_n) begin
            SCL_cnt <= 9'd0;
        end
        else if(SCL_En)begin
            if(SCL_cnt ==(clk_divide)-1) begin//cnt2==39,实际已经数到40
            SCL_cnt <= 9'd0;
        end
            else
            SCL_cnt <= SCL_cnt + 1'b1;
        end
        else    SCL_cnt<=9'd0;
    end
    
    assign          O_SCL=scl__o;
     
     /******SDA读写控制模块******/
     reg [5:0]    C_State;
     reg          SDA_IO_DIR;//SDA双向选择I/O口 1为输出,0为输入
     reg          SDA_reg;      //SDA的输出端口
     reg          O_Done;       //结束信号
     reg [7:0]    O_Data;       //读到的数据
     reg          O_Error;		//传输错误指示信号
    // 
    ///****状态定义*****/
     parameter    Start=6'd0;  //开始
     parameter    iic_address=6'd1;//写入oled地址 这里只有7位
     parameter    w_or_r=6'd2;//写入读写位
     parameter    Ack_1=6'd3;  //第一次ack
     parameter    reg_addr=6'd4;//写入寄存器地址值
     parameter    Ack_2=6'd5;    //Ack_2
     parameter    data_addr=6'd6; //写入寄存器数据
     parameter    Ack_3=6'd7;    //Ack_3
     parameter    Stop=6'd8;    //停止发送数据,此刻时钟不停
     parameter    Idle=6'd9;    //进入空闲状态,停止时钟
     parameter    ReStart=6'd10;//准备重新开始
     reg[5:0]    sda_cnt;
    
    always @ (posedge I_Clk_in or negedge I_Rst_n)begin
         if(~I_Rst_n)begin
            sda_cnt<=6'd0;
            SDA_IO_DIR<=1'b1;//默认设置为输出管脚
     		SDA_reg<=1'b1;      //SDA输出默认拉高
     		O_Error<=1'b0;
            SCL_En<=1'b0;
            O_Done<=1'b0;
            C_State<=Start;
         end
         else  if(I_Start)
             begin
                 case (C_State)
                     Start: begin
                         O_Error<=1'b0; 
                         SCL_En<=1'b1;
                         if(scl_h_mid)
                             begin
                                 SDA_reg<=1'b0;
                                 sda_cnt<=6'd1;
                                 C_State<=iic_address;
                             end
                         else
                             begin
                                 SDA_reg<=1'b1;
                                 C_State<=C_State;
                             end
                     end
                     iic_address : begin 
                         if(scl_l_mid)
                             begin
                                if(sda_cnt==6'd8)begin
                                 sda_cnt<=6'd0;
                                 C_State<=w_or_r;
                                    end
                                else if(scl_l_mid)
                                begin
                                 SDA_reg=I_Slave_Addr[6'd7-sda_cnt];
                                 sda_cnt<=sda_cnt+1'd1;
                                end
                             end
                         else
                             begin
                                 C_State<=C_State;
                             end
                     end
                     w_or_r : begin 
                         if(scl_l_mid)
                             begin
                                 SDA_reg<=1'b0;
                                 C_State<=Ack_1;
                             end
                         else
                             begin
                                 C_State<=C_State;
                             end
                     end
                     Ack_1 : begin 
                             SDA_IO_DIR<=1'b0;
                         if(scl_h_mid && sda_cnt==6'd0 )
                             begin
                                 O_Error<=IO_SDA;
                                 sda_cnt<=6'd1;
                             end
                         else if(O_Error==1'd0 && scl_h2l)
                             begin
                                 SDA_IO_DIR<=1'b1;
                                 SDA_reg<=1'b0;
                                 sda_cnt<=6'd0;
    
                                 C_State<= reg_addr;
                             end 
                         else if(O_Error==1'd1 && scl_h2l)
                             begin
                                 SDA_IO_DIR<=1'b1;
                                 SDA_reg<=1'b0;
                                 sda_cnt<=6'd0;
    
                                 C_State<= Stop;
                             end 
                         else         
                             begin
                                 C_State<=C_State;
                             end             
                     end
                     reg_addr : begin 
                         if(scl_l_mid)
                             begin
                                if(sda_cnt==6'd8)begin
                                 sda_cnt<=6'd0;
                                 C_State<=Ack_2;
                                end
                                 else if(scl_l_mid)
                                begin
                                 SDA_reg=I_R_W_Data[6'd15-sda_cnt];
                                 sda_cnt<=sda_cnt+1'd1;
                                end
                             end
    
                         else
                             begin
                                 C_State<=C_State;
                             end
                     end
                     Ack_2 : begin 
                             SDA_IO_DIR<=1'b0;
                         if(scl_h_mid && sda_cnt==6'd0 )
                             begin
                                 O_Error<=IO_SDA;
                                 sda_cnt<=6'd1;
                             end
                         else if(O_Error==1'd0 && scl_h2l)
                             begin
                                 SDA_IO_DIR<=1'b1;
                                 SDA_reg<=1'b0;
                                 sda_cnt<=6'd0;
    
                                 C_State<= data_addr;
                             end 
                         else if(O_Error==1'd1 && scl_h2l)
                             begin
                                 SDA_IO_DIR<=1'b1;
                                 SDA_reg<=1'b0;
                                 sda_cnt<=6'd0;
    
                                 C_State<= Stop;
                             end 
                         else         
                             begin
                                 C_State<=C_State;
                             end      
                     end
                     data_addr : begin 
                         if(scl_l_mid)
                             begin
                                if(sda_cnt==6'd8)begin
                                 sda_cnt<=6'd0;
                                 C_State<=Ack_3;
                                end
                                 else if(scl_l_mid)
                                begin
                                 SDA_reg=I_R_W_Data[6'd7-sda_cnt];
                                 sda_cnt<=sda_cnt+1'd1;
                                end
                             end
                         else
                             begin
                                 C_State<=C_State;
                             end
                     end
                     Ack_3 : begin 
                             SDA_IO_DIR<=1'b0;
                         if(scl_h_mid && sda_cnt==6'd0 )
                             begin
                                 O_Error<=IO_SDA;
                                 sda_cnt<=6'd1;
                             end
                         else if(O_Error==1'd0 && scl_h2l)
                             begin
                                 SDA_IO_DIR<=1'b1;
                                 SDA_reg<=1'b0;
                                 sda_cnt<=6'd0;
    
                                 C_State<= Stop;
                             end 
                         else if(O_Error==1'd1 && scl_h2l)
                             begin
                                 SDA_IO_DIR<=1'b1;
                                 SDA_reg<=1'b0;
                                 sda_cnt<=6'd0;
    
                                 C_State<= Stop;
                             end 
                         else         
                             begin
                                 C_State<=C_State;
                             end       
                     end
                     Stop : begin 
                         if(scl_h_mid)
                             begin
                                 SDA_reg<=1'b1;
                                 C_State<=Idle;
                             end
                         else         
                             begin
                                 C_State<=C_State;
                             end   
                     end
                     Idle : begin 
                                SCL_En<=1'b0;
                                O_Done<=1'b1;//拉高Done信号
                                C_State<=ReStart;
    
                     end
                     ReStart : begin 
                            O_Done<=1'b0;
                            SDA_IO_DIR<=1'b1;
                            sda_cnt<=6'd0;
                            SDA_reg<=1'b1;      //SDA输出默认拉高
                            O_Error<=1'b0;
                            SCL_En<=1'b0;
                            C_State<=Start;
                     end
                     default: begin
                            O_Done<=1'b0;
                            SDA_IO_DIR<=1'b1;
                            sda_cnt<=6'd0;
                            SDA_reg<=1'b1;      //SDA输出默认拉高
                            O_Error<=1'b0;
                            SCL_En<=1'b0;
                            C_State<=Start;
    
                     end
                  endcase
             end
         else  begin   
                            O_Done<=1'b0;
                            SDA_IO_DIR<=1'b1;
                            sda_cnt<=6'd0;
                            SDA_reg<=1'b1;      //SDA输出默认拉高
                            O_Error<=1'b0;
                            SCL_En<=1'b0;
                            C_State<=Start;
         end
     end
    
     assign  IO_SDA=(SDA_IO_DIR)?SDA_reg:1'bz;
    
    
    endmodule
    


     

最新回复

感觉像只是做了i2c驱动,能点屏效果可以展示下。FPGA做像i2c驱动这些底层的都得自己写,还是要些verilog方面扎实的基础   详情 回复 发表于 2024-1-16 22:42

回复
举报

1115

帖子

17

TA的资源

纯净的硅(高级)

I2C官方IDE软件中有直接能用的IP么?

点评

有的,但我没用  详情 回复 发表于 2024-1-8 15:52

回复

88

帖子

0

TA的资源

一粒金砂(中级)

我也希望我今年能有机会学习一下FPGA知识


回复

55

帖子

0

TA的资源

一粒金砂(中级)

fxyc87 发表于 2024-1-8 08:52 I2C官方IDE软件中有直接能用的IP么?

有的,但我没用


回复

5637

帖子

6

TA的资源

版主

FPGA自写IIC如何保证速率?FPGA的速度应该很快

点评

SCL是IIC的时钟,速率由SCL决定,SCL由系统时钟分频产生,一般100K,普通最大400KHz,SPI的话可以达到1M  详情 回复 发表于 2024-1-9 10:21
个人签名

在爱好的道路上不断前进,在生活的迷雾中播撒光引


回复

55

帖子

0

TA的资源

一粒金砂(中级)

秦天qintian0303 发表于 2024-1-8 17:26 FPGA自写IIC如何保证速率?FPGA的速度应该很快

SCL是IIC的时钟,速率由SCL决定,SCL由系统时钟分频产生,一般100K,普通最大400KHz,SPI的话可以达到1M


回复

434

帖子

0

TA的资源

纯净的硅(初级)

感觉像只是做了i2c驱动,能点屏效果可以展示下。FPGA做像i2c驱动这些底层的都得自己写,还是要些verilog方面扎实的基础

点评

是的,只写了驱动,整套我用了网上开源的一个例程,所以没贴图片    详情 回复 发表于 2024-1-21 21:47

回复

55

帖子

0

TA的资源

一粒金砂(中级)

dirty 发表于 2024-1-16 22:42 感觉像只是做了i2c驱动,能点屏效果可以展示下。FPGA做像i2c驱动这些底层的都得自己写,还是要些verilog方 ...

是的,只写了驱动,整套我用了网上开源的一个例程,所以没贴图片

 


回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
推荐帖子
都是可调电阻(电位器)惹的祸,矩阵键盘调试手记

一个经验,一个教训。大家来看看。 这几天调试AVR单片机电路板,发现一个奇怪的现象:一大半的板子上的矩阵键盘都不能正确识别 ...

LPCXpresso LPC1114 SSP读写SD卡成功分享

通过多次,多天的不断努力终于把SSP总线方式下读写SD卡搞点,和大家一起分享一下。自带的程序部分不在叙述,只说明一下自己编写 ...

DIY五路独立输出可调电源

经常用到各种用电设备的你,是不是总是为各种特殊规格的输出电源而头疼?今天DIYer就教你制作一款整合电源,不仅可以同时输出五 ...

留住身边的美景,2015年夏天和秋天

本帖最后由 mmmllb 于 2015-11-12 13:23 编辑 时间过得很快呀,不久前穿着半袖吃冰棍,现在就过着羽绒服吃火锅了。 2015年的 ...

[CB5654智能语音开发板测评] 开发环境构建

要对开发板进行测评和开发,其开发环境的构建是不可或缺的,CDK集成开发软件是一款云端一体的IDE工具,它包括了组件下载、代码编 ...

最后一天!NI CompactDAQ​系统、LabVIEW、数据采集,欢迎联系!50元京东卡等您拿

589146 NI CompactDAQ​系统、LabVIEW的应用助力工程师解决自动测试难题 >>查看方案 CompactDAQ​系统 ...

全球首款RISC-V平板电脑PineTab-V发布,200多刀

平板电脑PineTab-V是200多刀的价格,是不是都想入手了?别冲动 https://pine64.com/product-category/pinetab/ 6905 ...

【极海APM32F407 Tiny Board】移植LWIP协议栈

这篇来学习下LWIP协议栈的移植,实现ping通网络。官网SDK包里面是lwip-1.4.1版本,下面移植最新版本到开发板。 一、下载 ...

【NUCLEO H533RE】ECC384 PKA模块测试

完整的工程间附件,替换\STM32Cube_FW_H5_V1.2.0\Projects\NUCLEO-H533RE\Applications\ThreadX\Tx_Thread_Creation 823601 ...

测评汇总:《大语言模型:原理与工程实践》

活动详情:【《大语言模型:原理与工程实践》】更新至 2024-09-12测评报告汇总:@rtyu789 《大语言模型:原理与工程实践》-预训 ...

关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表