mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Probleme mit SPI Übertragung (ATMega88 x2)


Autor: Frederik Krämer (n0ll4k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moin,

ich stehe vor folgendem Problem. Ich habe mir 2 ATMega88 auf eine 
Lochrasterplatine gesetzt und möchte das diese per SPI untereinander 
kommunizieren und mir dann ein Messergebnis über die serielle 
Schnittstelle vom Master ausgeben. Konkret geht es darum die interne 
Temperaturmessung etwas genauer zu betrachten.

Momentan habe ich beide Controller über einen MAX232 mit 2 
Schnittstellen verbunden. Dies soll aber momentan nur zum testen der SPI 
Schnittstelle dienen.

Problem ist aber das die Zeichen die ich mit dem Master sende nicht 
korrekt übertragen werden. Ab und dann kommt nach 5-6 Versuchen mal 
etwas korrektes an.

Beide Prozessoren laufen mit internen RC Oszilator.

Quelltext Master:
#include <stdint.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "adhandle.h"

#ifndef F_CPU
#define F_CPU 1000000UL
#endif
#include <util/delay.h>
#define sendByte(ch)    (UDR0=ch)
#define getByte()      (UDR0)

#define DDR_SPI DDRB
#define DD_MOSI DDB3
#define DD_SCK  DDB5
#define DD_SS  DDB2
#define SS    PB2

char getTemp = 0x00;
int16_t temperature;
uint16_t adTemp;
uint8_t i;
char ReceivedByte;
char sendZero = 0x00;

ISR(USART_RX_vect){
  if(getByte() == 0x54)
    getTemp = 0x01;
  else{
    sendZero = 0x01;
    ReceivedByte = getByte();
  }
}
void initUART( void ){
  UCSR0B |= ( 1<<RXEN0 ) | ( 1<<TXEN0 );
  UCSR0B |= ( 1<<RXCIE0 );
  UCSR0C |= ( 1<<UCSZ00 ) | ( 1<<UCSZ01 );
  UCSR0A |= ( 1<<U2X0 );
  UBRR0 = 12;

}

void UART_putc(unsigned char c){
  while((UCSR0A & (1 << UDRE0)) == 0){};
  sendByte(c);
}


void init_spiMaster( void ){
  /* Set MOSI and SCK output, all others input */
  DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS);
  /* Enable SPI, Master, set clock rate fck/16 */
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
}

void spiMasterTransmit(char cData)
{
/* Start transmission */
SPDR = cData;
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
;
}
int main( void ){

   //
  // Initialisierung des A/D-Wandlers
  //
  // ADC aktivieren und Prescaler auf 8 setzen
  ADCSRA |= ( 1<<ADEN ) | ( 1<<ADPS1 ) | ( 1<<ADPS0 );
  // Free Running Mode aktivieren, Reservierte Bits mit 0 beschreiben
  ADCSRB = 0x00; 
   initUART();
  init_spiMaster();
  sei();
  
  for(;;){    
    if( getTemp == 0x01){
      adTemp = readADC(0xC8);
      _delay_ms(20);
      adTemp = readADC(0xC8);
      temperature = ((adTemp-335)*88)/100;
      getTemp = 0x00;
      char one = (adTemp & 0xFF00) >> 8;
      char two = (adTemp & 0x00FF);
      char three = (temperature & 0xFF00) >> 8;
      char four = (temperature & 0x00FF);
      UART_putc(one);
      UART_putc(two);
      UART_putc(three);
      UART_putc(four);
      PORTB &= ~( 1<<SS);
      _delay_ms(1);
      spiMasterTransmit(0x00);
      _delay_ms(1);
      PORTB |= ( 1<<SS);
    }else if(sendZero == 0x01){
      UART_putc(ReceivedByte);
      sendZero = 0x00;
      PORTB &= ~( 1<<SS);
      _delay_ms(1);
      spiMasterTransmit(ReceivedByte);
      _delay_ms(1);
      PORTB |= ( 1<<SS);
    }else{
      i++;
      if ( i >= 200 )
        i = 0;
    }
  }
}


Quelltext Slave:
#include <stdint.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "adhandle.h"



#ifndef F_CPU
#define F_CPU 1000000UL
#endif
#include <util/delay.h>
#define sendByte(ch)    (UDR0=ch)
#define getByte()      (UDR0)

#define DDR_SPI DDRB
#define DD_MISO DDB4


char getTemp = 0x00;
int16_t temperature;
uint16_t adTemp;
uint8_t i;
char ReceivedByte;
char sendZero = 0x00;
char spiChar;

ISR(USART_RX_vect){
char ReceivedByte;
  if(getByte() == 0x54)
    getTemp = 0x01;
  else
    sendZero = 0x01;
}
ISR(SPI_STC_vect){
//PORTC |= (1<<PC0);
 spiChar = SPDR;
}

void initUART( void ){

  UCSR0B |= ( 1<<RXEN0 ) | ( 1<<TXEN0 );
  UCSR0B |= ( 1<<RXCIE0 );

  UCSR0C |= ( 1<<UCSZ00 ) | ( 1<<UCSZ01 );

  UCSR0A |= ( 1<<U2X0 );

  UBRR0 = 12;

}
void SPI_SlaveInit(void)
{
  /* Set MISO output, all others input */
  DDR_SPI = (1<<DD_MISO);
  /* Enable SPI */
  SPCR = (1<<SPIE)|(1<<SPE);
}


void UART_putc(unsigned char c){
  while((UCSR0A & (1 << UDRE0)) == 0){};
  sendByte(c);
}

int main( void ){

   //
  // Initialisierung des A/D-Wandlers
  //
  // ADC aktivieren und Prescaler auf 8 setzen
  ADCSRA |= ( 1<<ADEN ) | ( 1<<ADPS1 ) | ( 1<<ADPS0 );
  // Free Running Mode aktivieren, Reservierte Bits mit 0 beschreiben
  ADCSRB = 0x00; 
   initUART();
  SPI_SlaveInit();
  sei();
  
  for(;;){    
    if( getTemp == 0x01){
      adTemp = readADC(0xC8);
      _delay_ms(20);
      adTemp = readADC(0xC8);
      temperature = ((adTemp-335)*88)/100;
      getTemp = 0x00;
      char one = (adTemp & 0xFF00) >> 8;
      char two = (adTemp & 0x00FF);
      char three = (temperature & 0xFF00) >> 8;
      char four = (temperature & 0x00FF);
      UART_putc(one);
      UART_putc(two);
      UART_putc(three);
      UART_putc(four);
    }else if(sendZero == 0x01){
      UART_putc(spiChar);
      sendZero = 0x00;
    }else{
      i++;
      if ( i >= 200 )
        i = 0;
    }
  }
}

Autor: Fabian B. (fabs)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Interner RC und RS232... du hast dein Problem gefunden.

Gruß
Fabian

Autor: Frederik Krämer (n0ll4k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
komischerweise funktionier das bei beiden.

Echo Programm läuft ohne probleme.

Autor: Christian D. (chris83)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Frederik Krämer schrieb:
> #ifndef F_CPU
> #define F_CPU 1000000UL
> #endif

Hat der interne RC vom ATMega88 nicht 8 MHz. ?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn wir mal davon ausgehen, dass UART bei diesen Temperaturen mit dem 
internen RC funktioniert:

Dein Testprogramm ist mir immer noch zu kompliziert. Wenn du SPI in 
Verdacht hast, würde ich da mal extrem abspecken.

Der eine schickt alle 1 Sekunde ein bestimmtes Byte (von mir aus auch 
ein hochzählender Zähler) zum anderen und der andere gibt das empfangene 
Byte über UART aus. Je weniger Subsysteme du im Einsatz hast (ADC etc) 
und je einfacher die generelle Programmlogik ist, desto eher 
kristallisiert sich heraus, ob du ein Problem mit dem SPI hast, oder ob 
du irgendwo anders eine Race-Condition hast, die dir bisher verborgen 
blieb. (Kurz gesagt: ein Logikfehler im Programm)

Autor: Frederik Krämer (n0ll4k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian D. schrieb:
> Frederik Krämer schrieb:
>> #ifndef F_CPU
>> #define F_CPU 1000000UL
>> #endif
>
> Hat der interne RC vom ATMega88 nicht 8 MHz. ?

CLKDIV Fuse ist natürlich aktiviert.

@ Karl-Heinz

Werde das Programm mal abspecken und testen.


Hab grad mal mitm Oszi das SCK und MOSI angeschaut. Ab und an sah man 
mal beides zeitgleich meist aber waren die Signale versetzt.

Autor: Christian D. (chris83)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dann passt es ja, wobei ein externer Quarz da sicherlich die bessere 
Wahl wäre.

Autor: Frederik Krämer (n0ll4k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So hab den Code mal vereinfacht.

Master:
#include <stdint.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>

#ifndef F_CPU
#define F_CPU 1000000UL
#endif
#include <util/delay.h>

#define DDR_SPI DDRB
#define DD_MOSI DDB3
#define DD_SCK  DDB5
#define DD_SS  DDB2
#define SS    PB2

void init_spiMaster( void ){
  /* Set MOSI and SCK output, all others input */
  DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS);
  /* Enable SPI, Master, set clock rate fck/16 */
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
}

void spiMasterTransmit(char cData)
{
/* Start transmission */
SPDR = cData;
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
;
}
int main( void ){
  init_spiMaster();
  PORTB |= ( 1<<SS );
  for(;;){
    for(int i = 0; i <100; i++){
      _delay_ms(10);
    }
    PORTB &= ~ ( 1<<SS );
    _delay_us(150);  
    spiMasterTransmit(0xAA);
    _delay_ms(1);
    PORTB |= ( 1<<SS );
  }
}

Slave:
#include <stdint.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#ifndef F_CPU
#define F_CPU 1000000UL
#endif
#include <util/delay.h>

#define DDR_SPI DDRB
#define DD_MISO DDB4
#define sendByte(ch)    (UDR0=ch)
#define getByte()      (UDR0)

char sendChar = 0x00;
char spiChar;

void UART_putc(char c){
  while((UCSR0A & (1 << UDRE0)) == 0){};
  sendByte(c);
}
ISR(SPI_STC_vect){
  sendChar = 0x01;
  spiChar = SPDR;
  UART_putc(spiChar);
  PORTC ^= ( 1<<PC0 );
}

void initUART( void ){

  UCSR0B |= ( 1<<RXEN0 ) | ( 1<<TXEN0 );
  UCSR0B |= ( 1<<RXCIE0 );

  UCSR0C |= ( 1<<UCSZ00 ) | ( 1<<UCSZ01 );

  UCSR0A |= ( 1<<U2X0 );

  UBRR0 = 12;

}
void SPI_SlaveInit(void)
{
  /* Set MISO output, all others input */
  DDR_SPI = (1<<DD_MISO);
  /* Enable SPI */
  SPCR = (1<<SPIE)|(1<<SPE);
}




int main( void ){
  //DDRB |= ( 1<<DDB0);
   initUART();
  SPI_SlaveInit();
  sei();
  
  for(;;){    

  }
  
}

Wenn ich jetzt die Spannung anlege bekomme ich dann die Zeichen 
angezeigt. Es wird auch immer das selbe Zeichen gesendet.

Wenn ich nun die Spannung einmal wegnehme und wieder anlege kommt ein 
anders Zeichen. Vereinzelt kommt dann auch wirklich mal 0xAA an.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Frederik Krämer schrieb:

> Wenn ich nun die Spannung einmal wegnehme und wieder anlege kommt ein
> anders Zeichen. Vereinzelt kommt dann auch wirklich mal 0xAA an.

Ähm.
Das ist klar, denn SPI an sich hat ja keine Möglichkeit die 
Synchronisierung des Senders mit dem Empfänger zu gewährleisten.
Wenn sich der Empfänger in eine laufende Sendung einklinkt, sieht er nur 
Pulse auf der Clock Leitung. Aber bei welchem Clock Puls das nächste 
Byte anfängt .... kann er nicht wissen. SPI ist eine rein synchrone 
Übertragung, da gibt es keinen Timeout oder so was.

Wenn der Slave 5 Bits empfangen hat und du drehst dem Sender den Saft 
ab, dann wartet der Slave auf die nächsten 3 Bits und hängt die an die 
bereits empfangenen 5 Bits an um daraus ein Byte zu formen. Dasss diese 
3 Bits eigentlich schon der Anfang des nächsten Bytes gewesen wären, 
weiß der Empfänger nicht.

Autor: Frederik Krämer (n0ll4k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich nehme auch bei beiden die Spannung weg.

Zur Synchronisation ist doch eigentlich die SS Leitung Verantwortlich, 
oder wie läuft das ?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Frederik Krämer schrieb:
> Ich nehme auch bei beiden die Spannung weg.

Und in welcher Reihenfolge klemmst du sie wieder an?
Zuerst Empfänger dann Sender

> Zur Synchronisation ist doch eigentlich die SS Leitung Verantwortlich,
> oder wie läuft das ?

Aus dem Datenblatt des Mega16

Data is always shifted from Master to Slave on the Master Out – Slave 
In, MOSI, line, and from Slave to Master on the Master In – Slave Out, 
MISO, line. After each data packet, the Master will synchronize the 
Slave by pulling high the Slave Select, SS, line.
When configured as a Master, the SPI interface has no automatic control 
of the SS line. This must be handled by user software before 
communication can start.


Ich seh davon nichts in deinem Code.

Autor: Frederik Krämer (n0ll4k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sind beide über eine Spannungsversorgung versorgt. Wenn ich am Board die 
Spannung anlege bekommen beide Spannung.

Hier toggle ich den SS Pin:
PORTB &= ~ ( 1<<SS );
spiMasterTransmit(0xAA);
PORTB |= ( 1<<SS );

Die Delays hab ich nu mal rausgenommen jetzt bekomm ich immer 0xA8 statt 
0xAA

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Frederik Krämer schrieb:

> Hier toggle ich den SS Pin:

Oops.
Mein Fehler, hab ich übersehen.

Hmm. So gesehen entspricht das IMHO dem Datenblatt

Autor: Frederik Krämer (n0ll4k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kein Problem.

Dann bin ich wenigstens nicht alleine etwas ratlos warums nicht klappt.

Vielleicht hat ja noch wer anders ne Idee sonst muss ich mir evtl mal ne 
anderen Weg suchen.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.