AVR I2C trouble - Page 1
Forum section: electronics / general

author: Dan Moos
author_rank: Frequent Contributor
timestamp: September 19, 2016, 01:34:07 am
content: Ok, I am trying to create an I2C library for avr.My set up: atmega328P, AVR Dragon running debugWire in AVR Studio 7. LCD with I2C backpack (pcf8574 expander chip on back pack). Cheap knock off of Saleae logic analyzer using the Saleae software.My bit rate is 100K=kHz, as confirmed with the logic analyzer. (mcu running at 8Mghtz, 32 in the BR register, 0's in the prescaler);My problem is that none of my data bytes are being transmitted. I send a start, send the address+W byte, get an ACK, and then instead of my data being sent, the analyzer shows the stop being sent right away.I'm not doing any error checking in the code just yet, just watching the relevant registers in the debugger.After I send a start, I get 0x08 in the TWSR register. My understanding is that this is correct.After I load the the address into TWDR, I get 0x18 in the TWSR. Again, i believe this is correct. At this point in the process, the logic analyzer shows all is as expected, including an ACK after the address+w is sent.as I next step the debugger, just BEFORE I load the data to be sent into TWDR, TWSR reads 0x28. To me, this is odd, as that code means the data has already been sent, and an ACK recieved. (if i read the datasheet right anyway).I then load the TWDR, and send a stop. TWSR still reads 0x28 at this point.According to the logic analyzer, all that happened was START->Address+W->ACK->STOP. No sign of the data byte.My gut says I'm not handling the setting of the TWINT bit correctly. Its been a source of confusion for me. I feel like my process follows the datasheet's correctly, but other stuff I read seems to conflict. Specifically, my code (and the datasheet as far as I can tell) say to set TWINT HIGH before I do anything, and to wait for it to go LOW before continuing. My other resource, the Make:AVR programming book seems to imply I should wait for it to go HIGH before continuing.I never see TWINT go low in the debugger, as I thought I should.Here is the code for my Write, Start, and Stop routines.//Send startvoid TWI_Start(uint8_t address){TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); //TWINT clears interrupt flag (sets it to 1); TWSTA sends start bit; TWEN enables TWIwhile(!(TWCR & (1<<TWINT))) //check for interrupt flag to have been set to 0 by MCU before continuing{continue;}TWDR = address; //send slave addressTWCR = (1<< TWINT) | (1<< TWEN); //clear interrupt flag.}//write to current slavevoid Write_TWI(uint8_t data){TWI_Start(_writeAddress);TWCR = (1<<TWINT) | (1<<TWEN); //clear interrupt flagTWDR = data; //load byte to be sent into data registerTWCR = (1<<TWINT) | (1<<TWEN); //clear interrupt flagwhile(!(TWCR & (1<<TWINT))) //check for interrupt flag to have been set to 0 by MCU before continuing{continue;}TWI_Stop();}//Send stopvoid TWI_Stop(void){TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN); //TWINT clears interrupt flag (sets it to 1); TWSTO sends stop bit; TWEN enables TWI}

author: 2N3055
author_rank: Super Contributor
timestamp: September 19, 2016, 07:05:04 am
content: Did you read this one?

author: rx8pilot
author_rank: Super Contributor
timestamp: September 19, 2016, 04:25:41 pm
content: After sending the address, you need to wait until the TWCR INT flag is cleared. Something like.....Code:// Wait for TWINT flag set in TWCR Registerwhile (!(TWCR & (1 << TWINT)));Then load the BYTE and repeat - waiting for the TWINT to clear after each segment transmission and then read back to make sure you are getting the correct status code.EDIT: You already have that line after the BYTE is sent, just do that after each part of the transmission.

author: senso
author_rank: Frequent Contributor
timestamp: September 20, 2016, 12:07:00 pm
content: And if you i2c bus hangs in the right way you will be stuck in that while forever..And if you are doing i2c from scratch, search for Pete Fleury i2c lib for AVR's..I got bit so many times by that while that I'm now using it like this:while(!(TWCR & (1<<TWINT))){count++;_delay_us(15);if(count > 250){return 2;}}They delay was fine tuned so it wont mess with any i2c comm.

author: rx8pilot
author_rank: Super Contributor
timestamp: September 20, 2016, 05:55:30 pm
content: Quote from: senso on September 20, 2016, 12:07:00 pmI totally agree that un-monitored while loops can get you into trouble. There are many ways to go about it too, just pick a robust method. I chose to have the i2c monitored by the timer interrupt so that other tasks can be executed while it is waiting. Same concept though. Great point.