• 已解决 73482 个问题
  • 已帮助 5993 位优秀工程师

怎么用51单片机写一个I2C从机的代码

zzgege 2021-02-03 浏览量:1809
网上的I2C都是主机多然而从机的代码很少而且用软件模拟的更少希望有人发一些有用的资料学习一下
0 0 收起

我来回答

上传资料:
选择文件 文件大小不超过15M(格式支持:doc、ppt、xls、pdf、zip、rar、txt)
最佳答案
  • /@@*************        用户系统配置        **************/
    
    #define MAIN_Fosc                12000000L        //定义主时钟 模拟串口和红外接收会自动适应。5~36MHZ
    
    #define D_TIMER0                125                        //选择定时器时间 us 红外接收要求在60us~250us之间
    
    #define        User_code                0xFD02                //定义红外接收用户码
    #define DECVICE_ADD 0x55<<1
    
    /@@*************        以下宏定义用户请勿修改        **************/
    #include        "reg51.H"
    #include        "string.H"
    
    #define        uchar        unsigned char
    #define uint        unsigned int
    
    //#define freq_base                        (MAIN_Fosc / 1200)
    //#define Timer0_Reload                (65536 - (D_TIMER0 * freq_base / 10000))
    
    
    
    
    /@@*************        本地常量声明        **************/
    
    
    
    /@@*************        本地变量声明        **************/
    sbit        SDA = P3^0;                //SDA
    sbit        SCL = P3^1;                //SCL
    
    bit                PreState;                
    bit                NowState;
    bit     START_flag;
    bit     STOP_flag;                
    //uchar        IR_SampleCnt;                //采样计数
    uchar NN;
    uchar DEVICE_ADR;        //器件地址
    uchar WORD_ADR;                //内存地址
    uchar REC_DATA;                //接收到的内容
    unsigned int  *send_ptr;//
    code unsigned AA0[9] = {0x700x2c0x6e0x2a0x000x000x000xc0};  //
    code unsigned AA8[2] = {0xff0x2c};  //
    //code unsigned AA0[9] = {0x700x2c0x6e0x2a0x000x000x000xc0};  //
    code unsigned A00[33] = {0xed0xcf0x480xe60x890xe80x0c0xe8
                                                 0x970xaf0xd20x400x6d0xc20xc40x04
                                                     0xca0xd50xea0xa70xae0x430x370xe2
                                                     0x120x050x3b0x590xb70xfb0x3d0xa5
                                                     };  //
    code unsigned A20[33] = {0xCB0x850x8b0x540x6d0x970x3c0x05
                             0x460x0f0x450xb40xd10x0a0xb80x9b
                                                     0xdf0xa20x430x8d0xe60x7a0x6f0xf8
                                                     0x7f0xaa0x520x370xa60x8f0xdc0x71
                            
                                                    };  //
    code unsigned A40[33] = {
                             0x150x670xc50x820xfe0x010x870x44
                                                     0x100xcd0xf80xe20xd70xb10x7d0xb6
                                                     0x530xc00x430x390x3a0xb30x320xba
                                                     0x5d0x840xf10x030x130xcd0x740x28
    
                                                    };  //
    code unsigned A60[33] = {
                             0x340x390xab0x320x3b0x320x760x4b
                                                     0xff0x6f0x2d0xd60x200xab0x8c0x73
                                                     0x6b0xd00xad0xa60x830xdf0x600xe0
                                                     0x630x510xe40x760x1e0x580x9f0x52
                                                    
                                                    };  //
    
    
    uchar temp;
    
    
    
    
    
    
    /@@*************  外部函数和变量声明 *****************/
    void init(void);//初如化函数
    void delay_24C02(void); // 延时 5us
    void ACK(void);//应答
    void WaitACK(void);//等待主机回复
    
    /@@********************* 主函数 *************************/
    void main(void)
    {
      uchar ggMMicount;
      init();//初如化
    
      while(1)
       {
             NN=50; 
             PreState = SDA;         
             while(SCL && NN--) 
              {
                    NowState = SDA; 
                    if(PreState && !NowState) //启动
                     { 
                        START_flag = 1; 
                        //_DINT(); 
                     } 
                    if(!PreState && NowState) //结束
                     { 
                    STOP_flag = 1; 
                        //_EINT(); 
                     }
                    PreState = NowState; 
    
                    if(START_flag) //如果是启动的话
                     { 
                            START_flag=0; 
                            while(SCL); //START时的SCL高电平状态就等待 
                            for(gg=8;gg>0;gg--) //接收器件地址 
                         { 
                                    while(!SCL); //SCL低电平状态就等待 
                                    
                                    DEVICE_ADR<<=1; 
                                    if(SDA) //数据的第一个CLK高电平来临 
                                    DEVICE_ADR |= 0x01; 
                                    
                                    while(SCL); //SCL高电平状态就等待 
                             }
     
                            ACK(); //对设备地址ACK应答信 
                    //-----------以上收到了设备地址,并知晓主机要对从机进行读还是写操作--- 
                        for(gg=8;gg>0;gg--) //接收内存单元地址 
                             { 
                               while(!SCL); 
                                    
                                    WORD_ADR<<=1; 
                                    if(SDA) 
                                    WORD_ADR |= 0x01; 
                                    
                                    while(SCL); 
                             } 
                            //-----------以上就已经接收到内存单元地址------------ 
                            ACK(); //对内存单元ACK应答信号 
    
                            //--------------------- 以上就已经接收到内存单元地址 -------------------------
                            delay_24C02();//5us
                            delay_24C02();
                            delay_24C02();//5us
                            delay_24C02();
                            MM=50;
                            PreState = SDA;
                            while(SCL && MM--)
                             {
                                    NowState = SDA;
                                    if(PreState && !NowState) // 如果 SDA 下降沿且 SCL 为高 , 则示开始信号
                                    {
                                       START_flag = 1;
                                    }
                             }
    
                            //--------------------START_Flag=1: 表示随即读数据 -------------------------
                            if(START_flag==1)
                            {
                                    //--------------------------- 读取器件地址 ---------------------------------
                                    //while(!SCL);
                                    START_flag=0; // 清除标志位
                                    while(SCL); //START 时的 SCL 高电平状态就等待
                                    for(gg=8;gg>0;gg--) // 接收器件地址
                                    {
                                            while(!SCL); //SCL 低电平状态就等待
                                             DEVICE_ADR<<=1; // 高位在前
                                            if(SDA) // 数据的第一个 CLK 高电平来临
                                             DEVICE_ADR |= 0x01; // 上升沿读取数据位
                                            while(SCL); //SCL 高电平状态就等待
                                    }
                                    ACK(); // 对设备地址 ACK 应答信
    
            
                      
    
                                    //------------------ 判断地址是否为 0xa1 ,表示读数据指令 -------------------
                                    if(DEVICE_ADR == 0xab)           //( DECVICE_ADD+1) 
                                    {
                            
                                            switch(WORD_ADR)
                                         {
                                                    case 0xa0:  count=8; send_ptr =AA0;        //                  memcpy(tempAA0count);
                                                  break;
                                                case 0xa8:  count=1; send_ptr =AA8;        //            memcpy(tempAA8count);
                                                             break;
                                                    case 0x00:  count=32; send_ptr =A00; //  memcpy(tempA00count);
                                                             break;
                                                    case 0x20:  count=32; send_ptr =A20;//  memcpy(tempA20count);
                                                             break;
                                                    case 0x40:  count=32; send_ptr =A40; //                  memcpy(tempA40count);
                                                             break;
                                                    case 0x60:  count=32; send_ptr =A60;//        /          memcpy(tempA60count);
                                                             break;
                                                     
                                             }
                                                                                    
                                            for(i=0;i<count;i++) // 要 读 多 少 字 节 , 由
                                            {
                                                //@@*send_ptr =WORD_ADR;
                                                    temp = send_ptr[i];
                                                    for(gg=8;gg>0;gg--) // 输出数据给主机
                                                    {
                                                            while(SCL);//如果为高,保持
                                                            if( temp & 0x80)
                                                             {
                                                                    SDA=1; // 输出 1
                                                             }
                                                            else
                                                             {
                                                                    SDA=0; // 输出 0
                                                             }
                                                            while(!SCL);
                                                            //while(SCL); //SCL 为 1 ,就保持 SDA 输出不变
                                                        //  SDA=1;
                                                            //@@*send_ptr <<=1;
                                                            temp <<=1;
                                                    }
                                               WaitACK();//主机回复ACK
                                               //send_ptr++; 
                                            }
    
                                        
                                    }//if(DEVICE_ADR==0xa1)
                                    else  //如果不是,退出
                                    {
                                      
                                    }
                            }//if(START_flag==1)
                            else // 如果 START_flag!=1 则表示主机对从机进行写操作
                            {        
                                if(WORD_ADR == 0xa9)
                                 {
                                            for(i=0;i<8;i++)
                                            {
                                                    for(gg=8;gg>0;gg--) // 读取主机发来的数据
                                                    {
    //                                                        while(!SCL);
    //                                                         //READ_BUF[i] <<=1;
    //                                                         //SDA=0;
    //                                                        if(SDA)
    //                                                         ;
    //                                                         //READ_BUF[i] |= 0x01;
    //                                                         //SDA=0;
    //                                                        while(SCL);
                                                       
                                                       while(!SCL); 
                                                                                                       
                                                            while(SCL);
    
                                                    }
                                                    
                                                    ACK(); // 对设备地址 ACK 应答信
                                        }                                 
                                     
                                     }
    
    
                         }
    
    
    
    
    
              }// if(START_flag)
    
    
              }//end while(SCL && NN--) 
    
    
       }//end while(1)
    }
    
    /@@********************************************************************
    函数功能:初始化函数
    入口参数:无
    返 回:无。
    备 注:无。
    ********************************************************************/
    void init(void)
    {
            SDA=1; // 初始数据引脚
            SCL=1; // 初始时钟引脚
    //        EA=1; // 开总中断
    //        IP=0x90; // 中断优先级设定,设为串口 >INTO>T0>INT1>T1
    //        EX0=1; // 开外部中断 0
    }
    
    /@@********************************************************************
    函数功能:从机应答函数
    入口参数:无
    返 回:无。
    备 注:应答函数,保证在两个时钟跳变沿 SDA 为低,则主机认为是从机应答
    ********************************************************************/
    void ACK(void)
    {
    //        while(!SCL);
    //        SDA=0; // 第 9 个 CLK 变高的情况下, SDA 输出 0
    //        while(SCL);
    //        SDA=1; // 第 9 个 CLK 变低的情况下, SDA 输出 1
    
            while(SCL); //低就阻塞
            delay_24C02();
            SDA=0; // 第 9 个 CLK 变高的情况下, SDA 输出 0
            while(!SCL); //低就阻塞
            while(SCL);         //高就阻塞
            delay_24C02();
            SDA=1; // 第 9 个 CLK 变低的情况下, SDA 输出 1
    
    
    }
    
    
    /@@********************************************************************
    函数功能:主机应答函数
    入口参数:无
    返 回:无。
    备 注:应答函数,保证在两个时钟跳变沿 SDA 为低,则主机认为是从机应答
    ********************************************************************/
    void WaitACK(void)
    {
    //        while(!SCL);
    //        SDA=0; // 第 9 个 CLK 变高的情况下, SDA 输出 0
    //        while(SCL);
    //        SDA=1; // 第 9 个 CLK 变低的情况下, SDA 输出 1
    
            while(SCL); //高就阻塞
            delay_24C02();
            SDA=1; // 第 9 个 CLK 变高的情况下, SDA 输出 0
            while(!SCL); //低就阻塞
            while(SCL);         //高就阻塞
            delay_24C02();
            //SDA=1; // 第 9 个 CLK 变低的情况下, SDA 输出 1
    
    
    }
    /@@********************************************************************
    函数功能:延时 us
    入口参数:无
    返 回:无。
    备 注:函数是用来实现短暂延时,用于对接上主机时钟
    ********************************************************************/
    void delay_24C02(void) // 延时 5us
    {
    //; ; ; ; ;
     uchar i;
     i=3;
     while(i--);
    }
    
    • 发布于 2021-02-03
    • 举报
    • 评论 0
    • 0
    • 0

其他答案 数量:3
  • 5单片机实现不了,只能用支持从机硬件i2c的单片机,比如stm32,stm8,stc8系列都有
    • 发布于2021-02-03
    • 举报
    • 评论 0
    • 0
    • 0

  • 一样的,IIC的时序做好了,就成功了90%了

    从机是先接收,判断地址,主机是先发送地址,

    • 发布于2021-02-03
    • 举报
    • 评论 0
    • 0
    • 0

  • 可以用中断+状态机思维实现I2C从机时序,道理和软件UART一样的。
    • 发布于2021-02-04
    • 举报
    • 评论 0
    • 0
    • 0

相关问题

问题达人换一批

怎么用51单片机写一个I2C从机的代码