TWI Master SCL & SDA stuck low

Go To Last Post
4 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am trying to implement an interrupt driven TWI master in an ATMega328P using the AVR315 app. note as a guide. I am using Atmel Studio 6.1 beta and an Arduino Pro 328 (with the Arduino bootloader) as the dev. board.

Everything works fine when I talk to a slave that exists on the bus. The problem is that when I try to read or write to a non-existent slave the SDA and SCL lines are stuck low following the NACK to the address. They stay low until the next write to the bus when they go high (SDA then SCL) right before the next start condition.

The code from AVR315 doesn't do any error handling for this condition (TWI_MTX_ADR_NACK and TWI_MRX_ADR_NACK) and it just terminates the ISR state machine as normal.

I have tried to make an I2C stop condition happen following the address NACK by setting TWSTO but it seems that the TWI controller will not release the bus lines. Setting bit-7 in TWDR will set SDA high which confirms that the drivers are still active. The only way I've been able to release the lines is to momentarily disable the TWI controller which doesn't result in a proper stop condition on the bus.

The datasheet is not clear on what should be done when an address is NACKed. The suggested responses listed in Table 21-2 for status code 0x20 (addr. NACK) match those for code 0x18 (addr. ACK).

I want to have the master handle this situation properly in the event that a slave can't be reached. Can anyone think of what may be happening here?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well it seems I found a workable solution by poking through the code to the Arduino wire library. It seems that after setting TWSTO, you have to wait for it to clear before exiting the ISR.

TWCR = (1 << TWEN) | (0 << TWIE) | (1 << TWINT) |
       (0 << TWEA) | (0 << TWSTA) | (1 << TWSTO) |
       (0 << TWWC);
				   
while(TWCR & _BV(TWSTO));

Is this documented anywhere?

I don't care much for the delay inside the ISR (~10us) but this is an exceptional case that isn't normally executed.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

interesting.
first you should never have to wait in an ISR. For now you do not car, but in future it might give you a lot of unexpected problems when the system is more complicated.

I use the TWI master implementation AVR315 without this change nedeed.
What I have changed though (If I made my comments right) is that when you haev a bus error the original code is:

		asm("nop");
		
      TWCR = (1<<TWEN)|                                 // Enable TWI-interface and release TWI pins
      (0<<TWIE)|(0<<TWINT)|                      // Disable Interupt
      (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|           // No Signal requests
      (0<<TWWC);                                 //

and I changed it to:

      TWCR = (1<<TWEN)|                                 // Enable TWI-interface and release TWI pins
             (0<<TWIE)|(1<<TWINT)|                      // Disable Interupt
             (0<<TWEA)|(0<<TWSTA)|(1<<TWSTO)|           // No Signal requests need to stop transmission
             (0<<TWWC);                                 //

So you need to send a stop condition when you have an error to reset the peripheral.
No need to wait for anything inside the ISR....

hope this helps.

regards

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kevinpt wrote:
Well it seems I found a workable solution by poking through the code to the Arduino wire library. It seems that after setting TWSTO, you have to wait for it to clear before exiting the ISR.

Is this documented anywhere?

Sending a STOP condition does not generate an interrupt. But the TWSTO flag gets cleared automatically when STOP condition is transmitted.
So even if you don't use interrupts, after transmitting a STOP with TWSTO bit, you cannot poll TWINT bit to see when the STOP is sent, but you poll TWSTO bit to go low. The fact that interrupt is not generated after stop condition is not emphasized in the datasheet.

The datasheet suggests you can proceed different ways after receiving a NAK. Sending a STOP after NAK is no different than sending a STOP after ACK. So if your code works at all when sending a STOP, it should work now too. How does it now work?

One thing that caught my eye in the datasheet is that to recover from a bus error you could set TWSTO and TWSTART at the same time to recover from a bus error, the hardware will send STOP and you will get an interrupt after the START is sent. Perhaps this is useful? Or perhaps you should just set TWSTO, and in the main code don't initiate another transmission if TWSTO is still set.