Forum: Mikrocontroller und Digitale Elektronik ADC über SPI


von RT (mcu-programmer)


Lesenswert?

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
void sendeChar(char character);
7
void SPIEmpfange(uint8_t *data, int size);
8
void SPISende(uint8_t *data, int size);
9
10
int main(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
28
  GPIOA->AFR[0] &= ~(GPIO_AFRL_AFSEL5);
29
  GPIOA->AFR[0] |= GPIO_AFRL_AFSEL5_0 | GPIO_AFRL_AFSEL5_2;
30
31
  GPIOA->AFR[0] &= ~(GPIO_AFRL_AFSEL6);
32
  GPIOA->AFR[0] |= GPIO_AFRL_AFSEL6_0 | GPIO_AFRL_AFSEL6_2;
33
34
  GPIOA->AFR[0] &= ~(GPIO_AFRL_AFSEL7);
35
  GPIOA->AFR[0] |= GPIO_AFRL_AFSEL7_0 | GPIO_AFRL_AFSEL7_2;
36
37
  GPIOA->AFR[0] |= (5<<20)|(5<<24)|(5<<28);
38
39
  // Definition von GPIO A Pin 9 und 10 als alternative Funktion AF7
40
41
  GPIOA->AFR[1] |= GPIO_AFRH_AFSEL9;
42
  GPIOA->AFR[1] &= ~GPIO_AFRH_AFSEL9_3;
43
44
  GPIOA->AFR[1] |= GPIO_AFRH_AFSEL10;
45
  GPIOA->AFR[1] &= ~GPIO_AFRH_AFSEL10_3;
46
47
  //Pegel an GPIO A Pin 8 auf High, damit SPI Modul ruht, bis die Kommunikation beginnt.
48
  GPIOA->BSRR |= GPIO_BSRR_BS8;
49
50
51
  // Initialisierung des SPI
52
        
53
        // Clock für die SPI 1 Schnittstelle
54
  RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
55
56
  // SPI Clock Polarität (CPOL) und Phasenverschiebung (CPHA) werden eingestellt
57
  SPI1->CR1 &= ~SPI_CR1_CPHA_Msk;
58
  SPI1->CR1 &= ~SPI_CR1_CPOL_Msk;
59
60
  // Selektieren, ob es sich um einen Master oder Slave handelt
61
  SPI1->CR1 |= SPI_CR1_MSTR;
62
63
  // Einstellen der Baudrate = Clock des jeweiligen SPI Bus (fPCLK) / Prescaler (PSC)
64
  SPI1->CR1 |= SPI_CR1_BR; // 111: fPCLK/256
65
66
  // SPI1 wird aktiviert
67
  SPI1->CR1 |= SPI_CR1_SPE;
68
69
  // Format of the data: LSB
70
  SPI1->CR1 |= SPI_CR1_LSBFIRST;
71
72
  // Software Slavemanagement: 
73
  // SSM = SSI = 1 --> Software Slave management
74
  SPI1->CR1 |= SPI_CR1_SSM;
75
  SPI1->CR1 |= SPI_CR1_SSI;
76
77
  // SPI1->CR1 Bit 15 = 0: 2 Leitungen --> Voll-Duplex
78
  SPI1->CR1 &= ~(SPI_CR1_BIDIMODE_Msk);
79
  SPI1->CR1 &= ~(SPI_CR1_RXONLY_Msk);
80
81
  // DFF: Data frame format: 8-Bit
82
  SPI1->CR1 &= ~(SPI_CR1_DFF_Msk);
83
84
  // Das Register CR2 wird erstmal nicht genutzt
85
  SPI1->CR2 = 0;
86
87
  uint8_t initData[3] = {(uint8_t)SPI_Startbyte, (uint8_t)SPI_SelectChannel, (uint8_t)SPI_RandomByte};
88
89
  //Pegel an GPIO A Pin 8 auf Low, damit SPI Modul für die Kommunikation aktiv ist.
90
  GPIOA->BSRR |= GPIO_BSRR_BR8;
91
92
  // Senden der Startkonfig an SPI:
93
  SPISende(initData,3);
94
95
  //Pegel an GPIO A Pin 8 auf High, damit SPI Modul ruht, bis die Kommunikation beginnt.
96
  //GPIOA->BSRR |= GPIO_BSRR_BS8;
97
98
99
       // Initialisierung USART1
100
101
       // Aktivieren der Clock für USART 1 Schnittstelle
102
  RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
103
104
  // Aktiviere/Starte USART1
105
  USART1->CR1 |= USART_CR1_UE;
106
107
  // 1 Start-Bit and 8 Databits and n Stopp-Bits
108
  USART1->CR1 &= ~USART_CR1_M;
109
110
  // Oversampling 16
111
        USART1->CR1 &= ~USART_CR1_OVER8_Msk;
112
113
  // Baudrate einstellen:
114
  USART1->BRR = 0x8B;
115
116
  // Aktiviere/Starte Sender
117
  USART1->CR1 |= USART_CR1_TE;
118
119
        uint8_t messDaten = 0;
120
121
  // Einfacher Test
122
123
  while(1)
124
  {
125
    GPIOA->BSRR |= GPIO_BSRR_BR8;
126
    SPIEmpfange(&messDaten, 1);
127
    GPIOA->BSRR |= GPIO_BSRR_BS8;
128
    sendeChar(messDaten);
129
    sendeChar('5');
130
  }
131
}
132
133
void SPISende(uint8_t *data, int size)
134
{
135
  int i = 0;
136
  while (i < size)
137
  {
138
    while (!((SPI1->SR) & (SPI_SR_TXE))){};
139
140
    SPI1->DR = data[i];
141
142
    i++;
143
  }
144
145
  while (!(SPI1->SR & SPI_SR_TXE)){};
146
147
  while(SPI1->SR & SPI_SR_BSY){}; 
148
  uint8_t temp;
149
  temp = SPI1->DR;
150
  temp = SPI1->SR; 
151
}
152
153
void SPIEmpfange(uint8_t *data, int size)
154
{
155
  while (size)
156
  {
157
    while(SPI1->SR & SPI_SR_BSY){};
158
159
    SPI1->DR = 0; // senden von dummy Daten
160
161
    while (!(SPI1->SR & SPI_SR_RXNE)){}; 
162
163
    *data++ = (SPI1->DR); 
164
    size--;
165
  }
166
}
167
168
void sendeChar(char character)
169
{
170
  USART1->DR = character;
171
  while(!(USART1->SR & USART_SR_TC)){;}
172
}

von fuck (Gast)


Angehängte Dateien:

Lesenswert?

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?

von Gerald K. (geku)


Lesenswert?

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

Siehe Kapitel Protokollablauf und Einstellmöglichkeiten

Vielleicht ist im STM ein anderer Mode (Parameter „Clock Polarity“ 
(CPOL) und „Clock Phase“ (CPHA)) als beim ADC eingestellt.

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

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.

von Gerald K. (geku)


Lesenswert?

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.

von fuck (Gast)


Lesenswert?

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?

von Purzel H. (hacky)


Lesenswert?

Standardmaessig nimmt man irgendwelche Portpins und macht den SPI, 
genaustens gemaess Datenblatt des ADC zu Fuss.

von PittyJ (Gast)


Lesenswert?

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.

von Gerald K. (geku)


Lesenswert?

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.

: Bearbeitet durch User
von Steve van de Grens (roehrmond)


Lesenswert?

Ich finde es einfacher, diesen ADC per Bit-Banging (also ohne SPI 
Hardware) zu benutzen.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

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?

von Fillo Soof (Gast)


Lesenswert?

Man kann viel finden um am Thema vorbei zu reden und
den Thread in die Länge zu ziehen.

von Peter D. (peda)


Lesenswert?

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.

von Steve van de Grens (roehrmond)


Lesenswert?

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.

von M. K. (sylaina)


Lesenswert?

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.
1
uint16_t getValueFromMCP(uint8_t channel, uint8_t mode){
2
  uint8_t message[3] = {0b100|(mode << 1)|((channel & 0b100) >> 2), (channel & 0b11) << 6, 0x00};
3
  uint8_t answer[3] = {0x00, 0x00, 0x00};
4
  spiTransferMessage(message, answer, sizeof(message)/sizeof(uint8_t), &mcp3208);
5
  return (uint16_t)(((answer[1] & 0x0f) << 8)|answer[2]);
6
}

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)

von RT (mcu-programmer)


Lesenswert?

Erstmal danke an alle Antworten. Ich muss mich hier erstmal 
durcharbeiten.

von RT (mcu-programmer)


Lesenswert?

> "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.

von RT (mcu-programmer)


Lesenswert?

> 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.

von RT (mcu-programmer)


Lesenswert?

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.

von RT (mcu-programmer)


Lesenswert?

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

von RT (mcu-programmer)


Lesenswert?

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.

von RT (mcu-programmer)


Lesenswert?

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

von RT (mcu-programmer)


Lesenswert?

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.

von RT (mcu-programmer)


Lesenswert?

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.

von RT (mcu-programmer)


Lesenswert?

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.

von RT (mcu-programmer)


Lesenswert?

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.
>
>
1
uint16_t getValueFromMCP(uint8_t channel, uint8_t mode){
2
>   uint8_t message[3] = {0b100|(mode << 1)|((channel & 0b100) >> 2), 
3
> (channel & 0b11) << 6, 0x00};
4
>   uint8_t answer[3] = {0x00, 0x00, 0x00};
5
>   spiTransferMessage(message, answer, sizeof(message)/sizeof(uint8_t), 
6
> &mcp3208);
7
>   return (uint16_t)(((answer[1] & 0x0f) << 8)|answer[2]);
8
> }
>

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
1
void SPICommunicate(uint8_t *transmittedData, uint8_t *receivedData, int size)
2
{
3
4
  int i = 0;
5
    //Pegel an   uint8_t transmittedData[3] = {(uint8_t)SPI_Startbyte, (uint8_t)SPI_SelectChannel, SPI_RandomByte};
6
    // GPIO A Pin 8 auf Low, damit SPI Modul für die Kommunikation aktiv ist.
7
8
    GPIOB->BSRR |= GPIO_BSRR_BR0;
9
  while (i < size)
10
  {
11
    while (!((SPI1->SR) & (SPI_SR_TXE))){};
12
    SPI1->DR = transmittedData[i];
13
14
    receivedData[i] = SPI1->DR;
15
16
    i++;
17
18
    // Warten, bis der RX Zwischenspeicher leer ist
19
    while (!(SPI1->SR & SPI_SR_RXNE)){};
20
21
    // Am Anfang der Nachricht wird nochmal zur Sicherheit gewartet bis das SPI Protokoll nicht mehr beschäftigt ist!
22
    while(SPI1->SR & SPI_SR_BSY){};
23
  }
24
25
    //Pegel an GPIO B Pin 0 auf High, damit SPI Modul ruht, bis die Kommunikation beginnt.
26
    GPIOB->BSRR |= GPIO_BSRR_BS0;
27
}

Es wird kein Byte gesendet, glaube ich, weil die (SPI1->SR) & 
(SPI_SR_TXE) Register nicht zurückgesetzt werden.

von mitlesa (Gast)


Lesenswert?

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:
1
uint8_t SPI_TransferByte (uint8_t val)
2
{
3
  uint8_t ret_val;
4
5
  /*  wait for TX empty  */
6
  while ((SPI1->SR & SPI_I2S_FLAG_TXE) == 0)
7
  { /*  wait  */  };
8
  SPI1->DR = (uint8_t)(val);
9
10
  /*  wait for RX not empty  */
11
  while ((SPI1->SR & SPI_I2S_FLAG_RXNE) == 0)
12
  { /*  wait  */  };
13
  ret_val = SPI1->DR;
14
15
  /*  wait for Busy flag not active  */
16
  while ((SPI1->SR & SPI_I2S_FLAG_BSY) != 0);
17
18
  return ret_val;
19
20
} /* --- SPI_TransferByte --- */
21
22
uint16_t Read_ADC (void)
23
{
24
  uint8_t   byte_low, byte_high;
25
  uint8_t   config_byte;
26
  uint16_t  adc_val;
27
  uint8_t   mode = 1;
28
  uint8_t   channel = 0;
29
30
  config_byte = 0b100|(mode << 1) | ((channel & 0b100) >> 2);
31
32
  GPIOB->BSRR |= GPIO_BSRR_BR0;      // CS aktiv
33
  SPI_TransferByte (config_byte);    // Konfiguration schreiben
34
  byte_high = SPI_TransferByte (0);  // dummy schreiben, byte holen
35
  byte_low = SPI_TransferByte (0);   // dummy schreiben, byte holen
36
  GPIOB->BSRR |= GPIO_BSRR_BS0;      // CS inaktiv
37
38
  adc_val = ( ((uint16_t)byte_high) << 8) || ( (uint16_t)byte_low);
39
40
  return  adc_val;
41
42
} /* --- Read_ADC --- */

<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.

von mitlesa (Gast)


Lesenswert?

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?

von Jupp (Gast)


Lesenswert?

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.

von M. K. (sylaina)


Lesenswert?

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.

von RT (mcu-programmer)


Lesenswert?

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

: Bearbeitet durch User
von C-hater (c-hater)


Lesenswert?

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...

Beitrag #7422464 wurde von einem Moderator gelöscht.
von RT (mcu-programmer)


Lesenswert?

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?

von RT (mcu-programmer)


Angehängte Dateien:

Lesenswert?

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?

: Bearbeitet durch User
von Nils S. (wall-e)


Lesenswert?

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.

von Nils S. (wall-e)


Lesenswert?

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.

von RT (mcu-programmer)


Lesenswert?

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?

: Bearbeitet durch User
von RT (mcu-programmer)


Lesenswert?

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!!!!

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.