spi.c 26.4 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
#include "mpconfig.h"
Damien George's avatar
Damien George committed
31
32
33
34
35
#include "nlr.h"
#include "misc.h"
#include "qstr.h"
#include "obj.h"
#include "runtime.h"
36
#include "irq.h"
Damien George's avatar
Damien George committed
37
38
#include "pin.h"
#include "genhdr/pins.h"
39
#include "bufhelper.h"
Damien George's avatar
Damien George committed
40
#include "spi.h"
41
#include MICROPY_HAL_H
Damien George's avatar
Damien George committed
42

43
44
45
46
47
48
49
50
51
52
/// \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
53
///     spi = SPI(1, SPI.MASTER, baudrate=600000, polarity=1, phase=0, crc=0x7)
54
55
///
/// Only required parameter is mode, SPI.MASTER or SPI.SLAVE.  Polarity can be
56
57
58
/// 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.
59
60
61
62
63
64
65
///
/// 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
66

67
#if MICROPY_HW_ENABLE_SPI1
Damien George's avatar
Damien George committed
68
SPI_HandleTypeDef SPIHandle1 = {.Instance = NULL};
69
#endif
Damien George's avatar
Damien George committed
70
SPI_HandleTypeDef SPIHandle2 = {.Instance = NULL};
71
#if MICROPY_HW_ENABLE_SPI3
Damien George's avatar
Damien George committed
72
SPI_HandleTypeDef SPIHandle3 = {.Instance = NULL};
73
#endif
Damien George's avatar
Damien George committed
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
// 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

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); }

#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
Damien George's avatar
Damien George committed
135
136
    memset(&SPIHandle2, 0, sizeof(SPI_HandleTypeDef));
    SPIHandle2.Instance = SPI2;
137
#if MICROPY_HW_ENABLE_SPI3
Damien George's avatar
Damien George committed
138
139
    memset(&SPIHandle3, 0, sizeof(SPI_HandleTypeDef));
    SPIHandle3.Instance = SPI3;
140
#endif
Damien George's avatar
Damien George committed
141
142
143
}

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

151
152
153
    DMA_HandleTypeDef *rx_dma, *tx_dma;
    IRQn_Type rx_dma_irqn, tx_dma_irqn;

Damien George's avatar
Damien George committed
154
    const pin_obj_t *pins[4];
155
    if (0) {
156
#if MICROPY_HW_ENABLE_SPI1
157
    } else if (spi->Instance == SPI1) {
Damien George's avatar
Damien George committed
158
159
160
161
162
        // 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;
163
        GPIO_InitStructure.Alternate = GPIO_AF5_SPI1;
164
165
        // enable the SPI clock
        __SPI1_CLK_ENABLE();
166
167
168
169
170
171
172
173
174
        // 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;
175
#endif
176
    } else if (spi->Instance == SPI2) {
Damien George's avatar
Damien George committed
177
178
179
180
181
        // 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;
182
        GPIO_InitStructure.Alternate = GPIO_AF5_SPI2;
183
184
        // enable the SPI clock
        __SPI2_CLK_ENABLE();
185
186
187
188
189
190
191
192
193
        // 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;
Damien George's avatar
Damien George committed
194
#if MICROPY_HW_ENABLE_SPI3
195
    } else if (spi->Instance == SPI3) {
Damien George's avatar
Damien George committed
196
197
198
199
        pins[0] = &pin_A4;
        pins[1] = &pin_B3;
        pins[2] = &pin_B4;
        pins[3] = &pin_B5;
200
        GPIO_InitStructure.Alternate = GPIO_AF6_SPI3;
201
202
        // enable the SPI clock
        __SPI3_CLK_ENABLE();
203
204
205
206
207
208
209
210
211
        // 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
212
#endif
213
214
215
    } 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
216
217
    }

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

223
    // init the SPI device
Damien George's avatar
Damien George committed
224
225
226
227
    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
228
        printf("OSError: HAL_SPI_Init failed\n");
Damien George's avatar
Damien George committed
229
230
        return;
    }
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

    // 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
262
263
264
265
}

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

286
287
288
289
290
291
292
293
294
295
296
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
297
298
299
300
301
302
303
304
/******************************************************************************/
/* Micro Python bindings                                                      */

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

305
306
STATIC const pyb_spi_obj_t pyb_spi_obj[] = {
#if MICROPY_HW_ENABLE_SPI1
307
    {{&pyb_spi_type}, &SPIHandle1},
308
309
310
311
312
313
314
315
316
#else
    {{&pyb_spi_type}, NULL},
#endif
    {{&pyb_spi_type}, &SPIHandle2},
#if MICROPY_HW_ENABLE_SPI3
    {{&pyb_spi_type}, &SPIHandle3},
#else
    {{&pyb_spi_type}, NULL},
#endif
317
};
318
#define PYB_NUM_SPI MP_ARRAY_SIZE(pyb_spi_obj)
Damien George's avatar
Damien George committed
319

320
321
322
323
324
325
326
327
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;
}

Damien George's avatar
Damien George committed
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
STATIC void pyb_spi_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
    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) {
        print(env, "SPI(%u)", spi_num);
    } 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();
            }
            uint baudrate = spi_clock >> ((self->spi->Init.BaudRatePrescaler >> 3) + 1);
350
            print(env, "SPI(%u, SPI.MASTER, baudrate=%u", spi_num, baudrate);
Damien George's avatar
Damien George committed
351
        } else {
352
            print(env, "SPI(%u, SPI.SLAVE", spi_num);
Damien George's avatar
Damien George committed
353
        }
354
        print(env, ", 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);
355
356
357
358
        if (self->spi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLED) {
            print(env, ", crc=0x%x", self->spi->Init.CRCPolynomial);
        }
        print(env, ")");
Damien George's avatar
Damien George committed
359
360
361
    }
}

362
/// \method init(mode, baudrate=328125, *, polarity=1, phase=0, bits=8, firstbit=SPI.MSB, ti=False, crc=None)
363
364
365
366
367
///
/// 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).
368
369
370
371
372
373
374
375
376
377
378
379
380
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} },
        { 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
381

382
    // parse args
383
384
    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
385
386
387

    // set the SPI configuration values
    SPI_InitTypeDef *init = &self->spi->Init;
388
    init->Mode = args[0].u_int;
Damien George's avatar
Damien George committed
389
390
391
392
393
394
395
396
397
398
399

    // compute the baudrate prescaler from the requested baudrate
    // select a prescaler that yields at most the requested 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();
    }
400
    uint br_prescale = spi_clock / args[1].u_int;
Damien George's avatar
Damien George committed
401
402
403
404
405
406
407
    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; }
408
    else { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; }
Damien George's avatar
Damien George committed
409

410
411
412
413
414
415
416
417
    init->CLKPolarity = args[2].u_int == 0 ? SPI_POLARITY_LOW : SPI_POLARITY_HIGH;
    init->CLKPhase = args[3].u_int == 0 ? SPI_PHASE_1EDGE : SPI_PHASE_2EDGE;
    init->Direction = args[4].u_int;
    init->DataSize = (args[5].u_int == 16) ? SPI_DATASIZE_16BIT : SPI_DATASIZE_8BIT;
    init->NSS = args[6].u_int;
    init->FirstBit = args[7].u_int;
    init->TIMode = args[8].u_bool ? SPI_TIMODE_ENABLED : SPI_TIMODE_DISABLED;
    if (args[9].u_obj == mp_const_none) {
Damien George's avatar
Damien George committed
418
419
420
421
        init->CRCCalculation = SPI_CRCCALCULATION_DISABLED;
        init->CRCPolynomial = 0;
    } else {
        init->CRCCalculation = SPI_CRCCALCULATION_ENABLED;
422
        init->CRCPolynomial = mp_obj_get_int(args[9].u_obj);
Damien George's avatar
Damien George committed
423
424
425
    }

    // init the SPI bus
426
    spi_init(self->spi, init->NSS != SPI_NSS_SOFT);
Damien George's avatar
Damien George committed
427
428
429
430

    return mp_const_none;
}

431
432
433
434
435
436
437
/// \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.
438
439
440
441
442
443
444
445
///
/// 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.
446
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
447
448
449
450
    // check arguments
    mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);

    // get SPI number
451
    mp_int_t spi_id = mp_obj_get_int(args[0]) - 1;
Damien George's avatar
Damien George committed
452
453

    // check SPI number
454
    if (!(0 <= spi_id && spi_id < PYB_NUM_SPI && pyb_spi_obj[spi_id].spi != NULL)) {
Damien George's avatar
Damien George committed
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
        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;
}

471
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
472
473
474
475
    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);

476
477
/// \method deinit()
/// Turn off the SPI bus.
Damien George's avatar
Damien George committed
478
479
480
481
482
483
484
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);

485
486
487
488
489
490
491
/// \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`.
492
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
493
494
    // TODO assumes transmission size is 8-bits wide

495
496
497
498
    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
499

500
    // parse args
501
502
503
    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);
504
505

    // get the buffer to send from
Damien George's avatar
Damien George committed
506
    mp_buffer_info_t bufinfo;
507
    uint8_t data[1];
508
    pyb_buf_get_for_send(args[0].u_obj, &bufinfo, data);
Damien George's avatar
Damien George committed
509

510
    // send the data
511
512
513
514
515
516
517
518
519
    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
520
521

    if (status != HAL_OK) {
522
        mp_hal_raise(status);
Damien George's avatar
Damien George committed
523
524
525
526
    }

    return mp_const_none;
}
527
528
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_send_obj, 1, pyb_spi_send);

529
530
531
532
533
534
535
536
537
538
/// \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`.
539
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
540
541
    // TODO assumes transmission size is 8-bits wide

542
543
544
545
    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} },
    };
546
547

    // parse args
548
549
550
    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
551

552
553
    // get the buffer to receive into
    mp_buffer_info_t bufinfo;
554
    mp_obj_t o_ret = pyb_buf_get_for_recv(args[0].u_obj, &bufinfo);
555
556

    // receive the data
557
558
559
560
561
562
563
564
565
    HAL_StatusTypeDef status;
    if (query_irq() == IRQ_STATE_DISABLED) {
        status = HAL_SPI_Receive(self->spi, bufinfo.buf, bufinfo.len, args[1].u_int);
    } else {
        status = HAL_SPI_Receive_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
566
567

    if (status != HAL_OK) {
568
        mp_hal_raise(status);
Damien George's avatar
Damien George committed
569
570
    }

571
572
    // return the received data
    if (o_ret == MP_OBJ_NULL) {
573
        return args[0].u_obj;
574
575
576
    } else {
        return mp_obj_str_builder_end(o_ret);
    }
Damien George's avatar
Damien George committed
577
}
578
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_recv_obj, 1, pyb_spi_recv);
Damien George's avatar
Damien George committed
579

580
581
582
583
584
585
586
587
588
589
590
/// \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.
591
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
592
593
    // TODO assumes transmission size is 8-bits wide

594
595
596
597
598
    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
599

600
    // parse args
601
602
603
    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);
604
605
606

    // get buffers to send from/receive to
    mp_buffer_info_t bufinfo_send;
Damien George's avatar
Damien George committed
607
    uint8_t data_send[1];
608
609
610
    mp_buffer_info_t bufinfo_recv;
    mp_obj_t o_ret;

611
    if (args[0].u_obj == args[1].u_obj) {
612
        // same object for send and receive, it must be a r/w buffer
613
        mp_get_buffer_raise(args[0].u_obj, &bufinfo_send, MP_BUFFER_RW);
614
615
        bufinfo_recv = bufinfo_send;
        o_ret = MP_OBJ_NULL;
Damien George's avatar
Damien George committed
616
    } else {
617
        // get the buffer to send from
618
        pyb_buf_get_for_send(args[0].u_obj, &bufinfo_send, data_send);
619
620

        // get the buffer to receive into
621
        if (args[1].u_obj == MP_OBJ_NULL) {
622
623
624
625
626
627
            // only send argument given, so create a fresh buffer of the send length
            bufinfo_recv.len = bufinfo_send.len;
            bufinfo_recv.typecode = 'B';
            o_ret = mp_obj_str_builder_start(&mp_type_bytes, bufinfo_recv.len, (byte**)&bufinfo_recv.buf);
        } else {
            // recv argument given
628
            mp_get_buffer_raise(args[1].u_obj, &bufinfo_recv, MP_BUFFER_WRITE);
629
630
631
            if (bufinfo_recv.len != bufinfo_send.len) {
                nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "recv must be same length as send"));
            }
632
633
            o_ret = MP_OBJ_NULL;
        }
Damien George's avatar
Damien George committed
634
635
    }

636
    // send and receive the data
637
638
639
640
641
642
643
644
645
    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
646
647

    if (status != HAL_OK) {
648
        mp_hal_raise(status);
Damien George's avatar
Damien George committed
649
650
    }

651
652
    // return the received data
    if (o_ret == MP_OBJ_NULL) {
653
        return args[1].u_obj;
654
655
656
    } else {
        return mp_obj_str_builder_end(o_ret);
    }
Damien George's avatar
Damien George committed
657
}
658
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_send_recv_obj, 1, pyb_spi_send_recv);
Damien George's avatar
Damien George committed
659
660
661
662
663
664
665
666
667
668

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
669
670
671
672
    /// \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
673
674
675
676
    { 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
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
    /* 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,
};