Forum: Mikrocontroller und Digitale Elektronik STM32F4 Ausführungszeit


von Walt N. (belayason)


Angehängte Dateien:

Lesenswert?

Guten Morgen!
Mit meinem STM32F411RE Nucleo Board steuere ich einen MCP4921. Nach dem 
Triggersignal (siehe Anhang: Gelb), sende ich die Spannungswerte per SPI 
an den DAC. Die resulierende Frequenz der Spannung die am DAC ausgegeben 
wird, soll 40kHz betragen. Das grüne Signal im Anhang zeigt die 
Ausgangsspannung des DAC. Zu sehen ist eine gemessene Frequenz von knapp 
40kHz. Die Übertragung von jedem Wert läuft gleich ab:
1
void SPI_send(void)
2
{
3
          HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, 0);
4
          HAL_SPI_Transmit(&hspi2,high2_impulse, 1, HAL_MAX_DELAY);
5
          HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, 1);
6
                                                                          /*1*/
7
          HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, 0);
8
          HAL_SPI_Transmit(&hspi2,low2_impulse, 1, HAL_MAX_DELAY);
9
          HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, 1);
10
  /*************************************************************************/
11
          HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, 0);
12
          HAL_SPI_Transmit(&hspi2,high2_impulse, 1, HAL_MAX_DELAY);
13
          HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, 1);
14
                                                                          /*2*/
15
          HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, 0);
16
          HAL_SPI_Transmit(&hspi2,low2_impulse, 1, HAL_MAX_DELAY);
17
          HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, 1);
18
.
19
.
20
.
21
}

Dennoch ist die Dauer von zufälligen Flanken manchmal doppelt so kurz. 
Das ist für mich ein Hinweis, dass der Code hier schneller ausgeführt 
wurde. Den Systemtakt generiere ich durch die PPL Clock. Diese 
ermöglicht mir den Takt so fein zu justieren, dass die Frequenz 40kHz 
ergibt.
1
void SystemClock_Config(void)
2
{
3
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
4
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
5
6
  /**Configure the main internal regulator output voltage 
7
  */
8
  __HAL_RCC_PWR_CLK_ENABLE();
9
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
10
  /**Initializes the CPU, AHB and APB busses clocks 
11
  */
12
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
13
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
14
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
15
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
16
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
17
  RCC_OscInitStruct.PLL.PLLM = 8;
18
  RCC_OscInitStruct.PLL.PLLN = 75;
19
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
20
  RCC_OscInitStruct.PLL.PLLQ = 4;
21
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
22
  {
23
    Error_Handler();
24
  }
25
  /**Initializes the CPU, AHB and APB busses clocks 
26
  */
27
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
28
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
29
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
30
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV2;
31
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
32
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
33
34
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
35
  {
36
    Error_Handler();
37
  }
38
}

Hat jemand vielleicht eine Vermutung woran das liegen könnte?

von Uwe B. (Firma: TU Darmstadt) (uwebonnes)


Lesenswert?

Bit-Bang kann durch Interrupts unterbrochen werden. Ausserdem hat das 
STM32F4 Flash Latenzen, die man durch Caches (ART accelerator) zu 
verstecken versucht. Falls etwas  nicht frisch aus dem Flash geladen 
werden muss, geht es halt schneller. Dazu kommt noch die Auslastung der 
Bus Matrix, die auch Verzoegerungen beitragen kann. Wenn Du genaues 
Timing braucht, verwende geeignete Peripherie!

von PittyJ (Gast)


Lesenswert?

Wenn ich ein exaktes Timing brauche, dann verlasse ich mich nicht auf 
eine CPU, sondern verwende ein FPGA. Dann habe ich genaue Zustände auch 
im Microsekundenbereich, und es schlägt kein Interrupt oder Cache 
dazwischen.

Ein bisschen krank ist es schon, mit der CPU ein 40KHz Timing zu 
erzeugen.

von Walt N. (belayason)


Lesenswert?

Uwe B. schrieb:
> verwende geeignete Peripherie!

Dann würde ich mal anstatt den DAC einen GPIO benutzen um die 40kHz zu 
erzeugen. Das habe ich nämlich schon in der 
HAL_TIM_PWM_PulseFinishedCallback function gemacht, dort sind die 40kHz 
um einiges genauer. Jetzt muss ich aber das Trigger Signal als 
Startzeitpunkt festlegen und den Takt des Timers nutzen um den GPIO 
entsprechend zu schalten. Ich dachte an einen GPIO Interrupt die durch 
mein Startsignal ausgelöst wird, wie nutze ich aber den Takt des Timers 
um auf die 40kHz zu kommen?

von Adam P. (adamap)


Lesenswert?

Hallo Walt N.,

1. Ist die Schaltung bereits fertig, oder kannst du noch Änderungen 
vornehmen?
2. Möchtest du nur die Frequenz oder auch die Amplitude verändern?

Spontan fällt mir folgendes ein:
Über das SPI stellst du die gewünscht Amplitude ein und am DAC Ausgang 
verwendest du eine kleine Transisitorschaltung.
Über ein Timer kannst dann eine PWM erzeugen die den Transistor schaltet 
- ganz ohne CPU Zeit - der STM32F4 sollte sowas wohl haben.

: Bearbeitet durch User
von Walt N. (belayason)


Lesenswert?

Adam P. schrieb:
> 1. Ist die Schaltung bereits fertig, oder kannst du noch Änderungen
> vornehmen?
> 2. Möchtest du nur die Frequenz oder auch die Amplitude verändern?

Änderungen sollten noch möglich sein, in meinem Prototypen sind zum 
Glück beide Varianten implementiert. Einmal mit flexibler Amplitude über 
DAC als auch direkt mit maximaler Amplitude über GPIO. Anfangs war 
letztere Variante dazu gedacht ein kontinuierliches Signal über einen 
GPIO auszugeben. Dies habe ich in der ISR des Timers auch implementiert. 
Jetzt aber gilt es noch zusätzlich den Starttrigger zu nutzen und einen 
kurzen 40kHz Signalburst zu erzeugen.

von Adam P. (adamap)


Lesenswert?

Walt N. schrieb:
> Dies habe ich in der ISR des Timers auch implementiert.

Du benötigst aber keine ISR vom Timer, der kann ein GPIO auch direkt 
steuern.

Walt N. schrieb:
> Jetzt aber gilt es noch zusätzlich den Starttrigger zu nutzen und einen
> kurzen 40kHz Signalburst zu erzeugen.

Dann kannst du ja ein "Pin-Change" Interrupt nutzen der auf dein Trigger 
reagiert. In dem startest du dein Timer.
OK, hier bräuchtest du wohl eine ISR vom Timer um die Anzahl der Impulse 
zu zählen oder die vergangene Zeit, dann würdest du den Timer wieder 
ausschalten.

Müsstest vllt. mal mehrere Ansätze ausprobieren.

Schon mal dein SPI mit DMA probiert? Evtl. könnte man damit auch einiges 
verbessern.

Oder du greifst das erzeugte Signal mit einem Timer Capture Eingang ab 
und zählst dort die erzeugten Impulse, das wäre auch noch eine 
Möglichkeit.

von 900ss (900ss)


Lesenswert?

Du könntest auch versuchen, die SPI-Funktion RAM laufen zu lassen. Dann 
sind Flashzugriffzeiten schonmal raus.

Was ich aber machen würde, wäre einen Timer zu nehmen der den DMA 
anstösst. Der DMA füttert den SPI und damit den DAC. Dann brauchst du 
die CPU nur, um die DAC-Werte in den DMA Sourcebuffer abzulegen. Es gibt 
da einen IRQ, dass der DMA-Buffer halb leer ist. Den nutzt du dann um 
neue Werte für die leere Bufferhälfte zu berechnen.
Ich habe soetwas schon gemacht aber habe die Werte statt auf SPI direkt 
auf den eingebauten DAC des STM ausgegeben. Was ich nicht ganz 
verstanden habe ist das mit deinem Trigger.
Bei dieser Methode wird dann pro Timer Event DMA-Transfer angestoßen und 
das Timing ist nicht mehr von der CPU abhängig.

: Bearbeitet durch User
von A. B. (Gast)


Lesenswert?

Dss Timing der SPI-Schnittstelle ist weitgehend irrelevant (solange es 
genügend schnell geht), es gibt keinerlei Grund, dafür am CPU-Takt 
herumzudrehen. Um ein präzises Timing des Analog-Signals am DAC-Ausgang 
zu erzeugen, nimmt man entweder den LDAC-Eingang oder setzt den 
dauerhaft auf 0, und sorgt nur für ein präzises Timing der steigenen 
Flanke am CS-Eingang (etwa freilaufender Timer, Ausgang an CS):

"3.5 Latch DAC Input (LDAC)
The LDAC (latch DAC synchronization input) pin is
used to transfer the input latch register to the DAC reg-
ister (output latches, V OUT ). When this pin is low, V OUT
is updated with input register content. This pin can be
tied to low (V SS ) if the V OUT update is desired at the
rising edge of the CS pin. This pin can be driven by an
external control device such as an MCU I/O pin."

von 900ss (900ss)


Lesenswert?

A. B. schrieb:
> nimmt man entweder den LDAC-Eingang

Ich hatte garnicht ins Datenblatt geschaut vom DAC. Aber deinen 
Vorschlag sehe ich genauso als zielführend an. Dafür ist ja der 
LDAC-Eingang gedacht.

: Bearbeitet durch User
von A. B. (Gast)


Lesenswert?

900ss D. schrieb:
> Ich hatte garnicht ins Datenblatt geschaut vom DAC. Aber deinen
> Vorschlag sehe ich genauso als zielführend an. Dafür ist ja der
> LDAC-Eingang gedacht.

Wobei ich den eher nicht verwenden würde, denn der ist bei einem 
einzelnen einfach-DAC der pure Luxus. Und wenn man den DAC tauschen 
will/muss, hat man beim neuen eventuell Pech. Richtig nützlich ist der 
nur bei mehreren DACs, die synchron laufen sollen.

von Walt N. (belayason)


Angehängte Dateien:

Lesenswert?

Adam P. schrieb:
> Du benötigst aber keine ISR vom Timer, der kann ein GPIO auch direkt
> steuern.

Ich habe mich jetzt mal genauer in die Funktion des Timers eingelesen, 
sehe ich das richtig, dass sobald der Timer einmal durchlaufen ist im 
Status Register das UIF bit (Update interrupt flag) gesetzt wird? Die 
Funktion sieht folgendermaßen aus:
1
void PM(void)
2
{
3
  GPIOA->BSRR = (1<<24);;
4
  for(int p=0;p<=40;p++)
5
  {
6
    if(TIM2->SR & 0x01)
7
    GPIOA->ODR ^= (1<<8);                //Toggle output GPIOA PA8
8
  }
9
  GPIOA->BSRR = (1<<24);;
10
}
Der GPIO wird zwar umgeschaltet, aber nicht in der erwarteten Frequenz 
von 40kHz. Nach Änderung des Prescalers bleibt die Frequenz gleich. 
Außerdem befindet sich diese komische Lücke dazwischen...

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.