Forum: Mikrocontroller und Digitale Elektronik Frage Timing Cortex-M7


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Joachim (Gast)


Angehängte Dateien:

Bewertung
-1 lesenswert
nicht lesenswert
Hallo,

Ich bin gerade dabei, ein Phänomen zu untersuchen, welches ich nicht 
nachvollziehen kann.

Aufbau:
- ATMEL Cortex-M7 Controller 300MHz
- Timer TC läuft mit f 150MHz und erzeugt periodisch einen Interrupt.
- In der Interruptroutine wird per DMA eine Ausgabe an der SPI 
Schnittstelle gestartet, welche mit 50MHz läuft.

Es befinden sich anfangs nur 3 Befehle in der Interruptroutine, welches 
im Listfile so aussehen:
20400000 <TC8_Handler>:
{
20400000:  b490        push  {r4, r7}
20400002:  af00        add  r7, sp, #0
  PIOA->PIO_SODR = 1<<24;                //200ns bis Ende Routine
20400004:  4b2b        ldr  r3, [pc, #172]  ; (204000b4 <TC8_Handler+0xb4>)
20400006:  f04f 7280   mov.w  r2, #16777216  ; 0x1000000
2040000a:  631a        str  r2, [r3, #48]  ; 0x30
  XDMAC->XDMAC_CHID[1].XDMAC_CSA = &SPIBuffer[0];
2040000c:  4b2a        ldr  r3, [pc, #168]  ; (204000b8 <TC8_Handler+0xb8>)
2040000e:  4a2b        ldr  r2, [pc, #172]  ; (204000bc <TC8_Handler+0xbc>)
20400010:  f8c3 20a0   str.w  r2, [r3, #160]  ; 0xa0
  XDMAC->XDMAC_GE = 0x02;
20400014:  4b28        ldr  r3, [pc, #160]  ; (204000b8 <TC8_Handler+0xb8>)
20400016:  2202        movs  r2, #2
20400018:  61da        str  r2, [r3, #28]

Den Port habe ich zu Testzwecken gesetzt. Im Ernstfall sind nur die 
Befehle
XDMAC->XDMAC_CHID[1].XDMAC_CSA = &SPIBuffer[0];
XDMAC->XDMAC_GE = 0x02;
relevant.

Damit alles so schnell wie möglich läuft, habe ich diese Befehle ganz zu 
Anfang in die Interruptroutine gesetzt.
Es funktioniert alles, nur leider zu langsam.
Laut Beschreibung sollte der M7 eine maximale Interruptlatenz von 12 
Zyklen haben.
Der Rest der Taktzyklen ergibt sich aus der Laufzeit der o.g. Befehle.
Danach sollte die SPI eigentlich mit der Ausgabe beginnen.
Tut sie auch, aber erst nach ca. 320 Nanosekunden ab Aufruf des 
Interrupts.
Den Aufruf des Interrupts kann ich messen, weil der Timer zusätzlich 
hardwareseitig einen Port triggert (TIOx).
Der Interrupt wird an der fallenden Flanke des Timersignals generiert 
(gelb) und die SPI-Takte sind blau.
Das Phänomen sind nun diese ca. 310-350ns (Jitter durch die Latenz).
Für die paar Befehle scheint mir das sehr lange zu sein.
Caches sind alle aktiviert.
Ich habe sowohl die Interruptroutine als auch die Interruptvektortabelle 
ins RAM verlagert, welches angeblich mit f(CPU)/2 = 150MHz läuft.
Das wäre eine Zykluszeit von 6,6 ns.
Es dauert also ca 50 Zyklen bis sich an der SPI etwas tut.
Ich könnte mir nun ca 30 Zyklen für Latenz und Ausführungszeit erklären, 
kann aber die restliche Zeit nicht nachvollziehen.
Kann es sein, dass die CPU auf Peripherieadressen (egal ob PIO, SPI, DMA 
oder TIMER) immer mit einer langsamen Zugriffszeit bzw Waitstates 
arbeitet?

Die Flash-Zugriffszeit ist ja mit ca. 40ns (25MHz) angegeben. Das würde 
bei 3 Zugriffen auf Peripherieadressen die verlorenen 120ns erklären.
40ns sind aber gegenüber 6,6ns Takt aus dem RAM eine gefühlte Ewigkeit.

Das würde aber auch bedeuten, dass man daran nichts ändern kann, egal 
mit welchen Tricks man die CPU hochkitzelt.
Leider habe ich bisher keine konkreten Angaben gefunden, wie schnell die 
Peripheriezugriffe sind. Oder an der falschen Stelle gesucht.

Kennt sich da jemand aus?
Das ist übrigens kein Cortex-M7 Problem. Auch bei den M3 sind mir 
verdächtig lange Zeiten aufgefallen, die ich nicht erklären konnte.


Gruß

Joachim

von A. K. (prx)


Bewertung
1 lesenswert
nicht lesenswert
Joachim schrieb:
> Kann es sein, dass die CPU auf Peripherieadressen (egal ob PIO, SPI, DMA
> oder TIMER) immer mit einer langsamen Zugriffszeit bzw Waitstates
> arbeitet?

Das könnte so sein. Näheres verrät bestimmt die Dokumentation des 
geheimen Controllers.

> Das ist übrigens kein Cortex-M7 Problem

Yep. Weil dafür nicht der Core zuständig ist, sondern das, was den Core 
umgibt. Wie etwa diverse Systembusse.

: Bearbeitet durch User
von Ingo L. (corrtexx)


Bewertung
0 lesenswert
nicht lesenswert
Joachim schrieb:
> - In der Interruptroutine wird per DMA eine Ausgabe an der SPI
> Schnittstelle gestartet, welche mit 50MHz läuft.
Warum startet der Timer nicht direkt den DMA?

von Jim M. (turboj)


Bewertung
0 lesenswert
nicht lesenswert
Und da liegt auch definitiv keinen anderer IRQ Handler davor?

Ich frage das, weil der Namen TC8_Handler IMHO kein Vektor Namen ist - 
da kommt normalerweise ein IRQ drin vor, wie in Timer3_IRQHandler.

Ich sehe aber den Startup und die Vektortabelle hier natürlich nicht.

Ein übergeordneter Handler der erstmal mittels Registerzugriffen die 
korrekte Funktion ermittelt und per Function-Pointer aufruft, könnte die 
~300ns recht gut erklären.

: Bearbeitet durch User
von Joachim (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Näheres verrät bestimmt die Dokumentation des
> geheimen Controllers.

Wieso geheim? Cortex-M7 von ATMEL ist die ATSAME70 Serie.
Wobei sich SAME70 und SAMS70 oder auch SAMV70 in dieser Funktion nicht 
unterscheiden sollten.

Ingo L. schrieb:
> Warum startet der Timer nicht direkt den DMA?

Soweit bin ich nicht. Ich arbeite mich erst in die neue Reihe ein.
Einen Start eines Memory-to-SPI DMA per Timer direkt habe ich noch nicht 
probiert. Weiss gar nicht ob das überhaupt geht.

Jim M. schrieb:
> Und da liegt auch definitiv keinen anderer IRQ Handler davor?

In der Startup.c sind die Vektoren so benannt, z.B.
void TC5_Handler    (void) __attribute__ ((weak, alias("Dummy_Handler")));

wobei mein TC8 aus der Liste fehlt, weil er benutzt wird.
Im MAP-File wird angegeben

Ich habe die Vektortabelle ab Adresse 0x00400000 (Flash) ins RAM kopiert 
mit
uint32_t vectorTable[128] __attribute__ ((aligned(256)));

memcpy(vectorTable, (uint32_t*)0x00400000, sizeof(uint32_t) * 128);  
SCB->VTOR = (uint32_t) vectorTable;
zu Beginn der Mainroutine.

Das funktioniert auch. Würde es nicht, käme es zum Totalabsturz weil ich 
ja VTOR umgeleitet habe.

Im MAP-file wird die Lage meiner VektorTable auch richtig im RAM 
angezeigt.
0x20431600                vectorTable

Beim Interrupt sollte also der Handler direkt im RAM ohne Umwege 
angesprungen werden.

Ich suche weiter. Jedenfalls schonmal vielen Dank für die Tips.

von Ingo L. (corrtexx)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Joachim schrieb:
> Ich suche weiter. Jedenfalls schonmal vielen Dank für die Tips.
Das ist löblich, da du aber in einer Hochsprache programmierst (was auch 
löblich ist), bist du nun an dem Punkt angekommen an dem meiner Meinung 
nur zwei Wege weiterführend sind:

1.
Du stepst das ganze im Debugger durch und guckst, wo deine Takte 
verloren gehen und stellt u.U. fest, dass es so wie es ist nicht 
änderbar ist

oder

2.
Du machst es gleich richtig:
- Schaue im RM unter DMA nach, welchen DMA Channel dein Timer besetzt.
- Initialisiere diesen DMA Channel für dein SPI
- Initialisiere deinen Timer, dass er ein DMA-Request absetzt

Für einen STM32F0 sieht das so aus:
void Init_SPI ( void )
{
    ////////////////////////////////////////////////////////////////////////////////
    // SPI1 & TIM1 Power
    RCC_APB2PeriphClockCmd (RCC_APB2Periph_SPI1 | RCC_APB2Periph_TIM1, ENABLE );

    // IO Power
    RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOA, ENABLE );

    //DMA Power
    RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );

    ////////////////////////////////////////////////////////////////////////////////
    // Create Structs;
    SPI_InitTypeDef Spi_Struct;
    GPIO_InitTypeDef SPI_IO_Struct;
    DMA_InitTypeDef DMA_Init_Struct;
    TIM_TimeBaseInitTypeDef TIM_Init_Struct;

    ////////////////////////////////////////////////////////////////////////////////
    // Preinit Struct
    SPI_StructInit( &Spi_Struct );
    GPIO_StructInit( &SPI_IO_Struct );
    DMA_StructInit( &DMA_Init_Struct);
    TIM_TimeBaseStructInit( &TIM_Init_Struct);

    ////////////////////////////////////////////////////////////////////////////////
    // IOs
  SPI_IO_Struct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_4;
  SPI_IO_Struct.GPIO_Mode = GPIO_Mode_AF;
  SPI_IO_Struct.GPIO_OType = GPIO_OType_PP;
  SPI_IO_Struct.GPIO_Speed = GPIO_Speed_Level_1;
  SPI_IO_Struct.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOA, &SPI_IO_Struct);

    ////////////////////////////////////////////////////////////////////////////////
    // SPI
    /* Initialize the SPI_Direction member */
    Spi_Struct.SPI_Direction = SPI_Direction_1Line_Tx;
    /* Initialize the SPI_Mode member */
    Spi_Struct.SPI_Mode = SPI_Mode_Master;
    /* Initialize the SPI_DataSize member */
    Spi_Struct.SPI_DataSize = SPI_DataSize_16b;
    /* Initialize the SPI_CPOL member */
    Spi_Struct.SPI_CPOL = SPI_CPOL_High;
    /* Initialize the SPI_CPHA member */
    Spi_Struct.SPI_CPHA = SPI_CPHA_1Edge;
    /* Initialize the SPI_NSS member */
    Spi_Struct.SPI_NSS = SPI_NSS_Hard;
    /* Initialize the SPI_BaudRatePrescaler member */
    Spi_Struct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    /* Initialize the SPI_FirstBit member */
    Spi_Struct.SPI_FirstBit = SPI_FirstBit_MSB;
    /* Initialize the SPI_CRCPolynomial member */
    Spi_Struct.SPI_CRCPolynomial = 0;
    SPI_Init( SPI1, &Spi_Struct );

    /* Handle /SS */
    SPI_NSSPulseModeCmd( SPI1, ENABLE );
    /* Enable SPI1 Module */
    SPI_Cmd( SPI1, ENABLE);

    ////////////////////////////////////////////////////////////////////////////////
    /* Initialize the DMA_PeripheralBaseAddr member */
    DMA_Init_Struct.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->DR);
    /* Initialize the DMA_MemoryBaseAddr member */
    DMA_Init_Struct.DMA_MemoryBaseAddr = (uint32_t)&(SineTable[3][0]);
    /* Initialize the DMA_DIR member */
    DMA_Init_Struct.DMA_DIR = DMA_DIR_PeripheralDST;
    /* Initialize the DMA_BufferSize member */
    DMA_Init_Struct.DMA_BufferSize = 360;
    /* Initialize the DMA_PeripheralInc member */
    DMA_Init_Struct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    /* Initialize the DMA_MemoryInc member */
    DMA_Init_Struct.DMA_MemoryInc = DMA_MemoryInc_Enable;
    /* Initialize the DMA_PeripheralDataSize member */
    DMA_Init_Struct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    /* Initialize the DMA_MemoryDataSize member */
    DMA_Init_Struct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    /* Initialize the DMA_Mode member */
    DMA_Init_Struct.DMA_Mode = DMA_Mode_Circular;
    /* Initialize the DMA_Priority member */
    DMA_Init_Struct.DMA_Priority = DMA_Priority_High;
    /* Initialize the DMA_M2M member */
    DMA_Init_Struct.DMA_M2M = DMA_M2M_Disable;

    DMA_Init(DMA1_Channel5, &DMA_Init_Struct );
    DMA_Cmd( DMA1_Channel5, DISABLE);

    ////////////////////////////////////////////////////////////////////////////////
    // Timer1
    TIM_Init_Struct.TIM_Period = 1333;
    TIM_Init_Struct.TIM_Prescaler = 0;
    TIM_Init_Struct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_Init_Struct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_Init_Struct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit( TIM1, &TIM_Init_Struct );

    ////////////////////////////////////////////////////////////////////////////////
    TIM_UpdateRequestConfig(TIM1, TIM_UpdateSource_Global);
  TIM_ARRPreloadConfig(TIM1, ENABLE);
  TIM_UpdateDisableConfig(TIM1,DISABLE);
  TIM_DMACmd( TIM1, TIM_DMA_Update, ENABLE );
    TIM_Cmd( TIM1, DISABLE );
    DMA_Cmd( DMA1_Channel5, ENABLE);
}

Hier wird über TIM1 der DMA getriggert, welcher den SPI befeuert und 
eine Sinustabelle ausspuckt, sobald der Timer läuft (TIM_Cmd( TIM1, 
ENABLE );)

Sollte mich wundern, wenn ein M7 das nicht kann

: Bearbeitet durch User
von Bauform B. (bauformb)


Bewertung
1 lesenswert
nicht lesenswert
Je nach Einstellung der Floating Point Hardware werden 0 bis 17 Register 
zusätzlich gerettet. 17 * 6.6ns = 112.2ns.

von Joachim (Gast)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Habe weiteres herausgefunden.
Ich habe nun einen Port über die Laufzeit der Interruptroutine gesetzt 
und gemessen.
Der grobe Rahmen sieht so aus:
__attribute__ ((section(".ramfunc")))
void TC8_Handler(void)
{
  register int value;
  PIOA->PIO_SODR = 1<<24;                
  XDMAC->XDMAC_CHID[1].XDMAC_CSA = &SPIBuffer[0];
  XDMAC->XDMAC_GE = 0x02;
  value = TC2->TC_CHANNEL[2].TC_SR;
  if ...
  {
   ...
  }
  else
  {
    ...

  }
  PIOA->PIO_CODR = 1<<24;
}

Mit 200ns Gesamtdauer inklusive etwas beiläufigem Code nach dem Start 
des DMA sieht es gut aus.
Das komische ist aber dass die Ausgabe an der SPI erst NACH ENDE der 
Interruptroutine beginnt, obwohl ich den DMA dafür bereits am Anfang der 
Routine starte.

Es sieht also danach aus dass es nicht am Aufruf der Interruptroutine 
liegt, sondern daran dass zwischen dem Startbefehl des DMA bis zum 
Zeitpunkt an dem Daten an der SPI erscheinen über 200ns vergehen.
Das Problem (bzw. meines) scheint also am DMA zu liegen und nicht am 
Aufruf des Interrupts.
Der DMA des M7 ist leider recht komplex, vielleicht habe ich da was 
übersehen.
Ich versuche das ganze lowlevel zu verstehen und ASF von ATMEL möglichst 
zu umgehen, weil das sehr verschachtelt und schlecht dokumentiert ist.

Ich war bisher der Meinung, dass die SPI den DMA triggern muss (mit dem 
SPI_SR_TDRE Bit).
Ich gebe ja nicht nur einen Wert an die SPI, sondern eine Reihe von 6 
Werten. Hatte ich nicht anfangs erwähnt, weil ich den Fehler leider 
woanders gesucht habe.
Das klappt ja alles auch. DMA schiebt nacheinander die 6 Werte an die 
SPI, ohne zeitlichen Verlust zwischen den Paketen.
Mit dem Timer soll lediglich bestimmt werden, wann das Senden der 6 
Pakete beginnt.
Und eben bis das Senden des ersten Pakets beginnt, dauert es länger als 
die gesamte Interruptroutine.

Deaktviere ich z.B. den DMA und schreibe im Interrupt manuell etwas an 
die SPI, dann erscheint dies noch während der Laufzeit des Interrupt an 
der SPI. Ca 150ns ab Interrupt, also 150ns früher als mit DMA.

Ingo L. schrieb:
> Hier wird über TIM1 der DMA getriggert,

Es wird aber pro Timerzyklus nur ein Wert an SPI geschickt, oder sehe 
ich das falsch? Dann kann ja der Timer den Takt übernehmen und der 
Status der SPI ist unwichtig, solange sicher ist, dass der Timer nicht 
schneller als die SPI-Ausgabezeit ist.
In meinem Fall werden aber pro Timerzyklus 6 Werte an SPI geschickt. Die 
SPI muss dem DMA also mitteilen, wann die nächsten Daten geschrieben 
werden können.

Zumindest weiss ich jetzt dass es kein Interruptproblem ist, sondern ein 
DMA-Startproblem.

Vielen Dank!

von Ingo L. (corrtexx)


Bewertung
0 lesenswert
nicht lesenswert
Joachim schrieb:
> Es wird aber pro Timerzyklus nur ein Wert an SPI geschickt
Ja, aber der M7 dürfte etwas mehr Einstellungen besitzen, sodass der DMA 
n mal pro Timerinterrupt feuert. Das geht m.M.n.

von Christopher J. (christopher_j23)


Bewertung
0 lesenswert
nicht lesenswert
Angenommen die Daten, welche per DMA rausgeschrieben werden sollen 
liegen auch im SRAM, dann hast du einen gleichzeitigen Buszugriff von 
DMA und deinem Code auf den RAM. Einer muss dann warten.

Ingo L. schrieb:
> Ja, aber der M7 dürfte etwas mehr Einstellungen besitzen, sodass der DMA
> n mal pro Timerinterrupt feuert. Das geht m.M.n.

Das hat zwar meiner Meinung nach mit dem Kern direkt nichts zu tun, weil 
DMA herstellerspezifisch ist aber es sollte mit dem Boliden von 
Controller den du hast ganz sicher gehen und ist die m.M.n. einzig 
sinnvolle Lösung für niedrigen Latenzen. Trotzdem musst du etwaige 
Buskollisionen im Auge behalten, weil die eben für Jitter sorgen können.

von Test (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Da bin ich wohl zu spät.
Darüber hinaus:
Evtl. ist es sinnvoll den ISR Code statisch in den Cache zu schieben.
Das habe ich bis jetzt allerdings nur für ARM926ejs getan. Geht aber mit 
dem M7 bestimmt auch.

Wie sieht das ganze Verhalten aus, wenn der ISR Code im Flash verbleibt?
Das sollte ja schnell zu testen sein.

von Christopher J. (christopher_j23)


Bewertung
0 lesenswert
nicht lesenswert
Bezüglich der Frage ob es möglich ist, mittels Timer direkt den 
DMA-Transfer zu triggern, hier mal ein Auszug aus dem Kapitel des 
PWM-Controllers "49.2 Embedded Characteristics" aus dem Datenblatt des 
SAM E70:

> 8 Comparison Units Intended to Generate Interrupts, Pulses on Event Lines and 
DMA Controller Transfer Requests

Dazu dann noch die Tabelle in "35.4 DMA Controller Peripheral 
Connections", wo explizit PWM0 und PWM1 gelistet sind und man kann ganz 
sicher sagen, dass man DMA-Transfers auch direkt per Timer/PWM triggern 
kann.

Ich muss aber sagen, dass die Atmel-Datenblätter an der Stelle schon 
ganz schön verschwurbelt sind. Bei anderen Herstellern findet man 
eindeutig schneller einen Hinweis ob das so überhaupt möglich ist und 
wenn ja, dann wie.

von Joachim (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Christopher J. schrieb:
> dann hast du einen gleichzeitigen Buszugriff von
> DMA und deinem Code auf den RAM. Einer muss dann warten.

Das ist mir klar.
Es wundert mich eben nur, dass trotz dieses ganzen Multiport-RAM und 
mehrfacher Buscontroller es trotzdem ca. 60 Taktzyklen braucht, bis der 
DMA nach der Freigabe mal in die Gänge kommt und die SPI befeuert. Eine 
Latenz von einigen wenigen Buszyklen wäre ja kein Problem.
60 Taktzyklen für etwas erinnern mich eher an die alten 8086-Zeiten :-)
Ja es sind 60, nicht 30, denn bisher hatte ich mit 150MHz gerechnet. 
Stimmt aber nicht, denn wie es scheint läuft der Code mit 300MHz aus dem 
Cache.
200ns Verzögerung DMA<>SPI / 3,33ns = 60.

Test schrieb:
> Wie sieht das ganze Verhalten aus, wenn der ISR Code im Flash verbleibt?
> Das sollte ja schnell zu testen sein.

Es ändert sich messbar gar nichts.
Der Cache scheint dies bei der kurzen Routine gleich zu beschleunigen, 
egal woher der Code stammt und selbst egal ob die Vektortable im Flash 
oder im RAM liegt.
Ich habe folgende Kombinationen getestet:
- Code im Flash, Caches deaktivert, Dauer ca. 900ns Interruptstart bis 
SPI
- Code im RAM, Caches deaktviert, Dauer ca. 640ns
- Code im Flash, D-Cache aktiviert, Dauer 600ns
- Code im Flash, D+I-Cache aktivert, Dauer 320ns
- Code im RAM, D+I-Cache aktiviert, Dauer 320ns

Das erscheint plausibel. Aus dem Cache läuft alles mit maximaler 
CPU-Frequenz. Aus dem RAM ohne Cache mit halbem Takt (150MHz).
Das sind die 640 zu 320ns Laufzeitänderung.
RAM + Cache zusammen bringt nichts, da Cache 2x schneller als Code im 
RAM.
Das Flash ist sogar noch überraschend schnell.

Ich glaube im Moment, die letzte Möglichkeit ist, alles in den TCM zu 
verfrachten und schauen ob es beim vollen CPU-Takt und ohne den Cache 
etwas bringt bzw. ob der DMA irgendwie vom Cache beeinflusst wird.

Tja, sonst muss dann eben so sein.

Gruß

Joachim

von m.n. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Joachim schrieb:
> Ich glaube im Moment, die letzte Möglichkeit ist, alles in den TCM zu
> verfrachten und schauen ob es beim vollen CPU-Takt und ohne den Cache
> etwas bringt bzw. ob der DMA irgendwie vom Cache beeinflusst wird.

Mach das nicht, das sind keine Zugriffsprobleme auf die Speicher und da 
läuft an anderer Stelle etwas nicht richtig.
Lass mal den Interrupt weg und trigger den DMA nachdem Du einen Portpin 
gesetzt hast, um die Latenz zu sehen.

Beitrag #5417318 wurde vom Autor gelöscht.

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.