Hello,
I use ATTINY 402 as I2C Slave. SCL have 100kHz. Invalid Data are readed with default clock (=20/6 MHz). With 20MHz Clock the Data are readed correctly. What is the relationship between Fclk Periph and I2C data speed?
Hello,
I use ATTINY 402 as I2C Slave. SCL have 100kHz. Invalid Data are readed with default clock (=20/6 MHz). With 20MHz Clock the Data are readed correctly. What is the relationship between Fclk Periph and I2C data speed?
Please link to the example code you are using or post your custom code.
From memory, you can achieve 400kHz Slave with 8MHz tiny2313 with USI. So I would expect you can get 1MHz Slave @ 20MHz on a modern tiny402.
You should get 100kHz fine with F_CPU @ 3.33MHz
Untested. I will dig out old project when I see what library code you are using.
David.
Welcome to AVR Freaks
I use ATTINY 402 as I2C Slave. SCL have 100kHz
What are you using as the Master? Are you sure it is really doing 100kHz?
Invalid Data are read with default clock
In what way(s), exactly, is the data "invalid"? How are you observing this?
Do you have an oscilloscope or analyser to see what's actually happening on the wires?
Please see Tip #1 in my signature, below, for how to post source code:
#include <avr/io.h> #include <avr/interrupt.h> #define I2C_ADDRESS 0x80 #define SDATA_LENGHT 4 volatile uint8_t sended; volatile unsigned char sdata[2] [SDATA_LENGHT]; volatile uint8_t valid = 0; //first sdata index to valid data volatile bool sync_lock = false; int main(void) { //enable pullups PORTA.PIN0CTRL = 0x08; PORTA.PIN1CTRL = 0x08; PORTA.PIN2CTRL = 0x08; PORTA.PIN3CTRL = 0x08; //PORTA.PIN4CTRL = 0x08; //PORTA.PIN5CTRL = 0x08; PORTA.PIN6CTRL = 0x08; PORTA.PIN7CTRL = 0x08; //IIC output buffer init for(int i = 0; i < SDATA_LENGHT; i++) { sdata[0] [i] = 0x30 + i; sdata[1] [i] = 0x30 + i; } //IIC init TWI0.SADDR = I2C_ADDRESS; TWI0.SCTRLA = 0xE2; //all IIC interrupts enabled, smart mode TWI0.SCTRLA |= 0x01; //IIC enable sei(); while (1) { } } //*************************************************************************************** //IIC void SendByte() { TWI0.SDATA = sdata[valid] [sended]; if (++sended >= SDATA_LENGHT) { sended = SDATA_LENGHT; } } ISR(TWI0_TWIS_vect) { if (TWI0.SSTATUS & 0x40) //APIF interrupt { if (TWI0.SSTATUS & 0x01) { //address detection if (TWI0.SSTATUS & 0x02) //direction { //read sync_lock = true; sended = 0; TWI0.SCTRLB = 0x03; //ACK } else { //write not supported TWI0.SCTRLB = 0x07; //NACK } } else { //STOP condition TWI0.SCTRLB = 0x02; sync_lock = false; } } if (TWI0.SSTATUS & 0x80) //DIF interrupt { if (TWI0.SSTATUS & 0x02) //direction { //read if (sended) { if (TWI0.SSTATUS & 0x10) { //NACK received TWI0.SCTRLB = 0x07; } else { //ACK received SendByte(); //send next byte } } else { SendByte(); //send first byte } } else { //write not supported TWI0.SCTRLB = 0x07; //NACK } } }
Master: FT4232H, MPSSE used
I2C monitored by logic analyzer. SCL have period = 10 020 ns. Master and analyzer reads the same result 0x06, 0x3F, 0xFF, 0xFF. All bytes are confirmed by ACK.
Master and analyzer reads the same result 0x06, 0x3F, 0xFF, 0xFF.
So what does the Slave see, and how are you observing that?
EDIT
Sorry, which direction are you calling "read" here?
Master (FT4232H) read from Slave (ATTINY402).
Have you used the debugger to see what's happening inside the Tiny ?
Please post your Master test code. I normally just put it in an Arduino sketch.
i.e send some known command / data.
Receive data.
Compare what I received with what I expected.
Write the whole "test suite" like above. Stop when the comparison fails.
When all the tests pass. Print "done"
I will run your code on a Tiny817 later this afternoon.
David.
JTAG ICE 3 connected to UPDI.
and what have you learned from using that?
I went through it several times and I don't see a mistake. Write step by step what you would do.
I tried a 100kHz Master from a Uno with a Slave running on a Tiny817.
I altered the ISR() to support SLAVE_W. e.g. to control the valid variable. (and initialised sdata[1] = "5678" )
It ran fine at 20MHz div6 i.e. 3.33MHz
It ran ok at 20MHz div32 i.e. 625kHz but the Slave was very visibly stretching SCL.
It failed at 20MHz div48 i.e. 417kHz
It seems a fairly pointless Slave functionality. What do you really want to do ?
Do you have an official test suite ?
David.
Post your code, please.
I tested following combinations:
1. 20MHz with div2 and SCL 100kHz - OK
2. 20MHz with div4 and SCL 100kHz - Fail
3. 20MHz with div6 and SCL 25kHz - OK
When communication fails, Address Packet is accepted, ACK is sent from my slave. After this is first Data Packet send - at BUS is invalid. After first Data Packet is RXACK flag in SSTATUS set even though the master has sent an ACK.
I still tried to edit my Slave for writing. With 20MHz (no divided clock) and SCL 100kHz work fine. With default clock SDA line is corrupted. Slave Write NACK.
There are two ADCs on the same bus that communicate without problems.
Need measuring two signals frequency (<110Hz).
I don't have an official test suite.
I still wondered if there was a conflict with the galvanic separation ADUM1251.
Here is my Slave
/* * i2c_slave_t402.c * * Created: 13-Jan-22 14:19:56 * Author : David Prentice */ #include <avr/io.h> #include <avr/interrupt.h> #include <stdbool.h> #define I2C_ADDRESS 0x80 // 7-bit address 0x40 #define SDATA_LENGHT 4 volatile uint8_t sended; volatile unsigned char sdata[2] [SDATA_LENGHT]; volatile uint8_t valid = 0; //first sdata index to valid data volatile bool sync_lock = false; int8_t CLKCTRL_init() { uint8_t n; #if defined(__AVR_ATtiny817__) n = CLKCTRL_CLKSEL_OSC20M_gc | 1 << CLKCTRL_CLKOUT_bp; //PB5 #else n = CLKCTRL_CLKSEL_OSC20M_gc; //t402 has no CLKOUT pin #endif CCP = CCP_IOREG_gc; CLKCTRL.MCLKCTRLA = n; while (CLKCTRL.MCLKSTATUS & CLKCTRL_SOSC_bm) ; //n = 0 << CLKCTRL_PEN_bp; //div1 i.e. no prescale 20MHz //n = CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm; //div2 i.e. 10MHz n = CLKCTRL_PDIV_4X_gc | CLKCTRL_PEN_bm; //div4 i.e. 5MHz //n = CLKCTRL_PDIV_6X_gc | CLKCTRL_PEN_bm; //div4 i.e. 3.33MHz //n = CLKCTRL_PDIV_32X_gc | CLKCTRL_PEN_bm; //div16 i.e. 1.25MHz CCP = CCP_IOREG_gc; CLKCTRL.MCLKCTRLB = n; n = 0 << CLKCTRL_LOCKEN_bp; CCP = CCP_IOREG_gc; CLKCTRL.MCLKLOCK = n; CLKCTRL.OSC20MCTRLA = 0 << CLKCTRL_RUNSTDBY_bp; CLKCTRL.OSC32KCTRLA = 0 << CLKCTRL_RUNSTDBY_bp; #if defined(__AVR_ATtiny817__) //t402 has no XOSC32K CLKCTRL.XOSC32KCTRLA = CLKCTRL_CSUT_1K_gc; #endif return 1; } int main(void) { CLKCTRL_init(); //prescale 20MHz RC. CLKOUT on PB5 #if defined(__AVR_ATtiny817__) PORTMUX.CTRLB |= PORTMUX_TWI0_ALTERNATE_gc; //use PA1,PA2 [SDA, SCL] on t817 #endif //enable pullups .kbv I don't know why PORTA.PIN0CTRL = 0x08; PORTA.PIN1CTRL = 0x08; PORTA.PIN2CTRL = 0x08; PORTA.PIN3CTRL = 0x08; //PORTA.PIN4CTRL = 0x08; //PORTA.PIN5CTRL = 0x08; PORTA.PIN6CTRL = 0x08; PORTA.PIN7CTRL = 0x08; //IIC output buffer init for(int i = 0; i < SDATA_LENGHT; i++) { sdata[0] [i] = 0x30 + i; //"0123" sdata[1] [i] = 0x35 + i; //"5678" } //IIC init TWI0.SADDR = I2C_ADDRESS; TWI0.SCTRLA = 0xE2; //all IIC interrupts enabled, smart mode. DIEN APIEN PIEN SMEN TWI0.SCTRLA |= TWI_ENABLE_bm; //IIC enable sei(); while (1) { asm("nop"); // somewhere to break } } //*************************************************************************************** //IIC void SendByte() { TWI0.SDATA = sdata[valid] [sended]; if (++sended >= SDATA_LENGHT) { sended = SDATA_LENGHT; } } ISR(TWI0_TWIS_vect) { if (TWI0.SSTATUS & 0x40) //APIF interrupt { if (TWI0.SSTATUS & TWI_AP_bm) { //address detection if (TWI0.SSTATUS & TWI_DIR_bm) //direction { //read sync_lock = true; sended = 0; TWI0.SCTRLB = TWI_SCMD_gm | (0 << TWI_ACKACT_bp); //ACK } else { //write not supported //TWI0.SCTRLB = TWI_SCMD_gm | (1 << TWI_ACKACT_bp); //NACK //.kbv enable ADDR_W TWI0.SCTRLB = TWI_SCMD_gm | (0 << TWI_ACKACT_bp); //ACK } } else { //STOP condition TWI0.SCTRLB = (2 << TWI_SCMD_gp); //COMPTRANS sync_lock = false; } } if (TWI0.SSTATUS & TWI_DIF_bm) //DIF interrupt { if (TWI0.SSTATUS & TWI_DIR_bm) //direction { //read #if 0 uint8_t c = 'X'; //return X for overruns if (sended < SDATA_LENGHT) c = sdata[valid][sended++]; TWI0.SDATA = c; // I don't see why a Slave sets TWI_ACKACT if ((TWI0.SSTATUS & TWI_RXACK_bm)) { //NACK received TWI0.SCTRLB = TWI_SCMD_gm | (1 << TWI_ACKACT_bp); } #else if (sended) { if (TWI0.SSTATUS & TWI_RXACK_bm) { //NACK received TWI0.SCTRLB = TWI_SCMD_gm | (1 << TWI_ACKACT_bp); } else { //ACK received SendByte(); //send next byte } } else { SendByte(); //send first byte } #endif } else { //write not supported //TWI0.SCTRLB = TWI_SCMD_gm | (1 << TWI_ACKACT_bp); //NACK //.kbv enable DATA_W TWI0.SCTRLB = TWI_SCMD_gm | (0 << TWI_ACKACT_bp); //ACK valid = TWI0.SDATA & 0x01; } } }
And this is an Arduino sketch to test it.
#include <Wire.h> const int SLAVE = 0x80 >> 1; //Wire uses 7-bit addressing void setup() { Serial.begin(9600); Wire.begin(); } void loop() { static uint8_t cnt; Wire.beginTransmission(SLAVE); Wire.write(cnt++); //toggles 'valid' on odd values Wire.endTransmission(); int n = Wire.requestFrom(SLAVE, 4); //ask for more to view overruns Serial.print(n); Serial.println(" bytes"); while (Wire.available()) // slave may send less than requested { char c = Wire.read(); // receive a byte as character Serial.print(c); // print the character } Serial.println(); delay(5000); }
Note that your Tiny402 does not need Alternate SDA, SCL. And you don't have a CLKOUT pin.
My DATA_R logic looks simpler than yours. Edit the #if 0
Obviously I have external pullup resistors as Nature intended. (4k7)
And I was running at a civilised 3.3V. I can change Tiny817 and "Uno clone" to 5V if you want.
David.
Edit. Edited the Slave code conditionals to compile with Tiny402 out of the box.
I connected ATTINY404 directly to I2C from FT4232H. The problem persists, but I was able to measure what was happening. ATTINY keeps the SCL on Low for the duration of the interrupt servicing. The FT4232H cannot work with this. If Fclk Per is small, this condition lasts a long time and several SCL pulses are lost.
The search for the cause has been complicated by the use of the ADUM1251 galvanic isolation, where the SCL is unidirectional. The logic analyzer was connected only on the Master side where everything seemed fine.
The ATtiny402 is an 8-pin device with SDA , SCL on PA1, PA2
The ATtiny404 is a 14-pin device with SDA , SCL on PB1, PB0.
Neither have CLKOUT.
FT4232H seems to be a multi-role USB chip.
ADUM1251 seems to be an I2C isolator with only SDA bidirectional.
I suggest that you always start tests with regular I2C bus.
When regular is shown to be ok:
I would hope that you can bench test with common GND.
And the Logic Analyser can show both sides of the ADUM1251.
It seems very odd that SCL is unidirectional. After all, I2C requires SCL clock stretching, ...
Yes, any MCU-style Slave will hold SCL through the ISR(). All Masters should cope with this.
I could post Saleae traces for the F_CPU=1.25MHz. The ACK response is noticeably delayed. After all, the ISR() takes a few cycles.
Since you have a Logic Analyser you have probably seen this for yourself.
It is very easy to implement your "Master tests" with an Arduino. And it means that anyone in the world can replicate (if they have a tiny402, 404, 406, 414, 417, ... or even any of the mega4809, avr128xxx chips
David.
Looking at the ADUM1250 / ADUM1251 datasheet, the ADUM1250 has bidirectional SCL and has the same package and pinout.
I suggest that you use the ADUM1250.
But you can test the FT4232 chip without the isolator. I bet that it understands clock stretching.
David.
The search for the cause has been complicated by ...
As David says, that's the reason why it's always best to start simple.
Get the basics working first, then - and only then - move on to adding complications.
Also note that you had never previously mentioned anything about using an ADUM1251 - so there's no way we could have foreseen that as a potential problem.
This is why posting a full schematic is always helpful.