spi.c 27.7 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
STATIC HAL_StatusTypeDef spi_wait_dma_finished(SPI_HandleTypeDef *spi, uint32_t timeout) {
293
294
295
    // Note: we can't use WFI to idle in this loop because the DMA completion
    // interrupt may occur before the WFI.  Hence we miss it and have to wait
    // until the next sys-tick (up to 1ms).
296
297
298
299
300
301
302
303
304
    uint32_t start = HAL_GetTick();
    while (HAL_SPI_GetState(spi) != HAL_SPI_STATE_READY) {
        if (HAL_GetTick() - start >= timeout) {
            return HAL_TIMEOUT;
        }
    }
    return HAL_OK;
}

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

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

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

331
332
333
334
335
336
337
338
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;
}

339
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
340
341
342
343
344
345
346
347
    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) {
348
        mp_printf(print, "SPI(%u)", spi_num);
Damien George's avatar
Damien George committed
349
350
351
352
353
354
355
356
357
358
359
    } 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();
            }
360
361
            uint log_prescaler = (self->spi->Init.BaudRatePrescaler >> 3) + 1;
            uint baudrate = spi_clock >> log_prescaler;
362
            mp_printf(print, "SPI(%u, SPI.MASTER, baudrate=%u, prescaler=%u", spi_num, baudrate, 1 << log_prescaler);
Damien George's avatar
Damien George committed
363
        } else {
364
            mp_printf(print, "SPI(%u, SPI.SLAVE", spi_num);
Damien George's avatar
Damien George committed
365
        }
366
        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);
367
        if (self->spi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLED) {
368
            mp_printf(print, ", crc=0x%x", self->spi->Init.CRCPolynomial);
369
        }
370
        mp_print_str(print, ")");
Damien George's avatar
Damien George committed
371
372
373
    }
}

374
/// \method init(mode, baudrate=328125, *, polarity=1, phase=0, bits=8, firstbit=SPI.MSB, ti=False, crc=None)
375
376
377
378
379
///
/// 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).
380
381
382
383
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} },
384
        { MP_QSTR_prescaler, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
385
386
387
388
389
390
391
392
393
        { 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
394

395
    // parse args
396
397
    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
398
399
400

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

403
404
405
406
407
408
409
410
411
412
413
414
415
    // 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
416
417
418
419
420
421
422
423
    }
    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; }
424
    else { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; }
Damien George's avatar
Damien George committed
425

426
427
428
429
430
431
432
433
    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
434
435
436
437
        init->CRCCalculation = SPI_CRCCALCULATION_DISABLED;
        init->CRCPolynomial = 0;
    } else {
        init->CRCCalculation = SPI_CRCCALCULATION_ENABLED;
438
        init->CRCPolynomial = mp_obj_get_int(args[10].u_obj);
Damien George's avatar
Damien George committed
439
440
441
    }

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

    return mp_const_none;
}

447
448
449
450
451
452
453
/// \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.
454
455
456
457
458
459
460
461
///
/// 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.
462
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
463
464
465
    // check arguments
    mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);

466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
    // work out SPI bus
    int spi_id = 0;
    if (MP_OBJ_IS_STR(args[0])) {
        const char *port = mp_obj_str_get_str(args[0]);
        if (0) {
        #ifdef MICROPY_HW_SPI1_NAME
        } else if (strcmp(port, MICROPY_HW_SPI1_NAME) == 0) {
            spi_id = 1;
        #endif
        #ifdef MICROPY_HW_SPI2_NAME
        } else if (strcmp(port, MICROPY_HW_SPI2_NAME) == 0) {
            spi_id = 2;
        #endif
        #ifdef MICROPY_HW_SPI3_NAME
        } else if (strcmp(port, MICROPY_HW_SPI3_NAME) == 0) {
            spi_id = 3;
        #endif
        } else {
            nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
                "SPI(%s) does not exist", port));
        }
    } else {
        spi_id = mp_obj_get_int(args[0]);
        if (spi_id < 1 || spi_id > MP_ARRAY_SIZE(pyb_spi_obj)
490
            || pyb_spi_obj[spi_id - 1].spi == NULL) {
491
492
493
            nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
                "SPI(%d) does not exist", spi_id));
        }
Damien George's avatar
Damien George committed
494
495
496
    }

    // get SPI object
497
    const pyb_spi_obj_t *spi_obj = &pyb_spi_obj[spi_id - 1];
Damien George's avatar
Damien George committed
498
499
500
501
502
503
504
505
506
507
508

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

509
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
510
511
512
513
    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);

514
515
/// \method deinit()
/// Turn off the SPI bus.
Damien George's avatar
Damien George committed
516
517
518
519
520
521
522
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);

523
524
525
526
527
528
529
/// \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`.
530
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
531
532
    // TODO assumes transmission size is 8-bits wide

533
534
535
536
    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
537

538
    // parse args
539
540
541
    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);
542
543

    // get the buffer to send from
Damien George's avatar
Damien George committed
544
    mp_buffer_info_t bufinfo;
545
    uint8_t data[1];
546
    pyb_buf_get_for_send(args[0].u_obj, &bufinfo, data);
Damien George's avatar
Damien George committed
547

548
    // send the data
549
550
551
552
553
554
555
556
557
    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
558
559

    if (status != HAL_OK) {
560
        mp_hal_raise(status);
Damien George's avatar
Damien George committed
561
562
563
564
    }

    return mp_const_none;
}
565
566
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_send_obj, 1, pyb_spi_send);

567
568
569
570
571
572
573
574
575
576
/// \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`.
577
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
578
579
    // TODO assumes transmission size is 8-bits wide

580
581
582
583
    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} },
    };
584
585

    // parse args
586
587
588
    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
589

590
    // get the buffer to receive into
591
592
    vstr_t vstr;
    mp_obj_t o_ret = pyb_buf_get_for_recv(args[0].u_obj, &vstr);
593
594

    // receive the data
595
596
    HAL_StatusTypeDef status;
    if (query_irq() == IRQ_STATE_DISABLED) {
597
        status = HAL_SPI_Receive(self->spi, (uint8_t*)vstr.buf, vstr.len, args[1].u_int);
598
    } else {
599
        status = HAL_SPI_Receive_DMA(self->spi, (uint8_t*)vstr.buf, vstr.len);
600
601
602
603
        if (status == HAL_OK) {
            status = spi_wait_dma_finished(self->spi, args[1].u_int);
        }
    }
Damien George's avatar
Damien George committed
604
605

    if (status != HAL_OK) {
606
        mp_hal_raise(status);
Damien George's avatar
Damien George committed
607
608
    }

609
    // return the received data
610
611
    if (o_ret != MP_OBJ_NULL) {
        return o_ret;
612
    } else {
613
        return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
614
    }
Damien George's avatar
Damien George committed
615
}
616
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_recv_obj, 1, pyb_spi_recv);
Damien George's avatar
Damien George committed
617

618
619
620
621
622
623
624
625
626
627
628
/// \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.
629
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
630
631
    // TODO assumes transmission size is 8-bits wide

632
633
634
635
636
    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
637

638
    // parse args
639
640
641
    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);
642
643
644

    // get buffers to send from/receive to
    mp_buffer_info_t bufinfo_send;
Damien George's avatar
Damien George committed
645
    uint8_t data_send[1];
646
    mp_buffer_info_t bufinfo_recv;
647
    vstr_t vstr_recv;
648
649
    mp_obj_t o_ret;

650
    if (args[0].u_obj == args[1].u_obj) {
651
        // same object for send and receive, it must be a r/w buffer
652
        mp_get_buffer_raise(args[0].u_obj, &bufinfo_send, MP_BUFFER_RW);
653
        bufinfo_recv = bufinfo_send;
654
        o_ret = args[0].u_obj;
Damien George's avatar
Damien George committed
655
    } else {
656
        // get the buffer to send from
657
        pyb_buf_get_for_send(args[0].u_obj, &bufinfo_send, data_send);
658
659

        // get the buffer to receive into
660
        if (args[1].u_obj == MP_OBJ_NULL) {
661
            // only send argument given, so create a fresh buffer of the send length
662
663
664
665
            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;
666
667
        } else {
            // recv argument given
668
            mp_get_buffer_raise(args[1].u_obj, &bufinfo_recv, MP_BUFFER_WRITE);
669
670
671
            if (bufinfo_recv.len != bufinfo_send.len) {
                nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "recv must be same length as send"));
            }
672
            o_ret = args[1].u_obj;
673
        }
Damien George's avatar
Damien George committed
674
675
    }

676
    // send and receive the data
677
678
679
680
681
682
683
684
685
    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
686
687

    if (status != HAL_OK) {
688
        mp_hal_raise(status);
Damien George's avatar
Damien George committed
689
690
    }

691
    // return the received data
692
693
    if (o_ret != MP_OBJ_NULL) {
        return o_ret;
694
    } else {
695
        return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr_recv);
696
    }
Damien George's avatar
Damien George committed
697
}
698
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_send_recv_obj, 1, pyb_spi_send_recv);
Damien George's avatar
Damien George committed
699
700
701
702
703
704
705
706
707
708

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
709
710
711
712
    /// \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
713
714
715
716
    { 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
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
    /* 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,
};