ATSAMD21 - GPIO per DMA auslesen

Gast #5796783
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?
Moderator Persönliche Seite #5796837
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.)
Gast #5800956
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.
Moderator Persönliche Seite #5801058
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.
Gast #5801190
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
}
Gast #5802316
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...
Gast #5804604
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;
Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren