Forum: Compiler & IDEs USI Probleme am ATtiny2313


von Karl W. (karlw)


Lesenswert?

Hallo,

ich bekomme die USI im 3-wire-mode bei einem ATtiny2313 einfach nicht zu 
laufen. Auf Interrupts möchte ich hierbei verzichten, da sie eventuell 
in andere Programmteile "reinhauen" könnten, daher habe ich folgende 
einfache Implementierung zum Testen gewählt (nach ASM-Beispiel aus 
Datasheet):
1
#include <avr/io.h>
2
3
#define USI_DDR       DDRB
4
#define USI_PORT      PORTB
5
#define USI_PIN        PINB
6
#define USI_SS        4
7
#define USI_DO        5
8
#define USI_DI        6
9
#define USI_SCK        7
10
11
void wait (void);
12
13
int main (void)
14
{
15
  unsigned int i;
16
17
  //Initialization
18
  USI_DDR |= ((1<<USI_DO)|(1<<USI_SCK)|(1<<USI_SS));
19
  USI_DDR &= ~(1<<USI_DI);
20
  USI_PORT |= (1<<USI_SS);
21
22
  while(1)
23
  {
24
    USI_PORT &= ~(1<<USI_SS);  //start frame by pulling SS low
25
26
    USIDR = 170;
27
28
    for(i=0;i<=7;i++)
29
    {
30
      USICR = (1<<USIWM0)|(1<<USICS0)|(1<<USITC);
31
      wait();
32
      USICR = (1<<USIWM0)|(1<<USICS0)|(1<<USICLK)|(1<<USITC);
33
      wait();
34
    }
35
36
    USI_PORT |= (1<<USI_SS);  //end frame by pulling CS high
37
    wait();
38
  }
39
  
40
  return(0);
41
}
42
void wait (void)
43
{
44
  volatile unsigned long l = 50000;
45
46
  while(l>0) l--;
47
}

Der Taktpin toggelt und das CS-Signal wird auch ordentlich generiert. 
Leider bleibt der Datenpin konstant "low"?! Was ist los, habe ich 
Tomaten auf den Augen?!

Gruß Karl

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Hmm, ich blicke gerade mit den einzelnen Optionen nicht ganz durch.
Nur schnell: hier ein Code, der zumindest auf dem ATtiny84
funktioniert:
1
static uint8_t
2
SPITransfer(uint8_t d)
3
{
4
        USIDR = d;
5
        USISR = _BV(USIOIF);
6
        do {
7
                USICR = _BV(USIWM0) | _BV(USICS1) |
8
                        _BV(USICLK) | _BV(USITC);
9
        } while ((USISR & _BV(USIOIF)) == 0);
10
        return USIDR;
11
}
Was auffällt ist, dass ich USICS1 benutze, du USICS0.  Deine Variante
dürfte irgendwas sein, wo Timer/Counter 0 den Takt treibt, oder?

von Karl W. (karlw)


Lesenswert?

Vielen Dank Jörg,

Dein Code funktioniert aber leider auch nicht. Die USI-Optionen 
irritieren mich auch, ich habe schon folgendes probiert:

1) USICS1 und USICS0 sind gelöscht / USICLK und USITC generieren Takt

Meiner Meinung nach ist das die software clock strobe Variante. Das 
gleiche Problem, Daten bleiben konstant low!

2) USICS0 oder USICS1 gesetzt / USICLK und USITC generieren Takt

Ich habe mehrere Codebeispiele (Datenblatt+Forum) gefunden, in denen 
unterschiedlich USICS0 oder USICS1 gesetzt werden. Das gleiche Problem, 
Daten bleiben konstant low!

3) USICS0 oder USICS1 gesetzt / USICLK oder USITC generieren Takt

Egal ob ich USICLK oder USITC setze, die Daten bleiben low?!

Wie Du also siehst, habe ich schon alle möglichen Kombinationen 
ausprobiert und KEINE hat funktioniert?!

Viele Grüße
Karl

PS: Zwischenzeitlich habe ich den DO-Pin mal ohne USI per PORT auf 
HIGH/LOW gezogen. Diese Funktionialität ist nicht kaputt... ;-)

von Karl W. (karlw)


Lesenswert?

Hallo,

in der Zwischenzeit habe ich mir eine komplette Software-SPI 
geschrieben, dann bin ich auch flexibler mit den Pins und es 
funktioniert wenigstens... ;-)
1
void SPI_Init (void)
2
{
3
  //Initialization
4
  SPI_DDR |= ((1<<SPI_DO)|(1<<SPI_SCK)|(1<<SPI_SS));
5
  SPI_DDR &= ~(1<<SPI_DI);
6
  SPI_PORT |= (1<<SPI_SS);
7
}
8
9
unsigned int SPI_Transfer (unsigned int data)
10
{
11
    unsigned int i;
12
    
13
    SPI_PORT &= ~(1<<SPI_SS);  //start frame by pulling SS low
14
15
    for(i=0;i<=7;i++)      //transfer 8 bits = 1 byte
16
    {
17
      //enable output data
18
      if((data & (1<<i)) == (1<<i)) SPI_PORT |= (1<<SPI_DO);
19
        else SPI_PORT &= ~(1<<SPI_DO);
20
21
      //clock rising edge
22
      SPI_PORT |= (1<<SPI_SCK);
23
24
      //sample input data
25
      if( (SPI_PIN & (1<<SPI_DI)) == (1<<SPI_DI)) data |= (1<<i);
26
        else data &= ~ (1<<i);
27
28
      //clock falling edge
29
      SPI_PORT &= ~(1<<SPI_SCK);
30
    }
31
32
    SPI_PORT |= (1<<SPI_SS);  //end frame by pulling CS high
33
    return(data);
34
}

Trotzdem ist es unbefriedigend, warum die USI nicht läuft. Würde mich 
freuen, wenn wir doch noch eine Lösung finden würden.

Grüße Karl

von Peter D. (peda)


Lesenswert?

Wenn Du mal in das Assemblerlisting schaust, wirst Du sehen, daß da ganz 
schön viel Code erzeugt wird.

(1<<i) mag der Compiler nicht, da es dafür keinen Opcode gibt.

Auch macht es keinen Sinn, 16Bit Variablen zu verwenden, wenn man nur 
8Bit braucht.

Wesentlich kürzer und schneller wirds daher so:
1
#include <avr\io.h>
2
3
4
struct bits {
5
  uint8_t b0:1;
6
  uint8_t b1:1;
7
  uint8_t b2:1;
8
  uint8_t b3:1;
9
  uint8_t b4:1;
10
  uint8_t b5:1;
11
  uint8_t b6:1;
12
  uint8_t b7:1;
13
} __attribute__((__packed__));
14
15
#define SBIT(port,pin) ((*(volatile struct bits*)&port).b##pin)
16
17
18
#define SPI_CLK         SBIT( PORTB, 0 )        // clock
19
#define SPI_CLK_DDR     SBIT( DDRB,  0 )
20
#define SPI_MOSI        SBIT( PORTB, 1 )        // data out
21
#define SPI_MOSI_DDR    SBIT( DDRB,  1 )
22
#define SPI_MISO_PIN    SBIT( PINB,  2 )
23
24
25
uint8_t shift_io( uint8_t b )  // send / receive byte
26
{
27
  uint8_t i;
28
29
  SPI_CLK_DDR = 1;
30
  SPI_MOSI_DDR = 1;
31
32
  for( i = 8; i; i-- ){         // 8 bits
33
    SPI_MOSI = 0;
34
    if( b & 0x80 )              // high bit first
35
      SPI_MOSI = 1;
36
    b <<= 1;
37
    SPI_CLK = 1;
38
    if( SPI_MISO_PIN )
39
      b++;
40
    SPI_CLK = 0;
41
  }
42
  return b;
43
}


Peter

von Karl W. (karlw)


Lesenswert?

Hallo Peter,

danke für Deine Korrekturen.
Stimmt, mein Fehler "unsigned int" auf dem Atmel zu verwenden, da kommt 
davon wenn man immer zwischen Controllern hin- und her schalten muss... 
;-)

Peter Dannegger wrote:
> Wenn Du mal in das Assemblerlisting schaust, wirst Du sehen, daß da ganz
> schön viel Code erzeugt wird.
>
> (1<<i) mag der Compiler nicht, da es dafür keinen Opcode gibt.
>
> Auch macht es keinen Sinn, 16Bit Variablen zu verwenden, wenn man nur
> 8Bit braucht.
>

Für das Schieben (1<<i) gibt es keinen Opcode? Ich hatte mir extra der 
Übersichtlichkeit wegen die Schreibweise: PORT |= (1<<BIT); angewöhnt?!

Oder kann dies der Compiler optimieren, da ja BIT eine Konstante ist?

Gruß Karl

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl W. wrote:

> Oder kann dies der Compiler optimieren, da ja BIT eine Konstante ist?

Richtig, das muss nicht der Prozessor machen, anders als 1 << i.

von sb (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,
ich spiele grade schon eine Weile mit einem Attiny 45 und habe ein 
ähnliches USI-Problem. Jörgs Code funktionierte bei mir auf Anhieb. Nun 
kann ich damit auch ohen Probleme einen 74HC595 zum Ausgeben zwingen. 
Mein Problem besteht darin, dass ich nicht weiss, wie ich die Clockphase 
für einen 74HC165 ändere? Ich hab aus dem Datenblatt mal die Tabelle 
rausgesucht, die denke ich die notwendigen Register beinhaltet und die 
Funktion folgendermaßen implementiert:
1
static uint8_t spi165(void)
2
{
3
  USISR = _BV(USIOIF);
4
  do {
5
    USICR = _BV(USIWM0) |                 /* USIWM0=1, USIWM1=0 -> Three Wire Mode */
6
        _BV(USICS1) | _BV(USICS0) | _BV(USICLK) |  /* all 3 set -> Configure external clock, negative edge */
7
        _BV(USITC);                 /* writing to the USITC strobe bit will directly clock the 4-bit counter */
8
  } while ((USISR & _BV(USIOIF)) == 0);
9
    return USIDR;
10
}

Nun toggle ich den Latchpin und lasse die Funktion durchlaufen. Die 
Daten werden nun auf dem 74HC595 geschrieben. Komischerweise bekomme ich 
aber jedesmal 0xFF dort angezeigt -> Es scheint also nicht zu 
funktionieren. Kann mir bitte jemand sagen warum nicht und was ich 
anders machen sollte?

MfG sb

von sb (Gast)


Lesenswert?

Hallo,
ich hab die Sache gelöst. Damit die Daten von den Eingängen in die 
internen Register übernommen werden, musste der SCK-Pin auch High sein. 
Es lag also am schalten des Latches...

LG sb

von thomasH (Gast)


Lesenswert?

ich weiß, ich graber hier einen alten Artikel aus, aber ich hatte gerade 
das selbe Problem.

@ Karl W.
bei dir sind DI und DO vertauscht, darum bekommst du auch keinen Output. 
Leider wird bei der USI immer MOSI mit DI und MISO mit DO geshared, da 
sind Verwechslungen ja schon fast vorprogrammiert, wenn man von den 
größeren AVRs mit richtiger SPI Schnittstelle kommt ;-)

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.