Forum: Mikrocontroller und Digitale Elektronik TWI/I2C am ATtiny417


von attiny417 (Gast)


Lesenswert?

Hallo,

Ich benötige einen I2C Slave auf einem ATTiny417. Leider ändert sich 
beim einem Broadcast auf alle Adressen vom Master im SSTATUS Register 
nichts. Ich mutmaße das ich den Clock, Port oder CPUINT nicht richtig 
configuriert habe.

Was muss ich bei den Ports einstellen? Genügt das MUX oder muss ich den 
Port auf Output oder input stellen?
1
PORTMUX_CTRLB = 0x10;  // twi0

Bei den Clocks habe ich nichts geändert. CLK_MAIN dürfte auf 20MHz 
stehen. Der Prescaler steht auf 6. Aber wo kann ich den Takt für CLK_PER 
und CLK_CPU einstellen/aktivieren? Oder hängen beide direkt am Prescaler 
und haben nun 3,33Mhz?


Initialisierung:
1
      TWI0_SADDR = 0x11 << 1;
2
      //TWI0_SADDRMASK = 0xff;
3
      TWI0_SCTRLA = 0x71;
4
      TWI0_SCTRLB = 0x7;

Abfrage um festzustellen ob Adresse stimmt
1
      if(twi_test_ap() | twi_test_apif()) {
2
        REG->wr_bits.led_vtr1 = ~REG->wr_bits.led_vtr1;
3
        twi_ackact();
4
        state = (TWI0_SDATA & 1) ? DATX : DATX;
5
      }

von Rubimix (Gast)


Lesenswert?

Hmm,
wenn Du über

TWI0_SCTRLA = 0x71;

die Interrupts aktivierst, brauchst Du eine ISR. Hast Du die?
Ansonsten noch: das obere Nibble (die 7) geht nach DB auch mit 
mindestens einem Bit ins Nirvana.

von attiny417 (Gast)


Lesenswert?

>>Hmm,
>>wenn Du über
>>
>>TWI0_SCTRLA = 0x71;
>>
>>die Interrupts aktivierst, brauchst Du eine ISR. Hast Du die?
>>Ansonsten noch: das obere Nibble (die 7) geht nach DB auch mit
>>mindestens einem Bit ins Nirvana.

Nein habe ich nicht. Ich habe auch nicht vor den Slave über interrupt 
service routinen zu Behandeln. Ich wollte nur das "SSTATUS" Register 
abfragen. Damit ich aber die Bits im SSTATUS Register nutzen kann muss 
ich doch diese im TWI0_SCTRLA aktivieren oder?

von Rubimix (Gast)


Lesenswert?

Nee, Die Statusflags werden auch ohne die aktivierten Interrupts 
gesetzt.
Andersrum: Wenn Du ohne ISR die Interrupts aktivierst, geht der 
Controller in der Regel auf die Klappe (führt bei jedem Interrupt zum 
Reset, weil in der Interrupttabelle kein gültiger Vektor steht).
Fazit: Setz das SCTRLA mal auf 0x01 (Nur das Enable-Bit)

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ob dein ATtiny417 mit 3,33MHz taktet hängt davon ab ob du das richtig 
eingestellt hast.
Interner MainClock sollte 20MHz sein und dein Prescaler 6 ist schon 
korrekt.
Der MainClock kann aber auch per Fuse zwischen 16 und 20MHz umgestellt 
werden.
Musste nachschauen um sicherzugehen oder am ClockOut nachmessen.
Siehe Manual ab 10.3.3.
Hast du auch den Registerzugriffsschutz beachtet vor deren 
Registeränderung für die Takteinstellungen?
CPU_CCP = CCP_IOREG_gc;
Siehe Manual 10.3.5.

Wegen Interrupt. Wenn man keine zugehörige ISR hat, darf man auch nicht 
die Interrupts aktivieren. Sonst springt das Programm an der Stelle ins 
Nirwarna. Die Statusbits kann man auch so abfragen und muss dann ggf. 
händisch löschen.

von attiny417 (Gast)


Lesenswert?

Bei den Interrupts/Status Register sei dazu gesagt. Ich möchte keine 
Interrupt Routinen verwenden (ich möchte nur TWI0.SSTATUS auswerten), 
daher frage ich mich ob ich wirklich CPUINT configurieren muss?

von attiny417 (Gast)


Lesenswert?

>>Hast du auch den Registerzugriffsschutz beachtet vor deren
>>Registeränderung für die Takteinstellungen?
>>CPU_CCP = CCP_IOREG_gc;
Ich habe diese Einstellung gelassen. Ich habe am Clock nichts geändert. 
Das Fusebit OSCCFG.FREQSEL steht auf 20MHz (sagt MC Studio beim 
auslesen).

Bezüglich des SSTATUS Registers habt ihr recht :D
Ich habe die Interrupt sachen rausgenommen.

meine Initialisierung:
1
//   PB0    I2C_CLK
2
//  PB1    I2C_SDA
3
// PB{0,1} ist aktuell als Input definiert
4
5
  PORTMUX_CTRLB = 0x10;  // twi0
6
  TWI0_SADDR = 0x10 | 0x01;
7
  TWI0_SCTRLB = 0x7;
8
  TWI0_SCTRLA = TWI_ENABLE_bm;
1
    case IDLE:      
2
      if(twi_test_ap() | twi_test_apif()) {
3
        REG->wr_bits.led_vtr1 = ~REG->wr_bits.led_vtr1; // DEBUG
4
        twi_ackact();
5
        state = (TWI0_SDATA & 1) ? DARX : DATX;
6
      }
7
    break;
8
9
.
10
.
11
.
12
13
/* Address or Stop */
14
ui8 twi_test_ap() {
15
  return (TWI0_SSTATUS & TWI_AP_bm) ? 1:0;
16
}
17
18
/* Address or Stop Interrupt Flag */
19
ui8 twi_test_apif() {
20
  return (TWI0_SSTATUS & TWI_APIF_bm) ? 1:0;
21
}

von Rubimix (Gast)


Lesenswert?

Veit D. schrieb:
> Sonst springt das Programm an der Stelle ins
> Nirwarna.

Ich hab's gerade noch mal im Listing nachgeschaut.

Ohne die Definition eines ISR(BADISR_vect){}...
00000054 <__bad_interrupt>:
54:  d5 cf         rjmp  .-86       ; 0x0 <__vectors>

...rennt der Controller wohldefiniert auf den Resetvektor 0.


Mit ISR(BADISR_vect){}...
00000054 <__bad_interrupt>:
54:  00 c1         rjmp  .+512      ; 0x256 <__vector_default>
... rennt er ebenso wohldefiniert auf die ISR(BADISR_vect)).

Nirvana war gestern ;)

von attiny417 (Gast)


Lesenswert?

>> Nirvana war gestern ;)
Zu einem Reset ist es bei mir tatsächlich auch nicht gekommen :D

Mein SSTATUS Register steht immer auf 0x00. Es ändert sich leider nie 
etwas. Mir kommt so so vor als ob der Clock fehlt oder ob es nicht 
aktiviert ist oder ob die I2C Signale nicht an der I2C Logik ankommen.
Beim SAM Controller musste ich explizit CLocks aktivieren und die 
Logiken einzeln zuschalten. Bin mir jetzt wirklich nicht sicher ob das 
beim Tiny417 schon auch so ist.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

wenn vermutlich nichts taktet, dann musste das prüfen. Programmiere eine 
blinkende LED und du weißt es und kannst deren Takt messen ob alles 
passt. Wenn du das ohne hartes delay machst, kannste die 
"Arbeitsanzeige" auch weiterverwenden wenn das Programm wächst.

Anonsten solltest du das komplette Programm zeigen, möglichts auf das 
Wesentliche reduziert was den Fehler noch zeigt. Kannst du für die 
Registerkonfig statt Hex auch Bitnamen bzw. Bitnummern verwenden? Das 
wäre leichter lesbar. Veroderte Hexwerte habe ich noch nicht gesehen.

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

Keine Ahnung, was Du da für Zahlen wohin schreibst. Ein funktionierendes 
Beispiel für einen ATtiny416 sieht so aus:
1
#define MY_ADDRESS    0x20
2
#define BIT(x)    (1 << x)
3
#define SCL_PIN    2   // PA2
4
#define SDA_PIN    1   // PA1
5
6
uint8_t tw_status, tw_cmd = 0;
7
8
void init_iic()
9
{
10
  PORTA_DIRCLR = (BIT(SCL_PIN) | BIT(SDA_PIN));   // auf Eingang
11
  PORTA_INTFLAGS = (BIT(SCL_PIN) | BIT(SDA_PIN)); // löschen
12
  PORTA_PIN1CTRL = PORT_PULLUPEN_bm;              // mit pullup
13
  PORTA_PIN2CTRL = PORT_PULLUPEN_bm;              // mit pullup
14
15
  PORTMUX.CTRLB |= PORTMUX_TWI0_bm;                // PA1+PA2 als alternative 
16
  TWI0_SCTRLA = TWI_ENABLE_bm;
17
  TWI0_SADDR  = MY_ADDRESS;
18
}

von attiny417 (Gast)


Lesenswert?

Hallo,

Ich habe den MAIN_CLK auf PB5 gelegt und er lag in etwa bei 3,3 MHz

Hier nun das gesamte Program. Es macht nichts weiteres als zu blinken 
und es sollte bei der erkannten slave adresse LED2 einschalten. Dies 
passiert leider nie. Generell bleibt TWI0_SSTATUS immer 0x00.
1
#include <avr/io.h>
2
3
#define MY_ADDRESS    0x20
4
#define BIT(x)    (1 << x)
5
//#define LED1     6  // PA6
6
//#define LED2     7  // PA7
7
#define SCL_PIN    0   // PB0
8
#define SDA_PIN    1   // PB1
9
10
#define LED1      0x40
11
#define LED2      0x80
12
13
14
uint8_t tw_status, tw_cmd = 0;
15
16
void init_iic()
17
{
18
  PORTB_DIRCLR = (BIT(SCL_PIN) | BIT(SDA_PIN));   // auf Eingang
19
  PORTB_INTFLAGS = (BIT(SCL_PIN) | BIT(SDA_PIN)); // löschen
20
  PORTB_PIN1CTRL = PORT_PULLUPEN_bm;              // mit pullup
21
  PORTB_PIN2CTRL = PORT_PULLUPEN_bm;              // mit pullup
22
  PORTMUX.CTRLB |= PORTMUX_TWI0_bm;                
23
  TWI0_SCTRLA = TWI_ENABLE_bm;
24
  TWI0_SADDR  = MY_ADDRESS;
25
}
26
27
void update_iic() {
28
  if((TWI0_SSTATUS & TWI_APIF_bm) || (TWI0_SSTATUS & TWI_AP_bm))  {
29
    PORTA.OUTTGL = LED2;
30
  }
31
}
32
33
int main(void)
34
{
35
  init_iic();
36
  
37
  //PORTA_DIRSET = (BIT(LED1)|BIT(LED2));
38
  PORTA_DIRSET = LED1|LED2;
39
  
40
    while (1) 
41
    {
42
    
43
    update_iic();
44
    
45
    {
46
      // debug
47
      static uint32_t cnt = 0;
48
      if(cnt>25000) {
49
        cnt = 0;
50
        PORTA.OUTTGL = LED1;
51
      }
52
      cnt++;
53
    }
54
    }
55
}

von m.n. (Gast)


Lesenswert?

Wenn Du PB0 und PB1 verwenden willst, darfst Du den Multiplexer 
bezüglich TWI nicht aktivieren.

von attiny417 (Gast)


Lesenswert?

>>Wenn Du PB0 und PB1 verwenden willst, darfst Du den Multiplexer
>>bezüglich TWI nicht aktivieren.
Danke sehr. Jetzt lleuchtet die LED und der bus ist gestört weil mein 
slave nicht korrekt anwortet :D

Aber wieso darf ich PB0/1 nicht zu TWI0 multiplexen?

von attiny417 (Gast)


Angehängte Dateien:

Lesenswert?

Ich verstehe nicht was dieses Flag bewirkt.

Was ist denn mit Alternative gemeint? offensichtlich wird hier nicht 
PB{0,1} auf TWI0 gemuxt.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

default für I2C ist Pin PB0 und PB1.
Mit Portmux liegt I2C auf PA0 und PA1.
Das heißt wenn du im PORTMUX.CTRLB das "TWI0" Bit setzt, dann ist I2C 
auf PA0, PA1 aktiv.
Was du aber nicht machen darfst, wenn du auf Port.A deine Leds 
betreibst.
Also Portmux für I2C nicht einschalten!
Das geht auch aus der Tabelle 5.1 hervor.

von attiny417 (Gast)


Lesenswert?

Vielen Dank für die Erklärung. Ja zu Fußnote 3 ist der Hinweis mit 
PORTMUX. Das habe ich komplett falsch verstanden. Ich dachte 
defaultmäßig sind die Pins erstmal an den PORT{A,B,C} gemuxt und wenn 
ich was anderes haben möchte nutze ich PORTMUX. Ich glaube diese Fehler 
hätte ich nie gefunden :D

von Veit D. (devil-elec)


Lesenswert?

Hallo,

kleine Minikorrektur. Mit Portmux liegt I2C natürlich auf PA1/PA2.  ;-)
Haste bestimmt selbst schon bemerkt. Weitermachen ...

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.