2-wire: TWINT is never set in TWCR after start condition is sent

81 Views Asked by At

I'm using the ATmega328P TWI interface as follows.

The problem is that it goes into an infinite loop strobing the LED, and never turns the LED off. If I change the condition to TRUE then it turns the LED off and continues.

I've made it explicit which bits are being set; let me know what more detail would be helpful.

PASS_T twi_master_tx(U8_T addr, U8_T* data, SIZE_T len)
{
    /* Set TWINT, TWSTA, and TWEN */
    TWCR.byte = (1u << 7) | (1u << 5) | (1u << 2);

    /* Wait for start condition to be sent. */
    while (FALSE == TWCR.bits.TWINT) {
        dsc_led_toggle(DSC_LED_CANBOARD_1);
    }
    dsc_led_set(DSC_LED_CANBOARD_1, OFF);
    ...

It compiles to

    1dc6:   84 ea           ldi r24, 0xA4   ; 164
    1dc8:   80 93 bc 00     sts 0x00BC, r24 ; 0x8000bc <__DATA_REGION_ORIGIN__+0x5c>
    1dcc:   80 91 bc 00     lds r24, 0x00BC ; 0x8000bc <__DATA_REGION_ORIGIN__+0x5c>
    1dd0:   90 e0           ldi r25, 0x00   ; 0
    1dd2:   87 ff           sbrs    r24, 7
    1dd4:   14 c0           rjmp    .+40        ; 0x1dfe <twi_master_tx.constprop.15+0x38>

So we know that the register (0x00BC) is being set, the register is being read, and the TWINT bit (7) is being tested.

What am I missing? I thought I'm following the datasheet to the letter (in particular, page 185), but TWINT never gets set to indicate end of START transmission.

According to the datasheet (page 180), SCL frequency = CPU clock / (16 + 2 * TWBR * Prescale). I have TWBR and Prescale set to 0, so I should be getting 16-MHz / 16 = 1-MHz.

Note: I'm not using the avr-gcc standard libraries, I'm using my own because this is a learning exercise. I'm aware that bit fields are not recommended, but I think we see that the assembly generated is correct. Here is how my registers are linked:

SECTIONS {
  /* TWI */
  TWBR          = 0xB8;
  TWSR          = 0xB9;
  TWAR          = 0xBA;
  TWDR          = 0xBB;
  TWCR          = 0xBC;
  TWAMR         = 0xBD;
  ...
1

There are 1 best solutions below

0
sunriax On

Take a look at the datasheet on Page 199:

Have you set everything? Is the baudrate defined correctly, is the bus free or are there other masters? Check the signals on the bus with an oscilloscope!

Check if the bitrate and the prescaler are set correctly:

TWBR = ???;
TWSR = ???;

Normaly the start condition should work:

TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // Start a TWI transmission
                            
// Check if START done
while(!(TWCR & (1<<TWINT)))
  asm volatile("NOP");

Also it is necessary to pull up SCL and SDA to VCC. This can be done with the internal pull-ups or with external ones. Internal pullups can be enabled through the PORT register:

PORTX = (1<<SCL_PIN_POSITION) | (1<<SDA_PIN_POSITION);

// For TQFP/MLF Package:
PORTC = (1<<PINC5) | (1<<PINC4);

Note: With internal pull-ups enabled the bus speed should be screwed down a bit. Otherwise errors in transmission can be the result.