[学习资料]

PIC单片机之I2C通信(从模式)

[复制链接]
518|10
手机看帖
扫描二维码
随时随地手机跟帖
kxsi|  楼主 | 2019-7-25 11:12 | 显示全部楼层 |阅读模式

网上有许多讲解单片机 实现I2C主模式,但是从模式的很少。我现在就来讲讲PIC单片机使用MSSP模块实现I2C从模式。

    有关I2C协议的具体介绍可以看 《PIC单片机之I2C(主模式)》,我们这里直接讲解实例

    实例讲解:我们模仿 AT24C02 EEPROM 的协议。让一个主模式的单片机,来读取从模式单片机的数据。

      下面为AT24C02的随机地址读取的协议。

           第一个字节 :输入7位地址和一位的写状态位,

           第二个字节:然后写入EEPROM数据地址,

           第三个字节:输入7位地址和一位的读状态位,

           第四~N个字节:读出的EEPROM的数据。

      20130520144633284.jpg


使用特权

评论回复
kxsi|  楼主 | 2019-7-25 11:12 | 显示全部楼层

我们来讲解下程序的基本思路:我们使能了MSSP中断,即是I2C接收中断,当PIC单片机接收到一个数据后就会产生中断。那是接收到设备地址,还是接收到数据,由SSP1STAT寄存器的状态位来判断。

需要判断的状态位分别是 :

       数据和地址:  用来判断接收到是地址还是数据

       启动位:         用来判断是否接收到启动位

       读写:             用来判断是写状态还是读状态。

       缓存满:        用来判断缓冲区是否满

    我们以随机地址读取为例:讲讲程序执行的过程

     1,从单片机接收到启示位和设备地址中断:我们判断SSP1STAT的状态位为(写状态,地址,缓存满,接收到启示位) 然后读取缓存中的设备地址, 接着在读取 需要读/写的数据地址。

     2,单片机再次接收到设备地址:我们判断是SSP1STAT的状态为(读状态)然后从设备就输出数据

20130520154554325.jpg


使用特权

评论回复
kxsi|  楼主 | 2019-7-25 11:13 | 显示全部楼层
我们以写字节数据为例:

1,从单片机接收到启示位和设备地址中断:我们判断SSP1STAT的状态位为(写状态,地址,缓存满,接收到启示位) 然后读取缓存中的设备地址, 接着在读取 需要读/写的数据地址。

  2,单片机判断SSP1STAT的状态位为(写状态,数据,缓存满)那么单片机就接收输入的数据。

     初始化设置:

     1,设置I2C通信的两引脚为CLK SCL为输入,

          TRISB6 = input;
          TRISB4 = input;

     2,将MSSP设置为I2C从模式,七位从地址

           SSP1CONbits.SSPM0 = 0;
           SSP1CONbits.SSPM1 = 1;
           SSP1CONbits.SSPM2 = 1;
           SSP1CONbits.SSPM3 = 0;// I2C slave mode ,7bit address

      3,使能CLK时钟

        SSP1CONbits.CKP = 1; // enable clock

      4,设置从设备地址为 0xA0

      SSP1ADD =0xA0;       //slave address is 0xa0

      5,开启I2C

      SSP1CONbits.SSPEN=1;//enable I2c

      6,清楚状态标志
      SSPSTAT=0;
     7,使能I2C中断
     PIE1bits.SSP1IE = 1;//Enabe interrupt MSSP
     INTCONbits.PEIE = 1;
     INTCONbits.GIE =  1;

使用特权

评论回复
kxsi|  楼主 | 2019-7-25 11:13 | 显示全部楼层
  如果你要使用PIC单片机I2C从模式只要使用下面的代码:

  将void i2c_salve_interrupt_tx();void i2c_salve_interrupt_rx();放到中断程序中,如下:

void interrupt isr(void)
{
      if(SSP1IE && SSP1IF)
   {
        i2c_salve_interrupt_tx();
        i2c_salve_interrupt_rx();
        SSP1IF=0;
   }

}

使用特权

评论回复
kxsi|  楼主 | 2019-7-25 11:13 | 显示全部楼层
将初始化函数init_i2c_slave();放到主函数中

void main()

{

  init_i2c_slave();

}

使用特权

评论回复
kxsi|  楼主 | 2019-7-25 11:24 | 显示全部楼层
头文件 :i2c_salve.h

#ifndef _I2C_SALVE_H
#define _I2C_SALVE_H
void init_i2c_slave();
void i2c_salve_interrupt_tx();
void i2c_salve_interrupt_rx();
#endif

使用特权

评论回复
kxsi|  楼主 | 2019-7-25 11:24 | 显示全部楼层
代码:i2c_salve.c

   #include<pic.h>;
#define input  1

#define  RX_BUF_LEN  29

#define  while_delay 6000

unsigned char i2c_address,word_address,Register[29];
unsigned  char RANDOM_READ,i2c_counter;
extern unsigned char A_readflag;
/*I2C SALVE */
void init_i2c_slave()
{
    TRISB6 = input;
    TRISB4 = input;
    SSP1CONbits.SSPM0 = 0;
    SSP1CONbits.SSPM1 = 1;
    SSP1CONbits.SSPM2 = 1;
    SSP1CONbits.SSPM3 = 0;// I2C slave mode ,7bit address
    SSP1CONbits.CKP = 1; // enable clock
    SSP1ADD =0xA0;       //slave address is 0xa0

    SSP1CONbits.SSPEN=1;//enable I2c
    SSPSTAT=0;


    PIE1bits.SSP1IE = 1;//Enabe interrupt MSSP
    INTCONbits.PEIE = 1;
    INTCONbits.GIE =  1;


}

使用特权

评论回复
kxsi|  楼主 | 2019-7-25 11:24 | 显示全部楼层
/*I2C salve mode interrupt */
void i2c_salve_interrupt_tx()//master read
{
    unsigned char Temp;
    unsigned int  timercounter;


    Temp=SSP1STAT;
    Temp &= 0x2D;
    if(SSP1STATbits.R_nW ==1)//Read operation.
    {
                A_readflag=0;
              SSP1IF = 0;
              i2c_address =  SSP1BUF;
              i2c_counter = word_address;
             while(i2c_counter < RX_BUF_LEN)
             {
               SSP1BUF=Register[i2c_counter];//send data
               SSP1CONbits.CKP=1;// enable colck
               timercounter=while_delay;
             while(PIR1bits.SSP1IF == 0)
               {
                 timercounter--;
                 if(timercounter==0)
                 {
                     return;
                 }
                }//waiting for ~ACK
               SSP1IF = 0;
             if(SSP1CON2bits.ACKSTAT == 1)
              {
                return ; //NOACK
               }
               else
              {
              i2c_counter++;//ACK
            
               }
             }
            SSP1IF = 0;
    }
}

使用特权

评论回复
kxsi|  楼主 | 2019-7-25 11:25 | 显示全部楼层
void i2c_salve_interrupt_rx()//master writer
{
    unsigned char rx_status;
    unsigned char Temp;
    unsigned int  timercounter;
    rx_status=false;
    Temp=SSP1STAT;
    Temp &= 0x2D;
    if(Temp==0x09)//Write operation,last byte was an address,buffer is full
    {


         SSP1IF = 0;
         i2c_address =  SSP1BUF;
         timercounter=while_delay;
          while(PIR1bits.SSP1IF == 0)
          {
              timercounter--;
              if(timercounter==0)
               {
                     return ;
               }


          }//waiting for send ~ACK
          SSP1IF = 0;
          word_address = SSP1BUF;
          return ;
    }
    if(Temp==0x29)//Write operation,last byte was data,buffer is full
    {
        
        SSP1IF=0;
        Register[word_address]=SSP1BUF;
        word_address++;
        if(word_address>=RX_BUF_LEN)
            {
                word_address=0;
            }
    }


}

使用特权

评论回复
mjs0528| | 2019-10-6 20:00 | 显示全部楼层
感谢分享,感谢楼主!!!

使用特权

评论回复
wahahaheihei| | 2019-10-7 07:47 | 显示全部楼层
这个I2C我的薄弱环节。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

44

主题

3304

帖子

2

粉丝