forked from stm32duino/Arduino_Core_STM32
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSPI.cpp
455 lines (398 loc) · 14.1 KB
/
SPI.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
/*
* Copyright (c) 2010 by Cristian Maglie <[email protected]>
* Copyright (c) 2014 by Paul Stoffregen <[email protected]> (Transaction API)
* SPI Master library for arduino.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either the GNU General Public License version 2
* or the GNU Lesser General Public License version 2.1, both as
* published by the Free Software Foundation.
*/
#include "SPI.h"
SPIClass SPI;
/**
* @brief Default constructor. Uses pin configuration of variant.h.
*/
SPIClass::SPIClass() : _CSPinConfig(NO_CONFIG)
{
_spi.pin_miso = digitalPinToPinName(MISO);
_spi.pin_mosi = digitalPinToPinName(MOSI);
_spi.pin_sclk = digitalPinToPinName(SCK);
_spi.pin_ssel = NC;
}
/**
* @brief Constructor to create another SPI instance attached to another SPI
* peripheral different of the default SPI. All pins must be attached to
* the same SPI peripheral. See datasheet of the microcontroller.
* @param mosi: SPI mosi pin. Accepted format: number or Arduino format (Dx)
* or ST format (Pxy).
* @param miso: SPI miso pin. Accepted format: number or Arduino format (Dx)
* or ST format (Pxy).
* @param sclk: SPI clock pin. Accepted format: number or Arduino format (Dx)
* or ST format (Pxy).
* @param ssel: SPI ssel pin (optional). Accepted format: number or
* Arduino format (Dx) or ST format (Pxy). By default is set to NC.
* This pin must correspond to a hardware CS pin which can be managed
* by the SPI peripheral itself. See the datasheet of the microcontroller
* or look at PinMap_SPI_SSEL[] inside the file PeripheralPins.c
* corresponding to the board. If you configure this pin you can't use
* another CS pin and don't pass a CS pin as parameter to any functions
* of the class.
*/
SPIClass::SPIClass(uint8_t mosi, uint8_t miso, uint8_t sclk, uint8_t ssel) : _CSPinConfig(NO_CONFIG)
{
_spi.pin_miso = digitalPinToPinName(miso);
_spi.pin_mosi = digitalPinToPinName(mosi);
_spi.pin_sclk = digitalPinToPinName(sclk);
_spi.pin_ssel = digitalPinToPinName(ssel);
}
/**
* @brief Initialize the SPI instance.
* @param _pin: chip select pin (optional). If this parameter is filled,
* it gives the management of the CS pin to the SPI class. In this case
* do not manage the CS pin outside of the SPI class.
*/
void SPIClass::begin(uint8_t _pin)
{
uint8_t idx;
if (_pin > NUM_DIGITAL_PINS) {
return;
}
idx = pinIdx(_pin, ADD_NEW_PIN);
if (idx >= NB_SPI_SETTINGS) {
return;
}
if ((_pin != CS_PIN_CONTROLLED_BY_USER) && (_spi.pin_ssel == NC)) {
pinMode(_pin, OUTPUT);
digitalWrite(_pin, HIGH);
}
_spi.handle.State = HAL_SPI_STATE_RESET;
spi_init(&_spi, spiSettings[idx].clk,
spiSettings[idx].dMode,
spiSettings[idx].bOrder);
_CSPinConfig = _pin;
#if __has_include("WiFi.h")
// Wait wifi shield initialization.
// Should be better to do in SpiDrv::begin() of WiFi library but it seems
// there is no more update on this library as shield is retired.
delay(2000);
#endif
}
/**
* @brief This function should be used to configure the SPI instance in case you
* don't use the default parameters set by the begin() function.
* @param _pin: CS pin (optional). This pin will be attached with the settings.
* @param settings: SPI settings(clock speed, bit order, data mode).
* @Note For each SPI instance you are able to manage until NB_SPI_SETTINGS
* devices attached to the same SPI peripheral. You need to indicate the
* CS pin used to the transfer() function and the SPI instance will be
* configured with the right settings.
*/
void SPIClass::beginTransaction(uint8_t _pin, SPISettings settings)
{
uint8_t idx;
if (_pin > NUM_DIGITAL_PINS) {
return;
}
idx = pinIdx(_pin, ADD_NEW_PIN);
if (idx >= NB_SPI_SETTINGS) {
return;
}
spiSettings[idx].clk = settings.clk;
spiSettings[idx].dMode = settings.dMode;
spiSettings[idx].bOrder = settings.bOrder;
spiSettings[idx].noReceive = settings.noReceive;
if ((_pin != CS_PIN_CONTROLLED_BY_USER) && (_spi.pin_ssel == NC)) {
pinMode(_pin, OUTPUT);
digitalWrite(_pin, HIGH);
}
spi_init(&_spi, spiSettings[idx].clk,
spiSettings[idx].dMode,
spiSettings[idx].bOrder);
_CSPinConfig = _pin;
}
/**
* @brief Remove the CS pin and the settings associated to the SPI instance.
* @param _pin: CS pin (optional)
*/
void SPIClass::endTransaction(uint8_t _pin)
{
if (_pin > NUM_DIGITAL_PINS) {
return;
}
RemovePin(_pin);
_CSPinConfig = NO_CONFIG;
}
/**
* @brief Deinitialize the SPI instance and stop it.
*/
void SPIClass::end()
{
spi_deinit(&_spi);
RemoveAllPin();
_CSPinConfig = NO_CONFIG;
}
/**
* @brief Deprecated function.
* Configure the bit order: MSB first or LSB first.
* @param _pin: CS pin associated to a configuration (optional).
* @param _bitOrder: MSBFIRST or LSBFIRST
*/
void SPIClass::setBitOrder(uint8_t _pin, BitOrder _bitOrder)
{
if (_pin > NUM_DIGITAL_PINS) {
return;
}
uint8_t idx = pinIdx(_pin, GET_IDX);
if (idx >= NB_SPI_SETTINGS) {
return;
}
spiSettings[idx].bOrder = _bitOrder;
spi_init(&_spi, spiSettings[idx].clk,
spiSettings[idx].dMode,
spiSettings[idx].bOrder);
}
/**
* @brief Deprecated function.
* Configure the data mode (clock polarity and clock phase)
* @param _pin: CS pin associated to a configuration (optional).
* @param _mode: SPI_MODE0, SPI_MODE1, SPI_MODE2 or SPI_MODE3
* @note
* Mode Clock Polarity (CPOL) Clock Phase (CPHA)
* SPI_MODE0 0 0
* SPI_MODE1 0 1
* SPI_MODE2 1 0
* SPI_MODE3 1 1
*/
void SPIClass::setDataMode(uint8_t _pin, uint8_t _mode)
{
if (_pin > NUM_DIGITAL_PINS) {
return;
}
uint8_t idx = pinIdx(_pin, GET_IDX);
if (idx >= NB_SPI_SETTINGS) {
return;
}
if (SPI_MODE0 == _mode) {
spiSettings[idx].dMode = SPI_MODE_0;
} else if (SPI_MODE1 == _mode) {
spiSettings[idx].dMode = SPI_MODE_1;
} else if (SPI_MODE2 == _mode) {
spiSettings[idx].dMode = SPI_MODE_2;
} else if (SPI_MODE3 == _mode) {
spiSettings[idx].dMode = SPI_MODE_3;
}
spi_init(&_spi, spiSettings[idx].clk,
spiSettings[idx].dMode,
spiSettings[idx].bOrder);
}
/**
* @brief Deprecated function.
* Configure the clock speed
* @param _pin: CS pin associated to a configuration (optional).
* @param _divider: the SPI clock can be divided by values from 1 to 255.
* If 0, default SPI speed is used.
*/
void SPIClass::setClockDivider(uint8_t _pin, uint8_t _divider)
{
if (_pin > NUM_DIGITAL_PINS) {
return;
}
uint8_t idx = pinIdx(_pin, GET_IDX);
if (idx >= NB_SPI_SETTINGS) {
return;
}
if (_divider == 0) {
spiSettings[idx].clk = SPI_SPEED_CLOCK_DEFAULT;
} else {
/* Get clk freq of the SPI instance and compute it */
spiSettings[idx].clk = spi_getClkFreq(&_spi) / _divider;
}
spi_init(&_spi, spiSettings[idx].clk,
spiSettings[idx].dMode,
spiSettings[idx].bOrder);
}
/**
* @brief Transfer one byte on the SPI bus.
* begin() or beginTransaction() must be called at least once before.
* @param _pin: CS pin to select a device (optional). If the previous transfer
* used another CS pin then the SPI instance will be reconfigured.
* @param data: byte to send.
* @param _mode: (optional) can be SPI_CONTINUE in case of multiple successive
* send or SPI_LAST to indicate the end of send.
* If the _mode is set to SPI_CONTINUE, keep the SPI instance alive.
* That means the CS pin is not reset. Be careful in case you use
* several CS pin.
* @return byte received from the slave.
*/
byte SPIClass::transfer(uint8_t _pin, uint8_t data, SPITransferMode _mode)
{
uint8_t rx_buffer = 0;
if (_pin > NUM_DIGITAL_PINS) {
return rx_buffer;
}
uint8_t idx = pinIdx(_pin, GET_IDX);
if (_pin != _CSPinConfig) {
if (idx >= NB_SPI_SETTINGS) {
return rx_buffer;
}
spi_init(&_spi, spiSettings[idx].clk,
spiSettings[idx].dMode,
spiSettings[idx].bOrder);
_CSPinConfig = _pin;
}
if ((_pin != CS_PIN_CONTROLLED_BY_USER) && (_spi.pin_ssel == NC)) {
digitalWrite(_pin, LOW);
}
spi_transfer(&_spi, &data, &rx_buffer, sizeof(uint8_t), SPI_TRANSFER_TIMEOUT, spiSettings[idx].noReceive);
if ((_pin != CS_PIN_CONTROLLED_BY_USER) && (_mode == SPI_LAST) && (_spi.pin_ssel == NC)) {
digitalWrite(_pin, HIGH);
}
return rx_buffer;
}
/**
* @brief Transfer two bytes on the SPI bus in 16 bits format.
* begin() or beginTransaction() must be called at least once before.
* @param _pin: CS pin to select a device (optional). If the previous transfer
* used another CS pin then the SPI instance will be reconfigured.
* @param data: bytes to send.
* @param _mode: (optional) can be SPI_CONTINUE in case of multiple successive
* send or SPI_LAST to indicate the end of send.
* If the _mode is set to SPI_CONTINUE, keep the SPI instance alive.
* That means the CS pin is not reset. Be careful in case you use
* several CS pin.
* @return bytes received from the slave in 16 bits format.
*/
uint16_t SPIClass::transfer16(uint8_t _pin, uint16_t data, SPITransferMode _mode)
{
uint16_t rx_buffer = 0;
uint16_t tmp;
if (_pin > NUM_DIGITAL_PINS) {
return rx_buffer;
}
uint8_t idx = pinIdx(_pin, GET_IDX);
if (idx >= NB_SPI_SETTINGS) {
return rx_buffer;
}
if (_pin != _CSPinConfig) {
spi_init(&_spi, spiSettings[idx].clk,
spiSettings[idx].dMode,
spiSettings[idx].bOrder);
_CSPinConfig = _pin;
}
if (spiSettings[idx].bOrder) {
tmp = ((data & 0xff00) >> 8) | ((data & 0xff) << 8);
data = tmp;
}
if ((_pin != CS_PIN_CONTROLLED_BY_USER) && (_spi.pin_ssel == NC)) {
digitalWrite(_pin, LOW);
}
spi_transfer(&_spi, (uint8_t *)&data, (uint8_t *)&rx_buffer, sizeof(uint16_t),
SPI_TRANSFER_TIMEOUT, spiSettings[idx].noReceive);
if ((_pin != CS_PIN_CONTROLLED_BY_USER) && (_mode == SPI_LAST) && (_spi.pin_ssel == NC)) {
digitalWrite(_pin, HIGH);
}
if (spiSettings[idx].bOrder) {
tmp = ((rx_buffer & 0xff00) >> 8) | ((rx_buffer & 0xff) << 8);
rx_buffer = tmp;
}
return rx_buffer;
}
/**
* @brief Transfer several bytes. Only one buffer used to send and receive data.
* begin() or beginTransaction() must be called at least once before.
* @param _pin: CS pin to select a device (optional). If the previous transfer
* used another CS pin then the SPI instance will be reconfigured.
* @param _buf: pointer to the bytes to send. The bytes received are copy in
* this buffer.
* @param _count: number of bytes to send/receive.
* @param _mode: (optional) can be SPI_CONTINUE in case of multiple successive
* send or SPI_LAST to indicate the end of send.
* If the _mode is set to SPI_CONTINUE, keep the SPI instance alive.
* That means the CS pin is not reset. Be careful in case you use
* several CS pin.
*/
void SPIClass::transfer(uint8_t _pin, void *_buf, size_t _count, SPITransferMode _mode)
{
if ((_count == 0) || (_buf == NULL) || (_pin > NUM_DIGITAL_PINS)) {
return;
}
uint8_t idx = pinIdx(_pin, GET_IDX);
if (_pin != _CSPinConfig) {
if (idx >= NB_SPI_SETTINGS) {
return;
}
spi_init(&_spi, spiSettings[idx].clk,
spiSettings[idx].dMode,
spiSettings[idx].bOrder);
_CSPinConfig = _pin;
}
if ((_pin != CS_PIN_CONTROLLED_BY_USER) && (_spi.pin_ssel == NC)) {
digitalWrite(_pin, LOW);
}
spi_transfer(&_spi, ((uint8_t *)_buf), ((uint8_t *)_buf), _count,
SPI_TRANSFER_TIMEOUT, spiSettings[idx].noReceive);
if ((_pin != CS_PIN_CONTROLLED_BY_USER) && (_mode == SPI_LAST) && (_spi.pin_ssel == NC)) {
digitalWrite(_pin, HIGH);
}
}
/**
* @brief Transfer several bytes. One buffer contains the data to send and
* another one will contains the data received. begin() or
* beginTransaction() must be called at least once before.
* @param _pin: CS pin to select a device (optional). If the previous transfer
* used another CS pin then the SPI instance will be reconfigured.
* @param _bufout: pointer to the bytes to send.
* @param _bufin: pointer to the bytes received.
* @param _count: number of bytes to send/receive.
* @param _mode: (optional) can be SPI_CONTINUE in case of multiple successive
* send or SPI_LAST to indicate the end of send.
* If the _mode is set to SPI_CONTINUE, keep the SPI instance alive.
* That means the CS pin is not reset. Be careful in case you use
* several CS pin.
*/
void SPIClass::transfer(byte _pin, void *_bufout, void *_bufin, size_t _count, SPITransferMode _mode)
{
if ((_count == 0) || (_bufout == NULL) || (_bufin == NULL) || (_pin > NUM_DIGITAL_PINS)) {
return;
}
uint8_t idx = pinIdx(_pin, GET_IDX);
if (_pin != _CSPinConfig) {
if (idx >= NB_SPI_SETTINGS) {
return;
}
spi_init(&_spi, spiSettings[idx].clk,
spiSettings[idx].dMode,
spiSettings[idx].bOrder);
_CSPinConfig = _pin;
}
if ((_pin != CS_PIN_CONTROLLED_BY_USER) && (_spi.pin_ssel == NC)) {
digitalWrite(_pin, LOW);
}
spi_transfer(&_spi, ((uint8_t *)_bufout), ((uint8_t *)_bufin), _count,
SPI_TRANSFER_TIMEOUT, spiSettings[idx].noReceive);
if ((_pin != CS_PIN_CONTROLLED_BY_USER) && (_mode == SPI_LAST) && (_spi.pin_ssel == NC)) {
digitalWrite(_pin, HIGH);
}
}
/**
* @brief Not implemented.
*/
void SPIClass::usingInterrupt(uint8_t interruptNumber)
{
UNUSED(interruptNumber);
}
/**
* @brief Not implemented.
*/
void SPIClass::attachInterrupt(void)
{
// Should be enableInterrupt()
}
/**
* @brief Not implemented.
*/
void SPIClass::detachInterrupt(void)
{
// Should be disableInterrupt()
}