spi.c 26.8 KB
Newer Older
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
/*
 * This file is part of the Micro Python project, http://micropython.org/
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2013, 2014 Damien P. George
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

Damien George's avatar
Damien George committed
27
28
29
#include <stdio.h>
#include <string.h>

30
31
#include "py/nlr.h"
#include "py/runtime.h"
32
#include "irq.h"
Damien George's avatar
Damien George committed
33
34
#include "pin.h"
#include "genhdr/pins.h"
35
#include "bufhelper.h"
Damien George's avatar
Damien George committed
36
#include "spi.h"
37
#include MICROPY_HAL_H
Damien George's avatar
Damien George committed
38

39
40
41
42
43
44
45
46
47
48
/// \moduleref pyb
/// \class SPI - a master-driven serial protocol
///
/// SPI is a serial protocol that is driven by a master.  At the physical level
/// there are 3 lines: SCK, MOSI, MISO.
///
/// See usage model of I2C; SPI is very similar.  Main difference is
/// parameters to init the SPI bus:
///
///     from pyb import SPI
49
///     spi = SPI(1, SPI.MASTER, baudrate=600000, polarity=1, phase=0, crc=0x7)
50
51
///
/// Only required parameter is mode, SPI.MASTER or SPI.SLAVE.  Polarity can be
52
53
54
/// 0 or 1, and is the level the idle clock line sits at.  Phase can be 0 or 1
/// to sample data on the first or second clock edge respectively.  Crc can be
/// None for no CRC, or a polynomial specifier.
55
56
57
58
59
60
61
///
/// Additional method for SPI:
///
///     data = spi.send_recv(b'1234')        # send 4 bytes and receive 4 bytes
///     buf = bytearray(4)
///     spi.send_recv(b'1234', buf)          # send 4 bytes and receive 4 into buf
///     spi.send_recv(buf, buf)              # send/recv 4 bytes from/to buf
62

63
#if MICROPY_HW_ENABLE_SPI1
Damien George's avatar
Damien George committed
64
SPI_HandleTypeDef SPIHandle1 = {.Instance = NULL};
65
#endif
66
#if MICROPY_HW_ENABLE_SPI2
Damien George's avatar
Damien George committed
67
SPI_HandleTypeDef SPIHandle2 = {.Instance = NULL};
68
#endif
69
#if MICROPY_HW_ENABLE_SPI3
Damien George's avatar
Damien George committed
70
SPI_HandleTypeDef SPIHandle3 = {.Instance = NULL};
71
#endif
Damien George's avatar
Damien George committed
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
// Possible DMA configurations for SPI busses:
// SPI1_RX: DMA2_Stream0.CHANNEL_3 or DMA2_Stream2.CHANNEL_3
// SPI1_TX: DMA2_Stream3.CHANNEL_3 or DMA2_Stream5.CHANNEL_3
// SPI2_RX: DMA1_Stream3.CHANNEL_0
// SPI2_TX: DMA1_Stream4.CHANNEL_0
// SPI3_RX: DMA1_Stream0.CHANNEL_0 or DMA1_Stream2.CHANNEL_0
// SPI3_TX: DMA1_Stream5.CHANNEL_0 or DMA1_Stream7.CHANNEL_0

#define SPI1_DMA_CLK_ENABLE __DMA2_CLK_ENABLE
#define SPI1_RX_DMA_STREAM (DMA2_Stream2)
#define SPI1_TX_DMA_STREAM (DMA2_Stream5)
#define SPI1_DMA_CHANNEL (DMA_CHANNEL_3)
#define SPI1_RX_DMA_IRQN (DMA2_Stream2_IRQn)
#define SPI1_TX_DMA_IRQN (DMA2_Stream5_IRQn)
#define SPI1_RX_DMA_IRQ_HANDLER DMA2_Stream2_IRQHandler
#define SPI1_TX_DMA_IRQ_HANDLER DMA2_Stream5_IRQHandler

#define SPI2_DMA_CLK_ENABLE __DMA1_CLK_ENABLE
#define SPI2_RX_DMA_STREAM (DMA1_Stream3)
#define SPI2_TX_DMA_STREAM (DMA1_Stream4)
#define SPI2_DMA_CHANNEL (DMA_CHANNEL_0)
#define SPI2_RX_DMA_IRQN (DMA1_Stream3_IRQn)
#define SPI2_TX_DMA_IRQN (DMA1_Stream4_IRQn)
#define SPI2_RX_DMA_IRQ_HANDLER DMA1_Stream3_IRQHandler
#define SPI2_TX_DMA_IRQ_HANDLER DMA1_Stream4_IRQHandler

#define SPI3_DMA_CLK_ENABLE __DMA1_CLK_ENABLE
#define SPI3_RX_DMA_STREAM (DMA1_Stream2)
#define SPI3_TX_DMA_STREAM (DMA1_Stream7)
#define SPI3_DMA_CHANNEL (DMA_CHANNEL_0)
#define SPI3_RX_DMA_IRQN (DMA1_Stream2_IRQn)
#define SPI3_TX_DMA_IRQN (DMA1_Stream7_IRQn)
#define SPI3_RX_DMA_IRQ_HANDLER DMA1_Stream2_IRQHandler
#define SPI3_TX_DMA_IRQ_HANDLER DMA1_Stream7_IRQHandler

#if MICROPY_HW_ENABLE_SPI1
STATIC DMA_HandleTypeDef spi1_rx_dma_handle;
STATIC DMA_HandleTypeDef spi1_tx_dma_handle;
void SPI1_RX_DMA_IRQ_HANDLER(void) { HAL_DMA_IRQHandler(&spi1_rx_dma_handle); }
void SPI1_TX_DMA_IRQ_HANDLER(void) { HAL_DMA_IRQHandler(&spi1_tx_dma_handle); }
#endif

115
#if MICROPY_HW_ENABLE_SPI2
116
117
118
119
STATIC DMA_HandleTypeDef spi2_rx_dma_handle;
STATIC DMA_HandleTypeDef spi2_tx_dma_handle;
void SPI2_RX_DMA_IRQ_HANDLER(void) { HAL_DMA_IRQHandler(&spi2_rx_dma_handle); }
void SPI2_TX_DMA_IRQ_HANDLER(void) { HAL_DMA_IRQHandler(&spi2_tx_dma_handle); }
120
#endif
121
122
123
124
125
126
127
128

#if MICROPY_HW_ENABLE_SPI3
STATIC DMA_HandleTypeDef spi3_rx_dma_handle;
STATIC DMA_HandleTypeDef spi3_tx_dma_handle;
void SPI3_RX_DMA_IRQ_HANDLER(void) { HAL_DMA_IRQHandler(&spi3_rx_dma_handle); }
void SPI3_TX_DMA_IRQ_HANDLER(void) { HAL_DMA_IRQHandler(&spi3_tx_dma_handle); }
#endif

Damien George's avatar
Damien George committed
129
130
void spi_init0(void) {
    // reset the SPI handles
131
#if MICROPY_HW_ENABLE_SPI1
Damien George's avatar
Damien George committed
132
133
    memset(&SPIHandle1, 0, sizeof(SPI_HandleTypeDef));
    SPIHandle1.Instance = SPI1;
134
#endif
135
#if MICROPY_HW_ENABLE_SPI2
Damien George's avatar
Damien George committed
136
137
    memset(&SPIHandle2, 0, sizeof(SPI_HandleTypeDef));
    SPIHandle2.Instance = SPI2;
138
#endif
139
#if MICROPY_HW_ENABLE_SPI3
Damien George's avatar
Damien George committed
140
141
    memset(&SPIHandle3, 0, sizeof(SPI_HandleTypeDef));
    SPIHandle3.Instance = SPI3;
142
#endif
Damien George's avatar
Damien George committed
143
144
145
}

// TODO allow to take a list of pins to use
146
void spi_init(SPI_HandleTypeDef *spi, bool enable_nss_pin) {
147
148
149
150
    // init the GPIO lines
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
151
    GPIO_InitStructure.Pull = spi->Init.CLKPolarity == SPI_POLARITY_LOW ? GPIO_PULLDOWN : GPIO_PULLUP;
152

153
154
155
    DMA_HandleTypeDef *rx_dma, *tx_dma;
    IRQn_Type rx_dma_irqn, tx_dma_irqn;

Damien George's avatar
Damien George committed
156
    const pin_obj_t *pins[4];
157
    if (0) {
158
#if MICROPY_HW_ENABLE_SPI1
159
    } else if (spi->Instance == SPI1) {
Damien George's avatar
Damien George committed
160
161
162
163
164
        // X-skin: X5=PA4=SPI1_NSS, X6=PA5=SPI1_SCK, X7=PA6=SPI1_MISO, X8=PA7=SPI1_MOSI
        pins[0] = &pin_A4;
        pins[1] = &pin_A5;
        pins[2] = &pin_A6;
        pins[3] = &pin_A7;
165
        GPIO_InitStructure.Alternate = GPIO_AF5_SPI1;
166
167
        // enable the SPI clock
        __SPI1_CLK_ENABLE();
168
169
170
171
172
173
174
175
176
        // configure DMA
        SPI1_DMA_CLK_ENABLE();
        spi1_rx_dma_handle.Instance = SPI1_RX_DMA_STREAM;
        spi1_rx_dma_handle.Init.Channel = SPI1_DMA_CHANNEL;
        spi1_tx_dma_handle.Instance = SPI1_TX_DMA_STREAM;
        rx_dma = &spi1_rx_dma_handle;
        tx_dma = &spi1_tx_dma_handle;
        rx_dma_irqn = SPI1_RX_DMA_IRQN;
        tx_dma_irqn = SPI1_TX_DMA_IRQN;
177
#endif
178
#if MICROPY_HW_ENABLE_SPI2
179
    } else if (spi->Instance == SPI2) {
Damien George's avatar
Damien George committed
180
181
182
183
184
        // Y-skin: Y5=PB12=SPI2_NSS, Y6=PB13=SPI2_SCK, Y7=PB14=SPI2_MISO, Y8=PB15=SPI2_MOSI
        pins[0] = &pin_B12;
        pins[1] = &pin_B13;
        pins[2] = &pin_B14;
        pins[3] = &pin_B15;
185
        GPIO_InitStructure.Alternate = GPIO_AF5_SPI2;
186
187
        // enable the SPI clock
        __SPI2_CLK_ENABLE();
188
189
190
191
192
193
194
195
196
        // configure DMA
        SPI2_DMA_CLK_ENABLE();
        spi2_rx_dma_handle.Instance = SPI2_RX_DMA_STREAM;
        spi2_rx_dma_handle.Init.Channel = SPI2_DMA_CHANNEL;
        spi2_tx_dma_handle.Instance = SPI2_TX_DMA_STREAM;
        rx_dma = &spi2_rx_dma_handle;
        tx_dma = &spi2_tx_dma_handle;
        rx_dma_irqn = SPI2_RX_DMA_IRQN;
        tx_dma_irqn = SPI2_TX_DMA_IRQN;
197
#endif
Damien George's avatar
Damien George committed
198
#if MICROPY_HW_ENABLE_SPI3
199
    } else if (spi->Instance == SPI3) {
Damien George's avatar
Damien George committed
200
201
202
203
        pins[0] = &pin_A4;
        pins[1] = &pin_B3;
        pins[2] = &pin_B4;
        pins[3] = &pin_B5;
204
        GPIO_InitStructure.Alternate = GPIO_AF6_SPI3;
205
206
        // enable the SPI clock
        __SPI3_CLK_ENABLE();
207
208
209
210
211
212
213
214
215
        // configure DMA
        SPI3_DMA_CLK_ENABLE();
        spi3_rx_dma_handle.Instance = SPI3_RX_DMA_STREAM;
        spi3_rx_dma_handle.Init.Channel = SPI3_DMA_CHANNEL;
        spi3_tx_dma_handle.Instance = SPI3_TX_DMA_STREAM;
        rx_dma = &spi3_rx_dma_handle;
        tx_dma = &spi3_tx_dma_handle;
        rx_dma_irqn = SPI3_RX_DMA_IRQN;
        tx_dma_irqn = SPI3_TX_DMA_IRQN;
Damien George's avatar
Damien George committed
216
#endif
217
218
219
    } else {
        // SPI does not exist for this board (shouldn't get here, should be checked by caller)
        return;
Damien George's avatar
Damien George committed
220
221
    }

222
    for (uint i = (enable_nss_pin ? 0 : 1); i < 4; i++) {
Damien George's avatar
Damien George committed
223
224
225
226
        GPIO_InitStructure.Pin = pins[i]->pin_mask;
        HAL_GPIO_Init(pins[i]->gpio, &GPIO_InitStructure);
    }

227
    // init the SPI device
Damien George's avatar
Damien George committed
228
229
230
231
    if (HAL_SPI_Init(spi) != HAL_OK) {
        // init error
        // TODO should raise an exception, but this function is not necessarily going to be
        // called via Python, so may not be properly wrapped in an NLR handler
232
        printf("OSError: HAL_SPI_Init failed\n");
Damien George's avatar
Damien George committed
233
234
        return;
    }
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

    // configure DMA

    rx_dma->Init.Direction           = DMA_PERIPH_TO_MEMORY;
    rx_dma->Init.PeriphInc           = DMA_PINC_DISABLE;
    rx_dma->Init.MemInc              = DMA_MINC_ENABLE;
    rx_dma->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    rx_dma->Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
    rx_dma->Init.Mode                = DMA_NORMAL;
    rx_dma->Init.Priority            = DMA_PRIORITY_LOW;
    rx_dma->Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
    rx_dma->Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
    rx_dma->Init.MemBurst            = DMA_MBURST_INC4;
    rx_dma->Init.PeriphBurst         = DMA_PBURST_INC4;

    tx_dma->Init = rx_dma->Init; // copy rx settings
    tx_dma->Init.Direction = DMA_MEMORY_TO_PERIPH;

    __HAL_LINKDMA(spi, hdmarx, *rx_dma);
    HAL_DMA_DeInit(rx_dma);
    HAL_DMA_Init(rx_dma);

    __HAL_LINKDMA(spi, hdmatx, *tx_dma);
    HAL_DMA_DeInit(tx_dma);
    HAL_DMA_Init(tx_dma);

    // Enable the relevant IRQs.
    HAL_NVIC_SetPriority(rx_dma_irqn, 6, 0);
    HAL_NVIC_EnableIRQ(rx_dma_irqn);
    HAL_NVIC_SetPriority(tx_dma_irqn, 6, 0);
    HAL_NVIC_EnableIRQ(tx_dma_irqn);
Damien George's avatar
Damien George committed
266
267
268
269
}

void spi_deinit(SPI_HandleTypeDef *spi) {
    HAL_SPI_DeInit(spi);
270
271
272
    if (0) {
#if MICROPY_HW_ENABLE_SPI1
    } else if (spi->Instance == SPI1) {
273
274
        __SPI1_FORCE_RESET();
        __SPI1_RELEASE_RESET();
Damien George's avatar
Damien George committed
275
        __SPI1_CLK_DISABLE();
276
#endif
277
#if MICROPY_HW_ENABLE_SPI2
Damien George's avatar
Damien George committed
278
    } else if (spi->Instance == SPI2) {
279
280
        __SPI2_FORCE_RESET();
        __SPI2_RELEASE_RESET();
Damien George's avatar
Damien George committed
281
        __SPI2_CLK_DISABLE();
282
#endif
283
#if MICROPY_HW_ENABLE_SPI3
284
    } else if (spi->Instance == SPI3) {
285
286
        __SPI3_FORCE_RESET();
        __SPI3_RELEASE_RESET();
Damien George's avatar
Damien George committed
287
        __SPI3_CLK_DISABLE();
288
#endif
Damien George's avatar
Damien George committed
289
290
291
    }
}

292
293
294
295
296
297
298
299
300
301
302
STATIC HAL_StatusTypeDef spi_wait_dma_finished(SPI_HandleTypeDef *spi, uint32_t timeout) {
    uint32_t start = HAL_GetTick();
    while (HAL_SPI_GetState(spi) != HAL_SPI_STATE_READY) {
        if (HAL_GetTick() - start >= timeout) {
            return HAL_TIMEOUT;
        }
        __WFI();
    }
    return HAL_OK;
}

Damien George's avatar
Damien George committed
303
304
305
306
307
308
309
310
/******************************************************************************/
/* Micro Python bindings                                                      */

typedef struct _pyb_spi_obj_t {
    mp_obj_base_t base;
    SPI_HandleTypeDef *spi;
} pyb_spi_obj_t;

311
312
STATIC const pyb_spi_obj_t pyb_spi_obj[] = {
#if MICROPY_HW_ENABLE_SPI1
313
    {{&pyb_spi_type}, &SPIHandle1},
314
315
316
#else
    {{&pyb_spi_type}, NULL},
#endif
317
#if MICROPY_HW_ENABLE_SPI2
318
    {{&pyb_spi_type}, &SPIHandle2},
319
320
321
#else
    {{&pyb_spi_type}, NULL},
#endif
322
323
324
325
326
#if MICROPY_HW_ENABLE_SPI3
    {{&pyb_spi_type}, &SPIHandle3},
#else
    {{&pyb_spi_type}, NULL},
#endif
327
};
328
#define PYB_NUM_SPI MP_ARRAY_SIZE(pyb_spi_obj)
Damien George's avatar
Damien George committed
329

330
331
332
333
334
335
336
337
SPI_HandleTypeDef *spi_get_handle(mp_obj_t o) {
    if (!MP_OBJ_IS_TYPE(o, &pyb_spi_type)) {
        nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "expecting an SPI object"));
    }
    pyb_spi_obj_t *self = o;
    return self->spi;
}

338
STATIC void pyb_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
Damien George's avatar
Damien George committed
339
340
341
342
343
344
345
346
    pyb_spi_obj_t *self = self_in;

    uint spi_num;
    if (self->spi->Instance == SPI1) { spi_num = 1; }
    else if (self->spi->Instance == SPI2) { spi_num = 2; }
    else { spi_num = 3; }

    if (self->spi->State == HAL_SPI_STATE_RESET) {
347
        mp_printf(print, "SPI(%u)", spi_num);
Damien George's avatar
Damien George committed
348
349
350
351
352
353
354
355
356
357
358
    } else {
        if (self->spi->Init.Mode == SPI_MODE_MASTER) {
            // compute baudrate
            uint spi_clock;
            if (self->spi->Instance == SPI1) {
                // SPI1 is on APB2
                spi_clock = HAL_RCC_GetPCLK2Freq();
            } else {
                // SPI2 and SPI3 are on APB1
                spi_clock = HAL_RCC_GetPCLK1Freq();
            }
359
360
            uint log_prescaler = (self->spi->Init.BaudRatePrescaler >> 3) + 1;
            uint baudrate = spi_clock >> log_prescaler;
361
            mp_printf(print, "SPI(%u, SPI.MASTER, baudrate=%u, prescaler=%u", spi_num, baudrate, 1 << log_prescaler);
Damien George's avatar
Damien George committed
362
        } else {
363
            mp_printf(print, "SPI(%u, SPI.SLAVE", spi_num);
Damien George's avatar
Damien George committed
364
        }
365
        mp_printf(print, ", polarity=%u, phase=%u, bits=%u", self->spi->Init.CLKPolarity == SPI_POLARITY_LOW ? 0 : 1, self->spi->Init.CLKPhase == SPI_PHASE_1EDGE ? 0 : 1, self->spi->Init.DataSize == SPI_DATASIZE_8BIT ? 8 : 16);
366
        if (self->spi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLED) {
367
            mp_printf(print, ", crc=0x%x", self->spi->Init.CRCPolynomial);
368
        }
369
        mp_print_str(print, ")");
Damien George's avatar
Damien George committed
370
371
372
    }
}

373
/// \method init(mode, baudrate=328125, *, polarity=1, phase=0, bits=8, firstbit=SPI.MSB, ti=False, crc=None)
374
375
376
377
378
///
/// Initialise the SPI bus with the given parameters:
///
///   - `mode` must be either `SPI.MASTER` or `SPI.SLAVE`.
///   - `baudrate` is the SCK clock rate (only sensible for a master).
379
380
381
382
STATIC mp_obj_t pyb_spi_init_helper(const pyb_spi_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
    static const mp_arg_t allowed_args[] = {
        { MP_QSTR_mode,     MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
        { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 328125} },
383
        { MP_QSTR_prescaler, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
384
385
386
387
388
389
390
391
392
        { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT,  {.u_int = 1} },
        { MP_QSTR_phase,    MP_ARG_KW_ONLY | MP_ARG_INT,  {.u_int = 0} },
        { MP_QSTR_dir,      MP_ARG_KW_ONLY | MP_ARG_INT,  {.u_int = SPI_DIRECTION_2LINES} },
        { MP_QSTR_bits,     MP_ARG_KW_ONLY | MP_ARG_INT,  {.u_int = 8} },
        { MP_QSTR_nss,      MP_ARG_KW_ONLY | MP_ARG_INT,  {.u_int = SPI_NSS_SOFT} },
        { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT,  {.u_int = SPI_FIRSTBIT_MSB} },
        { MP_QSTR_ti,       MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
        { MP_QSTR_crc,      MP_ARG_KW_ONLY | MP_ARG_OBJ,  {.u_obj = mp_const_none} },
    };
Damien George's avatar
Damien George committed
393

394
    // parse args
395
396
    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
    mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
Damien George's avatar
Damien George committed
397
398
399

    // set the SPI configuration values
    SPI_InitTypeDef *init = &self->spi->Init;
400
    init->Mode = args[0].u_int;
Damien George's avatar
Damien George committed
401

402
403
404
405
406
407
408
409
410
411
412
413
414
    // configure the prescaler
    mp_uint_t br_prescale = args[2].u_int;
    if (br_prescale == 0xffffffff) {
        // prescaler not given, so select one that yields at most the requested baudrate
        mp_uint_t spi_clock;
        if (self->spi->Instance == SPI1) {
            // SPI1 is on APB2
            spi_clock = HAL_RCC_GetPCLK2Freq();
        } else {
            // SPI2 and SPI3 are on APB1
            spi_clock = HAL_RCC_GetPCLK1Freq();
        }
        br_prescale = spi_clock / args[1].u_int;
Damien George's avatar
Damien George committed
415
416
417
418
419
420
421
422
    }
    if (br_prescale <= 2) { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; }
    else if (br_prescale <= 4) { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; }
    else if (br_prescale <= 8) { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; }
    else if (br_prescale <= 16) { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; }
    else if (br_prescale <= 32) { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; }
    else if (br_prescale <= 64) { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; }
    else if (br_prescale <= 128) { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128; }
423
    else { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; }
Damien George's avatar
Damien George committed
424

425
426
427
428
429
430
431
432
    init->CLKPolarity = args[3].u_int == 0 ? SPI_POLARITY_LOW : SPI_POLARITY_HIGH;
    init->CLKPhase = args[4].u_int == 0 ? SPI_PHASE_1EDGE : SPI_PHASE_2EDGE;
    init->Direction = args[5].u_int;
    init->DataSize = (args[6].u_int == 16) ? SPI_DATASIZE_16BIT : SPI_DATASIZE_8BIT;
    init->NSS = args[7].u_int;
    init->FirstBit = args[8].u_int;
    init->TIMode = args[9].u_bool ? SPI_TIMODE_ENABLED : SPI_TIMODE_DISABLED;
    if (args[10].u_obj == mp_const_none) {
Damien George's avatar
Damien George committed
433
434
435
436
        init->CRCCalculation = SPI_CRCCALCULATION_DISABLED;
        init->CRCPolynomial = 0;
    } else {
        init->CRCCalculation = SPI_CRCCALCULATION_ENABLED;
437
        init->CRCPolynomial = mp_obj_get_int(args[10].u_obj);
Damien George's avatar
Damien George committed
438
439
440
    }

    // init the SPI bus
441
    spi_init(self->spi, init->NSS != SPI_NSS_SOFT);
Damien George's avatar
Damien George committed
442
443
444
445

    return mp_const_none;
}

446
447
448
449
450
451
452
/// \classmethod \constructor(bus, ...)
///
/// Construct an SPI object on the given bus.  `bus` can be 1 or 2.
/// With no additional parameters, the SPI object is created but not
/// initialised (it has the settings from the last initialisation of
/// the bus, if any).  If extra arguments are given, the bus is initialised.
/// See `init` for parameters of initialisation.
453
454
455
456
457
458
459
460
///
/// The physical pins of the SPI busses are:
///
///   - `SPI(1)` is on the X position: `(NSS, SCK, MISO, MOSI) = (X5, X6, X7, X8) = (PA4, PA5, PA6, PA7)`
///   - `SPI(2)` is on the Y position: `(NSS, SCK, MISO, MOSI) = (Y5, Y6, Y7, Y8) = (PB12, PB13, PB14, PB15)`
///
/// At the moment, the NSS pin is not used by the SPI driver and is free
/// for other use.
461
STATIC mp_obj_t pyb_spi_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
Damien George's avatar
Damien George committed
462
463
464
465
    // check arguments
    mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);

    // get SPI number
466
    mp_int_t spi_id = mp_obj_get_int(args[0]) - 1;
Damien George's avatar
Damien George committed
467
468

    // check SPI number
469
    if (!(0 <= spi_id && spi_id < PYB_NUM_SPI && pyb_spi_obj[spi_id].spi != NULL)) {
Damien George's avatar
Damien George committed
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "SPI bus %d does not exist", spi_id + 1));
    }

    // get SPI object
    const pyb_spi_obj_t *spi_obj = &pyb_spi_obj[spi_id];

    if (n_args > 1 || n_kw > 0) {
        // start the peripheral
        mp_map_t kw_args;
        mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
        pyb_spi_init_helper(spi_obj, n_args - 1, args + 1, &kw_args);
    }

    return (mp_obj_t)spi_obj;
}

486
STATIC mp_obj_t pyb_spi_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
Damien George's avatar
Damien George committed
487
488
489
490
    return pyb_spi_init_helper(args[0], n_args - 1, args + 1, kw_args);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_init_obj, 1, pyb_spi_init);

491
492
/// \method deinit()
/// Turn off the SPI bus.
Damien George's avatar
Damien George committed
493
494
495
496
497
498
499
STATIC mp_obj_t pyb_spi_deinit(mp_obj_t self_in) {
    pyb_spi_obj_t *self = self_in;
    spi_deinit(self->spi);
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_spi_deinit_obj, pyb_spi_deinit);

500
501
502
503
504
505
506
/// \method send(send, *, timeout=5000)
/// Send data on the bus:
///
///   - `send` is the data to send (an integer to send, or a buffer object).
///   - `timeout` is the timeout in milliseconds to wait for the send.
///
/// Return value: `None`.
507
STATIC mp_obj_t pyb_spi_send(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
Damien George's avatar
Damien George committed
508
509
    // TODO assumes transmission size is 8-bits wide

510
511
512
513
    static const mp_arg_t allowed_args[] = {
        { MP_QSTR_send,    MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} },
    };
Damien George's avatar
Damien George committed
514

515
    // parse args
516
517
518
    pyb_spi_obj_t *self = pos_args[0];
    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
    mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
519
520

    // get the buffer to send from
Damien George's avatar
Damien George committed
521
    mp_buffer_info_t bufinfo;
522
    uint8_t data[1];
523
    pyb_buf_get_for_send(args[0].u_obj, &bufinfo, data);
Damien George's avatar
Damien George committed
524

525
    // send the data
526
527
528
529
530
531
532
533
534
    HAL_StatusTypeDef status;
    if (query_irq() == IRQ_STATE_DISABLED) {
        status = HAL_SPI_Transmit(self->spi, bufinfo.buf, bufinfo.len, args[1].u_int);
    } else {
        status = HAL_SPI_Transmit_DMA(self->spi, bufinfo.buf, bufinfo.len);
        if (status == HAL_OK) {
            status = spi_wait_dma_finished(self->spi, args[1].u_int);
        }
    }
Damien George's avatar
Damien George committed
535
536

    if (status != HAL_OK) {
537
        mp_hal_raise(status);
Damien George's avatar
Damien George committed
538
539
540
541
    }

    return mp_const_none;
}
542
543
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_send_obj, 1, pyb_spi_send);

544
545
546
547
548
549
550
551
552
553
/// \method recv(recv, *, timeout=5000)
///
/// Receive data on the bus:
///
///   - `recv` can be an integer, which is the number of bytes to receive,
///     or a mutable buffer, which will be filled with received bytes.
///   - `timeout` is the timeout in milliseconds to wait for the receive.
///
/// Return value: if `recv` is an integer then a new buffer of the bytes received,
/// otherwise the same buffer that was passed in to `recv`.
554
STATIC mp_obj_t pyb_spi_recv(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
Damien George's avatar
Damien George committed
555
556
    // TODO assumes transmission size is 8-bits wide

557
558
559
560
    static const mp_arg_t allowed_args[] = {
        { MP_QSTR_recv,    MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} },
    };
561
562

    // parse args
563
564
565
    pyb_spi_obj_t *self = pos_args[0];
    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
    mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
Damien George's avatar
Damien George committed
566

567
    // get the buffer to receive into
568
569
    vstr_t vstr;
    mp_obj_t o_ret = pyb_buf_get_for_recv(args[0].u_obj, &vstr);
570
571

    // receive the data
572
573
    HAL_StatusTypeDef status;
    if (query_irq() == IRQ_STATE_DISABLED) {
574
        status = HAL_SPI_Receive(self->spi, (uint8_t*)vstr.buf, vstr.len, args[1].u_int);
575
    } else {
576
        status = HAL_SPI_Receive_DMA(self->spi, (uint8_t*)vstr.buf, vstr.len);
577
578
579
580
        if (status == HAL_OK) {
            status = spi_wait_dma_finished(self->spi, args[1].u_int);
        }
    }
Damien George's avatar
Damien George committed
581
582

    if (status != HAL_OK) {
583
        mp_hal_raise(status);
Damien George's avatar
Damien George committed
584
585
    }

586
    // return the received data
587
588
    if (o_ret != MP_OBJ_NULL) {
        return o_ret;
589
    } else {
590
        return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
591
    }
Damien George's avatar
Damien George committed
592
}
593
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_recv_obj, 1, pyb_spi_recv);
Damien George's avatar
Damien George committed
594

595
596
597
598
599
600
601
602
603
604
605
/// \method send_recv(send, recv=None, *, timeout=5000)
///
/// Send and receive data on the bus at the same time:
///
///   - `send` is the data to send (an integer to send, or a buffer object).
///   - `recv` is a mutable buffer which will be filled with received bytes.
///   It can be the same as `send`, or omitted.  If omitted, a new buffer will
///   be created.
///   - `timeout` is the timeout in milliseconds to wait for the receive.
///
/// Return value: the buffer with the received bytes.
606
STATIC mp_obj_t pyb_spi_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
Damien George's avatar
Damien George committed
607
608
    // TODO assumes transmission size is 8-bits wide

609
610
611
612
613
    static const mp_arg_t allowed_args[] = {
        { MP_QSTR_send,    MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_recv,    MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} },
    };
Damien George's avatar
Damien George committed
614

615
    // parse args
616
617
618
    pyb_spi_obj_t *self = pos_args[0];
    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
    mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
619
620
621

    // get buffers to send from/receive to
    mp_buffer_info_t bufinfo_send;
Damien George's avatar
Damien George committed
622
    uint8_t data_send[1];
623
    mp_buffer_info_t bufinfo_recv;
624
    vstr_t vstr_recv;
625
626
    mp_obj_t o_ret;

627
    if (args[0].u_obj == args[1].u_obj) {
628
        // same object for send and receive, it must be a r/w buffer
629
        mp_get_buffer_raise(args[0].u_obj, &bufinfo_send, MP_BUFFER_RW);
630
        bufinfo_recv = bufinfo_send;
631
        o_ret = args[0].u_obj;
Damien George's avatar
Damien George committed
632
    } else {
633
        // get the buffer to send from
634
        pyb_buf_get_for_send(args[0].u_obj, &bufinfo_send, data_send);
635
636

        // get the buffer to receive into
637
        if (args[1].u_obj == MP_OBJ_NULL) {
638
            // only send argument given, so create a fresh buffer of the send length
639
640
641
642
            vstr_init_len(&vstr_recv, bufinfo_send.len);
            bufinfo_recv.len = vstr_recv.len;
            bufinfo_recv.buf = vstr_recv.buf;
            o_ret = MP_OBJ_NULL;
643
644
        } else {
            // recv argument given
645
            mp_get_buffer_raise(args[1].u_obj, &bufinfo_recv, MP_BUFFER_WRITE);
646
647
648
            if (bufinfo_recv.len != bufinfo_send.len) {
                nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "recv must be same length as send"));
            }
649
            o_ret = args[1].u_obj;
650
        }
Damien George's avatar
Damien George committed
651
652
    }

653
    // send and receive the data
654
655
656
657
658
659
660
661
662
    HAL_StatusTypeDef status;
    if (query_irq() == IRQ_STATE_DISABLED) {
        status = HAL_SPI_TransmitReceive(self->spi, bufinfo_send.buf, bufinfo_recv.buf, bufinfo_send.len, args[2].u_int);
    } else {
        status = HAL_SPI_TransmitReceive_DMA(self->spi, bufinfo_send.buf, bufinfo_recv.buf, bufinfo_send.len);
        if (status == HAL_OK) {
            status = spi_wait_dma_finished(self->spi, args[2].u_int);
        }
    }
Damien George's avatar
Damien George committed
663
664

    if (status != HAL_OK) {
665
        mp_hal_raise(status);
Damien George's avatar
Damien George committed
666
667
    }

668
    // return the received data
669
670
    if (o_ret != MP_OBJ_NULL) {
        return o_ret;
671
    } else {
672
        return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr_recv);
673
    }
Damien George's avatar
Damien George committed
674
}
675
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_send_recv_obj, 1, pyb_spi_send_recv);
Damien George's avatar
Damien George committed
676
677
678
679
680
681
682
683
684
685

STATIC const mp_map_elem_t pyb_spi_locals_dict_table[] = {
    // instance methods
    { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_spi_init_obj },
    { MP_OBJ_NEW_QSTR(MP_QSTR_deinit), (mp_obj_t)&pyb_spi_deinit_obj },
    { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&pyb_spi_send_obj },
    { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&pyb_spi_recv_obj },
    { MP_OBJ_NEW_QSTR(MP_QSTR_send_recv), (mp_obj_t)&pyb_spi_send_recv_obj },

    // class constants
686
687
688
689
    /// \constant MASTER - for initialising the bus to master mode
    /// \constant SLAVE - for initialising the bus to slave mode
    /// \constant MSB - set the first bit to MSB
    /// \constant LSB - set the first bit to LSB
690
691
692
693
    { MP_OBJ_NEW_QSTR(MP_QSTR_MASTER), MP_OBJ_NEW_SMALL_INT(SPI_MODE_MASTER) },
    { MP_OBJ_NEW_QSTR(MP_QSTR_SLAVE),  MP_OBJ_NEW_SMALL_INT(SPI_MODE_SLAVE) },
    { MP_OBJ_NEW_QSTR(MP_QSTR_MSB),    MP_OBJ_NEW_SMALL_INT(SPI_FIRSTBIT_MSB) },
    { MP_OBJ_NEW_QSTR(MP_QSTR_LSB),    MP_OBJ_NEW_SMALL_INT(SPI_FIRSTBIT_LSB) },
Damien George's avatar
Damien George committed
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
    /* TODO
    { MP_OBJ_NEW_QSTR(MP_QSTR_DIRECTION_2LINES             ((uint32_t)0x00000000)
    { MP_OBJ_NEW_QSTR(MP_QSTR_DIRECTION_2LINES_RXONLY      SPI_CR1_RXONLY
    { MP_OBJ_NEW_QSTR(MP_QSTR_DIRECTION_1LINE              SPI_CR1_BIDIMODE
    { MP_OBJ_NEW_QSTR(MP_QSTR_NSS_SOFT                    SPI_CR1_SSM
    { MP_OBJ_NEW_QSTR(MP_QSTR_NSS_HARD_INPUT              ((uint32_t)0x00000000)
    { MP_OBJ_NEW_QSTR(MP_QSTR_NSS_HARD_OUTPUT             ((uint32_t)0x00040000)
    */
};

STATIC MP_DEFINE_CONST_DICT(pyb_spi_locals_dict, pyb_spi_locals_dict_table);

const mp_obj_type_t pyb_spi_type = {
    { &mp_type_type },
    .name = MP_QSTR_SPI,
    .print = pyb_spi_print,
    .make_new = pyb_spi_make_new,
    .locals_dict = (mp_obj_t)&pyb_spi_locals_dict,
};