spi.c 26.6 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
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();
            }
349
350
351
            uint log_prescaler = (self->spi->Init.BaudRatePrescaler >> 3) + 1;
            uint baudrate = spi_clock >> log_prescaler;
            print(env, "SPI(%u, SPI.MASTER, baudrate=%u, prescaler=%u", spi_num, baudrate, 1 << log_prescaler);
Damien George's avatar
Damien George committed
352
        } else {
353
            print(env, "SPI(%u, SPI.SLAVE", spi_num);
Damien George's avatar
Damien George committed
354
        }
355
        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);
356
357
358
359
        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
360
361
362
    }
}

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

384
    // parse args
385
386
    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
387
388
389

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

392
393
394
395
396
397
398
399
400
401
402
403
404
    // 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
405
406
407
408
409
410
411
412
    }
    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; }
413
    else { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; }
Damien George's avatar
Damien George committed
414

415
416
417
418
419
420
421
422
    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
423
424
425
426
        init->CRCCalculation = SPI_CRCCALCULATION_DISABLED;
        init->CRCPolynomial = 0;
    } else {
        init->CRCCalculation = SPI_CRCCALCULATION_ENABLED;
427
        init->CRCPolynomial = mp_obj_get_int(args[10].u_obj);
Damien George's avatar
Damien George committed
428
429
430
    }

    // init the SPI bus
431
    spi_init(self->spi, init->NSS != SPI_NSS_SOFT);
Damien George's avatar
Damien George committed
432
433
434
435

    return mp_const_none;
}

436
437
438
439
440
441
442
/// \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.
443
444
445
446
447
448
449
450
///
/// 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.
451
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
452
453
454
455
    // check arguments
    mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);

    // get SPI number
456
    mp_int_t spi_id = mp_obj_get_int(args[0]) - 1;
Damien George's avatar
Damien George committed
457
458

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

476
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
477
478
479
480
    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);

481
482
/// \method deinit()
/// Turn off the SPI bus.
Damien George's avatar
Damien George committed
483
484
485
486
487
488
489
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);

490
491
492
493
494
495
496
/// \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`.
497
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
498
499
    // TODO assumes transmission size is 8-bits wide

500
501
502
503
    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
504

505
    // parse args
506
507
508
    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);
509
510

    // get the buffer to send from
Damien George's avatar
Damien George committed
511
    mp_buffer_info_t bufinfo;
512
    uint8_t data[1];
513
    pyb_buf_get_for_send(args[0].u_obj, &bufinfo, data);
Damien George's avatar
Damien George committed
514

515
    // send the data
516
517
518
519
520
521
522
523
524
    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
525
526

    if (status != HAL_OK) {
527
        mp_hal_raise(status);
Damien George's avatar
Damien George committed
528
529
530
531
    }

    return mp_const_none;
}
532
533
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_send_obj, 1, pyb_spi_send);

534
535
536
537
538
539
540
541
542
543
/// \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`.
544
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
545
546
    // TODO assumes transmission size is 8-bits wide

547
548
549
550
    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} },
    };
551
552

    // parse args
553
554
555
    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
556

557
558
    // get the buffer to receive into
    mp_buffer_info_t bufinfo;
559
    mp_obj_t o_ret = pyb_buf_get_for_recv(args[0].u_obj, &bufinfo);
560
561

    // receive the data
562
563
564
565
566
567
568
569
570
    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
571
572

    if (status != HAL_OK) {
573
        mp_hal_raise(status);
Damien George's avatar
Damien George committed
574
575
    }

576
577
    // return the received data
    if (o_ret == MP_OBJ_NULL) {
578
        return args[0].u_obj;
579
580
581
    } else {
        return mp_obj_str_builder_end(o_ret);
    }
Damien George's avatar
Damien George committed
582
}
583
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_recv_obj, 1, pyb_spi_recv);
Damien George's avatar
Damien George committed
584

585
586
587
588
589
590
591
592
593
594
595
/// \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.
596
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
597
598
    // TODO assumes transmission size is 8-bits wide

599
600
601
602
603
    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
604

605
    // parse args
606
607
608
    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);
609
610
611

    // get buffers to send from/receive to
    mp_buffer_info_t bufinfo_send;
Damien George's avatar
Damien George committed
612
    uint8_t data_send[1];
613
614
615
    mp_buffer_info_t bufinfo_recv;
    mp_obj_t o_ret;

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

        // get the buffer to receive into
626
        if (args[1].u_obj == MP_OBJ_NULL) {
627
628
629
630
631
632
            // 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
633
            mp_get_buffer_raise(args[1].u_obj, &bufinfo_recv, MP_BUFFER_WRITE);
634
635
636
            if (bufinfo_recv.len != bufinfo_send.len) {
                nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "recv must be same length as send"));
            }
637
638
            o_ret = MP_OBJ_NULL;
        }
Damien George's avatar
Damien George committed
639
640
    }

641
    // send and receive the data
642
643
644
645
646
647
648
649
650
    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
651
652

    if (status != HAL_OK) {
653
        mp_hal_raise(status);
Damien George's avatar
Damien George committed
654
655
    }

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

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
674
675
676
677
    /// \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
678
679
680
681
    { 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
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
    /* 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,
};