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?
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 ;-)
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.)
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.
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?
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.
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?
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.
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.
achso, GCLK0 = Sysclk! dann hab ich das doch schon richtig gemacht :)
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).
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 | } |
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...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.