Liebes Forum,
Ich und meine Teamkollegen sind Programmieranfänger und bauen für einen Wettbewerb gerade einen fahrenden Roboter, der unter anderem in der Lage sein muss Analogwerte auszuwerten. Dazu haben wir uns zwei MCP3008 besorgt. Das ist ein A/D-Wandler, mit dem man über den SPI-Bus kommunizieren kann und der acht Eingangskanäle für acht Analogwerte hat.
Um den MCP3008 auszulesen haben wir zunächst diese Library verwendet.
Diese funktioniert zwar und lässt sich sehr leicht bedienen, allerdings stellte sich heraus, dass das Auslesen des Wandlers viel zu lange dauert. Für die Abfrage eines einzelnen der acht Eingangskanäle am A/D-Wandler wird mehrfach eine Datei geöffnet und geschlossen, was letztlich ca. 25 Millisekunden pro Kanal dauert. Bei insgesamt 12 Analogwerten die wir verarbeiten müssen, ist das für uns nicht zu gebrauchen.
Daher haben wir uns der Pigpio Library zugewendet. Auf deren Homepage gibt es ein Beispielprogramm (Code s. unten), dass wir auch schon ausprobiert haben. Das Auslesen geht hier sehr schnell.
In dem Beispielprogramm geht es zwar darum, zwei Kanäle von zwei MCP3008 gleichzeitig abzufragen. Für unsere Zwecke würde es reichen die Kanäle zweier MCP3008 alle nacheinander abzufragen, aber wenn man immer zwei gleichzeitig bekommt, jeweils einen von jeweils einem der beiden Wandler ist das natürlich auch ok.
Dazu müssten wir es allerdings mit dem Programm hinbekommen den abgefragten Kanal auch zu wechseln und daran scheitern wir momentan.
Wir haben deher zwei Fragen:
a) Kann uns jemand eine einfachere Möglichkeit aufzeigen zwei MCP3008 auszulesen?
b) Oder kann uns jemand helfen das Beispielprogramm richtig zu verstehen, so dass wir die Kanäle wechseln können?
Zu a)
Wir wollten ungerne WiringPi verwenden, da wir aus verschiedenen Gründen dringend Pigpio brauchen, aber vielleicht kann man unter Umständen ja auch gefahrlos beide kombinieren? Das wäre dann wohl ein Thema für einen anderen Thread, bzw. eine lange Reihe von Feldversuchen.
Zu b)
Nach unserem bisherigen Verständnis müsste man den Wert von „buf“ in Zeile 147 so ändern, dass die Funktion rawWaveAddSPI die richtige Bitfolge für den MCP3008 kennt, entsprechend dem Kommentar in den Zeilen 127-134.
Im Beispiel steht an dieser Stelle „0xC0“, also binär „11000000“. Die Bits für die Auswahl des Kanals wären dem Kommentar entsprechend der vierte, fünfte und sechste von rechts. Gewählt wäre hier also Kanal 0.
Ändern wir den Wert von „buf“ beispielsweise in „F8“, also „1111100“, müsste man also Kanal 7 ausgewählt haben. Oder „C8“ für „11001000“ also Kanal 1.
So klappt es aber leider nicht. Der Kanal der abgefragt wird bleibt bei beiden angeschlossenen Wandlern der Kanal 0.
An Kanal 0 kann Spannung gemessen werden und an keinem der anderen. Wir haben auch nicht vergessen neu zu kompilieren. Wir hocken schon ziemlich lange daran.
Unser Verdacht ist, das an mindestens einer anderen Stelle noch was geändert werden muss und dass das in den Kommentaren nicht gekennzeichnet ist, da der Zweck des Programms ja ein anderer ist. Nämlich zu demonstrieren wie man zwei der Wandler zum gleichen Zeitpunkt ausliest.
Vielen Dank euch schonmal!
Hier das Programm von dem ich spreche (hier auch nochmal ohne Zeilennummern):
001 /*
002
003 rawMCP3008.c
004 Public Domain
005 2016-03-20
006
007 gcc -Wall -pthread -o rawMCP3008 rawMCP3008.c -lpigpio
008
009 This code shows how to bit bang SPI using DMA.
010
011 Using DMA to bit bang allows for two advantages
012
013 1) the time of the SPI transaction can be guaranteed to within a
014 microsecond or so.
015
016 2) multiple devices of the same type can be read or written
017 simultaneously.
018
019 This code shows how to read more than one MCP3008 at a time.
020
021 Each MCP3008 shares the SPI clock, MOSI, and slave select lines but has
022 a unique MISO line.
023
024 This code should also work with the MCP3004.
025 */
026
027 #include <stdio.h>
028 #include <stdlib.h>
029 #include <unistd.h>
030
031 #include <pigpio.h>
032
033 #define SPI_SS 25 // GPIO for slave select.
034
035 #define ADCS 5 // Number of connected MCP3008.
036
037 #define BITS 10 // Bits per reading.
038 #define BX 8 // Bit position of data bit B9.
039 #define B0 (BX + BITS - 1) // Bit position of data bit B0.
040
041 #define MISO1 6 // ADC 1 MISO.
042 #define MISO2 26 // 2
043 #define MISO3 13 // 3
044 #define MISO4 23 // 4
045 #define MISO5 24 // 5
046
047 #define BUFFER 250 // Generally make this buffer as large as possible.
048
049 #define REPEAT_MICROS 40 // Reading every x microseconds.
050
051 #define SAMPLES 2000000 // Number of samples to take,
052
053 int MISO[ADCS]={MISO1, MISO2, MISO3, MISO4, MISO5};
054
055 rawSPI_t rawSPI =
056 {
057 .clk = 5, // GPIO for SPI clock.
058 .mosi = 12, // GPIO for SPI MOSI.
059 .ss_pol = 1, // Slave select resting level.
060 .ss_us = 1, // Wait 1 micro after asserting slave select.
061 .clk_pol = 0, // Clock resting level.
062 .clk_pha = 0, // 0 sample on first edge, 1 sample on second edge.
063 .clk_us = 1, // 2 clocks needed per bit so 500 kbps.
064 };
065
066 /*
067 This function extracts the MISO bits for each ADC and
068 collates them into a reading per ADC.
069 */
070
071 void getReading(
072 int adcs, // Number of attached ADCs.
073 int *MISO, // The GPIO connected to the ADCs data out.
074 int OOL, // Address of first OOL for this reading.
075 int bytes, // Bytes between readings.
076 int bits, // Bits per reading.
077 char *buf)
078 {
079 int i, a, p;
080 uint32_t level;
081
082 p = OOL;
083
084 for (i=0; i<bits; i++)
085 {
086 level = rawWaveGetOut(p);
087
088 for (a=0; a<adcs; a++)
089 {
090 putBitInBytes(i, buf+(bytes*a), level & (1<<MISO[a]));
091 }
092
093 p--;
094 }
095 }
096
097
098 int main(int argc, char *argv[])
099 {
100 int i, wid, offset;
101 char buf[2];
102 gpioPulse_t final[2];
103 char rx[8];
104 int sample;
105 int val;
106 int cb, botCB, topOOL, reading, now_reading;
107 float cbs_per_reading;
108 rawWaveInfo_t rwi;
109 double start, end;
110 int pause;
111
112 if (argc > 1) pause = atoi(argv[1]); else pause =0;
113
114 if (gpioInitialise() < 0) return 1;
115
116 // Need to set GPIO as outputs otherwise wave will have no effect.
117
118 gpioSetMode(rawSPI.clk, PI_OUTPUT);
119 gpioSetMode(rawSPI.mosi, PI_OUTPUT);
120 gpioSetMode(SPI_SS, PI_OUTPUT);
121
122 gpioWaveAddNew(); // Flush any old unused wave data.
123
124 offset = 0;
125
126 /*
127 MCP3004/8 10-bit ADC 4/8 channels
128
129 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
130 SB SD D2 D1 D0 NA NA B9 B8 B7 B6 B5 B4 B3 B2 B1 B0
131
132 SB 1
133 SD 0=differential 1=single
134 D2/D1/D0 0-7 channel
135 */
136
137 /*
138 Now construct lots of bit banged SPI reads. Each ADC reading
139 will be stored separately. We need to ensure that the
140 buffer is big enough to cope with any reasonable rescehdule.
141
142 In practice make the buffer as big as you can.
143 */
144
145 for (i=0; i<BUFFER; i++)
146 {
147 buf[0] = 0xC0; // Start bit, single ended, channel 0.
148
149 rawWaveAddSPI(&rawSPI, offset, SPI_SS, buf, 2, BX, B0, B0);
150
151 /*
152 REPEAT_MICROS must be more than the time taken to
153 transmit the SPI message.
154 */
155
156 offset += REPEAT_MICROS;
157 }
158
159 // Force the same delay after the last reading.
160
161 final[0].gpioOn = 0;
162 final[0].gpioOff = 0;
163 final[0].usDelay = offset;
164
165 final[1].gpioOn = 0; // Need a dummy to force the final delay.
166 final[1].gpioOff = 0;
167 final[1].usDelay = 0;
168
169 gpioWaveAddGeneric(2, final);
170
171 wid = gpioWaveCreate(); // Create the wave from added data.
172
173 if (wid < 0)
174 {
175 fprintf(stderr, "Can't create wave, %d too many?\n", BUFFER);
176 return 1;
177 }
178
179 /*
180 The wave resources are now assigned, Get the number
181 of control blocks (CBs) so we can calculate which reading
182 is current when the program is running.
183 */
184
185 rwi = rawWaveInfo(wid);
186
187 printf("# cb %d-%d ool %d-%d del=%d ncb=%d nb=%d nt=%d\n",
188 rwi.botCB, rwi.topCB, rwi.botOOL, rwi.topOOL, rwi.deleted,
189 rwi.numCB, rwi.numBOOL, rwi.numTOOL);
190
191 /*
192 CBs are allocated from the bottom up. As the wave is being
193 transmitted the current CB will be between botCB and topCB
194 inclusive.
195 */
196
197 botCB = rwi.botCB;
198
199 /*
200 Assume each reading uses the same number of CBs (which is
201 true in this particular example).
202 */
203
204 cbs_per_reading = (float)rwi.numCB / (float)BUFFER;
205
206 printf("# cbs=%d per read=%.1f base=%d\n",
207 rwi.numCB, cbs_per_reading, botCB);
208
209 /*
210 OOL are allocated from the top down. There are BITS bits
211 for each ADC reading and BUFFER ADC readings. The readings
212 will be stored in topOOL - 1 to topOOL - (BITS * BUFFER).
213 */
214
215 topOOL = rwi.topOOL;
216
217 fprintf(stderr, "starting...\n");
218
219 if (pause) time_sleep(pause); // Give time to start a monitor.
220
221 gpioWaveTxSend(wid, PI_WAVE_MODE_REPEAT);
222
223 reading = 0;
224
225 sample = 0;
226
227 start = time_time();
228
229 while (sample<SAMPLES)
230 {
231 // Which reading is current?
232
233 cb = rawWaveCB() - botCB;
234
235 now_reading = (float) cb / cbs_per_reading;
236
237 // Loop gettting the fresh readings.
238
239 while (now_reading != reading)
240 {
241 /*
242 Each reading uses BITS OOL. The position of this readings
243 OOL are calculated relative to the waves top OOL.
244 */
245 getReading(
246 ADCS, MISO, topOOL - ((reading%BUFFER)*BITS) - 1, 2, BITS, rx);
247
248 printf("%d", ++sample);
249
250 for (i=0; i<ADCS; i++)
251 {
252 // 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0
253 // B9 B8 B7 B6 B5 B4 B3 B2 | B1 B0 X X X X X X
254
255 val = (rx[i*2]<<2) + (rx[(i*2)+1]>>6);
256 printf(" %d", val);
257 }
258
259 printf("\n");
260
261 if (++reading >= BUFFER) reading = 0;
262 }
263 usleep(1000);
264 }
265
266 end = time_time();
267
268 printf("# %d samples in %.1f seconds (%.0f/s)\n",
269 SAMPLES, end-start, (float)SAMPLES/(end-start));
270
271 fprintf(stderr, "ending...\n");
272
273 if (pause) time_sleep(pause);
274
275 gpioTerminate();
276
277 return 0;
278 }
279
Alles anzeigen
Hier die Funktion rawWaveAddSPI aus der Pigpio-library Version 61
9185 int rawWaveAddSPI(
9186 rawSPI_t *spi,
9187 unsigned offset,
9188 unsigned spiSS,
9189 char *buf,
9190 unsigned spiTxBits,
9191 unsigned spiBitFirst,
9192 unsigned spiBitLast,
9193 unsigned spiBits)
9194 {
9195 int p, bit, dbv, halfbit;
9196 int rising_edge[2], read_cycle[2];
9197 uint32_t on_bits, off_bits;
9198 int tx_bit_pos;
9199
9200 DBG(DBG_USER,
9201 "spi=%08X off=%d spiSS=%d tx=%08X, num=%d fb=%d lb=%d spiBits=%d",
9202 (uint32_t)spi, offset, spiSS, (uint32_t)buf, spiTxBits,
9203 spiBitFirst, spiBitLast, spiBits);
9204
9205 CHECK_INITED;
9206
9207 if (spiSS > PI_MAX_USER_GPIO)
9208 SOFT_ERROR(PI_BAD_USER_GPIO, "bad gpio (%d)", spiSS);
9209
9210 /*
9211 CPOL CPHA
9212 0 0 read rising/write falling
9213 0 1 read falling/write rising
9214 1 0 read falling/write rising
9215 1 1 read rising/write falling
9216 */
9217
9218 if (spi->clk_pol) {rising_edge[0] = 0; rising_edge[1] = 1;}
9219 else {rising_edge[0] = 1; rising_edge[1] = 0;}
9220
9221 if (spi->clk_pha) {read_cycle[0] = 0; read_cycle[1] = 1;}
9222 else {read_cycle[0] = 1; read_cycle[1] = 0;}
9223
9224 p = 0;
9225
9226 if (offset)
9227 {
9228 wf[2][p].gpioOn = 0;
9229 wf[2][p].gpioOff = 0;
9230 wf[2][p].flags = 0;
9231 wf[2][p].usDelay = offset;
9232 p++;
9233 }
9234
9235 on_bits = 0;
9236 off_bits = 0;
9237
9238 tx_bit_pos = 0;
9239
9240 /* preset initial mosi bit */
9241
9242 if (getBitInBytes(tx_bit_pos, buf, spiTxBits))
9243 {
9244 on_bits |= (1<<(spi->mosi));
9245 dbv = 1;
9246 }
9247 else
9248 {
9249 off_bits |= (1<<(spi->mosi));
9250 dbv = 0;
9251 }
9252
9253 if (!spi->clk_pha) tx_bit_pos ++;
9254
9255 if (spi->ss_pol) off_bits |= (1<<spiSS);
9256 else on_bits |= (1<<spiSS);
9257
9258 if (spi->clk_pol) on_bits |= (1<<(spi->clk));
9259 else off_bits |= (1<<(spi->clk));
9260
9261 wf[2][p].gpioOn = on_bits;
9262 wf[2][p].gpioOff = off_bits;
9263 wf[2][p].flags = 0;
9264
9265 if (spi->clk_us > spi->ss_us) wf[2][p].usDelay = spi->clk_us;
9266 else wf[2][p].usDelay = spi->ss_us;
9267
9268 p++;
9269
9270 for (bit=1; bit<=spiBits; bit++)
9271 {
9272 for (halfbit=0; halfbit<2; halfbit++)
9273 {
9274 wf[2][p].usDelay = spi->clk_us;
9275 wf[2][p].flags = 0;
9276
9277 on_bits = 0;
9278 off_bits = 0;
9279
9280 if (read_cycle[halfbit])
9281 {
9282 if ((bit>=spiBitFirst) && (bit<=spiBitLast))
9283 wf[2][p].flags = WAVE_FLAG_READ;
9284 }
9285 else
9286 {
9287 if (getBitInBytes(tx_bit_pos, buf, spiTxBits))
9288 {
9289 if (!dbv) on_bits |= (1<<(spi->mosi));
9290 dbv = 1;
9291 }
9292 else
9293 {
9294 if (dbv) off_bits |= (1<<(spi->mosi));
9295 dbv = 0;
9296 }
9297
9298 ++tx_bit_pos;
9299 }
9300
9301 if (rising_edge[halfbit]) on_bits |= (1<<(spi->clk));
9302 else off_bits |= (1<<(spi->clk));
9303
9304 wf[2][p].gpioOn = on_bits;
9305 wf[2][p].gpioOff = off_bits;
9306
9307 p++;
9308 }
9309 }
9310
9311 on_bits = 0;
9312 off_bits = 0;
9313
9314 if (spi->ss_pol) on_bits |= (1<<spiSS);
9315 else off_bits |= (1<<spiSS);
9316
9317 wf[2][p].gpioOn = on_bits;
9318 wf[2][p].gpioOff = off_bits;
9319 wf[2][p].flags = 0;
9320 wf[2][p].usDelay = 0;
9321
9322 p++;
9323
9324 return rawWaveAddGeneric(p, wf[2]);
9325 }
Alles anzeigen