What bit to poll while waiting for I2C Transmit/ What am I doing wrong? - Page 1
Forum section: electronics / general

author: BurnedResistor
author_rank: Regular Contributor
timestamp: February 15, 2016, 05:21:21 am
content: Dear All,After getting a spi bus working with your help, I just could not leave it alone and managed to get stuck on trying to write my own peripheral handler for an I2C master.Here is my code:The function which transmits 4 12bit values, via 6 8bit transmitions:Code:BOOL motor_update(int mfl, int mfr, int mbl, int mbr) {char msg1 = ((0b111111110000 & mfl) >> 4);char msg2 = ((0b1111 & mfl) << 4) | ((0b1111000000000 & mfr) >> 8);char msg3 = ((0b11111111 & mfr));char msg4 = ((0b111111110000 & mbl) >> 4);char msg5 = ((0b1111 & mbl) << 4) | ((0b111100000000 & mbr) >> 8);char msg6 = (IIC1, ((0b11111111 & mbr)));IIC_start(IIC1);while (IIC_status(IIC1, IIC_STAT_START)) {;}//Send Address, waitIIC_address(IIC1, IIC_ADR_INT, IIC_WRITE);while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}if(IIC_status(IIC1,IIC_STAT_ACK)){terminate();return TRUE;}//Send ms1, waitIIC_put(IIC1, msg1);while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}if(IIC_status(IIC1,IIC_STAT_ACK)){terminate();return TRUE;}//Send ms2, waitIIC_put(IIC1, msg2);while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}if(IIC_status(IIC1,IIC_STAT_ACK)){terminate();return TRUE;}//Send ms3, waitIIC_put(IIC1, msg3);while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}if(IIC_status(IIC1,IIC_STAT_ACK)){terminate();return TRUE;}//Send ms4, waitIIC_put(IIC1, msg4);while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}if(IIC_status(IIC1,IIC_STAT_ACK)){terminate();return TRUE;}//Send ms5, waitIIC_put(IIC1, msg5);while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}if(IIC_status(IIC1,IIC_STAT_ACK)){terminate();return TRUE;}//Send ms6, waitIIC_put(IIC1, msg6);while(IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL) & IIC_status(IIC1, IIC_STAT_TRANSMIT)){;}if(IIC_status(IIC1,IIC_STAT_ACK)){terminate();return TRUE;}//StopIIC_stop(IIC1);while (IIC_status(IIC1, IIC_STAT_STOP)) {;}}void terminate(){IIC_stop(IIC1);while(IIC_status(IIC1, IIC_STAT_STOP)){;}}Here is the code for my IIC methods, which I call in the above function:Code:typedef enum {IIC_READ = 1,IIC_WRITE = 0} IIC_OPERATION;typedef enum {//A0000000 00000000 XXXXXXXX XXXXXXXX//A: 1 = CON register, 0 = Status register//X: Bit maskIIC_STAT_CLKSTRETCH = 0b10000000000000000000000001000000,IIC_STAT_RECEIVE = 0b10000000000000000000000000001000,IIC_STAT_START = 0b10000000000000000000000000000001,IIC_STAT_STOP = 0b10000000000000000000000000000100,IIC_STAT_RESTART = 0b10000000000000000000000000000010,IIC_STAT_ACK_SENT = 0b10000000000000000000000000010000,IIC_STAT_ACK = 0b00000000000000001000000000000000,IIC_STAT_TRANSMIT = 0b00000000000000000100000000000000,IIC_STAT_MASTER_COLLISION = 0b00000000000000000000010000000000,IIC_STAT_GENERAL_CALL = 0b00000000000000000000001000000000,IIC_STAT_10BIT_MATCH = 0b00000000000000000000000100000000,IIC_STAT_WRITE_COLLOSION = 0b00000000000000000000000010000000,IIC_STAT_RECEIVE_OVERFLOW = 0b00000000000000000000000001000000,IIC_STAT_DATA_NADDRESS = 0b00000000000000000000000000100000,IIC_STAT_STOP_DET = 0b00000000000000000000000000010000,IIC_STAT_START_DET = 0b00000000000000000000000000001000,IIC_STAT_READ_NWRITE_DET = 0b00000000000000000000000000000100,IIC_STAT_RECEIVE_FULL = 0b00000000000000000000000000000010,IIC_STAT_TRANSMIT_FULL = 0b00000000000000000000000000000001,} IIC_STATUS;void IIC_stop(IIC_CHANNEL channel) {if (channel == IIC2) {I2C2CONbits.PEN = 1;} else {//Default to Channel 1I2C1CONbits.PEN = 1;}}void IIC_start(IIC_CHANNEL channel) {if (channel == IIC2) {I2C2CONbits.SEN = 1;} else {//Default to Channel 1I2C1CONbits.SEN = 1;}}BOOL IIC_status(IIC_CHANNEL channel, IIC_STATUS stat){if(channel == IIC2){if((stat & 0x80000000)){return ((I2C2CON) & (0x7FFFFFFF & stat));} else {return ((I2C2STAT) & (0x7FFFFFFF & stat));}} else{if((stat & 0x80000000)){return ((I2C1CON) & (0x7FFFFFFF & stat));} else {return ((I2C1STAT) & (0x7FFFFFFF & stat));}}}void IIC_address(IIC_CHANNEL channel, char address, IIC_OPERATION op){char value = ((address & 0b1111111) << 1) | op;IIC_put(channel, value);}void IIC_put(IIC_CHANNEL channel,char data) {if(channel == IIC2){I2C2TRN = data;} else {//default to channel 1I2C1TRN = data;}}My issue is that, according to my scope, only the address + r/w bit is being sent, immediately followed by a Stop state:I am however quite certain that the function does not exit right after the IIC_address call, as the terminate method is never executed (checked by placing a stop point there) and the the method does not return TRUE, which would indicate an error.My best guess is that I am polling the wrong bits to check for completion, which results in all the future writes to the I2C1BUF being ignore, and then the STOP bit at the end of the function executing.In other words, I suspect an issue with:Code:while (IIC_status(IIC1, IIC_STAT_TRANSMIT_FULL)){ ...which is simply a complicated way of writing:Code:while(I2C1STATbits.TBF){...I am slightly confused what I should be polling, as the data sheet mentions booth the I2C1STATbits.TBF and I2C1CONbits.TRSTAT...Any help would be greatly appreciated.Side Note:XC32 ComplierPIC32MX120F064H

author: BurnedResistor
author_rank: Regular Contributor
timestamp: February 15, 2016, 05:36:22 am
content: I just tried to change the line in question to:Code:while(I2C1STATbits.TBF && I2C1STATbits.TRSTAT){;}and it transmits SOME of the following bytes, but skips others... Still makes me think I am polling the wrong bit..!

author: BurnedResistor
author_rank: Regular Contributor
timestamp: February 15, 2016, 06:16:12 am
content: furthermore it seems that that change caused it to only send every other byte... Again confirming the wrong polling bit theory?

author: dannyf
author_rank: Super Contributor
timestamp: February 15, 2016, 12:33:04 pm
content: Never used your chip so cannot be of much help.A few suggestions:1) I would see if you could use a hardware debugger to step through the code; If not, see if you can send debugging messages (uart, or pin flip patterns, etc.) so you have some sense where the issues are.2) i would check the datasheet / library you use to see which is the right status to check. Check the errata as well.3) from the flow, looks like it exited right after the address is sent, through terminate().4) the various channel selection things can be done via switch / case so it is more expandable.5) some of the formation steps for msg1..6 are not necessary.6) I typically organize my i2c routines into single byte send/ receive and multi-byte send / receive, likeCode:i2c_write(IIC2, addr, data_ptr); //write single bytei2c_writes(IIC1, addr, data_ptr, data_length); //write multiple bytesSuch a set-up allows a consistent approach should you decide in the future to standard transmission via an interrupt.

author: BurnedResistor
author_rank: Regular Contributor
timestamp: February 15, 2016, 01:05:56 pm
content: Quote from: dannyf on February 15, 2016, 12:33:04 pmThats the frustrating part... If I put a breakpoint on terminate(), it never triggers, but there is still a stop bit being sent. That is also confirmed because if I place a breakpoint onto the stopbit part it stops there...!

author: dannyf
author_rank: Super Contributor
timestamp: February 15, 2016, 01:21:21 pm
content: In the while() loop right after sending the address byte, keep flipping a pin. and see what happens.In the following if, before terminate(), keep flipping a (different) pin too.

author: mikerj
author_rank: Super Contributor
timestamp: February 16, 2016, 12:03:45 am
content: Quote from: BurnedResistor on February 15, 2016, 05:21:21 amThe last line above, and probably several other similar lines may not do what you probably want them to do. You have used a bitwise AND operator so you will only wait in this loop while both the TBF (buffer full) and the TRSTAT (transmit status) are set. However TBF will clear after the last data bit is transmitted, but TRSTAT will only be cleared after the ACK bit has been received. This means you jump out of the loop before the ACK is received, so your subsequent test of the ACK bit may not be (probably isn't) valid; you have a race condition.It looks like you should only need to look at TRSTAT for this particular case, but it might be easier to simply poll the I2CxMIF interrupt flag instead since all the typical master operations set this flag. Remember to clear I2CxMIF before starting a new operation.