I'm trying to interface an I2C device and so far have measured nothing but noise coming from the output of the device. I followed the exact process given by the AN3291.pdf file (which pretty much has the init and send functions already written). I am fairly certain that the actual I2C functions that I'm using (I2C_Open() and I2C_Send()) are correct. In addition, I added a timer wait of 250ms at the end of the I2C_Send() function to ensure that the slave device has enough time to process the request.
I'm thinking the problem could be the MODRR register. What should I be setting it to, if anything?
I've attached the AN3291.pdf file. Here's my code:
/************************* I2C.c *************************/
#include "I2C.h"
/************************* Public Functions *************************/
/************************* I2C_Open *************************/
// Inputs: none
// Outputs: none
// enable I2C interface on 9S12
void I2C_Open(void) {
MODRR = 0x00;
IBFD = 0x4C; // frequency divider register: 91kHz (@ 8MHz)
IBCR = 0x80; // enable I2C module with no interrupts
}
/************************* I2C_Send *************************/
// Inputs:
// slaveAddr - the address of the slave device (TEA6320T)
// slaveReg - the register to write to on TEA6320T
// data - data to send to slave device (TEA6320T)
// Outputs: none
// will transmit a byte of data to TEA6320T
void I2C_Send(uint8 slaveAddr, uint8 slaveReg, uint8 data) {
IBCR_TXAK = 0;
IBCR |= 0x30; // send start
IBDR = slaveAddr & (0xFE); // send slave address
while(!IBSR_IBIF); // wait for the address to be sent
IBSR_IBIF = 1; // clear IBIF
while(IBSR_RXAK); // wait for "ack" from slave
IBDR = slaveReg; // send first byte
while(!IBSR_IBIF); // wait for the data to be sent
IBSR_IBIF = 1; // clear IBIF
while(IBSR_RXAK); // wait for "ack" from slave
IBDR = slaveReg; // send second byte
while(!IBSR_IBIF); // wait for the data to be sent
IBSR_IBIF = 1; // clear IBIF
while(IBSR_RXAK); // wait for "ack" from slave
IBDR = data; // send data to slave
while (!IBSR_IBIF); // wait for the data to be sent
IBSR_IBIF = 1; // clear IBIF
while(IBSR_RXAK); // wait for "ack" from slave
IBCR_MS_SL = 0; // generate STOP condition;
//WAIT FOR SLAVE TO PROCESS DATA
Timer_msWait(250);
}
Check the port integration module (s12dp256pimv3.pdf). I2C is not routable to different pins. It is easy to check by looking at pin diagram. If you would see more than one instance of SDA or SCL, then I2C module would be routable to different pins and you would need to check module routing register (MODRR) settings.
Your code is wrong. It ALWAYS helps to look into datasheet and at your code again and again. What is TEA6320 protocol? It is 1) address byte, which is always 0x80 for this device, 2) subaddress (register number like volume, bass etc), 3) data. Now look at your code. It is 1) address, 2) subaddress, 3) again subaddress, 4) data.
It is useless on I2C. Master can't go further while slave is busy.
AN3291 is good example of dangerous code. First of all why do thy wait for RXAK? RXAK is updated when byte is transferred to the slave. RXAK is updated as soon as IBIF is set and won't get updated until next byte is transferred to the slave. In case of failure, when for some reason slave doesn't ack your data, your MCU will stuck in endless loop waiting for RXAK that will never occur because your MCU doesn't transfer more bytes because it is waiting for RXAK. If you expect RXAK=0 and it is =1, then you should signal error, change direction to read, read dummy byte from data register, generate stop condition and exit.
You were right. I did have one byte too many in mt send protocol, I guess that was a copy-and-paste error. Also, that AN3291 file is misleading, I thought the while(IBSR_RXAK) meant that we were waiting for a flag, not an actual value. Anyway, I fixed it and here's the new code in case anyone else has the same problems. Thanks for the quick response!
/************************* I2C.c *************************/
#include "I2C.h"
/************************* Public Functions *************************/
/************************* I2C_Open *************************/
// Inputs: none
// Outputs: none
// enable I2C interface on 9S12
void I2C_Open(void) {
IBFD = 0x4C; // frequency divider register: 91kHz (@ 8MHz)
IBCR = 0x80; // enable I2C module with no interrupts
}
/************************* I2C_Send *************************/
// Inputs:
// slaveAddr - the address of the slave device (TEA6320T)
// slaveReg - the register to write to on TEA6320T
// data - data to send to slave device (TEA6320T)
// Outputs: none
// will transmit a byte of data to TEA6320T
uint8 I2C_Send(const uint8 slaveAddr, const uint8 slaveReg, const uint8 data) {
uint8 dummy;
PTP_PTP7 = 1;
IBCR_TXAK = 0;
IBCR_MS_SL = 1; // generate start condition
IBCR_TX_RX = 1; // set MCU to transmit mode
IBDR = slaveAddr & (0xFE); // send slave address
while(!IBSR_IBIF); // wait for the address to be sent
IBSR_IBIF = 1; // clear IBIF
if(IBSR_RXAK) { // "nack" was sent back from slave
IBCR_TX_RX = 0; // set MCU to receive mode
dummy = IBDR; // dummy read
IBCR_MS_SL = 0; // generate STOP condition;
return 0; // report failure
}
// "ack" was sent back from slave
IBDR = slaveReg; // send first byte
while(!IBSR_IBIF); // wait for the data to be sent
IBSR_IBIF = 1; // clear IBIF
if(IBSR_RXAK) { // "nack" was sent back from slave
IBCR_TX_RX = 0; // set MCU to receive mode
dummy = IBDR; // dummy read
IBCR_MS_SL = 0; // generate STOP condition;
return 0; // report failure
}
// "ack" was sent back from slave
IBDR = data; // send data to slave
while (!IBSR_IBIF); // wait for the data to be sent
IBSR_IBIF = 1; // clear IBIF
if(IBSR_RXAK) { // "nack" was sent back from slave
IBCR_TX_RX = 0; // set MCU to receive mode
dummy = IBDR; // dummy read
IBCR_MS_SL = 0; // generate STOP condition;
return 0; // report failure
}
// "ack" was sent back from slave
IBCR_MS_SL = 0; // generate STOP condition;
PTP_PTP7 = 0;
Timer_msWait(500);
return 1; // report success
}
Also, when calling the I2C_Send() function, it's being done in a while loop like this:
while(!I2C_Send(...));
Actually that Timer_msWait(500); isn't needed (at the bottom of I2C_Send()). I just had it in there for debugging. I was oscillating an LED so I just wanted to slow it down so the flashing would be visible.