[elektro] szoftver I2C C-ben

Moczik Gabor pm_levlista at progzmaster.hu
Fri Aug 21 15:40:29 CEST 2009


Huszti Andras wrote:
> Hali!
> 
> Master vagy slave kell?

Nekem van master és slave is, de nem mondhatnám hogy egy szép és jól 
átgondolt megoldás, éppencsak működik.

Most nem tudom felrakni, beillesztem ide, kb. 10k az egész, remélem nem 
lesz sok anyázás miatta.


i2c.h
---------------------
#ifndef __I2C_H__
#define __I2C_H__

#include <pic18.h>

// I2C operation modes
#define I2C_MASTER         0x01
#define I2C_SLAVE          0x02
#define I2C_MULTIMASTER    0x03

// I2C interrupt level modes
#define I2C_LOW_PRIORITY   0x00
#define I2C_HIGH_PRIORITY  0x01

// R/W bit value
#define I2C_READ           0x01
#define I2C_WRITE          0x00

// I2C system state
#define I2CS_IDLE          0x00
#define I2CS_MASTER_TX     0x01
#define I2CS_START         0x02
#define I2CS_WAITING_DATA  0x03
#define I2CS_DATA_RECEIVED 0x04
#define I2CS_DATA_REQUEST  0x05
#define I2CS_NACK          0x06
#define I2CS_COLLISION     0x07

extern unsigned char i2c_state;
extern unsigned char i2c_databuf;

void i2c_init(unsigned char mode, unsigned char isr_priority, unsigned 
char address);
void i2c_set_master_brg(unsigned char brg);

// I2C results
#define I2CR_ACK   0x00   /* acknowledge */
#define I2CR_NAK   0x01   /* no ack */
#define I2CR_COLL  0x02   /* bus collision */

unsigned char i2c_start(unsigned char addr_rw);
unsigned char i2c_restart(unsigned char addr_rw);

unsigned char i2c_sendbyte(unsigned char addr, unsigned char data);
// ON SUCCESS: returns 1 and result placed in i2c_databuf
unsigned char i2c_readbyte(unsigned char addr, unsigned char data);

// this must be called from ISR after checking SSPIF & BCLIF bits
void i2c_isr();

// I2C data request must be fulfilled with this macro (slave transmitter 
function)
#define i2c_slave_tx(data) { SSPBUF=data; CKP=1; SSPIF=0; }
//#define i2c_slave_rx()     SSPBUF
#define i2c_slave_rx()     i2c_databuf
#define i2c_release_bus()  { CKP=1; SSPIF=0; }

#endif
-----------------------

i2c.c
-----------------------
#include "i2c.h"

#define I2C_NOT_IDLE  ((SSPCON2&0x1F)|(SSPSTAT&0x04))

// I2C settings
unsigned char i2c_mode;
unsigned char i2c_address;
unsigned char i2c_brg;

// I2C status
unsigned char i2c_state;
bit i2c_transmit;            /* value of R/W bit after the S condition 
0=write 1=read */

unsigned char i2c_databuf;

void i2c_init(unsigned char mode, unsigned char isr_priority, unsigned 
char address)
{
     i2c_mode=mode;
     i2c_address=address;
     SSPSTAT=0b10000000;
     SSPIF=0;
     BCLIF=0;
     // set interrupt priority
     SSPIP=(isr_priority==I2C_HIGH_PRIORITY ? 1 : 0);
     BCLIP=SSPIP;
     switch(i2c_mode) {
         case I2C_SLAVE       :// I2C slave mode without start/stop 
interrupts
                               SSPCON1=0b00110110;
                               // enable clock stretching
                               SSPCON2=0b00000001;
                               SSPADD=i2c_address;             // address
                               SSPIE=1;
                               break;
         case I2C_MASTER      :// master mode
                               SSPCON1=0b00111000;
                               SSPCON2=0b00000000;
                               break;
         case I2C_MULTIMASTER :// I2C slave mode with start/stop interrupts
                               SSPCON1=0b00111110;
                               // enable clock stretching
                               SSPCON2=0b00000001;
                               SSPADD=i2c_address;
                               BCLIE=1;
                               SSPIE=1;
                               break;
     }
     i2c_state=I2CS_IDLE;
     //SSPIE=1;
}

void i2c_set_master_brg(unsigned char brg)
{
     // FIXME! - if transmission in progress?
     i2c_brg=brg;
     SSPADD=brg;
}

void i2c_isr()
{
        // check status (excluding SMP and CKE bit)
         switch(SSPSTAT & 0b00111111) {
             case 0b00001000 :// START condition
                              i2c_state=I2CS_START;
                              SSPIF=0;
                              break;
             case 0b00001001 :// address match AND write
                              // read SSPBUF to clear BF (value doesn't 
matter, it's our address)
                              SSPBUF;
                              // master want to write (receive operation)
                              // the only action needed is clear 
interrupt flag and wait for the data
                              i2c_state=I2CS_WAITING_DATA;
                              SSPIF=0;
                              CKP=1;
                              break;
             case 0b00001100 :// address match AND read
                              // master want to read (transmit operation)
                              i2c_state=I2CS_DATA_REQUEST;
                              // after this isr routine exits with this 
state
                              // the user must use the i2c_slave_tx() to 
fulfill the request.
                              // SSPIF will be cleared only in the 
i2c_slave_tx() macro
                              break;
             case 0b00101001 :// data received AND write
                              i2c_state=I2CS_DATA_RECEIVED;
                              // after this, the user must read the 
data, and release the bus with
                              // i2c_release_bus() macro, otherwise the 
I2C bus will locked
                              i2c_databuf=SSPBUF;
                              SSPIF=0;
                              break;
             case 0b00101000 :// this indicates that the master sent 
NACK response to the slave transmitter
                              // which must be interpreted as the master 
doesn't wish to read further data
                              i2c_state=I2CS_NACK;
                              SSPIF=0;
                              break;
             default         :// probably STOP condition (which can 
occur in any situation)
                              i2c_state=I2CS_IDLE;
                              SSPIF=0;
         }
}

unsigned char i2c_start(unsigned char addr_rw)
{
     while(I2C_NOT_IDLE && !BCLIF);
     if (!BCLIF) {
         // initiate start condition
         SEN=1;
         while(SEN && !BCLIF);
         if (!BCLIF) {
             SSPBUF=addr_rw;
             SSPIF=0;
             while(!SSPIF && !BCLIF);
             if (!BCLIF) {
                 return ACKSTAT;
             }
         }
     }
     if (BCLIF) {
         BCLIF=0;
         return I2CR_COLL;
     }
}

unsigned char i2c_restart(unsigned char addr_rw)
{
     while(I2C_NOT_IDLE && !BCLIF);
     if (!BCLIF) {
         // initiate start condition
         RSEN=1;
         while(RSEN && !BCLIF);
         if (!BCLIF) {
             SSPBUF=addr_rw;
             SSPIF=0;
             while(!SSPIF && !BCLIF);
             if (!BCLIF) {
                 return ACKSTAT;
             }
         }
     }
     if (BCLIF) {
         BCLIF=0;
         return I2CR_COLL;
     }
}

void i2c_stop()
{
     PEN=1;
     while(!SSPIF && !BCLIF);
     SSPIF=0;
     if (BCLIF) {
         BCLIF=0;
     }
}

unsigned char i2c_write(unsigned char data)
{
     SSPBUF=data;
     SSPIF=0;
     while(!SSPIF && !BCLIF);
     if (BCLIF) {
         BCLIF=0;
         return I2CR_COLL;
     } else return ACKSTAT;
}

unsigned char i2c_read(unsigned char nack)
{
     SSPIF=0;
     RCEN=1;
     while(!SSPIF && !BCLIF);
     if (!BCLIF) {
         i2c_databuf=SSPBUF;
         SSPIF=0;
         ACKDT=nack;
         ACKEN=1;
         while(!SSPIF && !BCLIF);
         if (!BCLIF) {
             SSPIF=0;
             return I2CR_ACK;
         }
     };
     if (BCLIF) {
         BCLIF=0;
         return I2CR_COLL;
     }
}

unsigned char i2c_sendbyte(unsigned char addr, unsigned char data)
{
     unsigned char result;

     if ((result=i2c_start(addr|I2C_WRITE))==I2CR_ACK) {
         result=i2c_write(data);
     }
     SSPIF=0;
     if (result!=I2CR_COLL) i2c_stop();
     return result;
}

unsigned char i2c_readbyte(unsigned char addr, unsigned char data)
{
     unsigned char result;

     if ((result=i2c_start(addr|I2C_WRITE))==I2CR_ACK) {
         if ((result=i2c_write(data))==I2CR_ACK) {
             if ((result=i2c_restart(addr|I2C_READ))==I2CR_ACK) {
                 result=i2c_read(1);
                 SSPIF=0;
             }
         }
     }
     if (result!=I2CR_COLL) i2c_stop();
     return result;
}
---------------------



More information about the Elektro mailing list