Attiny85 USI I2C
Quote from steinm on January 6, 2024, 5:17 pmQuote from arcachofo on January 6, 2024, 3:57 pmAlso fixed the error in the counter at Rev 2127.
I'm compiling it and will try tomorrow. I also did some more testing on a real Attiny85 and created some diagrams which pretty well show what should happen on SDA and SCL. Once I find some more time, I'll put it all together and post it here.
Concerning the interrupt. We actually talked about different things. I ment the firmware interrupt. Thanks for clarifying.
Quote from arcachofo on January 6, 2024, 3:57 pmAlso fixed the error in the counter at Rev 2127.
I'm compiling it and will try tomorrow. I also did some more testing on a real Attiny85 and created some diagrams which pretty well show what should happen on SDA and SCL. Once I find some more time, I'll put it all together and post it here.
Concerning the interrupt. We actually talked about different things. I ment the firmware interrupt. Thanks for clarifying.
Quote from arcachofo on January 7, 2024, 5:23 amI also did some more testing on a real Attiny85 and created some diagrams which pretty well show what should happen on SDA and SCL. Once I find some more time, I'll put it all together and post it here.
That would be great!
I'm compiling it and will try tomorrow.
Don't worry too much by now, I did some test and there are still a few issues.
I already fixed some of them and now it is clocking out data more or less correctly, but still some timing issue.
Hopefully I will commit something in the next hours.
I also did some more testing on a real Attiny85 and created some diagrams which pretty well show what should happen on SDA and SCL. Once I find some more time, I'll put it all together and post it here.
That would be great!
I'm compiling it and will try tomorrow.
Don't worry too much by now, I did some test and there are still a few issues.
I already fixed some of them and now it is clocking out data more or less correctly, but still some timing issue.
Hopefully I will commit something in the next hours.
Quote from steinm on January 7, 2024, 4:17 pmI compiled 2132 and made some tests. It's already very close to a real Attiny85 but not quite, though it may not be relevant for a working TWI communication.
The following code for creating the start condition produces different timing on the SDA line
/* Release SCL to ensure that (repeated) Start can be performed */
PORT_USI |= (1<<PIN_USI_SCL); // Release SCL.
while( !(PORT_USI & (1<<PIN_USI_SCL)) ); // Verify that SCL becomes high.
_delay_us(T2_TWI);
/* Generate Start Condition */
PORT_USI &= ~(1<<PIN_USI_SDA); // Force SDA LOW.
_delay_us(T4_TWI);
PORT_USI &= ~(1<<PIN_USI_SCL); // Pull SCL LOW.
PORT_USI |= (1<<PIN_USI_SDA); // Release SDA.
SCL is already released, hence the first 3 lines of code doesn't matter (the real Attiny85 doesn't have SCL released, but I couldn't find out why. This might be another problem.) Forcing SDA to low is working and also the next line of code where SCL is pulled low is just fine. That should be sufficient to detect the start condition. But why is SDA released in the same microsec. when SCL is pulled low? There should be some delay, but there is none even if zooming in. Beside that, the real Attiny85 doesn't release SDA at all, even if PORTB0 (SDA) is set to 1. I suppose because the MSB of USIDR is 0. Quote from the Attiny85 documentation
When the output driver is enabled for the SDA pin it will force the line SDA low if the
output of the USI Data Register or the corresponding bit in the PORTB register is zero.After the code above, some data is shifted out, but that is also different from the real Attiny85. It looks like the countervalue 0x0f isn't reached. It ends at 0x0e and overflows one too early. That could be the reason why SCL is released at the beginning of the whole sequence.
I compiled 2132 and made some tests. It's already very close to a real Attiny85 but not quite, though it may not be relevant for a working TWI communication.
The following code for creating the start condition produces different timing on the SDA line
/* Release SCL to ensure that (repeated) Start can be performed */
PORT_USI |= (1<<PIN_USI_SCL); // Release SCL.
while( !(PORT_USI & (1<<PIN_USI_SCL)) ); // Verify that SCL becomes high.
_delay_us(T2_TWI);
/* Generate Start Condition */
PORT_USI &= ~(1<<PIN_USI_SDA); // Force SDA LOW.
_delay_us(T4_TWI);
PORT_USI &= ~(1<<PIN_USI_SCL); // Pull SCL LOW.
PORT_USI |= (1<<PIN_USI_SDA); // Release SDA.
SCL is already released, hence the first 3 lines of code doesn't matter (the real Attiny85 doesn't have SCL released, but I couldn't find out why. This might be another problem.) Forcing SDA to low is working and also the next line of code where SCL is pulled low is just fine. That should be sufficient to detect the start condition. But why is SDA released in the same microsec. when SCL is pulled low? There should be some delay, but there is none even if zooming in. Beside that, the real Attiny85 doesn't release SDA at all, even if PORTB0 (SDA) is set to 1. I suppose because the MSB of USIDR is 0. Quote from the Attiny85 documentation
When the output driver is enabled for the SDA pin it will force the line SDA low if the
output of the USI Data Register or the corresponding bit in the PORTB register is zero.
After the code above, some data is shifted out, but that is also different from the real Attiny85. It looks like the countervalue 0x0f isn't reached. It ends at 0x0e and overflows one too early. That could be the reason why SCL is released at the beginning of the whole sequence.
Quote from steinm on January 7, 2024, 4:48 pmI made another simple test.
unsigned char const tempUSISR_8bit =
(1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)| // Prepare register value to: Clear flags, and
(0x0<<USICNT0); // set USI to shift 8 bits i.e. count 16 clock edges.
unsigned char const tempUSICR =
(0<<USISIE)|(0<<USIOIE)| // Interrupts disabled
(1<<USIWM1)|(0<<USIWM0)| // Set USI in Two-wire mode.
(1<<USICS1)|(0<<USICS0)|(1<<USICLK)| // Software clock strobe as source.
(1<<USITC); // Toggle Clock Port.
while(1) {
USIDR = 0b00010001;
USISR = tempUSISR_8bit;
do {
USICR = tempUSICR;
_delay_us(3);
} while( !(USISR & (1<<USIOIF)) ); // Check for transfer complete.
}
SCL starts with 1 and ends in 0. Hence, the next sequence starts with 0 and ends with 1, and so on. The 16th write to USICR seems to be missing, because the timer overflow was detected too early.
Here is the timing of the real Attiny85
The edge on the SCL line marked with C is missing in the simulation.
I made another simple test.
unsigned char const tempUSISR_8bit =
(1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)| // Prepare register value to: Clear flags, and
(0x0<<USICNT0); // set USI to shift 8 bits i.e. count 16 clock edges.
unsigned char const tempUSICR =
(0<<USISIE)|(0<<USIOIE)| // Interrupts disabled
(1<<USIWM1)|(0<<USIWM0)| // Set USI in Two-wire mode.
(1<<USICS1)|(0<<USICS0)|(1<<USICLK)| // Software clock strobe as source.
(1<<USITC); // Toggle Clock Port.
while(1) {
USIDR = 0b00010001;
USISR = tempUSISR_8bit;
do {
USICR = tempUSICR;
_delay_us(3);
} while( !(USISR & (1<<USIOIF)) ); // Check for transfer complete.
}
SCL starts with 1 and ends in 0. Hence, the next sequence starts with 0 and ends with 1, and so on. The 16th write to USICR seems to be missing, because the timer overflow was detected too early.
Here is the timing of the real Attiny85
The edge on the SCL line marked with C is missing in the simulation.
Quote from arcachofo on January 7, 2024, 4:49 pm
After the code above, some data is shifted out, but that is also different from the real Attiny85. It looks like the countervalue 0x0f isn't reached. It ends at 0x0e and overflows one too early.
You are completely right, fixed at Rev 2133.
But why is SDA released in the same microsec. when SCL is pulled low? There should be some delay, but there is none even if zooming in.
I also noticed that and I'm still scratching my head...
Beside that, the real Attiny85 doesn't release SDA at all, even if PORTB0 (SDA) is set to 1. I suppose because the MSB of USIDR is 0. Quote from the Attiny85 documentation
When the output driver is enabled for the SDA pin it will force the line SDA low if the
output of the USI Data Register or the corresponding bit in the PORTB register is zero.Yes, that makes sense.
And this is related with another issue I don't understand:
I don't understand how exactly SDA and SCL Pins are controlled in TWI mode.This could be clarified running this code in a real device:
#define F_CPU 16000000UL #include <avr/io.h> #include <util/delay.h> int main() { DDRB = 0xFF; USICR = (0<<USISIE)|(0<<USIOIE)| // Interrupts disabled (1<<USIWM1)|(0<<USIWM0)| // Set USI in Two-wire mode. (1<<USICS1)|(0<<USICS0)|(1<<USICLK)| // Software clock strobe as source. (1<<USITC); // Toggle Clock Port. _delay_ms( 1000 ); USICR = 0; while(1){} }
This should set PORTB2 High for 1 second.
Then it could happen that it return to Low or it remains High.
This way I could understand how it works in a real device.
After the code above, some data is shifted out, but that is also different from the real Attiny85. It looks like the countervalue 0x0f isn't reached. It ends at 0x0e and overflows one too early.
You are completely right, fixed at Rev 2133.
But why is SDA released in the same microsec. when SCL is pulled low? There should be some delay, but there is none even if zooming in.
I also noticed that and I'm still scratching my head...
Beside that, the real Attiny85 doesn't release SDA at all, even if PORTB0 (SDA) is set to 1. I suppose because the MSB of USIDR is 0. Quote from the Attiny85 documentation
When the output driver is enabled for the SDA pin it will force the line SDA low if the
output of the USI Data Register or the corresponding bit in the PORTB register is zero.
Yes, that makes sense.
And this is related with another issue I don't understand:
I don't understand how exactly SDA and SCL Pins are controlled in TWI mode.
This could be clarified running this code in a real device:
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
int main()
{
DDRB = 0xFF;
USICR =
(0<<USISIE)|(0<<USIOIE)| // Interrupts disabled
(1<<USIWM1)|(0<<USIWM0)| // Set USI in Two-wire mode.
(1<<USICS1)|(0<<USICS0)|(1<<USICLK)| // Software clock strobe as source.
(1<<USITC); // Toggle Clock Port.
_delay_ms( 1000 );
USICR = 0;
while(1){}
}
This should set PORTB2 High for 1 second.
Then it could happen that it return to Low or it remains High.
This way I could understand how it works in a real device.
Quote from steinm on January 7, 2024, 5:27 pmDDRB = 0xFF;
Sets all ouput pins to low (because PORTB is by default 0).
The first setting of USICR toggles SCL to high and leaves SDA unchanged. Setting USICR to 0 has no impact on SDA and SCL. SDA stays low and SCL stays high. I repeat that over and over again, then SCL toggles whenever USITC is written, but SDA doesn't change.
DDRB = 0xFF;
Sets all ouput pins to low (because PORTB is by default 0).
The first setting of USICR toggles SCL to high and leaves SDA unchanged. Setting USICR to 0 has no impact on SDA and SCL. SDA stays low and SCL stays high. I repeat that over and over again, then SCL toggles whenever USITC is written, but SDA doesn't change.
Quote from arcachofo on January 7, 2024, 5:37 pm
The first setting of USICR toggles SCL to high and leaves SDA unchanged. Setting USICR to 0 has no impact on SDA and SCL. SDA stays low and SCL stays high. I repeat that over and over again, then SCL toggles whenever USITC is written, but SDA doesn't change.
Thank you very much.
That confirms what I suspected.If SCL remains High after USI release the control that means that PORTB register changed.
Or at least that's what I understand... in any case it is what happens in the simulation.
The first setting of USICR toggles SCL to high and leaves SDA unchanged. Setting USICR to 0 has no impact on SDA and SCL. SDA stays low and SCL stays high. I repeat that over and over again, then SCL toggles whenever USITC is written, but SDA doesn't change.
Thank you very much.
That confirms what I suspected.
If SCL remains High after USI release the control that means that PORTB register changed.
Or at least that's what I understand... in any case it is what happens in the simulation.