Hallo, ich möchte gern spezielle Speicherbereichen auslesen bzw. es sollen nur ein paar Stellen übersprungen werden oder ab und zu mal die selbe doppelt ausgegeben werden. Was wäre die beste Methode dafür? Eine Art Pointer auf die Database Adresse der DMA? Das ständige Abändern der Database Adresse im DMA Interrupt? Oder das Neubeschreiben des Zielspeichers im Interrupt? Bestimmt gibt es noch bessere Methoden, eventuell kennt jemand ein Beispiel? Gruß Marcus
Marcus P. schrieb: > Hallo, ich möchte gern spezielle Speicherbereichen auslesen bzw. es > sollen nur ein paar Stellen übersprungen werden oder ab und zu mal die > selbe doppelt ausgegeben werden. Das klingt irgendwie ganz schön konfus. > Was wäre die beste Methode dafür? Ich nehme mal an, daß Du in C programmierst?!? Warum definierst Du Dir nicht einen Zeiger und liest die Speicherstelle, die Dir gefällt? DMA brauchst Du dafür nicht. DMA nimmt man, wenn viele Daten am Stück übertragen werden sollen. Wobei bei den meisten DMA-Controllern sowohl der Quellspeicher (zum Füllen) als auch der Zielspeicher (für FIFOs) eine konstante Adresse haben kann. Grüße Sven
Hallo Sven, Ok, ich habe mich etwas dämlich ausgedrückt. Ich möchte ein freqenzvariables Signal mittels DAC ausgeben und für höhere Frequenzen sollen z.B. einige Speicherstellen der LookUP übersprungen werden. Ich arbeite in C und mit Zeigern scheint sich bei der DMA nichts reißen zu lassen, scheinbar wird die Basis Adresse des Speichers nur einmal eingelesen und von da aus über andere Funktionen weiter hochgezählt und nicht wieder jedes mal auf die angegebene Adresse geschaut,so dass man hier leider eine Zeiger definieren kann. Klar gibt es die direkten DAC Befehle, aber die DMA scheint mir für dieses Vorhaben angebrachter. Momentan versuche ich im DMA Interrupt diese Zeilen unterzubringen, aber das scheint auch nicht von Erfolg gekrönt zu sein: DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)neues_Lookuptable_Feld; DMA_Init(DMA2_Channel4, &DMA_InitStructure); Gruß Marcus
Ich würde die Kurvenform im Flash ablegen und je nachdem wie sie dann gebraucht wird, aufbereitet einmal ins Ram kopieren. Die DMA dann aus dem Ram laufen lassen. OK das kostet Ram aber vielleicht paßt das ja. Eine andere Möglichkeit sehe ich gerade nicht bei dem STM32.
Scheinbar gehts doch in dem ich die oben genannten Zeilen im Interrupt verwende, ich hatte vergessen den Buffer der DMA auf 1 zurück zu setzen, da ich vorher das automatische Auslesen der Lookup-table genutzt habe. Über die Qualität des Signals kann ich mich jetzt noch nicht wirklich auslassen, zumal ich noch einen Sprung drin habe. Ich muss wohl noch etwas an der Hochzählung arbeiten. Ich kenne mich nicht so gut aus, aber wenn ich ständig den Ram neu beschreibe, frisst das nicht mehr als ich am Ende durch die DMA gut mache? Bzw. wahrscheinlich ist auch bei meiner Variante der DMA schon wieder ziemlich sinnfrei geworden.
Marcus P. schrieb: > aber wenn ich ständig den Ram neu > beschreibe Du mußt das Ram doch nur einmal mit den gewünschten Werten aus der Lookup-Table (aus dem Flash) beschreiben. Solange kann dann die DMA diese Werte an den DAC senden. Erst wenn sich die Kurvenform ändert, dann benötigst du neue Werte aus der Lookuptable.
Wenn das ein DDS Generator werden soll kannst du DMA vergessen.
holger schrieb: > Wenn das ein DDS Generator werden soll kannst du DMA vergessen. Eine Begründung wäre jetzt schön. Hmmm... ja wenn ich länger drüber nachdenke... falls die Schrittweite nicht genau wieder am Anfang der Lookup-Table landet funktioniert das nicht. Dann müßte man jedesmal wieder neue Werte ins Ram kopieren wenn der Buffer von der DMA abgearbeitet wurde.
>> Wenn das ein DDS Generator werden soll kannst du DMA vergessen. > >Eine Begründung wäre jetzt schön. >Hmmm... ja wenn ich länger drüber nachdenke... falls die Schrittweite >nicht genau wieder am Anfang der Lookup-Table landet Genau das ist das Problem. Bei ungeraden Frequenzen schwebt der Samplezeiger durch das Array. Der nimmt nie die gleichen Speicherzellen.
Trotzdem wäre die DMA ein Vorteil denke ich. Sie bestimmt das Timing dann (also per Hardware). Dadurch ist das schon mal genau (wenn man die Priorität der DMA hoch genug hat). Dann braucht man nur mittels der DMA half and full Interrupts den Puffer aus der Lookup Table füllen. Ich nutze das in meiner Scopeuhr so (OK ist kein DDS). Aber dort triggert der DMA IRQ eine Task, die dann die leer geworde Pufferhälfte mit neuen Daten für den DAC füttert (und vorher berechnet). Das klappt sehr gut. Genauso könnte der DMA Puffer mit Werten aus der Lookup-Table gefüllt werden. Sollte gehen denke ich. Außerdem ist die Interruptrate dann auch recht gering. Je größer der RAM-Puffer desto geringer die Interrupts.
Wenn mir nicht was besseres einfällt, dann sollte das auf eine DDS rauslaufen. Was wäre so schlimm daran wenn ich nicht genau an den Anfang der LUT komme, ich würde einfach eine 16 oder 32 Bit Variable in Vielfachen von 2 hochlaufen lassen und mir die Bits greifen die ich brauche, oder über die Array Schreibweise gehen. Ich habe nur absolut keine Ahnung was der beste Weg ist dabei die DMA zu benutzen und ob die so noch sinnvoll ist, da ich ja eigentlich die CPU entlasten wollte, wenn ich aber ständig neue Adressen vergebe wird das sicher nicht sinnvoll sein. Ich versuche mich morgen noch mal an den Pointern, vielleicht bekommt man doch noch irgendwas hin. Ich hatte eigentlich gehofft fast die maximale Geschwindigkeit des DAC nutzen zu können und nebenbei noch Zeit für andere Dinge zu haben. Im übrigen ändert sich die Frequenz oft ständig, deswegen sollte es eben sehr dynamisch und nicht erst nach einer ganzen Periode veränderbar sein. Ich denke was 900ss D. schreibt klingt doch interessant, irgendwie muss man die DMA doch auch in der IRQ füttern können, also Stück für Stück. Momentan schreibe ich dort halt jedesmal meine Basis Adresse neu. Gibt es eigentlich eien Unterschied wenn man die Daten aus dem Ram ließt anstelle direkt vom Flash? Ram ist ja an sich schneller. Ich habe mir damals mal das ST Beispiel zum Dual Sinus angeschaut, was dort merkwürdig war: die schreiben den Wert doppelt in 32 Bit Variablen, so als wollten sie jeden Channel getrennt versorgen und lesen dann doch nur den Unteren Teil aus... Gruß Marcus
Marcus P. schrieb: > irgendwie muss > man die DMA doch auch in der IRQ füttern können Die DMA wird nicht in dem IRQ gefüttert. Der IRQ setzt nur ein Flag welches eine Task anschmeißt, die dann den DMA Puffer füttert. Machst du das alles im Interrupt, dann läuft nichts mehr richtig. Dauert zu lange für den Interrupt. Warum möchtest du die DMA im IRQ füttern? Die Basisadresse ständig zu ändern bringt dir rein garnichts. Marcus P. schrieb: > Ich hatte eigentlich > gehofft fast die maximale Geschwindigkeit des DAC nutzen zu können und > nebenbei noch Zeit für andere Dinge zu haben. Genau das ist möglich. Der DAC braucht ja keine SW Unterstützung zum arbeiten. Nur hin und wieder, wenn sein Puffer leer läuft.
Doch, mit der Änderung der Basisadresse ging das auch, obwohl ich nicht glaube, dass es von der Performance her sinnvoll war. Ich versuche mich jetzt gerade mal an der alternativen leichteren Variante, dem Ausgeben des Wertes mittels DAC_SetChannel1Data(DAC_Align_12b_R , xxx); Merkwürdigerweise funktioniert dies nicht, kannst du evetuell auf Anhieb sehen was mir fehlt, ich hatte es sogar schon mit einem deiner anderen Beiträge verglichen, wo sich herausstellte dass der Controller keine DAC hatte^^. (Meiner hat definitiv einen, da es ja mittels DMA-DAC funktioniert hat). GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); DAC_InitTypeDef DAC_InitStructure; DAC_DeInit(); DAC_InitStructure.DAC_Trigger = AC_Trigger_None; DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; DAC_Init(DAC_Channel_1, &DAC_InitStructure); DAC_Cmd(DAC_Channel_1, ENABLE); int main(void) { while (1) { testzaehler2++; if(sin_zaehler == 1) { DAC_SetChannel1Data(DAC_Align_12b_R , 2000); GPIO_WriteBit(GPIOE, GPIO_Pin_8, Bit_SET); } else { DAC_SetChannel1Data(DAC_Align_12b_R , 0); GPIO_WriteBit(GPIOE, GPIO_Pin_8, Bit_RESET); } } } Ich habe jetzt nur die für den DAC relevanten Einstellungen aufgeschrieben, das ganze sollte dieses Verfahren erst mal testen. Die if Bedingungen in der While Schleife werden aus dem Timer2 Interrupt erfüllt und die LED an Port E Pin 8 blinkt mit einer Frequenz von ca. 2Hz, somit werden die Bedingungen auch wirklich angesprochen und der Fehler kann dort nicht liegen. Wie gesagt mittles DMA hatte es funktioniert.
Ich hab jetzt nichts entdecken können und meinen eigenen Test den ich erst auch so aufgebaut hatte, den finde ich jetzt nicht wieder :-( Ich habe aber ein Beispiel von ST genommen. Das hat funktioniert. Marcus P. schrieb: > Doch, mit der Änderung der Basisadresse ging das auch Wenn du in jedem Interrupt die Basisadresse änderst, dann kannst du auch gleich die Werte in den DAC schreiben.
Ich hab es doch wieder gefunden. Hier der Init-Schnipsel und dann die Ausgaberoutine eines Dreieck-Signals (für 8 bit). Ist größtenteils geklaut aus den Beispielen von ST.
1 | void BSP_Init (void) |
2 | {
|
3 | GPIO_InitTypeDef GPIO_InitStructure; |
4 | |
5 | /*--- System Clocks Configuration **********************************************/
|
6 | /* Setup the microcontroller system. Initialize the Embedded Flash Interface,
|
7 | initialize the PLL and update the SystemFrequency variable. */
|
8 | SystemInit(); |
9 | |
10 | /*--- Configure all unused GPIO port pins in Analog Input mode (floating input
|
11 | trigger OFF), this will reduce the power consumption and increase the device
|
12 | immunity against EMI/EMC *************************************************/
|
13 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | |
14 | RCC_APB2Periph_GPIOC , ENABLE); |
15 | |
16 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; |
17 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; |
18 | GPIO_Init(GPIOA, &GPIO_InitStructure); |
19 | GPIO_Init(GPIOB, &GPIO_InitStructure); |
20 | GPIO_Init(GPIOC, &GPIO_InitStructure); |
21 | |
22 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | |
23 | RCC_APB2Periph_GPIOC , DISABLE); |
24 | |
25 | /*--- Configure IO connected to LD1, LD2, LD3 and LD4 leds *********************
|
26 | Enable GPIO_LED clock (GPIOC) */
|
27 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_LED, ENABLE); |
28 | |
29 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; |
30 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; |
31 | GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; |
32 | GPIO_Init(GPIO_LED, &GPIO_InitStructure); |
33 | |
34 | /*--- DAC Periph clock enable */
|
35 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); |
36 | |
37 | /* Setup SysTick Timer for 1 msec interrupts */
|
38 | if (SysTick_Config(SystemFrequency / 1000)) |
39 | {
|
40 | /* Capture error */
|
41 | while (1); |
42 | }
|
43 | }
|
Hier die Ausgabe eines Dreiecksignales:
1 | /* DAC channel1 Configuration */
|
2 | DAC_InitStructure.DAC_Trigger = DAC_Trigger_None; |
3 | DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; |
4 | DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bits8_0; |
5 | DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; |
6 | DAC_Init(DAC_Channel_1, &DAC_InitStructure); |
7 | |
8 | /* Enable DAC Channel1: Once the DAC channel1 is enabled, PA.04 is
|
9 | automatically connected to the DAC converter. */
|
10 | DAC_Cmd(DAC_Channel_1, ENABLE); |
11 | |
12 | /* Set DAC Channel1 DHR12L register */
|
13 | DAC_SetChannel1Data(DAC_Align_12b_L, 0x7FF0); |
14 | |
15 | while (1) { |
16 | if( flag ) |
17 | {
|
18 | GPIO_SetBits(GPIO_LED, GPIO_Pin_7); |
19 | |
20 | DAC_SetChannel1Data(DAC_Align_8b_R, val++); |
21 | /* Start DAC Channel1 conversion by software */
|
22 | //DAC_SoftwareTriggerCmd(DAC_Channel_1, ENABLE);
|
23 | if( val == 255) |
24 | flag = FALSE; |
25 | }
|
26 | else
|
27 | {
|
28 | GPIO_ResetBits(GPIO_LED, GPIO_Pin_7); |
29 | DAC_SetChannel1Data(DAC_Align_8b_R, val--); |
30 | /* Start DAC Channel1 conversion by software */
|
31 | //DAC_SoftwareTriggerCmd(DAC_Channel_1, ENABLE);
|
32 | if( val == 0) |
33 | flag = TRUE; |
34 | }
|
35 | OSTimeDly( MSEC(1)); // wait 1ms |
36 | }
|
Ich bin ja auch doof wie ne Tüte Suppenmehl und hab das "Oszi" an den GPIOE angeschlossen anstelle an den A -.- Da hattest du anhand des Codes wenig Chancen den Fehler zu finden ;-P Meine Fresse, die ganze Zeit mach ichs richtig und dann steck ichs um. Danke für deinen Code. Gruß Marcus
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.