/********************************************************************\ Name: i2c.c Created by: Stefan Ritt Contents: I2C interface \********************************************************************/ #include #include #include #include #include "mscbemb.h" /*------------------------------------------------------------------*/ // version A //sbit I2C_SDA = P0 ^ 0; //sbit I2C_SCL = P0 ^ 1; //version B sbit I2C_SDA = P0 ^ 6; sbit I2C_SCL = P0 ^ 7; sbit I2C_RST = P2 ^ 1; #define I2C_DELAY 6 // delay in us void i2c_init() { SFRPAGE = LEGACY_PAGE; I2C_RST = 1; I2C_SDA = 1; I2C_SCL = 1; } void i2c_start() { SFRPAGE = LEGACY_PAGE; I2C_SDA = 1; I2C_SCL = 1; DELAY_US(I2C_DELAY); I2C_SDA = 0; DELAY_US(I2C_DELAY); I2C_SCL = 0; } void i2c_stop() { I2C_SDA = 0; I2C_SCL = 1; DELAY_US(I2C_DELAY); I2C_SDA = 1; DELAY_US(I2C_DELAY); DELAY_US(I2C_DELAY); } void i2c_write(unsigned char d) { unsigned char i, ack; for (i=0 ; i<8 ; i++) { I2C_SDA = (d & 0x80) > 0; d <<= 1; DELAY_US(I2C_DELAY); I2C_SCL = 1; DELAY_US(I2C_DELAY); I2C_SCL = 0; DELAY_US(I2C_DELAY); } // Acknowledge from slave I2C_SDA = 1; DELAY_US(I2C_DELAY); I2C_SCL = 1; DELAY_US(I2C_DELAY); ack = I2C_SDA; // reack acknowledge I2C_SCL = 0; DELAY_US(I2C_DELAY); } unsigned char i2c_read(unsigned char ack) { unsigned char i, d; I2C_SDA = 1; DELAY_US(I2C_DELAY); d = 0; for (i=0 ; i<8 ; i++) { DELAY_US(I2C_DELAY); I2C_SCL = 1; DELAY_US(I2C_DELAY); d = (d << 1) | I2C_SDA; I2C_SCL = 0; DELAY_US(I2C_DELAY); } // Acknowledge from master I2C_SDA = ack; //set ACK / NACK DELAY_US(I2C_DELAY); I2C_SCL = 1; DELAY_US(I2C_DELAY); I2C_SCL = 0; DELAY_US(I2C_DELAY); return d; } unsigned char i2c_get_mux() { unsigned char d1, d2, adr, sa; // ---- first I2C multiplexer i2c_start(); // PCA9547 slave address sa = 0xE0 | (1 << 0); // R/!W = 1 i2c_write(sa); // PCA9547 control register d1 = i2c_read(1); i2c_stop(); // ---- second I2C multiplexer i2c_start(); // PCA9547 slave address sa = 0xE0 | (1 << 1) | (1 << 0); // R/!W = 1 i2c_write(sa); // PCA9547 control register d2 = i2c_read(1); i2c_stop(); if (d1 & 0x08) adr = d1 & 0x07; // first MUX active else adr = (d2 & 0x07) + 8; return adr; } void i2c_set_mux(unsigned char adr) /* set pair of PCA9547 I2C multiplexers adr=0...7 -> activate chip 0 adr=8...15 -> activate chip 1 */ { unsigned char sa, a0, a1; if (adr < 8) { a0 = 0x08 | adr; a1 = 0; } else { a0 = 0; a1 = 0x08 | (adr - 8); } // ---- first I2C multiplexer i2c_start(); // PCA9547 slave address sa = 0xE0 | 0; // 1110 a2a1a0!w i2c_write(sa); // PCA9547 control register i2c_write(a0); i2c_stop(); // ---- second I2C multiplexer i2c_start(); // PCA9547 slave address sa = 0xE0 | (1 << 1); // 1110 a2a1a0!w, a0 = 1 i2c_write(sa); // PCA9547 control register i2c_write(a1); i2c_stop(); } // // // When writing to the AD5593R, the user must begin with a start //command followed by an address byte R/W = 0), after which the //AD5593R acknowledges that it is prepared to receive data by //pulling SDA low. The AD5593R requires three bytes of data. The //first byte is the pointer byte. This byte contains information defining //the type of operation that is required of the AD5593R, such as //configuring the I/O pins and writing to a DAC. The pointer byte is //followed by the most significant byte and the least significant byte, //as shown in Figure 36. After these data bytes are acknowledged by //the AD5593R, a stop condition follows. // // // //When reading data back from the AD5593R, the user begins with a //start command followed by an address byte (R/W = 0), after which //the AD5593R acknowledges that it is prepared to transmit data by //pulling SDA low. The pointer byte is then written to select what is //to be read back. A repeat start or a new I2C transmission can then //follow to read two bytes of data from the AD5593R. Both bytes are //acknowledged by the master, as shown in Figure 37. //It is also possible to perform consecutive readbacks without having //to provide interim start and stop conditions or slave addresses. This //method can be used to read blocks of conversions from the ADC, //as shown in Figure 39. // // //7.5.4.1 Direct Format: Write //The simplest format for a PMBus write is direct format. After the start condition [S], the slave chip address is //sent, followed by an eighth bit indicating a write. The TPS53819A then acknowledges that it is being addressed, //and the master responds with an 8-bit register address byte. The slave acknowledges and the master sends the //appropriate 8-bit data byte. Again the slave acknowledges and the master terminates the transfer with the stop //condition [P]. //7.5.4.2 Combined Format: Read //After the start condition [S], the slave chip address is sent, followed by an eighth bit indicating a write. The //TPS53819A then acknowledges that it is being addressed, and the master responds with an 8-bit register //address byte. The slave acknowledges and the master sends the repeated start condition [Sr]. Again the slave //chip address is sent, followed by an eighth bit indicating a read. The slave responds with an acknowledge //followed by previously addressed 8 bit data byte. The master then sends a non-acknowledge (NACK) and finally //terminates the transfer with the stop condition [P] // void i2c_write_byte(unsigned char adr, unsigned char reg, unsigned char d) // write one byte to I2C with address and register { i2c_start(); i2c_write(adr<<1); i2c_write(reg); i2c_write(d); i2c_stop(); } void i2c_write_register(unsigned char adr, unsigned char reg) { i2c_start(); i2c_write(adr<<1); i2c_write(reg); i2c_stop(); } unsigned char i2c_read_byte(unsigned char adr, unsigned char reg) // read one byte from I2C with address and register { unsigned char d; i2c_start(); i2c_write(adr<<1); i2c_write(reg); i2c_start(); i2c_write(adr<<1 | 0x01); d = i2c_read(1); i2c_stop(); return d; } void i2c_write_word(unsigned char adr, unsigned char reg, unsigned short d) // write one byte to I2C with address and register { unsigned char d1, d2; d1 = (d & 0xFF00) >> 8; d2 = d & 0x00FF; i2c_start(); i2c_write(adr<<1); i2c_write(reg); i2c_write(d1); i2c_write(d2); i2c_stop(); } unsigned short i2c_read_word(unsigned char adr, unsigned char reg) // read two bytes from I2C with address and register { unsigned char d1, d2; i2c_start(); i2c_write(adr<<1); i2c_write(reg); i2c_stop(); i2c_start(); i2c_write(adr<<1 | 0x01); d1 = i2c_read(0); d2 = i2c_read(1); i2c_stop(); return d2 | (d1 << 8); }