AVR SPI

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
davrex
Inlägg: 149
Blev medlem: 18 januari 2009, 22:05:15

AVR SPI

Inlägg av davrex »

Sitter nu här och försöker få spi mellan 2 avr:er att funka stabilt. Men det funkar inte riktigt som jag vill.
Så här tänkte jag att man skulle kunna göra det. Att jag föst skickar 1byte med en adress om vilken info jag vill ha till baka och sedan lägger slave den datan i registret.
Men nu verkar problemet uppstå med att den lite då och då inte hinner med det utan skickar till baka adressen som den nyss fick istället :(. Det är inte ofta men det händer vilket jag inte gillar.

jag kör timern i 8mhz/1024

Kod: Markera allt

SIGNAL(TIMER0_OVF_vect)
{
    data = (int)SPI_Read(VOLYM1);
    Volym1L=data;
_delay_us(5);
    data = (int)SPI_Read(BAS1);
    Bas1L = data;
_delay_us(5);
    data = (int)SPI_Read(DIS1);
    dis = data;
}

unsigned char SPI_Read(unsigned char addr)
{
  // Activate the CS pin
  SPI_PORT &= ~(1<<SPI_CS);

  SPDR= addr;
  while(!(SPSR & (1<<SPIF)));
  _delay_us(10);
  // Send Dummy transmission for reading the data
  SPDR = 0x00;
  // Wait for transmission complete
  while(!(SPSR & (1<<SPIF)));  

  // CS pin is not active
  SPI_PORT |= (1<<SPI_CS);
  return(SPDR);
}
och på slaven kör jag följande:

Kod: Markera allt

unsigned char SPI_WriteRead(unsigned char dataout)
{
volatile uint8_t data;
  // Put Slave Data On SPDR
  SPDR=dataout;
  // Wait for transmission complete
  while(!(SPSR & (1<<SPIF)));
  // Return Serial In Value (MISO)

    
  return SPDR;
}

Kod: Markera allt

    while(1){
        datain = SPI_WriteRead(dataout);
        switch(datain) {
            case VOLYM1:
                dataout=volym1;
            break;

            case VOLYM2:
                dataout=volym2;
            break;
            case VOLYM3:
                dataout=volym3;
            break;

            case BAS1:
                dataout = bas1;
            break;

            case BAS2:
                dataout = bas2;
            break;
            case BAS3:
                dataout = bas3;
            break;

            case DIS1:
               dataout = dis1;
            break;

            case DIS2:
                dataout = dis2;
            break;
            
            case DIS3:
                dataout = dis3;
            break;

        }
    }
tack på förhand om någon kan peka ut vad som inte skulle funkar bra med detta och gärna kan ge förslag på bättre lösning


EDIT: Vid närmare ditt nu så verkar det vara ett annat värde den skciar först adressen 0x10 och ska då få svaret 0x3C men den får ibland svaret 0x13. Har inte den blekaste var 0x13 kommer ifrån då jag inte har någon "adress" som är på 0x13 eller värde.
Användarvisningsbild
stekern
Inlägg: 453
Blev medlem: 2 november 2008, 08:24:18
Ort: Esbo, Finland

Re: AVR SPI

Inlägg av stekern »

hur har du deklarerat data, Volym1L osv? är de deklarerade som volatile?

sen är det rekommenderat att köra ISR() isf SIGNAL()
davrex
Inlägg: 149
Blev medlem: 18 januari 2009, 22:05:15

Re: AVR SPI

Inlägg av davrex »

volatile uint8_t volym1 är alla deklarerade som.
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: AVR SPI

Inlägg av jesse »

Ja, SPI på AVR är lite kass på det viset att det inte finns någon buffert för data som ska sändas. Det måste alltså läggas in efter att första byten är klar och före andra byten startar. Det kan bli hur lite tid som helst för det. Har du dessutom t.ex. interruptrutiner som din slav anropar ibland så kan det ta lång tid om den råkar anropas just i det kritiska ögonblicket. Om du har det och kan tänka dig att fördröja ett eventuellt interrupt till SPI läst färdigt så kan du välja cli(); först och sei(); när det är klart.

Enda sättet jag kan komma på är att mastern helt enkelt väntar så länge innan andra byten skickas så att du är helt säker på att slaven hinner med.

Ett annat problem med din kod (och typiskt för Atmels exempelkoder) är att om slaven går in i receive-byte-rutinen och mastern inte skickar något just då, så kommer slaven att vara fast där tills mastern skickar en byte. Skulle den då bli avbruten, dvs. master skickar bara en byte, så fastnar slaven i andra läsningen... Om mastern då resettar och börjar om med att skicka två bytes så kommer slaven att tro att första byten är andra, och den skickar sin data, och andra byten tror den är nästa sändning. Därför bör du ha med något som avbryter rutinen om SPI_CS går hög, och returerar då t.ex. ett felmeddelande istället för värdet som mastern skulle ha skickat.
davrex
Inlägg: 149
Blev medlem: 18 januari 2009, 22:05:15

Re: AVR SPI

Inlägg av davrex »

MM kör en interruptrutiner på slaven med då den ska kolla en sådär 12 encoders är det tänkt. Så får vell se hur det kommer funka med timigen. Hur funkar cli()?
får om jag fattar dig rätt så kör man cli(); sei(); och på så sätt låter interrupet vänta på?
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: AVR SPI

Inlägg av jesse »

Ja, när ett interrupt ska ske händer en del saker i viss ordning:

a) en timer når overflow t.ex.
b) då sätts en flagga t.ex. TOV1
c) nu är denna flagga "ett" ända tills en interrupt sker eller om du nollställer den.
d) för att intrerrupt ska ske måste I-flaggan vara satt, annars händer inget.
e) om du sedan sätter I-flaggan till 1 med sei(); så utförs alla interrupt (i en viss ordning) vars flaggor är satta.

på så vis kan du vänta med en interrupt genom att skrivacli();..... och sedansei();
Användarvisningsbild
stekern
Inlägg: 453
Blev medlem: 2 november 2008, 08:24:18
Ort: Esbo, Finland

Re: AVR SPI

Inlägg av stekern »

Ett annat alternativ är att lägga slav-SPIkoden i ett interrupt och sen ha svaren i en array för snabb avkodning.
Hur fort kör du SPIn?
davrex
Inlägg: 149
Blev medlem: 18 januari 2009, 22:05:15

Re: AVR SPI

Inlägg av davrex »

kör spi just nu i clk/64 där clk är 8mhz

ska testa att lägga den i en interrupt.


EDIT: testa att lägg den i ett interrupt nu men det vart bara sämre :(. Att det skulle vara så här svårt att skicka över 18byte bara :/
davrex
Inlägg: 149
Blev medlem: 18 januari 2009, 22:05:15

Re: AVR SPI

Inlägg av davrex »

testade nu att byta ut

Kod: Markera allt

unsigned char SPI_WriteRead(unsigned char dataout)
{

   if(bit_get(PORTB,BIT(4))) return SPDR;
  // Put Slave Data On SPDR
  SPDR=dataout;
  // Wait for transmission complete
  while(!(SPSR & (1<<SPIF)));
  // Return Serial In Value (MISO)

    
  return SPDR;
}

Och hela min case sats till följande

Kod: Markera allt

ISR(SPI_STC_vect)
{
         switch(SPDR) {
            case 0:

            break;
            case VOLYML1:
                SPDR=volymL1;
            break;

            case VOLYML2:
                SPDR=volymL2;
            break;
            case VOLYML3:
                SPDR=volymL3;
            break;

            case BASL1:
                SPDR = basL1;
            break;

            case BASL2:
                SPDR = basL2;
            break;
            case BASL3:
                SPDR = basL3;
            break;

            case DISL1:
               SPDR = disL1;
            break;

            case DISL2:
                SPDR = disL2;
            break;
            
            case DISL3:
                SPDR = disL3;
            break;

        }   
}
Men detta verkar inte ge något då den bara skickar till baka adressen den just fick nu hela tiden.


EDIT: Detta verkar faktiskt funka hade bara glömt enable SPI interrupt när jag testa det första gången
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: AVR SPI

Inlägg av jesse »

Ja, nu riskerar du inte att någon annan interrupt går in och bryter när du väl är inne i den. Men fortfarande är det så att om du just påbörjat en annan interrupt när mastern börjar sända, så kommer den andra interrupten att köra färdigt innan din ISP-rutin startar. Det kan ju ta så lång tid att du missar bytes, om du har andra rutiener som är långa.

Själv har jag löst sådant genom att göra andra interruptrutiner extremt korta - de sätter bara en flagga , sedan kollar huvudprogrammet om flaggan är satt - då utför huvudprogrammet det som ska göras - och på så vis kan den avbrytas i arbetet precis när som helst om SPI:n skulle gå igång.

Exempel på sådana "korta" interrupt:

Kod: Markera allt

// globala variabler TIMER, ADC //
uint8_t volatile tick;
uint8_t volatile ADC_ready;

ISR(TIMER0_COMPA_vect) {     /* Timer0 OCR0A match interrupt   */
	tick = 1;
} 

ISR(ADC_vect)  {             /* ADC conversion ready interrupt */
	ADC_ready    = 1;
} 
och i main:

Kod: Markera allt

while (1) {
		if (tick) {
			tick = 0;
			...utför satser...
		}
}
davrex
Inlägg: 149
Blev medlem: 18 januari 2009, 22:05:15

Re: AVR SPI

Inlägg av davrex »

hmm k problemet med det är att min andra interrup som ska läsa av mina 12 encoders det blir inte så bra där heller om den avbryts och missar läsningar på den sidan istället .
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: AVR SPI

Inlägg av jesse »

Jo, en processor kan ju bara göra en sak åt gången. Därför gillar jag inte Atmel's lösning på SPI som är så himla beroende av att man bevakar den precis hela tiden. De kunde gott ha kostat på sig en buffert. (nu vet jag inte om andra processorfamiljer har bättre lösningar, men rent generellt tycker jag denna är för dålig. Den fungerar utmärkt som master , men inte som slav).
Användarvisningsbild
stekern
Inlägg: 453
Blev medlem: 2 november 2008, 08:24:18
Ort: Esbo, Finland

Re: AVR SPI

Inlägg av stekern »

Precis som jesse säger så måste du försöka hålla dina interrupt så korta som möjligt.
Du har fortfarande addressavkodningen i en switch sats, och en hyfsat lång sådan.
Ju mer case du har i switchen desto mer kod. Om du istället lägger värdena i en array så skalar det bättre.
dvs:

Kod: Markera allt

ISR(SPI_STC_vect)
{
  SPDR = dataout[SPDR];
}
och sen i uppdateringskoden för exempelvis volyml1:

Kod: Markera allt

dataout[VOLYML1]  =  vad_som_nu_mater_volyml1;
Vad är det för encodrar du har?
Måste de köras under interrupt, kan inte de kontinuerligt bara pollas i main-loopen och uppdatera dataout-arrayen?

jesse: Visst skulle de kunnat kosta på sig en dubbelbuffrad lösning på SPDR så man skulle kunna skriva in nästa värde i den medans den nuvarande skickas. Men i det här fallet skulle det väl inte vara till så mycket hjälp i och med att vi inte vet vilket värde som skall skickas förräns "vi" fått reda på vilken adress som skall avläsas?
davrex
Inlägg: 149
Blev medlem: 18 januari 2009, 22:05:15

Re: AVR SPI

Inlägg av davrex »

AHA, smart ide ska ta och testa den.

är vanliga rotations encoders jag kör med
Jonas L
Inlägg: 432
Blev medlem: 14 juli 2009, 14:28:41
Ort: Karlshamn

Re: AVR SPI

Inlägg av Jonas L »

Eftersom du har kontroll på mastern kan du kanske göra en medveten paus mellan bytarna? Eller finns det något som säger att du måste följa protokollet slaviskt?
Skriv svar