Forum: Compiler & IDEs AT91SAM7S-Problem: SPI-Transfer im Interrupt starten.


von Daniel J. (deejay)


Lesenswert?

Hallo,

hoffentlich kann mir jemand bei meinem Problem mit dem AT91SAM7S 
weiterhelfen. Ich bin hier langsam am verzweifeln.
Folgende Situation: Ich versuche einen AD-Wandler per SPI auszulesen. 
Ein PIO-Interrupt signalisiert, dass die AD-Wandlung komplett ist und 
ein neuer Wert ausgelesen werden kann (durch eine fallende Flanke am 
Pin).
Also habe ich auf den Pin einen PIO-Interrupt gelegt, das soweit auch 
klappt (die grüne LED blinkt fröhlich, also muss der entsprechende Code 
aufgerufen werden). Das Auslesen mittels SPI klappt auch, allerdings 
nur, wenn ich es außerhalb der Interrupt-Routine starte (beispielsweise 
in der main() oder direkt in der Methode, die das SPI initialisiert...).
Wenn ich nun aber in der Interrupt-Routine vom PIO den SPI-Datentransfer 
starten will, passiert nichts.
Der Code:
1
void pioISR(void)
2
{
3
  // reset PIO interrupt flag by reading PIO_ISR
4
  unsigned long int isr = AT91C_BASE_PIOA->PIO_ISR;
5
  if (((isr & AT91C_PA12_MISO) != 0) && ((AT91C_BASE_PIOA->PIO_PDSR & AT91C_PA12_MISO) == 0))
6
  {
7
    static unsigned char bar = 0;
8
    bar = !bar;
9
    if (bar)
10
      LED_GREEN_ON();
11
    else
12
      LED_GREEN_OFF();
13
    
14
    // check if transmit buffer is empty
15
    if ( (AT91C_BASE_SPI->SPI_SR & AT91C_SPI_TDRE) != 0)
16
    {
17
      debugUARTSendString("w\n");
18
      volatile unsigned long int dummy_1 = AT91C_BASE_SPI->SPI_RDR;
19
      volatile unsigned long int dummy_2 = AT91C_BASE_SPI->SPI_SR;
20
      volatile unsigned long int dummy_3 = AT91C_BASE_SPI->SPI_RDR;
21
      AT91C_BASE_SPI->SPI_TDR = 0x55;
22
    }
23
  }
24
}
25
26
27
void initSPI(void)
28
{
29
  debugUARTSendString("initSPI\n");
30
31
  // enable interrupt on DOUT/RDY on falling edge
32
  AT91C_BASE_AIC->AIC_IDCR = (1 << AT91C_ID_PIOA);
33
  AT91C_BASE_AIC->AIC_SMR[AT91C_ID_PIOA] = AT91C_AIC_SRCTYPE_INT_POSITIVE_EDGE| (AT91C_AIC_PRIOR & 3);
34
  AT91C_BASE_AIC->AIC_SVR[AT91C_ID_PIOA] = (unsigned long int)&pioISR;
35
  AT91C_BASE_AIC->AIC_ICCR = (1 << AT91C_ID_PIOA);
36
  
37
  AT91C_BASE_PMC->PMC_PCER = (1<< AT91C_ID_SPI);  // enable clock
38
39
  AT91C_BASE_PIOA->PIO_PDR = AT91C_PA14_SPCK | AT91C_PA13_MOSI | AT91C_PA12_MISO | AT91C_PA11_NPCS0;  // disable PIO function
40
  AT91C_BASE_PIOA->PIO_PPUDR = AT91C_PA14_SPCK | AT91C_PA13_MOSI | AT91C_PA12_MISO | AT91C_PA11_NPCS0;  // disable pullups
41
  AT91C_BASE_PIOA->PIO_ASR = AT91C_PA14_SPCK | AT91C_PA13_MOSI | AT91C_PA12_MISO | AT91C_PA11_NPCS0;    // select peripheral A function for TWI data and TWI clock
42
  
43
  // up to 5 MHz SPI clock, SPI mode 3 (CPOL=1, NCPHA=0)
44
  AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SWRST;  // reset SPI
45
  AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SPIEN;  // enable SPI
46
  AT91C_BASE_SPI->SPI_MR = AT91C_SPI_MSTR | AT91C_SPI_PS_FIXED | (AT91C_SPI_PCS & (14 << 16));
47
  AT91C_BASE_SPI->SPI_CSR[0] = AT91C_SPI_CPOL | AT91C_SPI_CSAAT | AT91C_SPI_BITS_8 | (AT91C_SPI_SCBR & (255 << 8));
48
  
49
  AT91C_BASE_AIC->AIC_IECR = (1 << AT91C_ID_PIOA); // enable PIO interrupt
50
  AT91C_BASE_PIOA->PIO_IER = AT91C_PA12_MISO;
51
52
53
  // starten des SPI-Datentransfers an dieser Stelle klappt!
54
55
  debugUARTSendString("\\initspi\n");
56
}

Nach Aufruf der initSPI() folgt in der main() eine Endlosschleife.
Die Ausgabe des Programms sieht wie folgt aus:
initSPI
\initspi
w

Das heisst, das TDR-Register vom SPI wird einmal beschrieben, aber die 
Daten werden nicht gesendet und das Register wird nicht wieder frei... 
Der PIO-Interrupt läuft fröhlich weiter (die grüne LED blinkt).

Wenn ich den SPI-Transfer außerhalb der ISR starte, kann ich die Daten 
auf dem Oszi sehen. Wird in der PIO-ISR jedoch der Transfer gestartet, 
passiert nichts (kein SPI-Clock, nichts). Was mache ich falsch? Es muss 
doch möglich sein innerhalb der PIO-ISR den SPI-Transfer zu starten?! 
Ich habe auch schon versucht, den PIO-Interrupt abzuschalten, bevor ich 
den SPI-Transfer starte... ändert aber nichts...
Ich habe vorher auch schon versucht den SPI-Transfer mit DMA zu 
realisieren, aber das klappt auch nicht. Deswegen dachte ich, ich 
probiers erstmal ohne DMA. Aber scheinbar habe ich etwas essentiell 
wichtiges übersehen. Hat jemand eine Idee?

Das Projekt basiert auf den Beispielen von Martin Thomas, d.h. Makefile, 
Startup-Files und Linkerscripte wurden übernommen. Verwendet wird ein 
AT91SAM7S256.

MfG
  Deejay

von Daniel J. (deejay)


Lesenswert?

Hi,

habe das Problem einkreisen und umgehen können. Und zwar tritt das 
Problem nur auf, wenn zwischen der Initalisierung des SPI-Moduls und dem 
SPI-Datentransfer zuviel Zeit liegt. Erfolgt das Senden sofort, klappt 
alles. Mache ich nach der Initialisierung ein kurzes Delay, wird nichts 
mehr gesendet... Seltsam.
Jetzt habe ich es so gemacht, dass ich vor dem Senden das SPI resette, 
neu konfiguriere und dann direkt die Daten schreibe... Alleine die 
SPI-Register neu zu beschreiben ohne Reset reicht nicht aus, erst der 
Software-Reset lässt es funktionieren...
Nur zur Info, falls mal jemand ähnliche Probleme hat.
Verstanden habe ich das ganze nicht, aber die Software läuft nun... 
Falls mir jemand das Verhalten erklären kann, würde ich mich natürlich 
freuen.

MfG
 Deejay

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.