本帖最后由 yu木风 于 2017-5-10 11:31 编辑
PIC单片机之I2C(从模式) 网上有许多讲解单片机 实现I2C主模式,但是从模式的很少。我现在就来讲讲PIC单片机使用MSSP模块实现I2C从模式。 有关I2C协议的具体介绍可以看 《PIC单片机之I2C(主模式)》,我们这里直接讲解实例 实例讲解:我们模仿 AT24C02 EEPROM 的协议。让一个主模式的单片机,来读取从模式单片机的数据。 下面为AT24C02的随机地址读取的协议。 第一个字节 :输入7位地址和一位的写状态位, 第二个字节:然后写入EEPROM数据地址, 第三个字节:输入7位地址和一位的读状态位, 第四~N个字节:读出的EEPROM的数据。 file:///C:\Users\admin\AppData\Local\Temp\ksohtml\wps7946.tmp.png 我们来讲解下程序的基本思路:我们使能了MSSP中断,即是I2C接收中断,当PIC单片机接收到一个数据后就会产生中断。那是接收到设备地址,还是接收到数据,由SSP1STAT寄存器的状态位来判断。 需要判断的状态位分别是 : 数据和地址: 用来判断接收到是地址还是数据 启动位: 用来判断是否接收到启动位 读写: 用来判断是写状态还是读状态。 缓存满: 用来判断缓冲区是否满 我们以随机地址读取为例:讲讲程序执行的过程 1,从单片机接收到启示位和设备地址中断:我们判断SSP1STAT的状态位为(写状态,地址,缓存满,接收到启示位) 然后读取缓存中的设备地址, 接着在读取 需要读/写的数据地址。 2,单片机再次接收到设备地址:我们判断是SSP1STAT的状态为(读状态)然后从设备就输出数 file:///C:\Users\admin\AppData\Local\Temp\ksohtml\wps7947.tmp.png 我们以写字节数据为例: 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; 如果你要使用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; } } 将初始化函数init_i2c_slave();放到主函数中 void main() { init_i2c_slave(); } 头文件 :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 代码:i2c_salve.c #include; #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; } /*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; } } 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; } } }
|