Forum: Mikrocontroller und Digitale Elektronik STM32 SPI Clk und DMA


von Janis W. (jotwe)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich habe schon mal vor einiger Zeit hier im Forum zu meinen Versuchen 
einen Positionssensor mit SSI-Schnittstelle auszulesen Hilfe gefunden: 
STM32 SPI im Halbduplex-Betrieb mit DMA. Leider habe ich immer noch 
Probleme mit der Aufgabe.

Der Sensor hat ein CSN-, CLK-, und DO-Pin. Diese habe ich bei meinem 
STM32F103CB mit SPI1_NSS (PA4). SPI1_SCK (PA5) und SPI1_MISO (PA6) 
verbunden.

Ich habe mal neben dem Quellcode die Messung eines Frames per Oszi und 
Logic Analyser und das Datenblatt-Diagramm des Sensors angehängt.

1. Frage: Wie man im angehängten Diagramm sieht, fällt die Clk-Leitung 
nach jedem Frame in einer schönen (1-e)-Funktion auf Low. Das liegt wohl 
daran, dass ich nach jedem Frame die SPI-Schnittstelle deaktiviere, um 
den Clk abzustellen. Eigentlich hätte ich gerne, dass die Clk-Leitung in 
dieser Zeit Idle High bleibt. Gibt es eine Möglichkeit den SPI Clk 
gezielt in Idle zu versetzen?

2. Frage: Ich habe das Problem, dass der DMA-Interrupt nach 3 Byte nicht 
aufgerufen wird. Ich habe einen Timeout eingebaut, der nach 200 us den 
Frame zwangsweise beendet, wenn er nicht vorher per DMA-Interrupt 
deaktiviert wird. Meine DMA-Konfiguration sieht folgendermaßen aus:
1
  // Initialize DMA for SPI1 receive
2
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
3
  DMA_InitStructure.DMA_PeripheralBaseAddr = 0x4001300C;
4
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) bufferRX;
5
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
6
  DMA_InitStructure.DMA_BufferSize = 3;
7
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
8
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
9
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
10
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
11
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
12
  DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
13
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
14
  DMA_Init(DMA1_Channel2, &DMA_InitStructure);
15
  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
16
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
17
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;
18
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
19
  NVIC_Init(&NVIC_InitStructure);
20
  DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);
21
  DMA_ITConfig(DMA1_Channel2, DMA_IT_TE, ENABLE);
22
  //DMA_Cmd(DMA1_Channel2, ENABLE);
23
24
  // Initialize DMA for SPI1 transmit
25
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) bufferTX;
26
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
27
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
28
  DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
29
  DMA_Init(DMA1_Channel3, &DMA_InitStructure);
30
  //DMA_Cmd(DMA1_Channel3, ENABLE);

Ich aktiviere die DMA-Kanäle nach einer Initialisierungszeit von 10us 
pro Frame.

Hat da jemand Ideen zu? Ich komme irgendwie nicht weiter und bin daher 
für jede Anregung dankbar :)

Janis

von Olaf (Gast)


Lesenswert?

> Ich habe mal neben dem Quellcode die Messung eines Frames per Oszi und
> Logic Analyser und das Datenblatt-Diagramm des Sensors angehängt.

Sehe ich das richtig? Dein Highpegel ist nur 0.3V? Oder hast du
uns einen 10:1 Tastkopf verschwiegen?
Wenn letzteres, findest du dein Unterschwingen nicht schon etwas heftig?

> fällt die Clk-Leitung
> nach jedem Frame in einer schönen (1-e)-Funktion auf Low.

Das ist nicht schoen. Wieviel Clocks mag dein Device wohl in der Zeit 
zaehlen wo du durch seinen Umschaltpunkt laeufst?

> Gibt es eine Möglichkeit den SPI Clk
> gezielt in Idle zu versetzen?

Ich kenne deinen Prozessor nicht, aber entweder man hat ein 
SPI-Interface das diese Funktionalitaet hat, oder man schaltet auf 
Portmodus zurueck und hat den Port dann als Ausgang mit dem Wunschpegel, 
oder aber man spendiert einen Widerstand. So wie es jetzt ist geht es 
jedenfalls niemals.

Olaf

von Jean Player (Gast)


Lesenswert?

Janis W. schrieb:
> 1. Frage: Wie man im angehängten Diagramm sieht, fällt die Clk-Leitung
> nach jedem Frame in einer schönen (1-e)-Funktion auf Low. Das liegt wohl
> daran, dass ich nach jedem Frame die SPI-Schnittstelle deaktiviere, um
> den Clk abzustellen. Eigentlich hätte ich gerne, dass die Clk-Leitung in
> dieser Zeit Idle High bleibt. Gibt es eine Möglichkeit den SPI Clk
> gezielt in Idle zu versetzen?

Du lässt das SPI einfach an !

> 2. Frage: Ich habe das Problem, dass der DMA-Interrupt nach 3 Byte nicht
> aufgerufen wird.

Logo siehe deine CodeZeile:
1
DMA_InitStructure.DMA_BufferSize = 3;

Gruß

von Janis W. (jotwe)


Lesenswert?

Hallo Olaf und Jean Player,

danke für die schnellen Antworten! Ja, ich hatte den Tastkopf 
versehentlich auf 10:1 stehen und gar nicht mehr nach der Amplitude 
geschaut...

Ich könnte natürlich den Pin immer wieder als normalen Portpin 
umkonfigurieren. Aber das muss doch auch anders gehen. Wenn ich den SPI 
aktiviert lasse, läuft der Clk immer durch.

Muss bei der Initialisierung
1
DMA_InitStructure.DMA_BufferSize = 2;

sein? Dann hatte ich das wohl falsch verstanden...

Gruß

Janis

von Janis W. (jotwe)


Lesenswert?

Ok, das mit dem Clk liegt wohl an dem Receive-only-Modus:

To start the communication in receive-only mode, configure and enable 
the SPI: In master mode, the communication starts immediately and stops 
when the SPE bit is cleared and the current reception stops. There is no 
need to read the BSY flag in this mode. It is always set when an SPI 
communication is ongoing.

Werde das mal auf Full Duplex umbauen und sehen, ob das das Problem 
lösen kann...

von Janis W. (jotwe)


Lesenswert?

Ich bin mir inzwischen ziemlich sicher, dass
1
DMA_InitStructure.DMA_BufferSize = 3;

für 3 Byte = 24 Bit richtig ist.

von ttl (Gast)


Lesenswert?

für 3 Byte lohnt DMA sowiso nicht, der Overhead mit Interrupt ist größer 
als die Zeitersparnis durch den DMA, lass das weg. Such dir lieber ein 
Beispiel aus den Libraries raus, die haben bei mir alle hervoragend 
funktioniert

von Janis W. (jotwe)


Angehängte Dateien:

Lesenswert?

Naja, inzwischen habe ich es geschafft, dass der Clk zwischen den Frames 
in Idle high geht. Allerdings wird der DMA Transfer Complete Interrupt 
zu früh geschmissen. Nämlich direkt nachdem ich DMA aktiviert habe. Zu 
sehen ist das am In 2 des Logic Analysers (CS). Der wird direkt nach der 
Active low durch den Interrupt wieder auf Idle high gesetzt. Und damit 
kommen auch keine Daten mehr an, obwohl die Taktleitung schön wackelt...

von Janis W. (jotwe)


Lesenswert?

Das Problem ist nicht der DMA TC Interrupt, sondern die TIM3 Compare 
Interrupts. Trotz Deaktivierung per
1
TIM_ITConfig(TIM3, TIM_IT_CC2, DISABLE);

scheinen die CCxIF Flags im TIMx_SR Register gesetzt zu werden. Diese 
werden dann beim nächsten Interrupt-Aufruf falscherweise mit 
abgearbeitet.

Ich mache zu dem Thema einen neuen Beitrag auf, da das nichts mehr mit 
DMA und SPI zu tun hat.

Janis

von Watcher (Gast)


Lesenswert?

Hallo Janis,

kann es sein das deine 3 Byte die Du senden möchtest sofort in einen 
Hardware-TX-FIFO deiner Schnittstelle gehen und deshalb der 
DMA-Interrupt sofort fertig ist?

Gruß

von Janis W. (jotwe)


Lesenswert?

Hallo Watcher,

es lag letztlich wirklich an den Timer-Interrupts. Hier ist letztlich 
die Lösung des Probelms aufgeführt:

Beitrag "STM32 Capture/Compare Interrupt deaktivieren"

Gruß

Janis

von Felix S. (Gast)


Lesenswert?

Hallo Janis

Wie hast Du es geschafft, dass der Clock im Idle high bleibt?
Ich habe dasselbe Problem mit der gleichen CPU-Famile.

Gruss
Felix

von Janis W. (jotwe)


Lesenswert?

Hallo Felix,

soweit ich mich erinnern kann, lag das am Modus. Mit

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

hat es dann funktioniert. Ich hoffe, das hilf Dir weiter.

Gruß

Janis

von Punkt. (Gast)


Lesenswert?

Hallo Felix,

Felix S. schrieb:
> Hallo Janis
>
> Wie hast Du es geschafft, dass der Clock im Idle high bleibt?
> Ich habe dasselbe Problem mit der gleichen CPU-Famile.

Bei SPI gibt es 4 Modi, die sich darin unterscheiden,
welches die aktive Flanke und welches der CLK Pegel
im Idle ist.

Weiss jetzt nicht auswendig, wie das beim STM32 einzustellen
ist, aber das Datenblatt gibt das sicher Auskunft

Punkt.

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.