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!
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".
"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
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.
Adam P. schrieb: > Was bedeuetet für dich "Gleichzeitig"? Damit meine ich eigentlich das ich meine Signale Kontinuierlich ohne Unterbrechung senden muss.
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
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.
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.
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...
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
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ä?
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
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.
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?
Walt N. schrieb: > Oder kann ich die während dem DMA auch ändern? Kannst du auch...je nachdem wie lange du dafür brauchst.
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.
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
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.
Ich wette zu 99% benötigst du für deinen Anwendungsfall garantiert kein besseres Echtzeit. Beschreibe dein Vorhaben doch mal genauer.
:
Bearbeitet durch User
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.
NichtWichtig schrieb: > Für Audioanwendungen wird gewöhnlich der I2S Bus, mit 192KS wäre US > vermutlich kontinuierlich machbar. Was bedeutet "US"?
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.
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?
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.
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.
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?
Stefan ⛄ F. schrieb: > Kannst du nicht und sollst du nicht. Warum willst du es denn tun? Sieht im Datenblatt irgendwie echt danach aus :-/
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.
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
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...
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
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.
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?
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.
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.
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.
@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.
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?
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.
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?
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..
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.