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;
...
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:
Normaly the start condition should work:
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
PORTregister: