Forum: Mikrocontroller und Digitale Elektronik TMS470 DMA->SPI


von MiGu (Gast)


Lesenswert?

Hallo Forum,

experimentiere seit einigen Tagen mit der DMA des TMS470 und dessen SPI. 
Ich möchte eine Datenübertragung aus dem internen RAM (ca. 30000 Bytes) 
an die SPI über die DMA realisieren, wobei die SPI nur als Master 
verwendet werden soll und nur Daten in Byte-Form senden soll. Über den 
Enable-Pin der SPI soll der SPI-Datenstrom angehalten werden 
können(jedoch nicht generell abgebrochen) und somit der DMA-Transfer 
angehalten werden. Ist der DMA-Counter auf Null, soll ein Interrupt 
ausgelöst werden, in dessen Routine die DMA wieder neu initialisiert 
werden soll und weiter arbeitet.

Anbei einige Code-Fragmente:

[1.] DMA-Initialisierung:

void InitDMA_STA013(void)
{
  DMAS    = 0;
  DMAC    = rDMAC;
  DMASA   = (unsigned int)&buffer[0];
  DMADA   = 0xFFF7F80C;
  DMATC   = 30000;
  DMACC0  = 0x00000a00;
  DMAGC   = 0x00000000;
  DMAGD   = 0x00000000;
}

mit

//DMA config reg
#define NCPACK  (0x00 << 24)    // next packet
#define INTEN   (1 << 15)       // interrupt after compleate
#define TRSIZE  (0 << 13)       // transfer access size
#define DSTINC  (0 << 11)       // dest address increment
#define SRCINC  (1 << 9)        // source address increment
#define DSTMOD  (0xF << 5)      // dest module
#define SRCMOD  (0x1 << 1)      // source module
#define DCHN    (1 << 0)        // Data chaining enable

#define rDMAC (0x00000000 | NCPACK | INTEN | TRSIZE | DSTINC | SRCINC | 
DSTMOD | SRCMOD | DCHN)


[2.] in der Interrupt-Routine:

void irq_handler(void)
{
  switch ((0xFF & IRQIVEC) - 1)
    {

      case DMA_1_INT:              // INT 28
        InitDMA_STA013();
        DMACPS  = 0x00000001;     //update for transfer
        DMACCP0 = DMA_EN2;        //enable the DMA to transfer
        libarm_enable_irq();
      break;
    }
}

[3.] SPI-Initialisierung

//--- initialisiere STA013 SPI-Bus ---
void InitSPI(void)
{
  unsigned char data;
  int i,j;

  //--- init the SPI1 bus for STA013 ---
  SPI1CTRL1= (8 << SPI1CTRL1_LPDOUT_BIT) |// 8 bit tief
             (1 << SPI1CTRL1_RXDOUT_BIT); // Vorteiler = 1

  SPI1CTRL2 = SPI1CTRL2_MASTER +          // MasterMode
              SPI1CTRL2_CLKMOD +          // Master Mode
              SPI1CTRL2_POLARITY;         // Polarity FLAG gesetzt

  SPI1CTRL3 = 0x10;                       // DMA-Req-En, SPI1 Int

  SPI1PC6 = SPI1PC6_SOMIFUN +
            SPI1PC6_SIMOIFUN +
            SPI1PC6_CLKFUN +
            SPI1PC6_ENAFUN;               // SOMI SIMO SPICLK ENA

  SPI1PC1 = //SPI1PC1_ENADIR +              // EN-Pin ist GPIO
            SPI1PC1_SCSDIR;               // CS-Pin is output for GIOP


  SPI1CTRL2 |= SPI1CTRL2_SPIEN;           // Enable  SPIs
  data = SPI1BUF;                         // Dummy Read to clear buffer
}


[4.] in der MAIN-Routine

int main(void)
{

  //--- initialisiere Peripherie --
  InitPorts();
  InitInterrupts();
  InitSPI();

  InitDMA_STA013();     // setup DMA and enable it
  __ARMLIB_enableIRQ();

  DMACPS  = 0x00000001;     //update for transfer
  DMACCP0 = DMA_EN2;        //enable the DMA to transfer

  while(1){
    asm("nop");
  };
}

So sollte die DMA ständig den Pufferinhalt über SPI raussenden. Wird der 
Enable-Pin auf LOW gezogen, stoppt der Transfer, solange er auf LOW 
bleibt. In der Interrup-Routine wird die DMA nachgeladen und neu 
gestartet.

Es ergaben sich nun folgende Probleme:
(a) Setze ich in der Interrupt-Routine einen Breakpoint, hält der 
Debugger auch ordentlich an, lasse ich das Programm über RUN 
weiterlaufen, wird die ISR nie wieder aufgerufen; stattdessen lande ich 
irgendwo in der Initialisierungsroutine "InitPorts()".
(b) Lasse ich den Enable der Interrupts in der ISR weg, wird die ISR nur 
einmal durchlaufen - danach scheint kein weiterer DMA-Transfer 
durchgeführt zu werden.
(c) Setze ich en Enable-Pin mehrmals auf LOW und wieder auf HIGH, kann 
es vorkommen, dass das gleiche Ergebnis wie bei (a) entsteht. Nach einem 
Reset der CPU und programmstart, ergibt sich keine weitere DMA-Funktion; 
erst wenn ich die Spannung vom Prozessor nehme sowie den OLIMEX abklemme 
und anschließend alles wieder einschalte, kann ich wieder einen 
DMA-Zyklus laufen.

Ich habe mir das Datasheet derDMA des TMS470 sowie SPNA105 angesehen; 
die Beispiele scheinen jedoch nicht wirklich zu laufen. Hat jemand eine 
Idee oder ein funktionierendes Beispiel für DMA->SPI ???

Ich wäre sehr dankbar,
Michael.


Ich verwende den TMS470R1B1M und für die Entwicklung CrossWorks von 
Rowley V1.6 Built 2 sowie den JTAG Dongle von OLIMEX. Das Board ist ein 
Eigenbau, der in zahlreichen anderen (nicht-DMA-Projekten) tadellos 
funktioniert. Die SPI funktioniert ebenfalls (ohne DNA) einwandfrei.

von Maxwell F. (maxwell78)


Lesenswert?

Hallo!

Also ich würde mir erstmal Gedanken machen, warum die CPU Befehle in der 
InitPorts() Funktion ausführen will bzw. der Reset ausgeführt wird. Bei 
sowas setze ich mir Breakpoints an diese Stellen und schaue mir 
schrittweise die ASM Befehle an. Meistens gibt das LR Auskunft oder mit 
Hilfe des SP das auf dem Stack abgelegt LR ansehen.
Vielleicht ist es auch ein Stack Problem. Ich weiss ja nicht wo und wie 
du dein Ausgabe Array angelegt hast. Was passieren könnte...
Die CPU wird beim Ausführen durch einen Interrupt unterbrochen...es 
kommt zu einem Mode-Wechsel..die CPU retten dein großes Array auf den 
Stack und der platzt. Der SP zeigt dann bei der Rückkehr aus der ISR 
irgendwohin..Vielleicht ist dein Memory mit 0x0 initialisiert worden..so 
lässt sich zB ein Reset erklären. Vielleicht steht auch zufällig die 
Addresse der InitPort() Funktion da.
Genug spekuliert, aber so ähnliche Dinge habe ich schon gesehen ;) Ich 
kann nur sagen, TMS470 und per DMA über SPI Daten ausgeben funktioniert.
Ich kann leider keinen Beispiel Code zur Verfügung stellen.

von Ralph (Gast)


Lesenswert?

es gibt da einges zu Beachten:
1. Ein Reset betrifft NUR den Core und cniht die Pereherie. Das heißt 
das DMA HET,... bei einem Reset nicht resetet werden, sondern 
weiterlaufen.
2. Die Perepherie muss bei der Initialierung nach dem Reset von Hand 
zurückgesetzt werden.
3. Bei einem Breakpoint mit Debugger ist es das gleiche. Nur der Core 
wird angehalten. Das hat zur Folge das bei Breakpoints und der Nutzung 
diverser Perepheriebaugruppen seltsame Effekte auftreten können.

Mögliche Ursache für a:

Das Auslesen des IRQ Registers um die IRQ Quelle festzustellen, setzt 
das Register zurück. Wenn jetzt in der IRQ Routine ein Breakpoint sitzt 
wird der Core angehalten, der DMA läuft weiter und will einen neuen IRQ 
auslösen.
Nach RUN ist das IRQ register gelöscht aber ein IRQ per hardware 
ausgelöst. Die Folge ist Reset da der IRQ nicht zugeordnet werden kann.

Zu b:
In der IRQ wurde ein DMA transfer gestartet, und der DMA ist irgendwann 
damit fertig.
Ist der IRQ disabled wird der DMA nicht für eine neue Übertragung 
initialisiert.
Da das IRQ register nach einem Auslesen gelöscht ist, wird der IRQ auch 
nicht mehr später behandelt, da er nciht mehr im Register steht.
Für einen neuen Transfer muss also der DMA von Hand getartet und die IRQ 
enabled werden.

von MiGu (Gast)


Lesenswert?

Hallo Ralph,

verstehe ich das richtig, dass die DMA trotz des Interrupt-Einsprunges 
(DMA-Counter ist dann zero) immernoch aktiv ist und läuft bzw. wenn ich 
direkt nach dem ISR-Einsprung (also in der ISR) alle Register nachlade 
und den DMA-Enable wieder starte, etwas schief zu gehen scheint??

Ist es sinnvoll, nach dem ISR-Einsprung erstmal einen DMA-Stop im 
DMAGD-Register durchzuführen, dann alle anderen Register der DMA zu 
updaten und danach den DMA-Stop zu entfernen und die DMA zu enablen??

Im Prinzip läuft nämlich die ganze Sache, jedoch gibt's irgendwo noch 
einen Haken...

Viele Grüße,
Michael.

von MiGu (Gast)


Lesenswert?

Hallo Ralph,

eine weitere Frage habe ich dann noch:
In folgender AppNote (http://focus.ti.com/lit/an/spna105/spna105.pdf)
habe ich eine "Andeutung von Code" für einen DMA->SPI Transfer des 
TMS470 gefunden.
Hier wird die DMA ebenfalls in der IRQ nachgeladen (zwar in einer 
Sub-Routine, die aber im ISR-Handler aufgerufen wird. Hier wird kein IRQ 
mehr zusätzlich freigegeben - dennoch sollte das Beispiel (oder habe ich 
das falsch gelesen??) permanent nachladen und einen DMA-Transfer 
darstellen??

Michael.

von Ralph (Gast)


Lesenswert?

Hallo Michael

Es gibt zu mindestens einem TMS470 , ist aber eine Version aus dem 
automotive Bereich , ein älteres Errata sheet das besagt das der DMA 
angehalten sein muss, wenn auf die Configurationsregister schreibend 
zugegriffen wird.
Und zwar gibt es die Flags "Halt" und "Stop", es muss das Flag "Stop" 
genutzt werden

Also es ist auf jedenfall sinnvoll in der IRQ erst den DMA anzuhalten, 
dann zu konfigurieren und dann erneut zu starten.

Zum IRQ enable.
Für den IRQ enable sollte es eigentlich reichen wenn er 1 mal gesetzt 
wird, zb in der Initialisierung.
Also wenn in deinem Code nicht irgendwo der IRQ disabled wird, ist es 
nicht notwendig , ihn in der IRQ wieder neu zu setzen.

von MiGu (Gast)


Lesenswert?

OK. Danke Ralph,

werde ich gleich mal ausprobieren. Die Möglichkeit mit dem STOP-Flag ist 
mir heute Nacht auch in den Sinn gekommen.

Danke, Michael.

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.