mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik STM32 SPI Clk und DMA


Autor: Janis W. (jotwe)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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:
  // Initialize DMA for SPI1 receive
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  DMA_InitStructure.DMA_PeripheralBaseAddr = 0x4001300C;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) bufferRX;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  DMA_InitStructure.DMA_BufferSize = 3;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel2, &DMA_InitStructure);
  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);
  DMA_ITConfig(DMA1_Channel2, DMA_IT_TE, ENABLE);
  //DMA_Cmd(DMA1_Channel2, ENABLE);

  // Initialize DMA for SPI1 transmit
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) bufferTX;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
  DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
  DMA_Init(DMA1_Channel3, &DMA_InitStructure);
  //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

Autor: Olaf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jean Player (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
DMA_InitStructure.DMA_BufferSize = 3;

Gruß

Autor: Janis W. (jotwe)
Datum:

Bewertung
0 lesenswert
nicht 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
DMA_InitStructure.DMA_BufferSize = 2;

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

Gruß

Janis

Autor: Janis W. (jotwe)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Janis W. (jotwe)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich bin mir inzwischen ziemlich sicher, dass
DMA_InitStructure.DMA_BufferSize = 3;

für 3 Byte = 24 Bit richtig ist.

Autor: ttl (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Janis W. (jotwe)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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...

Autor: Janis W. (jotwe)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das Problem ist nicht der DMA TC Interrupt, sondern die TIM3 Compare 
Interrupts. Trotz Deaktivierung per
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

Autor: Watcher (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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ß

Autor: Janis W. (jotwe)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Felix S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Janis W. (jotwe)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Punkt. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.