I have two functions to send I2C data and two to receive I2C data, one is for only one data byte and the other is for multiple data bytes. The issue is the single data byte function does not appear to be using the passed in values and I cannot work out why this would be. As you can see I have set the values of device_address and device_register in the functions to see if this prevents the I2C from hanging and it does. If I do not add these explicit declarations the code hangs at the first i2c_Data_Transmit_Delay(); in each of the one byte functions. Checking the passed in values of device_address and device_register they are both incorrect and appear to be 0x98 which does not appear in my code and neither does 0x4C which would be 0x98 shifted right once. I have also attached a copy of the full code for further reference.
Any help would be very much appreciated.
Here is the write function that uses accepts only one data byte
void i2c_Write(char device_address, char device_register, char data){ device_address = 0x4B; //TODO: Fix addressing issue device_register = 0x03; data = 0x12; I2C0_C1 |= I2C_C1_MST_MASK | I2C_C1_TX_MASK; //Send start bit // I2C0_C1 &= ~I2C_C1_TXAK_MASK; I2C0_D = (device_address << 1) | I2C_MASTER_WRITE; //Device address with write bit set while ((I2C0_S & I2C_S_IICIF_MASK) == 0){ //Wait for data to transmit } I2C0_S |= I2C_S_IICIF_MASK; // i2c_Data_Trasmit_Delay(); I2C0_D = device_register; //Device register to write to while ((I2C0_S & I2C_S_IICIF_MASK) == 0){ //Wait for data to transmit } I2C0_S |= I2C_S_IICIF_MASK; // i2c_Data_Trasmit_Delay(); I2C0_D = data; //Write to register while ((I2C0_S & I2C_S_IICIF_MASK) == 0){ //Wait for data to transmit } I2C0_S |= I2C_S_IICIF_MASK; // i2c_Data_Trasmit_Delay(); I2C0_S |= I2C_S_IICIF_MASK; I2C0_C1 &= ~I2C_C1_MST_MASK; while((I2C0_FLT & I2C_FLT_STOPF_MASK) == 0){ //Delay for stop bit //Wait for stop bit to transmit } I2C0_FLT &= ~I2C_FLT_STOPF_MASK; }
Here is the write function that accepts multiple data bytes
void i2c_Write_Multi(char device_address, char device_register, char* data_array, unsigned char length){ int i = 0; I2C0_C1 |= I2C_C1_MST_MASK | I2C_C1_TX_MASK | I2C_C1_IICEN_MASK; I2C0_C1 |= I2C_C1_MST_MASK | I2C_C1_TX_MASK; //Send start bit I2C0_C1 &= ~I2C_C1_TXAK_MASK; I2C0_D = (device_address << 1) | I2C_MASTER_WRITE; //Device address with write bit set i2c_Data_Trasmit_Delay(); I2C0_D = device_register; //Device register to write to i2c_Data_Trasmit_Delay(); for(i = 0; i < length; i++){ //Write n bytes I2C0_D = data_array[i]; i2c_Data_Trasmit_Delay(); } I2C0_S |= I2C_S_IICIF_MASK; I2C0_C1 &= ~I2C_C1_MST_MASK; while((I2C0_FLT & I2C_FLT_STOPF_MASK) == 0){ //Delay for stop bit //Wait for stop bit to transmit } I2C0_FLT &= ~I2C_FLT_STOPF_MASK; }
Here are the two write functions
char i2c_Read(uint8_t device_address, char device_register){ char temp; device_address = 0x4B; //TODO: Fix addressing issue device_register = 0x0B; I2C0_C1 |= I2C_C1_MST_MASK | I2C_C1_TX_MASK; //Send start bit // I2C0_C1 &= ~I2C_C1_TXAK_MASK; I2C0_D = (device_address << 1) | I2C_MASTER_WRITE; //Device address with write bit set i2c_Data_Trasmit_Delay(); I2C0_D = device_register; //Device register to read from i2c_Data_Trasmit_Delay(); I2C0_C1 |= I2C_C1_RSTA_MASK; //Send restart I2C0_D = (device_address << 1) | I2C_MASTER_READ; //Device address with read bit set i2c_Data_Trasmit_Delay(); I2C0_C1 &= ~I2C_C1_TX_MASK; //Change to Rx mode I2C0_C1 |= I2C_C1_TXAK_MASK; //Set NACK temp = I2C0_D; //Receive data byte i2c_Data_Trasmit_Delay(); I2C0_C1 &= ~I2C_C1_MST_MASK; //Set stop bit I2C0_C1 |= I2C_C1_TX_MASK; //Change to Tx mode return I2C0_D; } void i2c_Read_Multi(char device_address, char device_register, char* data_array, unsigned char length){ int i = 0; char temp; I2C0_C1 |= I2C_C1_MST_MASK | I2C_C1_TX_MASK | I2C_C1_IICEN_MASK; I2C0_C1 |= I2C_C1_MST_MASK | I2C_C1_TX_MASK; //Send start bit // I2C0_C1 &= ~I2C_C1_TXAK_MASK; I2C0_D = (device_address << 1) | I2C_MASTER_WRITE; //Device address with write bit set i2c_Data_Trasmit_Delay(); I2C0_D = device_register; //Device register to read from i2c_Data_Trasmit_Delay(); I2C0_C1 |= I2C_C1_RSTA_MASK; //Send restart I2C0_D = (device_address << 1) | I2C_MASTER_READ; //Device address with read bit set i2c_Data_Trasmit_Delay();
void i2c_Read_Multi(char device_address, char device_register, char* data_array, unsigned char length){ int i = 0; char temp; I2C0_C1 |= I2C_C1_MST_MASK | I2C_C1_TX_MASK | I2C_C1_IICEN_MASK; I2C0_C1 |= I2C_C1_MST_MASK | I2C_C1_TX_MASK; //Send start bit // I2C0_C1 &= ~I2C_C1_TXAK_MASK; I2C0_D = (device_address << 1) | I2C_MASTER_WRITE; //Device address with write bit set i2c_Data_Trasmit_Delay(); I2C0_D = device_register; //Device register to read from i2c_Data_Trasmit_Delay(); I2C0_C1 |= I2C_C1_RSTA_MASK; //Send restart I2C0_D = (device_address << 1) | I2C_MASTER_READ; //Device address with read bit set i2c_Data_Trasmit_Delay(); I2C0_C1 &= ~I2C_C1_TX_MASK; //Change to Rx mode temp = I2C0_D; i2c_Data_Trasmit_Delay(); for (i = 0; i < length - 2; i ++){ //Receive first n - 1 bytes data_array[i] = I2C0_D; i2c_Data_Trasmit_Delay(); } I2C0_C1 |= I2C_C1_TXAK_MASK; //Receive last byte data_array[i] = I2C0_D; i2c_Data_Trasmit_Delay(); I2C0_C1 &= ~I2C_C1_MST_MASK; //Set stop bit I2C0_C1 |= I2C_C1_TX_MASK; //Change to Tx mode data_array[i + 1] = I2C0_D; //Read final byte }
For clarity here is the i2c_Data_Transmit_Delay() function
void i2c_Data_Trasmit_Delay(){ while ((I2C0_S & I2C_S_IICIF_MASK) == 0){ //Wait for data to transmit } I2C0_S |= I2C_S_IICIF_MASK; }
Lastly here is the call from main
int main(void) { char temp; char i2cReadArray[] = {0,0,0,0,0,0,0,0,0}; char i2cWriteArray[] = {0,0,0,0,0,0,0,0,0}; clk_Init(); i2c_Init(); i2cWriteArray[0] = 0x12; i2cWriteArray[1] = 0x34; i2cWriteArray[2] = 0x56; i2cWriteArray[3] = 0x78; i2c_Write_Multi(0x4B, 0x04, i2cWriteArray, 2); i2c_Read_Multi(0x4B, 0x04, i2cReadArray, 2); while(1){ i2c_Write_Multi(0x4B, 0x04, i2cWriteArray, 2); i2c_Read_Multi(0x4B, 0x04, i2cReadArray, 2); i2c_Write_Multi(0x4B, 0x04, i2cWriteArray, 4); i2c_Read_Multi(0x4B, 0x04, i2cReadArray, 4); temp = i2c_Read(0x4B, 0x0B); i2c_Write(0x4B, 0x03, 0x12); } }
Point in the code where the i2c_wite() hangs and the values of the 3 passed values:
device_address value (same in both places)
device_register value (different from hover to watch window)
data register value (different from hover to watch window)
Looking into it further (using a logic analyzer) I can call i2c_write() or i2c_read() once before I get a hang on the calling either of these again. Any combination of i2c_write() and i2c_read() leads to a hang up on the second function call.
i2c_write() -> i2c_read() Hangs in i2c_read()
i2c_write() -> i2c_write() Hangs in i2c_write()
i2c_read() -> i2c_read() Hangs in i2c_read()
i2c_read() -> i2c_write() Hangs in i2c_write()
Evan adding a i2c_write_multi() or i2c_read_multi() between the two calls does not help anything but the multi calls work with no issue.
If there is at all any suggestions I would be happy to hear them as this is holding up my project, if there is any more information that is needed I will haphappilyswer what you need if it helps resolve this odd issue.
Kas
Original Attachment has been moved to: main.c.txt.zip
Solved! Go to Solution.
Kas
I would check the use of
I2C0_FLT &= ~I2C_FLT_STOPF_MASK;
This is a "write-'1'" to clear flag and so writing a '0' to it will not do anything.
Also you may prefer to use the busy bit to wait for the stop bit to complete (IBB in I2Cx_S). I know that sending a new message when the bus is still busy will cause problems (bus arbitration error) but generally I would send a repeated start anyway if the previous mesage has not completed because the HW will co-ordinate tagging on the following one, which is also more efficient (no stop/start states needed).
Regards
Mark
Kas
I would check the use of
I2C0_FLT &= ~I2C_FLT_STOPF_MASK;
This is a "write-'1'" to clear flag and so writing a '0' to it will not do anything.
Also you may prefer to use the busy bit to wait for the stop bit to complete (IBB in I2Cx_S). I know that sending a new message when the bus is still busy will cause problems (bus arbitration error) but generally I would send a repeated start anyway if the previous mesage has not completed because the HW will co-ordinate tagging on the following one, which is also more efficient (no stop/start states needed).
Regards
Mark
Hello Mark,
Is there any reason why the order of the lines I mentioned in my previous comment would matter, the one line is only to turn on and off the ACKs presumably when in Rx mode and would make no difference in Tx mode. Any hits as to what is really going on here would be nice.
Also why not clearing the STOPF caused issues for the single read/write but had no effect on the multi read/write and why it caused it to load wierd values in the registers but if the values were hard coded in the function the issue went away. An explinatin for this very odd behaviour would be helpful in better understanding how this microcontroller works.
Thanks
Kas
Hi Kas
I can't explain how the device operates in all circumstances (sometimes there are behavioural differences that can be postulated about but never fully understood) - I just do, for example,
I2C0_C1 = (IIC_IEN | IIC_TXAK); // send end condition and disable interrupts
to send the stop condition rather than using two operations (the final register content is known and so there is little point in two operations on it and introducing possible race state issues with the ordering of the operations and adding 6x more instructions than needed) and so never thought about it (nor saw any strange behavior involved).
Regards
Mark
Mark,
WOW!! One small bit set incorrectly... I used that to move away from a time delay and missed the w1c in the data sheet. After changing that the read works fine, as for the write it appears the order of changing back to write from read was wrong ie
I2C0_C1 &= ~I2C_C1_TXAK_MASK;
I2C0_C1 |= I2C_C1_MST_MASK | I2C_C1_TX_MASK;
VS.
I2C0_C1 |= I2C_C1_MST_MASK | I2C_C1_TX_MASK;
I2C0_C1 &= ~I2C_C1_TXAK_MASK;
The second one is correct the first one seems to cause the hang.
Thank you very much
Kas