Forum: Mikrocontroller und Digitale Elektronik STM32 Echtzeitproblem


von Walt N. (belayason)


Lesenswert?

Guten Morge Leute!

Mit meinem STM32F411RE NUCLEO Board habe ich mein Projekt gestartet. 
Jede Peripherie die ich für die Umsetzung benötige habe ich erfolgreich 
unter Kontrolle! Nun hat sich ein kleines Zeitproblem eingestellt, da 
ich meine Werte nicht Gleichzeitig über 3 Schnittstellen bekomme(2* 
SPI,1*i2c).

 Um die beste genauigkeit zu erzielen wäre eine gleichzeitige 
Ansteuerung der Schnittstellen nötig. Ich könnte natürlich mehrere µC 
auf meiner Prototypen Platine plazieren und die Schnittstellen somit 
gleichzeitig auf verschiedenen µC's laufen lassen . Ist natürlich nicht 
die besonders elegante Lösung....


Wie sieht es aus mit multi core µC's aus? Sind diese genau für diesen 
Zweck da? Dennoch erscheint mir es irgendwie ein bisschen 
überdimensioniert, wie sieht der Programmieraufwand aus wenn ich mich 
jetzt als fortgeschrittener Programmierer der STM32 µC Welt bezeichnen 
würde :O?

 In freeRTOS habe ich mich eingelesen und festgestellt das man es nicht 
mal auf die schnelle lernt. Hat hier jemand vielleicht einen Tipp wie 
ich mich da am besten reinarbeite? Sonst schonmal im vorraus danke für 
die Hilfe!

von Adam P. (adamap)


Lesenswert?

Walt N. schrieb:
> da
> ich meine Werte nicht Gleichzeitig über 3 Schnittstellen bekomme

Was bedeuetet für dich "Gleichzeitig"?

Wenn dir 1ms und kleiner "gleichzeitig" genug ist, dann sollte es doch 
auch so gehen...sonst hol dir die Daten per DMA, da erfolgt die 
Ansteuerung "gleichzeitig".

von Stephan (Gast)


Lesenswert?

"Gleichzeitig" ist natürlich ein (zeitlich) dehnbarer Begriff bei nur 
einem Core...
Wie hattest Du Dir vorgestellt, dass das funktioniert?

(Free)RTOS löst das Problem auch nicht wenn nur eine Recheneinheit da 
ist...
VG, Stephan

von Walt N. (belayason)


Lesenswert?

Stephan schrieb:
> "Gleichzeitig" ist natürlich ein (zeitlich) dehnbarer Begriff bei nur
> einem Core...
> Wie hattest Du Dir vorgestellt, dass das funktioniert?

Zum meinem bedauern musste ich festellen dass ich mein Signal 
kontinuierlich senden muss, nur ein gesendetes Signal reicht leider 
nicht aus um es auf Empfängerseite mit nötigem Erfolg zu entschlüsseln.

von Walt N. (belayason)


Lesenswert?

Adam P. schrieb:
> Was bedeuetet für dich "Gleichzeitig"?

Damit meine ich eigentlich das ich meine Signale Kontinuierlich ohne 
Unterbrechung senden muss.

von Adam P. (adamap)


Lesenswert?

Walt N. schrieb:
> Zum meinem bedauern musste ich festellen dass ich mein Signal
> kontinuierlich senden muss, nur ein gesendetes Signal reicht leider
> nicht aus um es auf Empfängerseite mit nötigem Erfolg zu entschlüsseln.

- Was für ein Signal?
- Entschlüsseln?

Also wenn dir deine SPI / I²C Peripherie auf eine Anfrage nicht 
antwortet,
dann hast du wohl ein anderes Problem...

Leider verstehe ich jetzt noch weniger, nach diesem Beitrag von dir :-/

Zu wenig Infos und etwas verwirrend.

"Kontinuierlich": Ist auch (zeitlich) nicht direkt spezifiziert.

Oder läuft dein µC in sich zu langsam (Taktfrequenz), dass du nicht 
hinterher kommst?

: Bearbeitet durch User
von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

Walt N. schrieb:
> da
> ich meine Werte nicht Gleichzeitig über 3 Schnittstellen bekomme(2*
> SPI,1*i2c).

Die Übertragung selbst dürfte kein größeres Problem sein, die drei 
Hardwareeinheiten arbeiten ja eigenständig und wenn man die DMA-Kanäle 
richtig gewählt hat auch nahezu unabhängig voneinander (auf Teilen des 
Bus sind sie aber auch wieder nacheinander).
Aber du wirst es nicht schaffen alle drei wirklich "Zeitgleich" zu 
starten, dass sind nunmal drei Kommandos die nur nacheinander ausgeführt 
werden können. Selbst mit mehreren Core ist es arg schwierig die 
Taktgenau gleichzeitig dieses Kommando ausführen zu lassen. Spätestens 
beim Speicherzugriff müssen auch die sich wieder schön der Reihen nach 
anstellen.
Du solltest vielleicht nochmal dein Konzept überdenken oder aber in 
Richtung FPGA überlegen, die können sowas eher.

von Walt N. (belayason)


Lesenswert?

Adam P. schrieb:
> - Was für ein Signal?
> - Entschlüsseln?

Meine I2C/SPI Verbindung funktioniert einwandfrei! Ich sende eine Signal 
über Ultraschall. Damit muss meine SPI Schnittstelle ohne unterbrechung 
senden. Dazwischen befindet sich natürlich noch ein bisschen Elektronik 
und aufgrund analoger Aktoren muss ich mein Signal ohne unterbrechung 
senden.

von Stephan (Gast)


Lesenswert?

Walt N. schrieb:
> Zum meinem bedauern musste ich festellen dass ich mein Signal
> kontinuierlich senden muss

Wobei jetzt GLEICHZEITIG und KONTINUIERLICH Begriffe aus 
unterschiedlichen Domänen sind...

Wenn ich Dich trotzdem zu verstehen versuche:
Du willst von einem System (1x Core) GEICHZEITIG über 2 Interfaces 
(SPI/I2C) Daten rausschicken... (die Empfängerseite mal ganz aussen 
vor).
Das wird nicht funktionieren, wenn Du mit GLEICHZEITG tatsächlich ein dt 
von 0 vorgibst...

von Adam P. (adamap)


Lesenswert?

Walt N. schrieb:
> aufgrund analoger Aktoren muss ich mein Signal ohne unterbrechung
> senden.


Dann nimm DMA mit double Buffer.
Der sendet dir dann "ohne" µC Last die ganze Zeit,
lediglich die Neuinitialisierung nach jedem DMA Block muss dann kurz im 
Interrupt stattfinden (in dieser Zeit versendet er ja den zweiten Buffer 
/ keine Unterbrechung)

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Walt N. schrieb:
> Wie sieht es aus mit multi core µC's aus? Sind diese genau für diesen
> Zweck da?

Nein eher nicht. Die Kerne teilen sich gewisse Ressourcen deswegen hast 
du da noch weniger unter Kontrolle, wann genau was passiert.

Möglicherweise ist noch nicht klar, was du machen willst. Es könnte die 
synchrone Übertragung von Daten auf unterschiedlichen Schnittstellen 
sein oder auch die parallele kontinuierliche Übertragung (aber nicht 
zwingend Synchron).

Höre mal mit den Geheimnissen auf und lege die Karten offen auf den 
Tisch. Was hast du vor zu erreichen und wozu ist das gut?

> Meine I2C/SPI Verbindung funktioniert einwandfrei!

Und doch wohl nicht so, wie du es brauchst. Deswegen ist dieser Hinweis 
vollkommen wertlos. Wenn wir wüssten, was du brauchst und eine 
Vorstellung von deinem bestehenden Lösungsansatz haben, der angeblich 
"einwandfrei" funktioniert, könnten wir Dir vielleicht hilfreiche Tipps 
geben.

Aber so sagst du nur: Ich habe alles richtig aber aber es geht nicht. 
Sagt mir nicht, was ich falsch gemacht habe, denn es ist ja alles 
richtig. Hä?

von Walt N. (belayason)


Lesenswert?

Adam P. schrieb:
> Dann nimm DMA

Ok das probiere ich mal! Danke

von Adam P. (adamap)


Lesenswert?

Stefan ⛄ F. schrieb:
> Höre mal mit den Geheimnissen auf und lege die Karten offen auf den
> Tisch.

-->

Walt N. schrieb:
> Adam P. schrieb:
>> Dann nimm DMA
>
> Ok das probiere ich mal! Danke

Da hat meine Klaskugel heute wohl einen guten Tag :-D

von Stefan F. (Gast)


Lesenswert?

Adam P. schrieb:
> Da hat meine Klaskugel heute wohl einen guten Tag

Ja, vermute ich auch. Wir müssen abwarten, was der TO dazu sagt.

von Walt N. (belayason)


Lesenswert?

Was ich machen möchte ist ein Phasenmoduliertes Signal über einen DAC 
(MCP4922) an einen Piezo schicken der mir dann ein Ultraschallsignal 
erzeugt. Daher muss ich aber nun eine statische Wertetabelle erzeugen um 
den DMA modus zu nutzen? Oder kann ich die während dem DMA auch ändern?

von Adam P. (adamap)


Lesenswert?

Walt N. schrieb:
> Oder kann ich die während dem DMA auch ändern?

Kannst du auch...je nachdem wie lange du dafür brauchst.

von John Doe (Gast)


Lesenswert?

Walt N. schrieb:
> Was ich machen möchte ist ein Phasenmoduliertes Signal über einen DAC
> (MCP4922) an einen Piezo schicken der mir dann ein Ultraschallsignal
> erzeugt. Daher muss ich aber nun eine statische Wertetabelle erzeugen um
> den DMA modus zu nutzen? Oder kann ich die während dem DMA auch ändern?

Natürlich.

von Volle (Gast)


Lesenswert?

SPI und I^2C  sind aus Core Sicht ja langsame Ereignisse
mit DMA, Interrupts, Timer kann man sehr viele Dinge parallel machen.

nur eine geht gar nicht: busy waiting

Multicore macht die Sache übrigens nicht einfacher da man auch noch die 
Cores synchronisieren muss

von Jan (Gast)


Lesenswert?

Zeig doch mal, wie deine I2C Kommunikation funktioniert. "Funktioniert" 
ist erstmal nicht viel wert, denn es gibt unterschiedliche Ansätze wie 
es funktionieren kann.

Wenn du etwas blockierend schickst, was in vielen Tutorials der erste 
und schnellste Schritt zum ersten Erfolg ist, kannst du in der Zeit eben 
nix anderes machen, der µC wartet.

Es gibt aber viele Methoden, Sachen quasiparallel zu machen.

Komplett analog kann das Ganze ja eh nicht sein, weil dein ADC ja auch 
nur diskrete Werte zu diskreten Zeitpunkten bekommt. Klar 
mittelt/interpoliert der das dann, aber erstmal kannst du nichts 
wirklich kontinuierlich erzeugen. Die Frage ist, welcher Minimalabstand 
zwischen den neuen Werten erlaubt ist und ab wann es Probleme im 
Ultraschall gibt.

Glaube mir, du möchtest keinen Multicore.

von Max M. (maxi123456)


Lesenswert?

Ich wette zu 99% benötigst du für deinen Anwendungsfall garantiert kein 
besseres Echtzeit.

Beschreibe dein Vorhaben doch mal genauer.

: Bearbeitet durch User
von NichtWichtig (Gast)


Lesenswert?

Walt N. schrieb:
> Adam P. schrieb:
>> - Was für ein Signal?
>> - Entschlüsseln?
>
> Meine I2C/SPI Verbindung funktioniert einwandfrei! Ich sende eine Signal
> über Ultraschall. Damit muss meine SPI Schnittstelle ohne unterbrechung
> senden. Dazwischen befindet sich natürlich noch ein bisschen Elektronik
> und aufgrund analoger Aktoren muss ich mein Signal ohne unterbrechung
> senden.

Für Audioanwendungen wird gewöhnlich der I2S Bus, mit 192KS wäre US 
vermutlich kontinuierlich machbar.

von Stefan F. (Gast)


Lesenswert?

NichtWichtig schrieb:
> Für Audioanwendungen wird gewöhnlich der I2S Bus, mit 192KS wäre US
> vermutlich kontinuierlich machbar.

Was bedeutet "US"?

von Stefan S. (chiefeinherjar)


Lesenswert?

Stefan ⛄ F. schrieb:
> NichtWichtig schrieb:
>> Für Audioanwendungen wird gewöhnlich der I2S Bus, mit 192KS wäre US
>> vermutlich kontinuierlich machbar.
>
> Was bedeutet "US"?

Ultraschall.
Siehe

Walt N. schrieb:
> Was ich machen möchte ist ein Phasenmoduliertes Signal über einen DAC
> (MCP4922) an einen Piezo schicken der mir dann ein Ultraschallsignal
> erzeugt.

von Walt N. (belayason)


Lesenswert?

Wie erstelle ich mein Chipselect Signal wenn ich die SPI Schnittstelle 
im DMA Modus benutze? Der MCP4922 übernimmt die Daten nur wenn ich mit 
dem Chipselect Signal. In der
1
void HAL_SPI_TxCpltCallback (SPI_HandleTypeDef * hspi)
 Funktion kann ich nach der Übertragung mein Chipselect Signal wieder 
auf HIGH schalten. Aber wo ziehe ich das CS Signal am besten auf LOW?

von Stefan F. (Gast)


Lesenswert?

Walt N. schrieb:
> Aber wo ziehe ich das CS Signal am besten auf LOW?

Ich würde mal sagen, direkt vor dem Start der DMA Übertragung.

von Walt N. (belayason)


Lesenswert?

Stefan ⛄ F. schrieb:
> Ich würde mal sagen, direkt vor dem Start der DMA Übertragung.

Wenn ich mit meiner DMA Übertragung eine Wertetabelle übertrage kann ich 
ja nicht nach jeden 16 bit ( wie es das Datenblatt vom MCP4922 verlangt) 
das Chipselect Signal dazwischen schalten. Der überträgt ja die Ganze 
Wertetabelle auf einmal.

von Stefan F. (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Ich würde mal sagen, direkt vor dem Start der DMA Übertragung.

Walt N. schrieb:
> Wenn ich mit meiner DMA Übertragung eine Wertetabelle übertrage kann ich
> ja nicht nach jeden 16 bit ( wie es das Datenblatt vom MCP4922 verlangt)
> das Chipselect Signal dazwischen schalten. Der überträgt ja die Ganze
> Wertetabelle auf einmal.

Kannst du nicht und sollst du nicht. Warum willst du es denn tun?

von Adam P. (adamap)


Lesenswert?

Stefan ⛄ F. schrieb:
> Kannst du nicht und sollst du nicht. Warum willst du es denn tun?

Sieht im Datenblatt irgendwie echt danach aus :-/

von Stefan F. (Gast)


Lesenswert?

Adam P. schrieb:
> Sieht im Datenblatt irgendwie echt danach aus

Was steht wo in welchem Datenblatt? Zitiere es bitte.

Wenn dein Peripherie-Chip alle 16 bit so einen Impuls braucht, dann 
taugt er nicht für DMA.

von Adam P. (adamap)


Lesenswert?

MCP4902/4912/4922 Datenblatt
Seite 23:

"The write command is initiated by driving the CS pin
low, followed by clocking the four Configuration bits and
the 12 data bits into the SDI pin on the rising edge of
SCK. The CS pin is then raised, causing the data to be
latched into the selected DAC’s input registers."

"Any clocks past the 16th clock will be ignored."

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Ja Kacke, dann geht DMA halt nicht.

von Adam P. (adamap)


Lesenswert?

Stefan ⛄ F. schrieb:
> Ja Kacke, dann geht DMA halt nicht.

Oder zumindest halt nur für die 2 Byte :-)
Da weiß ich aber nicht ob sich das zeitlich überhaupt lohnt...

von Walt N. (belayason)


Angehängte Dateien:

Lesenswert?

Stefan ⛄ F. schrieb:
> Warum willst du es denn tun?

Ich erzeuge mit dem mcp4922 ein 40Khz Signal und an ihn sende ich 
entweder 0x3fff oder 0x3000 mit der SPI Schnittstelle. Nach 20 
Schwingungen kommt ein Phasenwechsel (0x3fff, 0x3000 ...0x3fff, 0x3000 
(Phasenwechsel), 0x3000, 0x3fff). Dadurch wird meine Wertetabelle 
ziemlich groß obwohl die Werte sich eigentlich die ganze Zeit 
wiederholen, aber dadurch das ich nun DMA benutze muss ich sie halt so 
in den speicher schreiben. Und im Anhang der write command für meinen 
DAC, an dem CS Signal komme ich nicht dran vorbei.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Da wäre ein Wandler mit I²S Schnittstelle wohl besser geeignet gewesen. 
DMA scheint mir hier inzwischen sinnlos. Ich würde die Kommunikation mit 
einem Timer-Interrupt machen und dafür sorgen, dass dieser niemals 
gestört (unterbrochen) wird.

von Adam P. (adamap)


Lesenswert?

Stefan ⛄ F. schrieb:
> Ich würde die Kommunikation mit einem Timer-Interrupt machen

Seh ich das dann richtig...?

Walt N. schrieb:
> entweder 0x3fff oder 0x3000

1Hz hat dann 2 Zustände (Werte).

40kHz => T=25µs
Also 1 Wert alle 12,5µs

Meinst dass alles passt zeitlich in dieses Zeitfenster?

von Stefan F. (Gast)


Lesenswert?

Adam P. schrieb:
> Meinst dass alles passt zeitlich in dieses Zeitfenster?

Wenn du ein Rechteck-Signal mit 40kHz ausgeben willst, dann brauchst du 
80.000 Werte pro Sekunde, also Intervalle von 12,5 Mikrosekunden.

Aber Rechteck-Signale gibt es in der Natur nicht, dein Signal wird durch 
die Übertragungsstrecke mit mehreren Oberwellen stark verzerrt. Ich 
hoffe, du hast das bedacht.

von Walt N. (belayason)


Lesenswert?

Adam P. schrieb:
> Meinst dass alles passt zeitlich in dieses Zeitfenster?

Dafür muss halt die Schnittstelle schnell genug sein. Mit SPI habe ich 
es hinbekommen. Wegen der slew rate vom DAC sieht es nach einem 
Trapezsignal aus. Ist aber auch noch ok.

von Volle (Gast)


Lesenswert?

Wenn das CS Signal vom SPI erzeugt wird  und nicht als DIO kann man 
i.d.R es auch über DMA erzeugen. ( Ich hab zumindest noch kein µC 
gesehen bei dem das nicht geht)

Sonst muss man einen Interrupt verwenden der vom SPI ausgelöst wird wenn 
sein Sendebuffer leer ist.  Der Interrupt kann Daten direkt in die 
Senderegister schreiben oder den DMA neu aufsetzen. Und auch Pins 
setzten.

von Stefan F. (Gast)


Lesenswert?

@Volle: Guter Hinweis!

Ich habe gerade mal ins Datenblatt des STM32F303 geschaut, der scheint 
das zu können. Man kann die Größe des Frames auf 4 bis 16 bit einstellen 
und die NSS Einheit erzeugt automatisch Impulse alle n Bits.

Interessant, das Feature kannte ich noch nicht.

Dann ist mein Kommentar

> Ja Kacke, dann geht DMA halt nicht.

wohl nicht richtig gewesen.

von Walt N. (belayason)


Lesenswert?

Dann verusche ich mich mal da dran!

von Walt N. (belayason)


Lesenswert?

Adam P. schrieb:
> lediglich die Neuinitialisierung nach jedem DMA Block muss dann kurz im
> Interrupt stattfinden

Das bedeutet ich muss die Funktion HAL_SPI_Transmit(); nochmal in void 
HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) ausführen?

von void (Gast)


Lesenswert?

Walt N. schrieb:
> mcp4922 ein 40Khz Signal und an ihn sende ich entweder 0x3fff oder
> 0x3000

Das bedeutet, du erzeugst mit dem DAC ein binäres Signal bestehend aus 
den beiden Spannungswerten
1.  Vref * 4095/4096 ~= Vref * 100% = Vref
2.  Vref * 0/4096 = Vref * 0% = 0V

Warum benötigst du für diese Aufgabe einen DAC?
Das kann ein Portpin von deinem uC doch auch. Ein passend gesetzter 
Timerausgang erzeugt dir auch so ein PWM Signal mit 50% duty und den 
Phasenwechsel bekommst du, wenn du den Timerausgang invertierst.

von Walt N. (belayason)


Lesenswert?

void schrieb:
> den
> Phasenwechsel bekommst du, wenn du den Timerausgang invertierst.

Den Phasenwechsel bekomme ich dann aber nicht unabhängig von den anderen 
Prozessen hin oder?

von Walt N. (belayason)


Lesenswert?

Stefan ⛄ F. schrieb:
> Man kann die Größe des Frames auf 4 bis 16 bit einstellen
> und die NSS Einheit erzeugt automatisch Impulse alle n Bits.

Wie im Anhang zu sehen funktioniert das NSS Signal leider nicht so wie 
ich es benötige. Wie ich es einstelle dass es nach 16 Bit einen Impuls 
erzeugt habe ich nicht herausgefunden.... Meine 40 Datensätze werden 
richtig übertragen, anschließend kommen aber noch ein andere Werte die 
so nicht in meinem Array drin stehen.

Meine SPI3 Schnittstelle habe ich wie folgt initialisiert:
1
static void MX_SPI3_Init(void)
2
{
3
4
  /* USER CODE BEGIN SPI3_Init 0 */
5
6
  /* USER CODE END SPI3_Init 0 */
7
8
  /* USER CODE BEGIN SPI3_Init 1 */
9
10
  /* USER CODE END SPI3_Init 1 */
11
  /* SPI3 parameter configuration*/
12
  hspi3.Instance = SPI3;
13
  hspi3.Init.Mode = SPI_MODE_MASTER;
14
  hspi3.Init.Direction = SPI_DIRECTION_2LINES;
15
  hspi3.Init.DataSize = SPI_DATASIZE_16BIT;
16
  hspi3.Init.CLKPolarity = SPI_POLARITY_LOW;
17
  hspi3.Init.CLKPhase = SPI_PHASE_1EDGE;
18
  hspi3.Init.NSS = SPI_NSS_HARD_OUTPUT;
19
  hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
20
  hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB;
21
  hspi3.Init.TIMode = SPI_TIMODE_DISABLE;
22
  hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
23
  hspi3.Init.CRCPolynomial = 10;
24
  if (HAL_SPI_Init(&hspi3) != HAL_OK)
25
  {
26
    Error_Handler();
27
  }
28
  /* USER CODE BEGIN SPI3_Init 2 */
29
30
  /* USER CODE END SPI3_Init 2 */
31
32
}

Im File stm32f4xx_hal_msp.c wird die Initialisierung so generiert. Die 
einzige Änderung die ich vorgenommen habe war, GPIO_NOPULL -> 
GPIO_PULLUP für Pin PA15.
1
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
2
{
3
  GPIO_InitTypeDef GPIO_InitStruct = {0};
4
  if(hspi->Instance==SPI2)
5
  {
6
  /* USER CODE BEGIN SPI2_MspInit 0 */
7
8
  /* USER CODE END SPI2_MspInit 0 */
9
    /* Peripheral clock enable */
10
    __HAL_RCC_SPI2_CLK_ENABLE();
11
  
12
    __HAL_RCC_GPIOC_CLK_ENABLE();
13
    __HAL_RCC_GPIOB_CLK_ENABLE();
14
    /**SPI2 GPIO Configuration    
15
    PC3     ------> SPI2_MOSI
16
    PB10     ------> SPI2_SCK 
17
    */
18
    GPIO_InitStruct.Pin = GPIO_PIN_3;
19
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
20
    GPIO_InitStruct.Pull = GPIO_NOPULL;
21
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
22
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
23
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
24
25
    GPIO_InitStruct.Pin = GPIO_PIN_10;
26
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
27
    GPIO_InitStruct.Pull = GPIO_NOPULL;
28
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
29
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
30
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
31
32
  /* USER CODE BEGIN SPI2_MspInit 1 */
33
34
  /* USER CODE END SPI2_MspInit 1 */
35
  }
36
  else if(hspi->Instance==SPI3)
37
  {
38
  /* USER CODE BEGIN SPI3_MspInit 0 */
39
40
  /* USER CODE END SPI3_MspInit 0 */
41
    /* Peripheral clock enable */
42
    __HAL_RCC_SPI3_CLK_ENABLE();
43
  
44
    __HAL_RCC_GPIOB_CLK_ENABLE();
45
    __HAL_RCC_GPIOA_CLK_ENABLE();
46
    __HAL_RCC_GPIOC_CLK_ENABLE();
47
    /**SPI3 GPIO Configuration    
48
    PB12     ------> SPI3_SCK
49
    PA15     ------> SPI3_NSS
50
    PC12     ------> SPI3_MOSI 
51
    */
52
    GPIO_InitStruct.Pin = GPIO_PIN_12;
53
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
54
    GPIO_InitStruct.Pull = GPIO_NOPULL;
55
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
56
    GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;
57
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
58
59
    GPIO_InitStruct.Pin = GPIO_PIN_15;
60
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
61
    GPIO_InitStruct.Pull = GPIO_PULLUP;
62
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
63
    GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
64
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
65
66
    GPIO_InitStruct.Pin = GPIO_PIN_12;
67
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
68
    GPIO_InitStruct.Pull = GPIO_NOPULL;
69
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
70
    GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
71
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
72
73
    /* SPI3 DMA Init */
74
    /* SPI3_TX Init */
75
    hdma_spi3_tx.Instance = DMA1_Stream5;
76
    hdma_spi3_tx.Init.Channel = DMA_CHANNEL_0;
77
    hdma_spi3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
78
    hdma_spi3_tx.Init.PeriphInc = DMA_PINC_DISABLE;
79
    hdma_spi3_tx.Init.MemInc = DMA_MINC_ENABLE;
80
    hdma_spi3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
81
    hdma_spi3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
82
    hdma_spi3_tx.Init.Mode = DMA_NORMAL;
83
    hdma_spi3_tx.Init.Priority = DMA_PRIORITY_LOW;
84
    hdma_spi3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
85
    if (HAL_DMA_Init(&hdma_spi3_tx) != HAL_OK)
86
    {
87
      Error_Handler();
88
    }
89
90
    __HAL_LINKDMA(hspi,hdmatx,hdma_spi3_tx);
91
92
  /* USER CODE BEGIN SPI3_MspInit 1 */
93
94
  /* USER CODE END SPI3_MspInit 1 */
95
  }
96
97
}

Den Neustart der DMA Übertragung mache ich wie folgt in main.c
1
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
2
{
3
    //HAL_GPIO_WritePin(GPIOA, SPI3_CS_Pin, 0);
4
    HAL_SPI_Transmit_DMA(&hspi3, Goldcode_A_DMA_Array, 40);
5
}

Der Versuch das CS Signal hier selber zu erzeugen hat leider nur eine 
Flanke nach allen 40 Werten hervorgerufen..

von Walt N. (belayason)


Angehängte Dateien:

Lesenswert?

Walt N. schrieb:
> Wie im Anhang zu sehen

Anhang verschwunden...

von void (Gast)


Lesenswert?

Walt N. schrieb:
> Den Phasenwechsel bekomme ich dann aber nicht unabhängig von den anderen
> Prozessen hin oder?

In dem Punkt unterscheidet sich die Nutzung von einem per SPI angebunden 
DAC und einem Timerausgang nicht.

Den Timer stellst du auf 50% duty, passende Periode und Erzeugung von 
Timer overflow Interrupt. In jeder ISR zählst du mit, wie oft diese 
schon aufgerufen wurde. Beim 20. Aufruf der ISR investiert du den 
Timerausgang. (Zum Register update des Timers basierend auf dem Timer 
overflow interrupt könnte man natürlich noch den DMA nehmen, aber das 
würde ich nicht ohne Not machen.)

Die Unabhängigkeit von anderen Prozessen ergibt sich durch die 
Bearbeitung dieser Aufgabe vollständig innerhalb der ISR.

von void (Gast)



Lesenswert?

void schrieb:
> Die Unabhängigkeit von anderen Prozessen ergibt sich durch die
> Bearbeitung dieser Aufgabe vollständig innerhalb der ISR.

Hallo Walt, hier noch einmal etwas anschaulicher mit Pseudo-Code für 
Atmega328P (Arduino Nano).
1
void timer0_init_fastpwm(void) {
2
    DDRD |= (1<<DDD6);     // OC0A = pin PD6
3
    PORTD &= ~(1<<PORTD6);
4
    TCCR0A=0b10100011;     // fast pwm mode (COM0A1=1, COM0A0=0, Clear OC0A on Compare Match, set OC0A at BOTTOM, (non-inverting mode).)
5
    //TCCR0A=0b11100011;   // fast pwm mode (COM0A1=1, COM0A0=1, Set OC0A on Compare Match, clear OC0A at BOTTOM, (inverting mode).)
6
    TCCR0B=0b00000010;     // prescaler - 8 (results in 8kHz)
7
    OCR0A=128;             // duty cycle 50% for OC0A = pin PD6
8
    TIMSK0 = 1 << OCIE0A;  // OCIE0A: enable Interrupt by timer compare
9
}
10
11
void timer0_invert_mode_fastpwm(void)
12
{
13
    /* switch PWM on OC0A between inverting and non-inverting mode */
14
    TCCR0A ^=0b01000000;   // (toggle bit COM0A0 0<->1)
15
}
16
17
ISR(TIMER0_COMPA_vect) {
18
    static uint8_t countPwmCycles;
19
    countPwmCycles++;
20
    if (countPwmCycles >= 20) {
21
        timer0_invert_mode_fastpwm();
22
        countPwmCycles = 0;
23
    }
24
}
25
26
int main(void) {
27
    timer0_init_fastpwm();    /* init timer */
28
    sei();                    /* enable global interrupts */
29
    while(1) {
30
        DoAnotherTaskAtTheSameTime();
31
    }
32
    return 0;
33
}

Wie gesagt, in der ISR liegt hier die komplette Arbeit für die CPU.
Die ISR wird zwar verhältnismäßig oft aufgerufen, ist aber sehr kurz. 
Das unterbricht andere laufende Prozesse daher nur minimal.
Im Anhang ein Oszillogram
- gelb: Timerausgang mit 8kHz PWM und Phasen-Wechsel wie gewünscht nach 
20 Perioden
 (40kHz passte hier vom Vorteiler nicht, 64kHz ging und lief auch 
problemlos)
- blau: Ein anderer Prozess (schickt das Wort "task" per Uart)


Der Trick ist eben in der ISR nur zwischen invertiertem und 
nicht-invertiertem PWM mode(output compare) hin und her zu schalten.
> /* switch PWM on OC0A between inverting and non-inverting mode */
>     TCCR0A ^=0b01000000;   // (toggle bit COM0A0 0<->1)

Bei deinem STM32F411xC/E bietet sich da z.B. der General-purpose Timer 
(TIM2..TIM5) an.
Das äquivalente Register-Bit ist dann TIMx_CCMR1.OC1M.
siehe RM0383 Kapitel 13.4.7
> TIMx capture/compare mode register 1 (TIMx_CCMR1)
>
> bits 6:4 OC1M: Output compare 1 mode
>  110: PWM mode 1 - In upcounting, channel 1 is active as long as 
TIMx_CNT<TIMx_CCR1 else inactive. (..)
>  111: PWM mode 2 - In upcounting, channel 1 is inactive as long as 
TIMx_CNT<TIMx_CCR1 else active. (..)

Beitrag #6211580 wurde vom Autor gelöscht.
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.