-
Notifications
You must be signed in to change notification settings - Fork 491
/
spi.h
404 lines (377 loc) · 12.4 KB
/
spi.h
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
/**
* \file Hardware SPI master driver
*
* Part of esp-open-rtos
*
* \copyright Ruslan V. Uss, 2016
* BSD Licensed as described in the file LICENSE
*/
#ifndef _ESP_SPI_H_
#define _ESP_SPI_H_
#include <stdbool.h>
#include <stdint.h>
#include "esp/spi_regs.h"
#include "esp/clocks.h"
/**
* Macro for use with spi_init and spi_set_frequency_div.
* SPI frequency = 80000000 / divider / count
* dvider must be in 1..8192 and count in 1..64
*/
#define SPI_GET_FREQ_DIV(divider, count) (((count) << 16) | ((divider) & 0xffff))
/**
* Predefinded SPI frequency dividers
*/
#define SPI_FREQ_DIV_125K SPI_GET_FREQ_DIV(64, 10) ///< 125kHz
#define SPI_FREQ_DIV_250K SPI_GET_FREQ_DIV(32, 10) ///< 250kHz
#define SPI_FREQ_DIV_500K SPI_GET_FREQ_DIV(16, 10) ///< 500kHz
#define SPI_FREQ_DIV_1M SPI_GET_FREQ_DIV(8, 10) ///< 1MHz
#define SPI_FREQ_DIV_2M SPI_GET_FREQ_DIV(4, 10) ///< 2MHz
#define SPI_FREQ_DIV_4M SPI_GET_FREQ_DIV(2, 10) ///< 4MHz
#define SPI_FREQ_DIV_8M SPI_GET_FREQ_DIV(5, 2) ///< 8MHz
#define SPI_FREQ_DIV_10M SPI_GET_FREQ_DIV(4, 2) ///< 10MHz
#define SPI_FREQ_DIV_20M SPI_GET_FREQ_DIV(2, 2) ///< 20MHz
#define SPI_FREQ_DIV_40M SPI_GET_FREQ_DIV(1, 2) ///< 40MHz
#define SPI_FREQ_DIV_80M SPI_GET_FREQ_DIV(1, 1) ///< 80MHz
/*
* Possible Data Structure of SPI Transaction
*
* [COMMAND]+[ADDRESS]+[DataOUT]+[DUMMYBITS]+[DataIN]
*
* [COMMAND]+[ADDRESS]+[DUMMYBITS]+[DataOUT]
*
*/
#ifdef __cplusplus
extern "C"
{
#endif
typedef enum _spi_mode_t {
SPI_MODE0 = 0, ///< CPOL = 0, CPHA = 0
SPI_MODE1, ///< CPOL = 0, CPHA = 1
SPI_MODE2, ///< CPOL = 1, CPHA = 0
SPI_MODE3 ///< CPOL = 1, CPHA = 1
} spi_mode_t;
typedef enum _spi_endianness_t {
SPI_LITTLE_ENDIAN = 0,
SPI_BIG_ENDIAN
} spi_endianness_t;
typedef enum _spi_word_size_t {
SPI_8BIT = 1, ///< 1 byte
SPI_16BIT = 2, ///< 2 bytes
SPI_32BIT = 4 ///< 4 bytes
} spi_word_size_t;
/**
* SPI bus settings
*/
typedef struct
{
spi_mode_t mode; ///< Bus mode
uint32_t freq_divider; ///< Bus frequency as a divider. See spi_init()
bool msb; ///< MSB first if true
spi_endianness_t endianness; ///< Bus byte order
bool minimal_pins; ///< Minimal set of pins if true. Spee spi_init()
} spi_settings_t;
/**
* \brief Initalize SPI bus
* Initalize specified SPI bus and setup appropriate pins:
* Bus 0:
* - MISO = GPIO 7
* - MOSI = GPIO 8
* - SCK = GPIO 6
* - CS0 = GPIO 11 (if minimal_pins is false)
* - HD = GPIO 9 (if minimal_pins is false)
* - WP = GPIO 10 (if minimal_pins is false)
* Bus 1:
* - MISO = GPIO 12
* - MOSI = GPIO 13
* - SCK = GPIO 14
* - CS0 = GPIO 15 (if minimal_pins is false)
* Note that system flash memory is on the bus 0!
* \param bus Bus ID: 0 - system, 1 - user
* \param mode Bus mode
* \param freq_divider SPI bus frequency divider, use SPI_GET_FREQ_DIV() or predefined value
* \param msb Bit order, MSB first if true
* \param endianness Byte order
* \param minimal_pins If true use the minimal set of pins: MISO, MOSI and SCK.
* \return false when error
*/
bool spi_init(uint8_t bus, spi_mode_t mode, uint32_t freq_divider, bool msb, spi_endianness_t endianness, bool minimal_pins);
/**
* \brief Initalize SPI bus
* spi_init() wrapper.
* Example:
*
* const spi_settings_t my_settings = {
* .mode = SPI_MODE0,
* .freq_divider = SPI_FREQ_DIV_4M,
* .msb = true,
* .endianness = SPI_LITTLE_ENDIAN,
* .minimal_pins = true
* }
* ....
* spi_settings_t old;
* spi_get_settings(1, &old); // save current settings
* //spi_init(1, SPI_MODE0, SPI_FREQ_DIV_4M, true, SPI_LITTLE_ENDIAN, true); // use own settings
* // or
* spi_set_settings(1, &my_settings);
* // some work with spi here
* ....
* spi_set_settings(1, &old); // restore saved settings
*
* \param s Pointer to the settings structure
* \return false when error
*/
static inline bool spi_set_settings(uint8_t bus, const spi_settings_t *s)
{
return spi_init(bus, s->mode, s->freq_divider, s->msb, s->endianness, s->minimal_pins);
}
/**
* \brief Get current settings of the SPI bus
* See spi_set_settings().
* \param bus Bus ID: 0 - system, 1 - user
* \param s Pointer to the structure that receives SPI bus settings
*/
void spi_get_settings(uint8_t bus, spi_settings_t *s);
/**
* \brief Set SPI bus mode
* \param bus Bus ID: 0 - system, 1 - user
* \param mode Bus mode
*/
void spi_set_mode(uint8_t bus, spi_mode_t mode);
/**
* \brief Get mode of the SPI bus
* \param bus Bus ID: 0 - system, 1 - user
* \return Bus mode
*/
spi_mode_t spi_get_mode(uint8_t bus);
/**
* \brief Set SPI bus frequency
* Examples:
*
* spi_set_frequency_div(1, SPI_FREQ_DIV_8M); // 8 MHz, predefined value
* ...
* spi_set_frequency_div(1, SPI_GET_FREQ_DIV(8, 10)); // divider = 8, count = 10,
* // frequency = 80000000 Hz / 8 / 10 = 1000000 Hz
*
* \param bus Bus ID: 0 - system, 1 - user
* \param divider Predivider of the system bus frequency (80MHz) in the 2 low
* bytes and period pulses count in the third byte. Please note that
* divider must be be in range 1..8192 and count in range 2..64. Use the
* macro SPI_GET_FREQ_DIV(divider, count) to get the correct parameter value.
*/
void spi_set_frequency_div(uint8_t bus, uint32_t divider);
/**
* \brief Get SPI bus frequency as a divider
* Example:
*
* uint32_t old_freq = spi_get_frequency_div(1);
* spi_set_frequency_div(1, SPI_FREQ_DIV_8M);
* ...
* spi_set_frequency_div(1, old_freq);
*
* \param bus Bus ID: 0 - system, 1 - user
* \return SPI frequency, as divider.
*/
inline uint32_t spi_get_frequency_div(uint8_t bus)
{
return (FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) |
(FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1);
}
/**
* \brief Get SPI bus frequency in Hz
* \param bus Bus ID: 0 - system, 1 - user
* \return SPI frequency, Hz
*/
inline uint32_t spi_get_frequency_hz(uint8_t bus)
{
return APB_CLK_FREQ /
(FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) /
(FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1);
}
/**
* \brief Set SPI bus bit order
* \param bus Bus ID: 0 - system, 1 - user
* \param msb Bit order, MSB first if true
*/
void spi_set_msb(uint8_t bus, bool msb);
/**
* \brief Get SPI bus bit order
* \param bus Bus ID: 0 - system, 1 - user
* \return msb Bit order, MSB first if true
*/
inline bool spi_get_msb(uint8_t bus)
{
return !(SPI(bus).CTRL0 & (SPI_CTRL0_WR_BIT_ORDER | SPI_CTRL0_RD_BIT_ORDER));
}
/**
* \brief Set SPI bus byte order
* \param bus Bus ID: 0 - system, 1 - user
* \param endianness Byte order
*/
void spi_set_endianness(uint8_t bus, spi_endianness_t endianness);
/**
* \brief Get SPI bus byte order
* \param bus Bus ID: 0 - system, 1 - user
* \return endianness Byte order
*/
inline spi_endianness_t spi_get_endianness(uint8_t bus)
{
return SPI(bus).USER0 & (SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER)
? SPI_BIG_ENDIAN
: SPI_LITTLE_ENDIAN;
}
/**
* \brief Transfer 8 bits over SPI
* \param bus Bus ID: 0 - system, 1 - user
* \param data Byte to send
* \return Received byte
*/
uint8_t spi_transfer_8(uint8_t bus, uint8_t data);
/**
* \brief Transfer 16 bits over SPI
* \param bus Bus ID: 0 - system, 1 - user
* \param data Word to send
* \return Received word
*/
uint16_t spi_transfer_16(uint8_t bus, uint16_t data);
/**
* \brief Transfer 32 bits over SPI
* \param bus Bus ID: 0 - system, 1 - user
* \param data dword to send
* \return Received dword
*/
uint32_t spi_transfer_32(uint8_t bus, uint32_t data);
/**
* \brief Transfer buffer of words over SPI
* Please note that the buffer size is in words, not in bytes!
* Example:
*
* const uint16_t out_buf[] = { 0xa0b0, 0xa1b1, 0xa2b2, 0xa3b3 };
* uint16_t in_buf[sizeof(out_buf)];
* spi_init(1, SPI_MODE1, SPI_FREQ_DIV_4M, true, SPI_BIG_ENDIAN, true);
* spi_transfer(1, out_buf, in_buf, sizeof(out_buf), SPI_16BIT); // len = 4 words * 2 bytes = 8 bytes
*
* \param bus Bus ID: 0 - system, 1 - user
* \param out_data Data to send.
* \param in_data Receive buffer. If NULL, received data will be lost.
* \param len Buffer size in words
* \param word_size Size of the word
* \return Transmitted/received words count
*/
size_t spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len, spi_word_size_t word_size);
/**
* \brief Add permanent command bits when transfert data over SPI
* Example:
*
* spi_set_command(1,1,0x01); // Set one command bit to 1
* for (uint8_t i = 0 ; i < x ; i++ ) {
* spi_transfer_8(1,0x55); // Send 1 bit command + 8 bits data x times
* }
* spi_clear_command(1); // Clear command
* spi_transfer_8(1,0x55); // Send 8 bits data
*
* \param bus Bus ID: 0 - system, 1 - user
* \param bits Number of bits (max: 16).
* \param data Command to send for each transfert.
*/
static inline void spi_set_command(uint8_t bus,uint8_t bits, uint16_t data)
{
if(!bits) return ;
SPI(bus).USER0 |= SPI_USER0_COMMAND ; //enable COMMAND function in SPI module
uint16_t command = data << (16-bits); //align command data to high bits
command = ((command>>8)&0xff) | ((command<<8)&0xff00); //swap byte order
SPI(bus).USER2 = SET_FIELD(SPI(bus).USER2, SPI_USER2_COMMAND_BITLEN, --bits);
SPI(bus).USER2 = SET_FIELD(SPI(bus).USER2, SPI_USER2_COMMAND_VALUE, command);
}
/**
* \brief Add permanent address bits when transfert data over SPI
* Example:
*
* spi_set_address(1,8,0x45); // Set one address byte to 0x45
* for (uint8_t i = 0 ; i < x ; i++ ) {
* spi_transfer_16(1,0xC584); // Send 16 bits address + 16 bits data x times
* }
* spi_clear_address(1); // Clear command
* spi_transfer_16(1,0x55); // Send 16 bits data
*
* \param bus Bus ID: 0 - system, 1 - user
* \param bits Number of bits (max: 32).
* \param data Address to send for each transfert.
*/
static inline void spi_set_address(uint8_t bus,uint8_t bits, uint32_t data)
{
if(!bits) return ;
SPI(bus).USER0 |= SPI_USER0_ADDR ; //enable ADDRess function in SPI module
SPI(bus).ADDR = data<<(32-bits) ; //align address data to high bits
SPI(bus).USER1 = SET_FIELD(SPI(bus).USER1, SPI_USER1_ADDR_BITLEN, --bits);
}
/**
* \brief Add permanent dummy bits when transfert data over SPI
* Example:
*
* spi_set_dummy_bits(1,4,false); // Set 4 dummy bit before Dout
* for (uint8_t i = 0 ; i < x ; i++ ) {
* spi_transfer_16(1,0xC584); // Send 4 bits dummy + 16 bits Dout x times
* }
* spi_set_dummy_bits(1,4,true); // Set 4 dummy bit between Dout and Din
* spi_transfer_8(1,0x55); // Send 8 bits Dout + 4 bits dummy + 8 bits Din
*
* \param bus Bus ID: 0 - system, 1 - user
* \param bits Number of bits
* \param pos Position of dummy bit, between Dout and Din if true.
*/
static inline void spi_set_dummy_bits(uint8_t bus, uint8_t bits, bool pos)
{
if(!bits) return ;
if(pos) { SPI(bus).USER0 |= SPI_USER0_MISO; } // Dummy bit will be between Dout and Din data if set
SPI(bus).USER0 |= SPI_USER0_DUMMY; //enable dummy bits
SPI(bus).USER1 = SET_FIELD(SPI(bus).USER1, SPI_USER1_DUMMY_CYCLELEN, --bits);
}
/**
* \brief Clear adress Bits
* \param bus Bus ID: 0 - system, 1 - user
*/
static inline void spi_clear_address(uint8_t bus)
{
SPI(bus).USER0 &= ~(SPI_USER0_ADDR) ;
}
/**
* \brief Clear command Bits
* \param bus Bus ID: 0 - system, 1 - user
*/
static inline void spi_clear_command(uint8_t bus)
{
SPI(bus).USER0 &= ~(SPI_USER0_COMMAND) ;
}
/**
* \brief Clear dummy Bits
* \param bus Bus ID: 0 - system, 1 - user
*/
static inline void spi_clear_dummy(uint8_t bus)
{
SPI(bus).USER0 &= ~(SPI_USER0_DUMMY | SPI_USER0_MISO);
}
/**
* \brief Send many 8 bits template over SPI
* \param bus Bus ID: 0 - system, 1 - user
* \param data Byte template (8 bits)
* \param repeats Copy byte number
*/
void spi_repeat_send_8(uint8_t bus, uint8_t data, int32_t repeats);
/**
* \brief Send many 16 bits template over SPI
* \param bus Bus ID: 0 - system, 1 - user
* \param data Word template (16 bits)
* \param repeats Copy word number
*/
void spi_repeat_send_16(uint8_t bus, uint16_t data, int32_t repeats);
/**
* \brief Send many 32 bits template over SPI
* \param bus Bus ID: 0 - system, 1 - user
* \param data Dualword template (32 bits)
* \param repeats Copy dword number
*/
void spi_repeat_send_32(uint8_t bus, uint32_t data, int32_t repeats);
#ifdef __cplusplus
}
#endif
#endif /* _ESP_SPI_H_ */