Monday, January 19, 2015

PIC24FJ64GB004 I2C setup

I've been working on the I2C-setup on the MIDI2VC+ for a couple of days. First off I soldered the DAC and accompanying components to the board. I have been a bit worried about the I2C bus since it is connected to both 5V and 3V3 interfaces. The PIC has 3.3V logic and the DAC 5V. The I2C bus runs on 5V and the PIC I2C pins can tolerate 5.5V, so I thought that it might work since both devices compatible logic levels.
Running an I2C-bus this way is out of spec and may mean that you cannot run it full speed. Some testing proved that it was possible to run the bus at 360MHz without errors. I used the Beagle I2C analyzer from TotalPhase, and it was really good for analyzing lots of I2C data to see how much errors there were in the transfers. I ended up running the bus at 100kHz, to save power and add robustness.
Since I didn't have a digital oscilloscope previously, I adopted an old Tektronix 4030 that was being scrapped at work. This must be one of the earliest generations of DSO's, the scope looks very similar to my Tek 4065. I did get it running the way I wanted it to, but it wasn't that user friendly. You had to go into a menu blip yourself to the "Normal" trigger mode, a function that usually have it's own front panel button on modern scopes...
Tektronix 4030 capture of I2C transmission. Data on top trace, clock on bottom trace.


//I2C setup:
    I2C1CON = 0x8000; //I2C module enabled
    I2C1BRG = 0x13c; //= (32000000/100000 - 32000000/10000000) - 1, 100kHz SCL 

//Function i2cTransmission
//Polls flag to see if there is I2C data to be sent and
//walks through the transmission process
uint16_t i2cTransmission(uint16_t i2cTransmitCycle, uint16_t *i2cstatus, uint16_t i2cData[]) {  
   //Polls transmit cycle flag, I2C interrupt flag and ACK-status  
   switch (i2cTransmitCycle) {  
     case 0:  
       if (*i2cstatus == 1) {  
         i2cTransmitCycle = 1;  
         I2C1CONbits.SEN = 1; //start condition  
       }  
       break;  
     case 1:  
       if (I2C1CONbits.SEN == 0) { //when start condition is complete, load slave address  
         IFS1bits.MI2C1IF = 0;  
         I2C1TRN = (0x60 << 1);//load DAC address  
         i2cTransmitCycle++;  
       }  
       break;  
     case 2:  
       //when address is ACKed:  
       if (IFS1bits.MI2C1IF == 1 && I2C1STATbits.ACKSTAT == 0) {  
         IFS1bits.MI2C1IF = 0;  
         I2C1TRN = i2cData[0]; //load first data byte  
         i2cTransmitCycle++;  
       }  
       break;  
     case 3:  
       if (IFS1bits.MI2C1IF == 1 && I2C1STATbits.ACKSTAT == 0) {  
         IFS1bits.MI2C1IF = 0;  
         I2C1TRN = i2cData[1]; //load second data byte  
         i2cTransmitCycle++;  
       }  
       break;  
     case 4:  
       if (IFS1bits.MI2C1IF == 1 && I2C1STATbits.ACKSTAT == 0) {  
         I2C1CONbits.PEN = 1; //stop condition  
         i2cTransmitCycle++;  
       }  
       break;  
     case 5:  
       if (I2C1CONbits.PEN == 0) { //when stop condition is finished, transfer is complete  
         i2cTransmitCycle = 0;  
         *i2cstatus = 0;  
       }  
       break;  
     default:  
       break;  
   }  
   return i2cTransmitCycle;  
 }  

No comments:

Post a Comment