Forum: Mikrocontroller und Digitale Elektronik ATSAMD21 - GPIO per DMA auslesen


von Anfänger (Gast)


Lesenswert?

Hallo,

ich lerne gerade den Atmel SAMD21 kennen... mit dem ASF

Ich möchte einen Port möglichst schnell nach einer Flanke an einem Pin 
auslesen und dachte zuerst dass ich dafür den DMAC nutzen kann.

Jetzt sieht es für mich aber so aus als ob DMA nur mit einigen 
Peripherals machbar ist (ADC, DAC, Timer, etc). Zumindest habe ich keine 
Konfiguration gefunden für die GPIOs.

Ist das möglich, und wenn ja wie muss ich das konfigurieren?

von Rudolph (Gast)


Lesenswert?


von Anfänger (Gast)


Lesenswert?

OK, also nicht möglich. Danke!

Ist es deutlich langsamer wenn ich das per externen Interrupt und ISR 
mache?
Oder ist der Vorteil von DMA nicht in der Geschwindigkeit der Ausführung 
sondern dass die CPU nicht belegt wird?

Ich weiss, sind grundlegende Fragen, aber ich schrub ja schon das ich 
noch lerne ;-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Anfänger schrieb:
> Oder ist der Vorteil von DMA nicht in der Geschwindigkeit der Ausführung
> sondern dass die CPU nicht belegt wird?

Das auf jeden Fall.

Aber: der SAMD21 hat doch ein Event-System. Kannst du deinen gewünschten 
Effekt vielleicht damit erreichen? (Hab auf die Schnelle nicht gefunden, 
was man damit alles genau erledigen kann.)

von Noob A. (strippenzieher)


Lesenswert?

Was wäre denn der Vorteil von DMA gegenüber ISR wenn es nur auf die 
Abarbeitungsgeschwindigkeit (hab ich jetzt so verstanden) ankommt?

So wie ich das überblicke reicht ein "klassisches" interruptgesteuertes 
System aus.

von Anfänger (Gast)


Lesenswert?

Ich hab das jetzt mal per ISR und EXTINT ausprobiert...
funktioniert, allerdings mit recht hoher Verzögerung!
es dauert gut und gerne 30 Takte bis die ISR ausgeführt wird
Im Datenblatt lese ich allerdings 12 cycles...

Ist das normal?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Anfänger schrieb:
> Im Datenblatt lese ich allerdings 12 cycles...

Hast du an mögliche flash wait states gedacht?

Könnte schneller werden, wenn du die ISR in den RAM legst.

von Einer K. (Gast)


Lesenswert?

Anfänger schrieb:
> Im Datenblatt lese ich allerdings 12 cycles..
Sind da auch die nötigen Push schon drin, oder ist das nur die Zeit, 
BIS zum ISR Aufruf?

von Anfänger (Gast)


Lesenswert?

ich hab FlashWaitState auf 1, ist notwendig bei 48MHz Takt.

Allerdings bin ich mir nicht sicher ob ich den µC auch tatsächlich mit 
48MHz betreibe...
Habe festgestellt dass der Takt von ClockGenerator 0 nicht gleich mit 
dem Systemtakt ist.
Bisher fehlt mir die Info wie ich den SysClk auf einem Pin ausgeben 
kann...

Die ISR liegt im RAM, habe dazu das Macro RAMFUNC benutzt.
Das hat ein klein wenig was gebracht.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Anfänger schrieb:
> Bisher fehlt mir die Info wie ich den SysClk auf einem Pin ausgeben
> kann...

Die letzte Spalte der Tabelle mit dem IO-Multiplexing der Pins zeigt 
dir, auf welchen Pins du dir die GCLKs ausgeben lassen kannst. Für 
GCLK[0] wären das PA14, PB14, PA27 oder PA28 (PA30 als SWDCLK wirst du 
wohl eher nicht damit beaufschlagen wollen). Das entsprechende Portpin 
musst du auf peripheral function H einstellen, und im GCLK->GENCTRL 
musst du das OE-Bit setzen.

von Anfänger (Gast)


Lesenswert?

achso, GCLK0 = Sysclk!
dann hab ich das doch schon richtig gemacht :)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Mir ist zumindest so, dass GLCK[0] der CPU-Takt wäre. Aber ich müsste 
die Details jetzt auch im Datenblatt nachlesen, das Taktgenerator-System 
der SAMDs ist doch ein wenig komplex (und ziemlich flexibel angelegt).

von Anfänger (Gast)


Lesenswert?

Ich schaff es nicht unter 30 Takte interrupt latency zu kommen :(
Selbst mit allen Optimierungen eingeschaltet...

ICh generier mir per TCC eine Takt den ich dann wiederum als externen 
Interrupt nutze.
Kann es an diesem Umweg liegen?
Den hab ich mir ausgedacht, da ich es nicht schaffe den 
TCC0_Interrupt-Handler einzubinden.
Atmel Studio beschwert sich dann immer über "multiple definition"
komischerweise wird TCC0_Handler aber an exakt den gleichen Stellen 
definiert wie der EIC_Handler, den ich problemlos "überschreiben" 
konnte...
Aber dazu mach ich lieber noch ein gesondertes Thema auf...
1
#include <asf.h>
2
#include <stdio.h>
3
#include <string.h>
4
5
unsigned int uiPortIn0 = 0;
6
unsigned int uiPortIn1 = 0;
7
8
9
RAMFUNC void EIC_Handler()
10
{
11
  //if(EIC->INTFLAG.reg & EIC_INTFLAG_EXTINT0)
12
  {
13
    REG_PORT_OUTSET1 = PORT_PB22;
14
    uiPortIn0 = REG_PORT_IN0;
15
    uiPortIn1 = REG_PORT_IN1;
16
    EIC->INTFLAG.reg = EIC_INTFLAG_EXTINT0;
17
    REG_PORT_OUTCLR1 = PORT_PB22;
18
  }
19
}
20
21
22
void write_usart(unsigned char *string);
23
24
void write_usart(unsigned char *string)
25
{
26
  usart_write_buffer_wait(&USART_0, string, strlen((char*)string));
27
  return;
28
}
29
30
int main (void)
31
{
32
  unsigned char ucOutBuffer[128];
33
  unsigned int compare = 0;
34
35
  system_init();  
36
37
  struct usart_config config_usart;
38
  usart_get_config_defaults(&config_usart);
39
40
  config_usart.baudrate      = 115200;
41
  config_usart.character_size    = USART_CHARACTER_SIZE_8BIT;
42
  config_usart.parity        = USART_PARITY_NONE;
43
  config_usart.stopbits      = USART_STOPBITS_1;
44
  config_usart.mux_setting    = USART_RX_3_TX_2_XCK_3;
45
  config_usart.pinmux_pad0    = PINMUX_UNUSED;
46
  config_usart.pinmux_pad1    = PINMUX_UNUSED;
47
  config_usart.pinmux_pad2    = PINMUX_PA10C_SERCOM0_PAD2;
48
  config_usart.pinmux_pad3    = PINMUX_PA11C_SERCOM0_PAD3;
49
  config_usart.generator_source  = GCLK_GENERATOR_0;
50
    
51
  while (usart_init(&USART_0, SERCOM0, &config_usart) != STATUS_OK) {}
52
  usart_enable(&USART_0);
53
    
54
  struct port_config config_port_pin;
55
  port_get_config_defaults(&config_port_pin);
56
  config_port_pin.input_pull = PORT_PIN_PULL_UP;
57
  config_port_pin.direction = PORT_PIN_DIR_OUTPUT;
58
  port_pin_set_config(PIN_PA14, &config_port_pin);
59
  port_pin_set_config(PIN_PB22, &config_port_pin);
60
  config_port_pin.direction = PORT_PIN_DIR_INPUT;
61
  port_pin_set_config(PIN_PA16, &config_port_pin);
62
  
63
  struct extint_chan_conf config_extint_chan;
64
  extint_chan_get_config_defaults(&config_extint_chan);
65
  config_extint_chan.gpio_pin           = PIN_PA16A_EIC_EXTINT0;
66
  config_extint_chan.gpio_pin_mux       = MUX_PA16A_EIC_EXTINT0;
67
  config_extint_chan.gpio_pin_pull      = EXTINT_PULL_UP;
68
  config_extint_chan.detection_criteria = EXTINT_DETECT_RISING;
69
  config_extint_chan.filter_input_signal = true;
70
  extint_chan_set_config(0, &config_extint_chan);
71
72
  EIC->INTENSET.reg = EIC_INTENSET_EXTINT0;
73
  EIC->INTFLAG.reg = EIC_INTFLAG_EXTINT0;
74
75
  NVIC_EnableIRQ(EIC_IRQn);
76
  NVIC_SetPriority(EIC_IRQn,0);
77
    
78
    
79
  struct tcc_config config_tcc;
80
  struct tcc_module TCC_0;
81
  tcc_get_config_defaults(&config_tcc, TCC0);
82
  config_tcc.counter.clock_source = GCLK_GENERATOR_1;
83
  config_tcc.counter.clock_prescaler = TCC_CLOCK_PRESCALER_DIV1;
84
  config_tcc.counter.period = 0x3E;
85
  config_tcc.compare.wave_generation = TCC_WAVE_GENERATION_SINGLE_SLOPE_PWM;
86
  config_tcc.pins.enable_wave_out_pin[1] = true;
87
  config_tcc.compare.match[1] = 0x13;
88
  config_tcc.pins.wave_out_pin[1]        = PIN_PB11F_TCC0_WO5;
89
  config_tcc.pins.wave_out_pin_mux[1]    = PINMUX_PB11F_TCC0_WO5;
90
  tcc_init(&TCC_0, TCC0, &config_tcc);
91
  tcc_enable(&TCC_0);
92
    
93
  PORT->Group[0].DIRSET.reg |=  PORT_PA14;
94
  PORT->Group[0].PINCFG[14].reg |= PORT_PINCFG_PMUXEN;
95
  PORT->Group[0].PMUX[7].bit.PMUXE = 0x7; //Attach clock to I/O
96
    
97
  write_usart("\n\rlet's go...\n\r");
98
  while(1)
99
  {
100
    
101
  }
102
}

von ThomasF (Gast)


Lesenswert?

Der IO-Bus könnte auch noch was bringen.

PORT_IOBUS->Group[1].

von Anfänger (Gast)


Lesenswert?

Danke Thomas,

das hat dafür gesorgt dass die ISR an sich deutlich schneller 
abgearbeitet wird. Ich benutze es zum Setzen sowie zum Einlesen:
von 960ns runter auf 425ns!

Allerdings ist die interrupt latency nun von 520ns auf 900ns 
angestiegen, oder in Taktzyklen von 24 auf 41...

Außerdem ist es nicht egal in welcher Reihenfolge ich die Peripherie 
initialisiere und starte...

Konfiguriere ich den Taktausgang vor dem TCC dann erhöht sich die 
Latenzzeit, und das nicht nur unwesentlich!

Verändere ich die Frequenz vom TCC erhöht sich die Latenzzeit ebenfalls 
um ein paar Takte...

Ih kapier das nicht...

von ThomasF (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Anfänger,


Im Anhang ist ein AS7 Projekt mit dem ich getestet habe.

25 Takte bis Ausgang gesetzt 20 Takte für ISR

ATSAM läuft mit 50Mhz (Kann man am Oszi besser die Takte Zählen)

Man könnte vielleicht noch ein paar Takte herausholen
wenn man die "DeviceVectors" auch ins RAM legt (Ist im Projekt gemacht)


Für den EIC einen hohen Core-Takt nehmen bring ein paar Takte.
Ich habe keine Ahnung wie man das in ASF macht.

// Turn on clock for EIC core
GCLK->CLKCTRL.reg =   GCLK_CLKCTRL_ID(EIC_GCLK_ID)
    | GCLK_CLKCTRL_GEN(GEN_GCLK_96M)
    | GCLK_CLKCTRL_CLKEN ;


Bei mir ist die Optimierung -O1 am besten.


Das Test-Programm geht nur wenn ein externer 32kHz Quarz vorhanden ist.


Gruß Thomas


// Vorschlag Code Reihenfolge (Funktion unverändert)
  NVIC_SetPriority(EIC_IRQn,0);
  NVIC_EnableIRQ(EIC_IRQn);

  EIC->INTFLAG.reg = EIC_INTFLAG_EXTINT0;
  EIC->INTENSET.reg = EIC_INTENSET_EXTINT0;

  /*PORT->Group[0].DIRSET.reg |=  PORT_PA14;*/ Nicht notwendig
  PORT->Group[0].PMUX[7].bit.PMUXE = 0x7; //Attach clock to I/O
  PORT->Group[0].PINCFG[14].reg |= PORT_PINCFG_PMUXEN;

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.