Lecture 29 - I2C Programming In this lecture * Recap of I2C operation * Driverlib function calls for I2C * Example interface functions for temp sensor 10:10 Recap - The I2C bus is a multi-slave bus that connects one master and multiple slaves over a a two-wire interface. The wires include a data wire (SDA) and a clock wire (SCL). - Both wires can be read and written by any master and any slave. Of course, each member needs to follow the rules of the I2C protocol and can only write to it when allowed by the protocol. - The SDA, SCL ports on master and slave are open-collector. They can drive a zero but not a one. A one (logic high) is achieved through a pull-up resistor. - There are four possible transfers - single-byte data write: master writes a single data byte to slave - multi-byte data write: master writes multiple data bytes to slave - single-byte data read: master reads a single data byte from slave - multi-byte data read: master reads multiple data bytes from slave - The start and end of every transfer is indicated by specific transitions on SDA, SCL, called start bits and stop bits A start bit is a 1->0 transition on SDA while SCL is high A stop bit is a 0->1 transition on SDA while SCL is high - The first byte of a transfer holds the address and the direction (read/write) - After every data byte transferred, the receiver (master or slave) will send an acknowledge (with one exception, as shown later) - Protocol for single-byte data write Master | Start | Address+W | | BYTE | | STOP -------|--------|-----------|-----|------|-----|------ Slave | | | ACK | | ACK | - Protocol for single-byte data read Master | Start | Address+R | | | NACK | STOP -------|--------|------------|-----|-----|------|----- Slave | | | ACK | BYTE| | - Protocol for multi-byte data write Master | Start | Address+W | | BYTE | |BYTE | |STOP -------|--------|-----------|-----|------|-----|-----|----|----- Slave | | | ACK | | ACK | |ACK | - Protocol for multi-byte data read Master | Start | Address+R | | | ACK | | NACK | STOP -------|--------|-----------|-----|------|-----|------|------|------ Slave | | | ACK | BYTE | | BYTE | | 10:20 The I2C Peripheral The I2C peripheral is, essentially, nothing but a shift register and a clock generator. The peripheral has a local state machine that understands the basics of byte transmission and reception. -> Discuss block diagram p 959 of user manual (1) Slave mode: Upon reception of first byte, check if received byte matches the peripheral I2C address. Then, depending on the R/W bit, the peripheral goes in slave transmitter or slave receiver mode In slave transmitter mode, the peripheral keeps on sending data bytes until a NACK is received In slave receiver mode keeps on acking bytes until a stop is received. (2) Master mode: The devices uses the address loaded in the address register to talk to slaves Similar to Slave mode, there is a master tranmitter mode and a master receiver mode. 10:25 Driverlib calls (This week, we do not yet consider interrupts) (Explain through Driverlib) Master I2C_initMaster I2C_setSlaveAddress I2C_setMode I2C_enableModule Master Single Byte Transmit I2C_masterSendSingleByte Master Multiple Byte Transmission I2C_masterSendMultiByteStart I2C_masterSendMultiByteNext I2C_masterSendMultiByteStop Master Single Byte Reception I2C_masterReceiveSingleByte Master Multiple Byte Reception I2C_masterReceiveStart I2C_masterReceiveMultiByteNext I2C_masterReceiveMultiByteFinish I2C_masterReceiveMultiByteStop Slave I2C_initSlave I2C_setMode I2C_enableModule Slave Transmission API I2C_slavePutData Slave Reception API I2C_slaveGetData 10:30 Example: Using I2C to connect to the Temperature Chip The Schematic (from BoostXL manual) pullup pullup | | +------+ SCL -----+----|----| | UCB1 | | |------ TEMP_DRDY (is for interrupts) SDA ----------+----| | +-+--+-+ A1 A2 Where do we find connectivity? - BoostXL manual - identifies I2C port (UCB1) - Chip pinout - identifies what port UCB1 is shared with P6.5 - SCL P6.4 - SDA -> I2C UCB1 is mapped as a primary on these pins. So we need to use function calls like // GPIO P6.4 UCB1 SDA 2nd peripheral GPIO_setAsPeripheralModuleFunctionInputPin( GPIO_PORT_P6, GPIO_PIN4, GPIO_PRIMARY_MODULE_FUNCTION); to configure the pin 10:35 Access of the TMP006 chip The first step is to find the address that an I2C-enabled chip is mapped to. The I2C slave address is needed to establish communication. We find this information in the data sheet of the TMP006 chip. - Discuss Table 1 page 7 from TMP006 data sheet - What is the address in our case? 0x40 Next, we need to understand how the information in TMP006 is structured. The I2C slave address allows you the send bytes to, and receive bytes from, the TMP006. But, you have to find out how to get to the temperature values. I2C enabled chips very often have an internal address space for registers. In the case of the TMP006 chip, it looks as follows Register Register Address 00 16-bit Vobject 01 16-bit Tambient 02 16-bit Configuration Register FE 16-bit Manufacturer ID FF 16-bit Device ID Note that the manufacturer ID and device ID are useful to identify an unknown chip on an I2C chain. The point, now, is how do we get access to Vobject, Tambient, Manu ID and Device ID? This works as follows: 1/ Perform I2C write transfer to 0x40 Value = register address you would like to read/write from 2/ Perform a two-byte read (for reading device register) or device-write (for writing device register) each time to I2C address 0x40 10:45 Example: code to read Manufacturer ID (5449h) and Device ID (0067h) ======================================================== #include #include const eUSCI_I2C_MasterConfig i2cConfig = { EUSCI_B_I2C_CLOCKSOURCE_SMCLK, // SMCLK Clock Source 3000000, // SMCLK = 3MHz EUSCI_B_I2C_SET_DATA_RATE_400KBPS, // Desired I2C Clock of 400khz 0, // No byte counter threshold EUSCI_B_I2C_NO_AUTO_STOP // No Autostop }; // I2C address of temperature sensor #define TMP006_SLAVE_ADDRESS 0x40 // device register addresses #define TMP006_P_MAN_ID 0xFE #define TMP006_P_DEVICE_ID 0xFF #define TMP006_P_TAMBIENT 0x01 #define TMP006_P_VOBJECT 0x00 int main(void) { volatile unsigned i; WDT_A_holdTimer(); // Configure output pins // GPIO P6.5 UCB1 SCL 2nd peripheral GPIO_setAsPeripheralModuleFunctionInputPin( GPIO_PORT_P6, GPIO_PIN5, GPIO_PRIMARY_MODULE_FUNCTION); // GPIO P6.4 UCB1 SDA 2nd peripheral GPIO_setAsPeripheralModuleFunctionInputPin( GPIO_PORT_P6, GPIO_PIN4, GPIO_PRIMARY_MODULE_FUNCTION); // RED LED GPIO_setAsOutputPin (GPIO_PORT_P2, GPIO_PIN6); GPIO_setOutputLowOnPin (GPIO_PORT_P2, GPIO_PIN6); // Master Configuration I2C_initMaster (EUSCI_B1_BASE, &i2cConfig); I2C_setSlaveAddress(EUSCI_B1_BASE, TMP006_SLAVE_ADDRESS); I2C_enableModule (EUSCI_B1_BASE); GPIO_toggleOutputOnPin(GPIO_PORT_P2, GPIO_PIN6); while (1) { GPIO_toggleOutputOnPin(GPIO_PORT_P2, GPIO_PIN6); // Select TMP006_P_DEVICE_ID register I2C_setMode(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_MODE); I2C_masterSendSingleByte(EUSCI_B1_BASE, TMP006_P_MAN_ID); // Receive DEVICE ID data I2C_masterReceiveStart(EUSCI_B1_BASE); while(!(I2C_getInterruptStatus(EUSCI_B1_BASE, EUSCI_B_I2C_RECEIVE_INTERRUPT0))); I2C_masterReceiveMultiByteNext(EUSCI_B1_BASE); I2C_masterReceiveMultiByteFinish(EUSCI_B1_BASE); for (i=0; i<10000; i++) ; } } ======================================================== 10:50 Demonstration - Decode the transfer 10:55 Conclusions 11:00 Monday - Role of interrupts - Accelerometer