Forum: Mikrocontroller und Digitale Elektronik RFM70 -> Atmega88 @ 8 MHz geht, Attiny2313 @ 8MHz geht nicht, 1 MHz geht


von Timmo H. (masterfx)


Lesenswert?

Hallo zusammen.
Ich habe ein Problem. Ich habe einen funktionierenden RFM70 Code, 
welchen ich auf einem Atmega88 @ 8 MHz benutze. Diesen wollte ich nun 
auf einen Attiny2313 portieren.
Beim Atmega88 benutze ich den Hardware SPI, welchen es beim Attiny2313 
nicht gibt. Hier muss man mit dem USI arbeiten oder das ganze per 
Software machen.
Entsprechend müsste rein theoretisch nur die SPI-Transfer Routine 
geändert werden. (ja ich habe bedacht, dass beim USI der "MOSI" der DI 
ist und der "MISO" der DO, also genau verdreht)
Beim Atmega88 sah das ganze so aus:
1
unsigned char SPI_transfer( unsigned char value )
2
{
3
4
  SPDR = value;
5
  /* Wait for transmission complete */
6
  while(!(SPSR & (1<<SPIF)));
7
8
  return SPDR;
9
}
Beim Attiny2313 siehts für den USI so aus:
1
unsigned char SPI_transfer( unsigned char value )
2
{
3
  USIDR=value;
4
  USISR=(1<<USIOIF);
5
  do{
6
    USICR=  (1<<USIWM0)|(1<<USICS1)|(1<<USICLK)|(1<<USITC);
7
  }while (!(USISR&(1<<USIOIF)));
8
9
  return USIDR;
10
}
und da es bei mir nicht geklappt hat, habe ich das ganze auch nochmal in 
Software gemacht:
1
unsigned char SPI_transfer( unsigned char value )
2
{
3
  uint8_t bit_ctr;
4
  for(bit_ctr = 0; bit_ctr<8; bit_ctr++){
5
    if(value & 0x80)
6
      PORTB |= (1<<PB6); //MOSI
7
    else
8
      PORTB &= ~(1<<PB6);
9
  
10
    value = (value << 1);
11
    
12
    PORTB |= (1<<PB7);
13
    if(PINB & (1<<PB5))
14
      value |= 1;
15
    else
16
      value &= ~1;
17
    PORTB &= ~(1<<PB7);
18
  }  
19
  return value;
20
}
Beide Varianten führen jedoch zum gleichen Ergebnis. Ich kann zwar alles 
aus dem RFM70 lesen und geschrieben wird scheinbar auch richtig, aber er 
empfängt einfach nichts von meinem Sender (Statusregister bleibt bei 
0x0E).
Ich habe auch schon zwischen dem SCK HIGH/LOW ( PORTB |= (1<<PB7)) /
PORTB &= ~(1<<PB7)) ein paar _delay_us(...) eingebaut um den Takt zu 
verringern. Laut Oszi sieht auch alles gut aus.

Stelle ich nun aber den Systemclock des Attiny2313 auf 1 MHz (CKDIV8) 
klappt es auf einmal. Scheinbar ist da also ein Timing-Problem, die 
frage ist nur, was der Hardware-SPI vom Atmega88 anders macht als meine 
(Semi)-Softwarelösung. Wie gesagt, der Code ist ansonsten 1:1 gleich. 
1MHz geht, 8MHz geht nicht. Bei 8MHz am Atmega88 gehts hingegen 
(SPI-Clock = 125 kHz). Beim Tiny habe ich via _delay_us den Clock sogar 
schon auf bis 20 kHz runtergedreht, aber selbst das geht nicht. Erst 
wenn der Systemclock auf 1 MHz steht geht es.
Ich würde ja auch mit 1MHz arbeiten, aber dann schaffe ich über den 
USART nicht die benötigte Datenrate.

Interrupts habe ich momentan keine, also kanns auch nicht daran liegen.

Jemand eine Idee? Ich bin meinem Latein erstmal am Ende. Ich habe 
eigentlich schon an jeder erdenklichen Stelle vergebens Delays eingebaut 
(CE, CSN, SCK, etc). Bin für jeden Hinweis dankbar.

von Hope (Gast)


Lesenswert?

Timmo H. schrieb:
> Bin für jeden Hinweis dankbar.

Ich weiß zwar nicht ob es hilft,
aber prüfe mal folgende SPI-Einstellungen:

Clock-Idle-State: LOW und Senden mit steigender Clockflanke.

von Timmo H. (masterfx)


Lesenswert?

Hope schrieb:
> Timmo H. schrieb:
>> Bin für jeden Hinweis dankbar.
>
> Ich weiß zwar nicht ob es hilft,
> aber prüfe mal folgende SPI-Einstellungen:
>
> Clock-Idle-State: LOW und Senden mit steigender Clockflanke.
Ja das passt schon. In meiner "SPI-Init" (sorry, habs vergessen zu 
posten) sind die Start-States folgendermaßen:
1
void SPI_init(){  
2
3
  DDRB = (1<<PB3) | (1<<PB4) | (1<<PB6) | (1<<PB7); //CE & CSN & MOSI & SCK
4
  PORTB |= (1<<PB4); // CSN = HIGH
5
  PORTB &= ~((1<<PB3) | (1<<PB6) | (1<<PB7));  // CE, MOSI, SCK => LOW
6
7
}
Im Falle des "USI-SPI-Modes" wird der SCK via USITC immer getoggelt, als 
erstes kommt also eine steigende Flanke.

von Hope (Gast)


Lesenswert?

Dann hilft wohl nur der Vergleich der verwendeten Leitungen der 
funktionierenden Atmega88 @ 8 MHz Variante mit der nicht 
funktionierenden  Attiny2313 @ 8MHz Variante per DSO oder 
Logic-Analyzer.

von Timmo H. (masterfx)


Lesenswert?

Genau das wollte ich eigentlich vermeiden, da ich leider keinen Logic 
Analyzer habe. Mit einem 2 Kanal DSO wird das wohl ein ziemliches Hin- 
und Hergemesse. Naja ich werde mir nachher mal nach und nach die 
Leitungen ansehen müssen, wo genau da die Timing Unterschiede sind.
Beim Atmega88 mit USART im SPI-Mode hatte ich schonmal ein ähnliches 
Problem. Dort haben ein paar NOPs zwischen Register schreiben und lesen 
geholfen
1
unsigned char spi_transfer( unsigned char value )
2
{
3
  // Wait for empty transmit buffer.
4
  do {} while( (UCSR0A & (1<<UDRE0)) == 0 );
5
  
6
  // Send data.
7
  UDR0 = value;
8
  
9
  // Wait for transfer to complete and return received value.
10
  do {} while( (UCSR0A & (1<<RXC0)) == 0 );
11
        asm("nop");
12
        asm("nop");
13
  return UDR0;
14
}
oder so ähnlich.

von Timmo H. (masterfx)


Lesenswert?

So, ich habe mir jetzt einige Stunden einen ziemlichen Wolf gemessen, 
aber kann absolut keine Unterschiede sehen. Dennoch klappt es nicht.
Ich vermute bald, dass es daran liegt, dass der Tiny2313 einige Befehle 
nicht hat, die der atmega88 jedoch besitzt. Vielleicht gehen dadurch 
einige dinge langsamer und/oder schneller wodurch irgendwelche 
Timing-Probleme ergeben.

Ich wüsste zu gerne woran das liegt. Beim Atmega klappt es auch ohne 
Probleme mit einem SPI-Clock von 500 kHz. Wie gesagt, die Werte die ich 
mit dem Attiny lese passen, die Kommunikation geht also, nur will er 
partout nicht die Pakete empfangen.
Da hier scheinbar keiner eine Idee hat, muss ich wohl oder übel auf 
einen atmega umsteigen. Dennoch würde es mich brennend interessieren 
woran es nun genau liegt.

von Gerhard (Gast)


Lesenswert?

Hallo,

machst Du die SPI-Routine jetzt in Software oder über den USI?
Funktioniert es auch, wenn Du das Programm, das auf dem Tiny nicht 
läuft, mal auf den Atmega ummünzt?

Timmo H. schrieb:
> nur will er partout nicht die Pakete empfangen.

sendet er ein ACK zurück?



Gruß
Gerhard

von Timmo H. (masterfx)


Lesenswert?

Gerhard schrieb:
> Hallo,
>
> machst Du die SPI-Routine jetzt in Software oder über den USI?
Habe wie gesagt beides Probiert. Ich habe mir auch schon die RFM70 
Register Dumpen lassen, es steht das drin was ich reinschreiben wollte.

> Funktioniert es auch, wenn Du das Programm, das auf dem Tiny nicht
> läuft, mal auf den Atmega ummünzt?
Das Programm kommt schon 1:1 vom Atmega, ich habe halt nur die 
SPI_Transfer Routine angepasst (und natürlich die Ports für CE und CSN), 
aber ich glaube ich werde nachher mal den SoftwareSPI 1:1 auf den Atmega 
übersetzen, mal gucken ob es dann geht.

> sendet er ein ACK zurück?
AutoACK habe ich momentan aus. Könnte ich zum testen natürlich mal 
wieder aktivieren. Vermutlich macht er es aber auch dann nicht.
Für mich siehts bisher einfach so aus, als wenn der Receiver einfach 
nicht aktiv ist. PRIM_RX steht aber auf "1" Genauso wie PWR_UP

Ich teste nachher nochmal rum

von Werner B. (werner-b)


Lesenswert?

1
unsigned char SPI_transfer( unsigned char value )
2
{
3
  uint8_t bit_ctr;
4
5
  for(bit_ctr = 0; bit_ctr < 8; bit_ctr++) {
6
    if(value & 0x80)
7
      PORTB |= (1<<PB6); //MOSI
8
    else
9
      PORTB &= ~(1<<PB6);
10
  
11
    //value = (value << 1);  Erfolgt spaeter
12
    
13
    PORTB |= (1<<PB7);   
14
    // Der geaenderte Zusand des Port wird erst einen Takt 
15
    // NACH dem Befehl am phsikalischen Pin sichtbar.
16
    // Darum muss hier mindestens ein weiterer Befehl 
17
    // ausgeführt werden damit der RFM70 die Möglichkeit
18
    // hat zu reagieren.
19
    // Am einfachten - das value <<= 1; hierher verlagern.
20
    value <<= 1;
21
    if(PINB & (1<<PB5))  
22
      value |= 1;
23
    else   // ist eigenlich ueberfluessig da '<<' eine 0 nachschiebt. 
24
      value &= ~1;
25
    PORTB &= ~(1<<PB7);
26
  }  
27
  return value;
28
}

von Timmo H. (masterfx)


Lesenswert?

Hallo Werner,

ja deine Anmerkungen sind natürlich richtig. Habe wie gesagt auch schon 
überall Delays und nops zwischen gebaut (auch direkt nach 
PORTB|=(1<<PB7)), was jedoch auch nichts änderte. Und wie gesagt, werden 
alle Werte richtig übertragen und auch wieder ausgelesen.
Zudem bin ich jetzt auch wieder bei der Semi-Hardware USI-Variante, da 
es eh keinen Unterschied machte und ich so ein paar Bytes an Code spare.
Ich werde nachher nochmal versuchen den anderen Code soweit zu 
reduzieren, dass ich die Codeoptimierungen abschalten kann, bisher muss 
ich -Os oder -O2 nehmen. Vielleicht hat avrgcc beim Tiny2313 auch ne 
Macke und kloppt mir da was kaputt. Mal schauen was passiert.

von Hope (Gast)


Lesenswert?

Benutzt Du den ATtiny 2313 oder den 2313A?

Der  2313A unterscheidet sich zwar nur geringfügig vom 2313, aber in 
seiner Definitionsdatei tn2313Adef.inc sind mir z.B. im Interrupt 
Bereich folgende Fehler aufgefallen:

original:
; GIMSK - General Interrupt Mask Register
.equ  PCIE  = 5  ;

abgeändert, weil der 2313A auf allen Portpins Change Interrupts hat:
; GIMSK - General Interrupt Mask Register
.equ  PCIE1  = 3  ;
.equ  PCIE2  = 4  ;
.equ  PCIE0  = 5  ;

Vielleicht sind in den USI-Definitionen ähnliche Fehler eingebaut,
die zu unerwünschten Resultaten führen?

von Timmo H. (masterfx)


Lesenswert?

Ich verwende den non-A

von Timmo H. (masterfx)


Lesenswert?

So, es läuft nun endlich.
Es war nicht die SPI-Routine schuld.

Und zwar ist es so, dass es scheinbar eklatant wichtig ist, dass beim 
RFM70 das Bank1_Reg0_13 in einer gewissen Geschwindigkeit geschrieben 
werden muss.

Wenn ich z.B. aus diesen Zeilen
1
for(i=0;i<=8;i++)//reverse
2
{
3
  for(j=0;j<4;j++){
4
    WriteArr[j]=(Bank1_Reg0_13[i]>>(8*(j) ) )&0xff;
5
  }
6
  SPI_Write_Buf((WRITE_REG|i),&(WriteArr[0]),4);
7
}
soetwas mache
1
for(i=0;i<=8;i++)//reverse
2
{
3
  for(j=0;j<4;j++){
4
    WriteArr[j]=(Bank1_Reg0_13[i]>>(8*(j) ) )&0xff;
5
    _delay_us(10);
6
  }
7
  SPI_Write_Buf((WRITE_REG|i),&(WriteArr[0]),4);
8
}
oder
1
for(i=0;i<=8;i++)//reverse
2
{
3
  for(j=0;j<4;j++){
4
    WriteArr[j]=(Bank1_Reg0_13[i]>>(8*(j) ) )&0xff;
5
    
6
  }
7
  _delay_us(40);
8
  SPI_Write_Buf((WRITE_REG|i),&(WriteArr[0]),4);
9
}
macht, dann geht es nicht mehr. Also es wird wohl nicht direkt die Zeit 
sein, denn den SPI-Clock kann man ja auch verlangsamen, sondern es geht 
wohl um die "Anzahl" der fehlenden Clocks zwischen dem Schreiben des 
Blocks. Wenn man also das Bank1_Reg0_13-Array anders speichert, sodass 
die Zugriffe zu lange dauert (z.B. im PGM-Space) dann gehts nicht mehr, 
oder mit etwas glück geht es manchmal.

Schon merkwürdig... muss man nicht verstehen... steht leider auch 
nirgends...

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.