ich habe bereits viel gegoogelt, aber finde einfach keine Lösung für mein Problem...
Ziel: ADU Wert auslesen und an PC senden.
Hardware: Nucleo-Board mit STM32F446RE, AZDelivery UART-TTL USB Adapter mit CH340G und AD Wandler MCP3008
Problem: Über SPI lässt sich weder etwas an den ADU senden oder etwas auslesen. Das RX bzw. TX Buffer wird nach einem Lese- bzw. Schreibvorgang durch SPI1->DR nicht zurücksetzen.
Code:
1
#define SPI_Startbyte (0x1 << 7)
2
#define SPI_SelectChannel
3
#define SPI_RandomByte (0xFF)
4
5
// Funktionsprototypen
6
voidsendeChar(charcharacter);
7
voidSPIEmpfange(uint8_t*data,intsize);
8
voidSPISende(uint8_t*data,intsize);
9
10
intmain(void)
11
{
12
13
14
// Initialisierung der GPIO
15
16
// Aktivieren der Clock für GPIO Port A
17
RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN;
18
19
// Definition von GPIO A Pin 5, 6, 7, 9 und 10 als alternative Funktion (Für USART1 und SPI1) und Pin 8 als Output (Für CS)
20
GPIOA->MODER|=GPIO_MODER_MODER5_1;// SPI1 CLK
21
GPIOA->MODER|=GPIO_MODER_MODER6_1;// SPI1 MISO
22
GPIOA->MODER|=GPIO_MODER_MODER7_1;// SPI1 MOSI
23
GPIOA->MODER|=GPIO_MODER_MODER9_1;// USART 1 TX
24
GPIOA->MODER|=GPIO_MODER_MODER10_1;// USART 1 RX
25
GPIOA->MODER|=GPIO_MODER_MODER8_0;// GPIO OUTPUT
26
27
// Definition von GPIO A Pin 5, 6 und 7 als alternative Funktion AF5
Dafür um so weniger nachgedacht bevor du gepostet hast.
Lese den Anhang, verstehe ihn, verinnerliche ihn und handle
danach.
"Längerer Sourcecode" ist es wenn man dauernd scrollen muss um
alles zu sehen. Hast du es jetzt verstanden?
Vielleicht ist im STM ein anderer Mode (Parameter „Clock Polarity“
(CPOL) und „Clock Phase“ (CPHA)) als beim ADC eingestellt.
Ja. Das ist ein Problem. Ein ganz anderes (und allein auf STM-Mist gewachsenes) ist die Unterstützung für einen doch eher exotischen MultiMaster-SPI-Bus und die an völligen Schwachsinn grenzende Idee, dass auch noch zum Default für den SPI zu machen.
Die Typen sind doch völlig gehirnamputiert!!!
Allerdings: ob das auch bei dem genannten konkreten STM32 so verhält, entzieht sich derzeit meiner Kenntnis. Das möge der TO selber im passenden DB nachschlagen.
Allerdings: ob das auch bei dem genannten konkreten STM32 so verhält,
entzieht sich derzeit meiner Kenntnis. Das möge der TO selber im
passenden DB nachschlagen.
Das ist Käse. Lies das Datenblatt auf Seite 20, dort steht was
zu tun ist: ein Konfigurations-Byte schreiben und danach zwei
Bytes abholen. Tip: für jedes abzuholende Byte wird auch erst
eines geschrieben.
Bei meinem STM32H7 benutze ich:
HAL_SPI_TransmitReceive()
Das wurde von STM mitgeliefert, und funktionierte auf Anhieb.
Gibt es so etwas nicht für den STM32F, dass man alles von Hand machen muss?
Als nächstes würde ich ein Scope anschliessen, um zu sehen, was auf dem Bus los ist.
Standardmaessig nimmt man irgendwelche Portpins und macht den SPI,
genaustens gemaess Datenblatt des ADC zu Fuss.
Aber erst wenn es anders nicht geht.
Aber das "studieren" des Datenblattes ist Voraussetzung für die Verwendung von ADC & STM. Speziell wenn man das Interface selbst realisiert.
Man hätte eben den Controller im Titel nennen sollen.
So ohne jede Information lockt man viele an, die z.B. den ATmega8 kennen, aber den STM32F446RE nicht.
Max 3,6MHz des MCP3008 sollte selbst zu Fuß kein Problem sein. Das
HW-SPI wäre also auch nicht schneller.
Allerdings könnte man den SPI Port theoretisch mit DMA kombinieren, damit die CPU während der Übertragung nicht warten muss sondern etwas anderes nützliches tun kann.
Anbei meine C-Funktion um die MCP3008 bzw. MCP3208 via SPI auszulesen. Die MCPs verstehen SPI-Mode 0 und 1. Die sind eigentlich mega simpel zu verwenden und die Datenblätter sind IMHO gut beschrieben.
Sollte (zusammen mit dem Datasheet des MCP) selbsterklärend sein. Eigentlich mega einfach.
spiTransferMessage erwartet zwei Arrays (ginge eigentlich auch eines, die Größe des Arrays und ein SPI-Device (sodass man weiß, welcher Pin der CS-Pin für das Device ist)
Vielleicht ist im STM ein anderer Mode (Parameter „Clock Polarity“
(CPOL) und „Clock Phase“ (CPHA)) als beim ADC eingestellt.
Die Modi kenne ich. Ich habe auch geschaut (Datenblatt und diverse Implementierungen für Raspberry Pi) und meine da die richtige Einstellung zu haben.
Aktuell habe ich glaube ich den falschen Pegelwandler, weshalb ich auf CS kein High Signal bekomme, dass der ADU erkennt. Ich hab mir die "richtige" Hardware bestellt und probiere es nochmal aus.
Allerdings: ob das auch bei dem genannten konkreten STM32 so verhält,
entzieht sich derzeit meiner Kenntnis. Das möge der TO selber im
passenden DB nachschlagen.
Ich hatte gedacht man schickt die 3 Byte aus dem Datenblatt und darauf folgen dann nur noch Bytes die erhalten werden
... Dann habe ich das falsch verstanden.
Bei meinem STM32H7 benutze ich:
HAL_SPI_TransmitReceive()
Das wurde von STM mitgeliefert, und funktionierte auf Anhieb.
Gibt es so etwas nicht für den STM32F, dass man alles von Hand machen
muss?
Ich habe das so in einem Buch gelernt und fand das eigentlich verständlich. Deshalb arbeite ich weiter damit.
Man hätte eben den Controller im Titel nennen sollen.
So ohne jede Information lockt man viele an, die z.B. den ATmega8
kennen, aber den STM32F446RE nicht.
Sollte (zusammen mit dem Datasheet des MCP) selbsterklärend sein.
Eigentlich mega einfach.
Das liegt im Auge des Betrachters:D Danke das sieht sehr hilfreich aus. Ich habe wohl das Prinzip von SPI nicht verstanden, aber hiermit wird das glaube ich.
Anbei meine C-Funktion um die MCP3008 bzw. MCP3208 via SPI auszulesen.
Die MCPs verstehen SPI-Mode 0 und 1. Die sind eigentlich mega simpel zu
verwenden und die Datenblätter sind IMHO gut beschrieben.
<SPI_TransferByte > ist voll ausgetestet, bei <Read_ADC> musst
du noch geeignete Werte für mode und channel einsetzen. Vielleicht
sind da noch noch kleine Unsauberkeiten drin, aber so kann man
das Ganze wenigstens sauber überblicken.
Warum will man aber einen externen 10Bit-ADC benutzen, wenn der
STM32F446RE schon 3*12Bit-ADCs mit 24 Eingängen intern hat?
Weil sich der nur nutzen lässt, wenn der Pin nicht bereits mit einer anderen alternativen Funktion benutzt wird - was bei kleineren Pin-Counts schon mal vorkommt.
Abgesehen davon sind die fertigen Entwicklungsboards selten mit einer hochstabilen und sauberen Referenzspannung versehen. Die Wandler werden zu gern mit der Versorgungsspannung verbunden. Die Dinger bekommt man nie ruhig. Am Ende würde ich die 3.3V sehen, die im Verhältnis zur unsauberen Referenzspannung sehr gering ausfällt.
Ich nutzte auch einen diskreten 12Bit AD-Wandler, der über SPI an einen STM32F746 angeheftet wurde. Der lässt sich gut und stabil betreiben.
Ich habe mit bei der Programmierung des SPI an einem vorhandenen Init für ein Touch-Panel orientiert. Beim DMA muss man aber unbedingt auf die ERATA zum STM Controller achten. Die STM32 sind sehr interessante Cortex-M Vertreter, aber sie haben manchmal sehr üble Bugs in den ersten Versionen.
DMA geht manchmal nicht wie beschrieben, wenn andere Ressourcen zeitgleich verwendet werden. Deren Interrupt-Handling ist auch gern mal problematisch.
Also - sollte ein Verhalten nicht erklärbar sein, nach einem Erata schauen.
Schau dir noch mal genau an, was mein Code macht. Für Kanal 0 im Single-Ended Mode hast du die Bytes richtig ausgerechnet aber direkt danach schreibst du:
Dass er bei dir dann aber auch gar kein Byte sendet ist direkt noch ein anderes/weiteres Problem. Hast du denn zumindest ein Clock-Signal? Wenn du das nicht hast klingt das für mich schwer danach, dass dein SPI-Transmitter/Master gar nicht erst arbeitet, dann musst du da noch mal ran.
ich hatte jetzt lange keine Zeit mich mit dem Thema zu beschäftigen und sitzte wieder daran.
Ich verstehe das SPI Prinzip irgendwie nicht.
Also wenn ein Bit gesendet wird, erhält man stets ein Bit zurück.
Es gibt bei der initialisierung des SPI für STM32FXX Prozessoren das Register SPI control register 1 (SPI_CR1).
Und dort gibt es die Bits
Bit 10 (RXONLY): Im Fall: Bit 10 = 0 --> Das SPI-Modul kann nur Nachrichten empfangen, Im Fall Bit 10 = 1: Das SPI-Modul kann Nachrichten empfangen und senden?
Bit 14 (BIDOE): Aktiviert oder deaktiviert den Ausgang?
Bit 15 (BIMODE): Entscheidet, ob die Verbindung voll Duplex ist oder halbduplex?
Wenn ich jetzt nur Messwerte eines ADU auslesen möchte, welchen Wert müssen die oben genannten Bits haben, kann mir das einer erklären?
Bit 10 (RXONLY): Im Fall: Bit 10 = 0 --> Das SPI-Modul kann nur
Nachrichten empfangen, Im Fall Bit 10 = 1: Das SPI-Modul kann
Nachrichten empfangen und senden?
Bit 14 (BIDOE): Aktiviert oder deaktiviert den Ausgang?
Bit 15 (BIMODE): Entscheidet, ob die Verbindung voll Duplex ist oder
halbduplex?
Wenn ich jetzt nur Messwerte eines ADU auslesen möchte, welchen Wert
müssen die oben genannten Bits haben, kann mir das einer erklären?
Sollte im DB stehen. Man muß halt bloß auch den Rest lesen und nicht nur die Bezeichner der Bits...
Okey jetzt habe ich den Fehler gefunden. Ich habe die SPI Einstellungen auf Default gesetzt, die SPI Frequenz mit dem BR Teiler so stark reduziert, wie im Datenblatt gefordert und Das SPI nach den Einstellungen aktiviert. Jetzt klappt beinahe alles wie gewollt.
Das einzige, was nicht funktioniert ist, ich kriege falsche Werte vom ADU.
Ich habe ein VDD = VREF = 3,3V
Somit sollte, wenn am Eingang des ADU 3,3V vorliegt den 16-Bit Wert 65.536 erhalten. Aber ich erhalte nur ca. 50.000.
Das ist doch ein 10-bit AD-Wandler. Der kann nur 0...1023 ausgeben.
Im Datenblatt FIGURE 6-1: SPI Communication with the MCP3004/3008 using 8-bit segments
(Mode 0,0: SCLK idles low).
steht ganz explizit vorgegeben wie die Kommunikation aussehen soll. Sogar mit Beispiel. Deine SW macht m.E. was anderes.
Wahrscheinlich Bitreihenfolge vertauscht. In deiner SPI-Config seht "LSB first".
Der Chip gibt aber MSB-first aus. Wenn ich den Wert "0000 0011 1111 1111" = 1023 in Dezimal LSB-MSB-tausche kommt ca. 49407 in Dezimal raus.
Das ist doch ein 10-bit AD-Wandler. Der kann nur 0...1023 ausgeben.
Im Datenblatt FIGURE 6-1: SPI Communication with the MCP3004/3008 using
8-bit segments
(Mode 0,0: SCLK idles low).
steht ganz explizit vorgegeben wie die Kommunikation aussehen soll.
Sogar mit Beispiel. Deine SW macht m.E. was anderes.
Hallo,
ich habe mir das angesehen und ja du hast recht mit den 10-Bit. 3,3 V wären dann 1024.
Ich habe den Ausleseteil derartig geändert, dass die Bits 9 bis 0 aus der 16-Bit Variable verwendet werden (Mit der Zeile *WertDesADUx &= 0x3FF).
Ich habe jetzt einen kleinen Offset. Jetzt erhalte ich bei 3,3V Eingang ca. die Dezimalzahl 950, obwohl es 1024 sein sollten. Kann sich jemand die Differenz erklären?
Wahrscheinlich Bitreihenfolge vertauscht. In deiner SPI-Config seht "LSB
first".
Der Chip gibt aber MSB-first aus. Wenn ich den Wert "0000 0011 1111
1111" = 1023 in Dezimal LSB-MSB-tausche kommt ca. 49407 in Dezimal raus.