Hallo,
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
RT schrieb:> ich habe bereits viel gegoogelt
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?
Gerald K. schrieb:> Bei SPI existieren verschiefene Modi:>> https://de.m.wikipedia.org/wiki/Serial_Peripheral_Interface#:~:text=SLK%20(englisch%20Serial%20Clock)%20auch,englisch%20Master%20Output%2C%20Slave%20Input>> 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.
c-hater schrieb:> 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.
Sehe ich auch so.
RT schrieb:> SPISende(initData,3);
Das nützt erst mal gar nichts da der Wandler aktuell bei jedem
Lesen konfiguriert wird.
RT schrieb:> void SPIEmpfange(uint8_t *data, int size)
{
......
}
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.
RT schrieb:> #define SPI_SelectChannel
Und was soll dieses define bitte sehr?
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.
Purzel H. schrieb:> 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.
Steve van de Grens schrieb:> Ich finde es einfacher, diesen ADC per Bit-Banging (also ohne SPI> Hardware) zu benutzen.
Max 3,6MHz des MCP3008 sollte selbst zu Fuß kein Problem sein. Das
HW-SPI wäre also auch nicht schneller.
Warum will man aber einen externen 10Bit-ADC benutzen, wenn der
STM32F446RE schon 3*12Bit-ADCs mit 24 Eingängen intern hat?
Fillo Soof schrieb:> Man kann viel finden um am Thema vorbei zu reden
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.
Peter D. schrieb:> 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)
> "Längerer Sourcecode" ist es wenn man dauernd scrollen muss um> alles zu sehen. Hast du es jetzt verstanden?
Das ist mir wohl entgangen merke ich mir.
> 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.
c-hater schrieb:> Gerald K. schrieb:>> Bei SPI existieren verschiefene Modi:>>>>> 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.
Da müsste ich schauen.
fuck schrieb:> RT schrieb:>> SPISende(initData,3);>
Tip: für jedes abzuholende Byte wird auch erst
> eines geschrieben.>> RT schrieb:>> #define SPI_SelectChannel>> Und was soll dieses define bitte sehr?
Vergiss das Define.
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.
Danke
PittyJ schrieb:> 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.
Steve van de Grens schrieb:> Ich finde es einfacher, diesen ADC per Bit-Banging (also ohne SPI> Hardware) zu benutzen
Hmm ... So weit bin ich wohl noch nicht... Das sagt mir nichts
Peter D. schrieb:> Steve van de Grens schrieb:> Warum will man aber einen externen 10Bit-ADC benutzen, wenn der> STM32F446RE schon 3*12Bit-ADCs mit 24 Eingängen intern hat?
Es ist eine Übung ein SPI Modul zu nutzen.
Peter D. schrieb:> Fillo Soof schrieb:>> Man kann viel finden um am Thema vorbei zu reden>> 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.
Merke ich mir.
M. K. schrieb:> 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.
M. K. schrieb:> 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.>>
>
Also das verstehe ich nicht.
Ich möchte den ADU im single-ended Mode und Channel 0 nutzen.
gemäß deinem Code wären die messages:
uint8_t message[3] = {0b100|(1 << 1)|((0 & 0b100) >> 2), (0 & 0b11) <<
6, 0x00};
uint8_t message[3] = {0b110, 0b00000000, 0x00};
ich hätte aber gedacht man müsste der Reihenfolge entsprechend:
uint8_t message[3] = {0b00000001, 0b10000000, 0x00};
Ich habe die Auslese-Funktion geändert in
RT schrieb:> Es wird kein Byte gesendet, glaube ich
Glauben kann man viel. Ich kanns nicht mit ansehen .....
Bring doch mal Ordnung in dein Chaos.
Hier mein Vorschlag das Ganze übersichtlich zu gestalten:
<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.
RT schrieb:> return (uint16_t)(((answer[1] & 0x0f) << 8)|answer[2]);
Was glaubst du wohl was passiert bzw. was herauskommt wenn
man ein Byte achtmal nach links schiebt?
Peter D. schrieb:> 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.
RT schrieb:> gemäß deinem Code wären die messages:> uint8_t message[3] = {0b100|(1 << 1)|((0 & 0b100) >> 2), (0 & 0b11) <<> 6, 0x00};> uint8_t message[3] = {0b110, 0b00000000, 0x00};>
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:
RT schrieb:> ich hätte aber gedacht man müsste der Reihenfolge entsprechend:> uint8_t message[3] = {0b00000001, 0b10000000, 0x00};
und das ist natürlich nicht das selbe wie:
RT schrieb:> uint8_t message[3] = {0b110, 0b00000000, 0x00};
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.
Hallo,
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?
schöne Grüße
RT schrieb:> 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...
Fazit: Faulschwein. Aber sowas von...
Ich erhalte einfach keine Antwort vom ADU, sodass der Code aus
while ((SPI1->SR & SPI_SR_RXNE) == 0){};
nicht raus kommt.
Hat einer eine Idee woran das liegt?
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.
Ich frage mich woran das liegt.
Ich habe den Code angehangen. Weiß einer weiter?
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.
Nils S. schrieb:> 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?
Nils S. schrieb:> 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.
Jetzt komme ich auf 1024. Danke!!!!