Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 1 | #include <stdio.h> |
| 2 | #include <string.h> |
| 3 | |
| 4 | #include "stm32f4xx_hal.h" |
| 5 | |
Paul Sokolovsky | 9b71b16 | 2014-05-02 18:03:04 +0300 | [diff] [blame] | 6 | #include "mpconfig.h" |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 7 | #include "nlr.h" |
| 8 | #include "misc.h" |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 9 | #include "qstr.h" |
| 10 | #include "obj.h" |
| 11 | #include "runtime.h" |
| 12 | #include "pin.h" |
| 13 | #include "genhdr/pins.h" |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 14 | #include "bufhelper.h" |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 15 | #include "spi.h" |
| 16 | |
Damien George | 8d09640 | 2014-04-29 22:55:34 +0100 | [diff] [blame] | 17 | /// \moduleref pyb |
| 18 | /// \class SPI - a master-driven serial protocol |
| 19 | /// |
| 20 | /// SPI is a serial protocol that is driven by a master. At the physical level |
| 21 | /// there are 3 lines: SCK, MOSI, MISO. |
| 22 | /// |
| 23 | /// See usage model of I2C; SPI is very similar. Main difference is |
| 24 | /// parameters to init the SPI bus: |
| 25 | /// |
| 26 | /// from pyb import SPI |
| 27 | /// spi = SPI(1, SPI.MASTER, baudrate=600000, polarity=1, phase=1, crc=0x7) |
| 28 | /// |
| 29 | /// Only required parameter is mode, SPI.MASTER or SPI.SLAVE. Polarity can be |
| 30 | /// 0 or 1, and is the level the idle clock line sits at. Phase can be 1 or 2 |
| 31 | /// for number of edges. Crc can be None for no CRC, or a polynomial specifier. |
| 32 | /// |
| 33 | /// Additional method for SPI: |
| 34 | /// |
| 35 | /// data = spi.send_recv(b'1234') # send 4 bytes and receive 4 bytes |
| 36 | /// buf = bytearray(4) |
| 37 | /// spi.send_recv(b'1234', buf) # send 4 bytes and receive 4 into buf |
| 38 | /// spi.send_recv(buf, buf) # send/recv 4 bytes from/to buf |
Damien George | 34f01c2 | 2014-04-21 01:59:43 +0100 | [diff] [blame] | 39 | |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 40 | #if MICROPY_HW_ENABLE_SPI1 |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 41 | SPI_HandleTypeDef SPIHandle1 = {.Instance = NULL}; |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 42 | #endif |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 43 | SPI_HandleTypeDef SPIHandle2 = {.Instance = NULL}; |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 44 | #if MICROPY_HW_ENABLE_SPI3 |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 45 | SPI_HandleTypeDef SPIHandle3 = {.Instance = NULL}; |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 46 | #endif |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 47 | |
| 48 | void spi_init0(void) { |
| 49 | // reset the SPI handles |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 50 | #if MICROPY_HW_ENABLE_SPI1 |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 51 | memset(&SPIHandle1, 0, sizeof(SPI_HandleTypeDef)); |
| 52 | SPIHandle1.Instance = SPI1; |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 53 | #endif |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 54 | memset(&SPIHandle2, 0, sizeof(SPI_HandleTypeDef)); |
| 55 | SPIHandle2.Instance = SPI2; |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 56 | #if MICROPY_HW_ENABLE_SPI3 |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 57 | memset(&SPIHandle3, 0, sizeof(SPI_HandleTypeDef)); |
| 58 | SPIHandle3.Instance = SPI3; |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 59 | #endif |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 60 | } |
| 61 | |
| 62 | // TODO allow to take a list of pins to use |
| 63 | void spi_init(SPI_HandleTypeDef *spi) { |
Damien George | 57e4158 | 2014-04-20 00:30:09 +0100 | [diff] [blame] | 64 | // init the GPIO lines |
| 65 | GPIO_InitTypeDef GPIO_InitStructure; |
| 66 | GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; |
| 67 | GPIO_InitStructure.Speed = GPIO_SPEED_FAST; |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 68 | GPIO_InitStructure.Pull = spi->Init.CLKPolarity == SPI_POLARITY_LOW ? GPIO_PULLDOWN : GPIO_PULLUP; |
Damien George | 57e4158 | 2014-04-20 00:30:09 +0100 | [diff] [blame] | 69 | |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 70 | const pin_obj_t *pins[4]; |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 71 | if (0) { |
Dave Hylands | f70630c | 2014-04-20 00:06:03 -0700 | [diff] [blame] | 72 | #if MICROPY_HW_ENABLE_SPI1 |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 73 | } else if (spi->Instance == SPI1) { |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 74 | // X-skin: X5=PA4=SPI1_NSS, X6=PA5=SPI1_SCK, X7=PA6=SPI1_MISO, X8=PA7=SPI1_MOSI |
| 75 | pins[0] = &pin_A4; |
| 76 | pins[1] = &pin_A5; |
| 77 | pins[2] = &pin_A6; |
| 78 | pins[3] = &pin_A7; |
Damien George | 57e4158 | 2014-04-20 00:30:09 +0100 | [diff] [blame] | 79 | GPIO_InitStructure.Alternate = GPIO_AF5_SPI1; |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 80 | // enable the SPI clock |
| 81 | __SPI1_CLK_ENABLE(); |
Dave Hylands | f70630c | 2014-04-20 00:06:03 -0700 | [diff] [blame] | 82 | #endif |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 83 | } else if (spi->Instance == SPI2) { |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 84 | // Y-skin: Y5=PB12=SPI2_NSS, Y6=PB13=SPI2_SCK, Y7=PB14=SPI2_MISO, Y8=PB15=SPI2_MOSI |
| 85 | pins[0] = &pin_B12; |
| 86 | pins[1] = &pin_B13; |
| 87 | pins[2] = &pin_B14; |
| 88 | pins[3] = &pin_B15; |
Damien George | 57e4158 | 2014-04-20 00:30:09 +0100 | [diff] [blame] | 89 | GPIO_InitStructure.Alternate = GPIO_AF5_SPI2; |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 90 | // enable the SPI clock |
| 91 | __SPI2_CLK_ENABLE(); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 92 | #if MICROPY_HW_ENABLE_SPI3 |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 93 | } else if (spi->Instance == SPI3) { |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 94 | pins[0] = &pin_A4; |
| 95 | pins[1] = &pin_B3; |
| 96 | pins[2] = &pin_B4; |
| 97 | pins[3] = &pin_B5; |
Damien George | 57e4158 | 2014-04-20 00:30:09 +0100 | [diff] [blame] | 98 | GPIO_InitStructure.Alternate = GPIO_AF6_SPI3; |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 99 | // enable the SPI clock |
| 100 | __SPI3_CLK_ENABLE(); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 101 | #endif |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 102 | } else { |
| 103 | // SPI does not exist for this board (shouldn't get here, should be checked by caller) |
| 104 | return; |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 105 | } |
| 106 | |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 107 | for (uint i = 0; i < 4; i++) { |
| 108 | GPIO_InitStructure.Pin = pins[i]->pin_mask; |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 109 | HAL_GPIO_Init(pins[i]->gpio, &GPIO_InitStructure); |
| 110 | } |
| 111 | |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 112 | // init the SPI device |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 113 | if (HAL_SPI_Init(spi) != HAL_OK) { |
| 114 | // init error |
| 115 | // TODO should raise an exception, but this function is not necessarily going to be |
| 116 | // called via Python, so may not be properly wrapped in an NLR handler |
| 117 | printf("HardwareError: HAL_SPI_Init failed\n"); |
| 118 | return; |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | void spi_deinit(SPI_HandleTypeDef *spi) { |
| 123 | HAL_SPI_DeInit(spi); |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 124 | if (0) { |
| 125 | #if MICROPY_HW_ENABLE_SPI1 |
| 126 | } else if (spi->Instance == SPI1) { |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 127 | __SPI1_FORCE_RESET(); |
| 128 | __SPI1_RELEASE_RESET(); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 129 | __SPI1_CLK_DISABLE(); |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 130 | #endif |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 131 | } else if (spi->Instance == SPI2) { |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 132 | __SPI2_FORCE_RESET(); |
| 133 | __SPI2_RELEASE_RESET(); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 134 | __SPI2_CLK_DISABLE(); |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 135 | #if MICROPY_HW_ENABLE_SPI3 |
Dave Hylands | f70630c | 2014-04-20 00:06:03 -0700 | [diff] [blame] | 136 | } else if (spi->Instance == SPI3) { |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 137 | __SPI3_FORCE_RESET(); |
| 138 | __SPI3_RELEASE_RESET(); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 139 | __SPI3_CLK_DISABLE(); |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 140 | #endif |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 141 | } |
| 142 | } |
| 143 | |
| 144 | /******************************************************************************/ |
| 145 | /* Micro Python bindings */ |
| 146 | |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 147 | typedef struct _pyb_spi_obj_t { |
| 148 | mp_obj_base_t base; |
| 149 | SPI_HandleTypeDef *spi; |
| 150 | } pyb_spi_obj_t; |
| 151 | |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 152 | STATIC const pyb_spi_obj_t pyb_spi_obj[] = { |
| 153 | #if MICROPY_HW_ENABLE_SPI1 |
Dave Hylands | f70630c | 2014-04-20 00:06:03 -0700 | [diff] [blame] | 154 | {{&pyb_spi_type}, &SPIHandle1}, |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 155 | #else |
| 156 | {{&pyb_spi_type}, NULL}, |
| 157 | #endif |
| 158 | {{&pyb_spi_type}, &SPIHandle2}, |
| 159 | #if MICROPY_HW_ENABLE_SPI3 |
| 160 | {{&pyb_spi_type}, &SPIHandle3}, |
| 161 | #else |
| 162 | {{&pyb_spi_type}, NULL}, |
| 163 | #endif |
Dave Hylands | f70630c | 2014-04-20 00:06:03 -0700 | [diff] [blame] | 164 | }; |
Damien George | 6d3c5e4 | 2014-04-26 10:47:29 +0100 | [diff] [blame] | 165 | #define PYB_NUM_SPI ARRAY_SIZE(pyb_spi_obj) |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 166 | |
| 167 | STATIC void pyb_spi_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { |
| 168 | pyb_spi_obj_t *self = self_in; |
| 169 | |
| 170 | uint spi_num; |
| 171 | if (self->spi->Instance == SPI1) { spi_num = 1; } |
| 172 | else if (self->spi->Instance == SPI2) { spi_num = 2; } |
| 173 | else { spi_num = 3; } |
| 174 | |
| 175 | if (self->spi->State == HAL_SPI_STATE_RESET) { |
| 176 | print(env, "SPI(%u)", spi_num); |
| 177 | } else { |
| 178 | if (self->spi->Init.Mode == SPI_MODE_MASTER) { |
| 179 | // compute baudrate |
| 180 | uint spi_clock; |
| 181 | if (self->spi->Instance == SPI1) { |
| 182 | // SPI1 is on APB2 |
| 183 | spi_clock = HAL_RCC_GetPCLK2Freq(); |
| 184 | } else { |
| 185 | // SPI2 and SPI3 are on APB1 |
| 186 | spi_clock = HAL_RCC_GetPCLK1Freq(); |
| 187 | } |
| 188 | uint baudrate = spi_clock >> ((self->spi->Init.BaudRatePrescaler >> 3) + 1); |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 189 | print(env, "SPI(%u, SPI.MASTER, baudrate=%u", spi_num, baudrate); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 190 | } else { |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 191 | print(env, "SPI(%u, SPI.SLAVE", spi_num); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 192 | } |
Damien George | 3771a09 | 2014-04-21 01:14:14 +0100 | [diff] [blame] | 193 | print(env, ", polarity=%u, phase=%u, bits=%u", self->spi->Init.CLKPolarity == SPI_POLARITY_LOW ? 0 : 1, self->spi->Init.CLKPhase == SPI_PHASE_1EDGE ? 1 : 2, self->spi->Init.DataSize == SPI_DATASIZE_8BIT ? 8 : 16); |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 194 | if (self->spi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLED) { |
| 195 | print(env, ", crc=0x%x", self->spi->Init.CRCPolynomial); |
| 196 | } |
| 197 | print(env, ")"); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 198 | } |
| 199 | } |
| 200 | |
Damien George | baa2afb | 2014-05-03 16:42:27 +0100 | [diff] [blame] | 201 | /// \method init(mode, baudrate=328125, *, polarity=1, phase=1, bits=8, firstbit=SPI.MSB, ti=False, crc=None) |
Damien George | 8d09640 | 2014-04-29 22:55:34 +0100 | [diff] [blame] | 202 | /// |
| 203 | /// Initialise the SPI bus with the given parameters: |
| 204 | /// |
| 205 | /// - `mode` must be either `SPI.MASTER` or `SPI.SLAVE`. |
| 206 | /// - `baudrate` is the SCK clock rate (only sensible for a master). |
Damien George | dbc81df | 2014-04-26 11:19:17 +0100 | [diff] [blame] | 207 | STATIC const mp_arg_t pyb_spi_init_args[] = { |
| 208 | { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, |
| 209 | { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 328125} }, |
| 210 | { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, |
| 211 | { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, |
| 212 | { MP_QSTR_dir, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = SPI_DIRECTION_2LINES} }, |
| 213 | { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, |
| 214 | { MP_QSTR_nss, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = SPI_NSS_SOFT} }, |
| 215 | { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = SPI_FIRSTBIT_MSB} }, |
| 216 | { MP_QSTR_ti, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, |
| 217 | { MP_QSTR_crc, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 218 | }; |
Damien George | dbc81df | 2014-04-26 11:19:17 +0100 | [diff] [blame] | 219 | #define PYB_SPI_INIT_NUM_ARGS ARRAY_SIZE(pyb_spi_init_args) |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 220 | |
| 221 | STATIC mp_obj_t pyb_spi_init_helper(const pyb_spi_obj_t *self, uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 222 | // parse args |
Damien George | dbc81df | 2014-04-26 11:19:17 +0100 | [diff] [blame] | 223 | mp_arg_val_t vals[PYB_SPI_INIT_NUM_ARGS]; |
| 224 | mp_arg_parse_all(n_args, args, kw_args, PYB_SPI_INIT_NUM_ARGS, pyb_spi_init_args, vals); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 225 | |
| 226 | // set the SPI configuration values |
| 227 | SPI_InitTypeDef *init = &self->spi->Init; |
| 228 | init->Mode = vals[0].u_int; |
| 229 | |
| 230 | // compute the baudrate prescaler from the requested baudrate |
| 231 | // select a prescaler that yields at most the requested baudrate |
| 232 | uint spi_clock; |
| 233 | if (self->spi->Instance == SPI1) { |
| 234 | // SPI1 is on APB2 |
| 235 | spi_clock = HAL_RCC_GetPCLK2Freq(); |
| 236 | } else { |
| 237 | // SPI2 and SPI3 are on APB1 |
| 238 | spi_clock = HAL_RCC_GetPCLK1Freq(); |
| 239 | } |
| 240 | uint br_prescale = spi_clock / vals[1].u_int; |
| 241 | if (br_prescale <= 2) { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; } |
| 242 | else if (br_prescale <= 4) { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; } |
| 243 | else if (br_prescale <= 8) { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; } |
| 244 | else if (br_prescale <= 16) { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; } |
| 245 | else if (br_prescale <= 32) { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; } |
| 246 | else if (br_prescale <= 64) { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; } |
| 247 | else if (br_prescale <= 128) { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128; } |
Damien George | fd6925b | 2014-04-20 01:25:58 +0100 | [diff] [blame] | 248 | else { init->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; } |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 249 | |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 250 | init->CLKPolarity = vals[2].u_int == 0 ? SPI_POLARITY_LOW : SPI_POLARITY_HIGH; |
| 251 | init->CLKPhase = vals[3].u_int == 1 ? SPI_PHASE_1EDGE : SPI_PHASE_2EDGE; |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 252 | init->Direction = vals[4].u_int; |
| 253 | init->DataSize = (vals[5].u_int == 16) ? SPI_DATASIZE_16BIT : SPI_DATASIZE_8BIT; |
| 254 | init->NSS = vals[6].u_int; |
| 255 | init->FirstBit = vals[7].u_int; |
| 256 | init->TIMode = vals[8].u_bool ? SPI_TIMODE_ENABLED : SPI_TIMODE_DISABLED; |
| 257 | if (vals[9].u_obj == mp_const_none) { |
| 258 | init->CRCCalculation = SPI_CRCCALCULATION_DISABLED; |
| 259 | init->CRCPolynomial = 0; |
| 260 | } else { |
| 261 | init->CRCCalculation = SPI_CRCCALCULATION_ENABLED; |
| 262 | init->CRCPolynomial = mp_obj_get_int(vals[9].u_obj); |
| 263 | } |
| 264 | |
| 265 | // init the SPI bus |
| 266 | spi_init(self->spi); |
| 267 | |
| 268 | return mp_const_none; |
| 269 | } |
| 270 | |
Damien George | 8d09640 | 2014-04-29 22:55:34 +0100 | [diff] [blame] | 271 | /// \classmethod \constructor(bus, ...) |
| 272 | /// |
| 273 | /// Construct an SPI object on the given bus. `bus` can be 1 or 2. |
| 274 | /// With no additional parameters, the SPI object is created but not |
| 275 | /// initialised (it has the settings from the last initialisation of |
| 276 | /// the bus, if any). If extra arguments are given, the bus is initialised. |
| 277 | /// See `init` for parameters of initialisation. |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 278 | STATIC mp_obj_t pyb_spi_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { |
| 279 | // check arguments |
| 280 | mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); |
| 281 | |
| 282 | // get SPI number |
| 283 | machine_int_t spi_id = mp_obj_get_int(args[0]) - 1; |
| 284 | |
| 285 | // check SPI number |
Damien George | ff91156 | 2014-04-20 19:06:15 +0100 | [diff] [blame] | 286 | if (!(0 <= spi_id && spi_id < PYB_NUM_SPI && pyb_spi_obj[spi_id].spi != NULL)) { |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 287 | nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "SPI bus %d does not exist", spi_id + 1)); |
| 288 | } |
| 289 | |
| 290 | // get SPI object |
| 291 | const pyb_spi_obj_t *spi_obj = &pyb_spi_obj[spi_id]; |
| 292 | |
| 293 | if (n_args > 1 || n_kw > 0) { |
| 294 | // start the peripheral |
| 295 | mp_map_t kw_args; |
| 296 | mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); |
| 297 | pyb_spi_init_helper(spi_obj, n_args - 1, args + 1, &kw_args); |
| 298 | } |
| 299 | |
| 300 | return (mp_obj_t)spi_obj; |
| 301 | } |
| 302 | |
| 303 | STATIC mp_obj_t pyb_spi_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { |
| 304 | return pyb_spi_init_helper(args[0], n_args - 1, args + 1, kw_args); |
| 305 | } |
| 306 | STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_init_obj, 1, pyb_spi_init); |
| 307 | |
Damien George | 8d09640 | 2014-04-29 22:55:34 +0100 | [diff] [blame] | 308 | /// \method deinit() |
| 309 | /// Turn off the SPI bus. |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 310 | STATIC mp_obj_t pyb_spi_deinit(mp_obj_t self_in) { |
| 311 | pyb_spi_obj_t *self = self_in; |
| 312 | spi_deinit(self->spi); |
| 313 | return mp_const_none; |
| 314 | } |
| 315 | STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_spi_deinit_obj, pyb_spi_deinit); |
| 316 | |
Damien George | 8d09640 | 2014-04-29 22:55:34 +0100 | [diff] [blame] | 317 | /// \method send(send, *, timeout=5000) |
| 318 | /// Send data on the bus: |
| 319 | /// |
| 320 | /// - `send` is the data to send (an integer to send, or a buffer object). |
| 321 | /// - `timeout` is the timeout in milliseconds to wait for the send. |
| 322 | /// |
| 323 | /// Return value: `None`. |
Damien George | dbc81df | 2014-04-26 11:19:17 +0100 | [diff] [blame] | 324 | STATIC const mp_arg_t pyb_spi_send_args[] = { |
| 325 | { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, |
| 326 | { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 327 | }; |
Damien George | dbc81df | 2014-04-26 11:19:17 +0100 | [diff] [blame] | 328 | #define PYB_SPI_SEND_NUM_ARGS ARRAY_SIZE(pyb_spi_send_args) |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 329 | |
| 330 | STATIC mp_obj_t pyb_spi_send(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 331 | // TODO assumes transmission size is 8-bits wide |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 332 | |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 333 | pyb_spi_obj_t *self = args[0]; |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 334 | |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 335 | // parse args |
Damien George | dbc81df | 2014-04-26 11:19:17 +0100 | [diff] [blame] | 336 | mp_arg_val_t vals[PYB_SPI_SEND_NUM_ARGS]; |
| 337 | mp_arg_parse_all(n_args - 1, args + 1, kw_args, PYB_SPI_SEND_NUM_ARGS, pyb_spi_send_args, vals); |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 338 | |
| 339 | // get the buffer to send from |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 340 | mp_buffer_info_t bufinfo; |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 341 | uint8_t data[1]; |
| 342 | pyb_buf_get_for_send(vals[0].u_obj, &bufinfo, data); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 343 | |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 344 | // send the data |
| 345 | HAL_StatusTypeDef status = HAL_SPI_Transmit(self->spi, bufinfo.buf, bufinfo.len, vals[1].u_int); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 346 | |
| 347 | if (status != HAL_OK) { |
| 348 | // TODO really need a HardwareError object, or something |
| 349 | nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_Exception, "HAL_SPI_Transmit failed with code %d", status)); |
| 350 | } |
| 351 | |
| 352 | return mp_const_none; |
| 353 | } |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 354 | STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_send_obj, 1, pyb_spi_send); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 355 | |
Damien George | 8d09640 | 2014-04-29 22:55:34 +0100 | [diff] [blame] | 356 | /// \method recv(recv, *, timeout=5000) |
| 357 | /// |
| 358 | /// Receive data on the bus: |
| 359 | /// |
| 360 | /// - `recv` can be an integer, which is the number of bytes to receive, |
| 361 | /// or a mutable buffer, which will be filled with received bytes. |
| 362 | /// - `timeout` is the timeout in milliseconds to wait for the receive. |
| 363 | /// |
| 364 | /// Return value: if `recv` is an integer then a new buffer of the bytes received, |
| 365 | /// otherwise the same buffer that was passed in to `recv`. |
Damien George | dbc81df | 2014-04-26 11:19:17 +0100 | [diff] [blame] | 366 | STATIC const mp_arg_t pyb_spi_recv_args[] = { |
| 367 | { MP_QSTR_recv, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, |
| 368 | { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 369 | }; |
Damien George | dbc81df | 2014-04-26 11:19:17 +0100 | [diff] [blame] | 370 | #define PYB_SPI_RECV_NUM_ARGS ARRAY_SIZE(pyb_spi_recv_args) |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 371 | |
| 372 | STATIC mp_obj_t pyb_spi_recv(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 373 | // TODO assumes transmission size is 8-bits wide |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 374 | |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 375 | pyb_spi_obj_t *self = args[0]; |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 376 | |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 377 | // parse args |
Damien George | dbc81df | 2014-04-26 11:19:17 +0100 | [diff] [blame] | 378 | mp_arg_val_t vals[PYB_SPI_RECV_NUM_ARGS]; |
| 379 | mp_arg_parse_all(n_args - 1, args + 1, kw_args, PYB_SPI_RECV_NUM_ARGS, pyb_spi_recv_args, vals); |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 380 | |
| 381 | // get the buffer to receive into |
| 382 | mp_buffer_info_t bufinfo; |
| 383 | mp_obj_t o_ret = pyb_buf_get_for_recv(vals[0].u_obj, &bufinfo); |
| 384 | |
| 385 | // receive the data |
| 386 | HAL_StatusTypeDef status = HAL_SPI_Receive(self->spi, bufinfo.buf, bufinfo.len, vals[1].u_int); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 387 | |
| 388 | if (status != HAL_OK) { |
| 389 | // TODO really need a HardwareError object, or something |
| 390 | nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_Exception, "HAL_SPI_Receive failed with code %d", status)); |
| 391 | } |
| 392 | |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 393 | // return the received data |
| 394 | if (o_ret == MP_OBJ_NULL) { |
| 395 | return vals[0].u_obj; |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 396 | } else { |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 397 | return mp_obj_str_builder_end(o_ret); |
| 398 | } |
| 399 | } |
| 400 | STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_recv_obj, 1, pyb_spi_recv); |
| 401 | |
Damien George | 8d09640 | 2014-04-29 22:55:34 +0100 | [diff] [blame] | 402 | /// \method send_recv(send, recv=None, *, timeout=5000) |
| 403 | /// |
| 404 | /// Send and receive data on the bus at the same time: |
| 405 | /// |
| 406 | /// - `send` is the data to send (an integer to send, or a buffer object). |
| 407 | /// - `recv` is a mutable buffer which will be filled with received bytes. |
| 408 | /// It can be the same as `send`, or omitted. If omitted, a new buffer will |
| 409 | /// be created. |
| 410 | /// - `timeout` is the timeout in milliseconds to wait for the receive. |
| 411 | /// |
| 412 | /// Return value: the buffer with the received bytes. |
Damien George | dbc81df | 2014-04-26 11:19:17 +0100 | [diff] [blame] | 413 | STATIC const mp_arg_t pyb_spi_send_recv_args[] = { |
| 414 | { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, |
| 415 | { MP_QSTR_recv, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, |
| 416 | { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 417 | }; |
Damien George | dbc81df | 2014-04-26 11:19:17 +0100 | [diff] [blame] | 418 | #define PYB_SPI_SEND_RECV_NUM_ARGS ARRAY_SIZE(pyb_spi_send_recv_args) |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 419 | |
| 420 | STATIC mp_obj_t pyb_spi_send_recv(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { |
| 421 | // TODO assumes transmission size is 8-bits wide |
| 422 | |
| 423 | pyb_spi_obj_t *self = args[0]; |
| 424 | |
| 425 | // parse args |
Damien George | dbc81df | 2014-04-26 11:19:17 +0100 | [diff] [blame] | 426 | mp_arg_val_t vals[PYB_SPI_SEND_RECV_NUM_ARGS]; |
| 427 | mp_arg_parse_all(n_args - 1, args + 1, kw_args, PYB_SPI_SEND_RECV_NUM_ARGS, pyb_spi_send_recv_args, vals); |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 428 | |
| 429 | // get buffers to send from/receive to |
| 430 | mp_buffer_info_t bufinfo_send; |
| 431 | uint8_t data_send[1]; |
| 432 | mp_buffer_info_t bufinfo_recv; |
| 433 | mp_obj_t o_ret; |
| 434 | |
| 435 | if (vals[0].u_obj == vals[1].u_obj) { |
| 436 | // same object for send and receive, it must be a r/w buffer |
| 437 | mp_get_buffer_raise(vals[0].u_obj, &bufinfo_send, MP_BUFFER_RW); |
| 438 | bufinfo_recv = bufinfo_send; |
| 439 | o_ret = MP_OBJ_NULL; |
| 440 | } else { |
| 441 | // get the buffer to send from |
| 442 | pyb_buf_get_for_send(vals[0].u_obj, &bufinfo_send, data_send); |
| 443 | |
| 444 | // get the buffer to receive into |
| 445 | if (vals[1].u_obj == MP_OBJ_NULL) { |
| 446 | // only send argument given, so create a fresh buffer of the send length |
| 447 | bufinfo_recv.len = bufinfo_send.len; |
| 448 | bufinfo_recv.typecode = 'B'; |
| 449 | o_ret = mp_obj_str_builder_start(&mp_type_bytes, bufinfo_recv.len, (byte**)&bufinfo_recv.buf); |
| 450 | } else { |
| 451 | // recv argument given |
| 452 | mp_get_buffer_raise(vals[1].u_obj, &bufinfo_recv, MP_BUFFER_WRITE); |
Damien George | 34f01c2 | 2014-04-21 01:59:43 +0100 | [diff] [blame] | 453 | if (bufinfo_recv.len != bufinfo_send.len) { |
| 454 | nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "recv must be same length as send")); |
| 455 | } |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 456 | o_ret = MP_OBJ_NULL; |
| 457 | } |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 458 | } |
| 459 | |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 460 | // send and receive the data |
| 461 | HAL_StatusTypeDef status = HAL_SPI_TransmitReceive(self->spi, bufinfo_send.buf, bufinfo_recv.buf, bufinfo_send.len, vals[2].u_int); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 462 | |
| 463 | if (status != HAL_OK) { |
| 464 | // TODO really need a HardwareError object, or something |
| 465 | nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_Exception, "HAL_SPI_TransmitReceive failed with code %d", status)); |
| 466 | } |
| 467 | |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 468 | // return the received data |
| 469 | if (o_ret == MP_OBJ_NULL) { |
| 470 | return vals[1].u_obj; |
| 471 | } else { |
| 472 | return mp_obj_str_builder_end(o_ret); |
| 473 | } |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 474 | } |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 475 | STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_send_recv_obj, 1, pyb_spi_send_recv); |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 476 | |
| 477 | STATIC const mp_map_elem_t pyb_spi_locals_dict_table[] = { |
| 478 | // instance methods |
| 479 | { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_spi_init_obj }, |
| 480 | { MP_OBJ_NEW_QSTR(MP_QSTR_deinit), (mp_obj_t)&pyb_spi_deinit_obj }, |
| 481 | { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&pyb_spi_send_obj }, |
| 482 | { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&pyb_spi_recv_obj }, |
| 483 | { MP_OBJ_NEW_QSTR(MP_QSTR_send_recv), (mp_obj_t)&pyb_spi_send_recv_obj }, |
| 484 | |
| 485 | // class constants |
Damien George | 8d09640 | 2014-04-29 22:55:34 +0100 | [diff] [blame] | 486 | /// \constant MASTER - for initialising the bus to master mode |
| 487 | /// \constant SLAVE - for initialising the bus to slave mode |
| 488 | /// \constant MSB - set the first bit to MSB |
| 489 | /// \constant LSB - set the first bit to LSB |
Damien George | eb8bdf4 | 2014-04-21 00:10:04 +0100 | [diff] [blame] | 490 | { MP_OBJ_NEW_QSTR(MP_QSTR_MASTER), MP_OBJ_NEW_SMALL_INT(SPI_MODE_MASTER) }, |
| 491 | { MP_OBJ_NEW_QSTR(MP_QSTR_SLAVE), MP_OBJ_NEW_SMALL_INT(SPI_MODE_SLAVE) }, |
| 492 | { MP_OBJ_NEW_QSTR(MP_QSTR_MSB), MP_OBJ_NEW_SMALL_INT(SPI_FIRSTBIT_MSB) }, |
| 493 | { MP_OBJ_NEW_QSTR(MP_QSTR_LSB), MP_OBJ_NEW_SMALL_INT(SPI_FIRSTBIT_LSB) }, |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 494 | /* TODO |
| 495 | { MP_OBJ_NEW_QSTR(MP_QSTR_DIRECTION_2LINES ((uint32_t)0x00000000) |
| 496 | { MP_OBJ_NEW_QSTR(MP_QSTR_DIRECTION_2LINES_RXONLY SPI_CR1_RXONLY |
| 497 | { MP_OBJ_NEW_QSTR(MP_QSTR_DIRECTION_1LINE SPI_CR1_BIDIMODE |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 498 | { MP_OBJ_NEW_QSTR(MP_QSTR_NSS_SOFT SPI_CR1_SSM |
| 499 | { MP_OBJ_NEW_QSTR(MP_QSTR_NSS_HARD_INPUT ((uint32_t)0x00000000) |
| 500 | { MP_OBJ_NEW_QSTR(MP_QSTR_NSS_HARD_OUTPUT ((uint32_t)0x00040000) |
Damien George | d689430 | 2014-04-20 00:16:30 +0100 | [diff] [blame] | 501 | */ |
| 502 | }; |
| 503 | |
| 504 | STATIC MP_DEFINE_CONST_DICT(pyb_spi_locals_dict, pyb_spi_locals_dict_table); |
| 505 | |
| 506 | const mp_obj_type_t pyb_spi_type = { |
| 507 | { &mp_type_type }, |
| 508 | .name = MP_QSTR_SPI, |
| 509 | .print = pyb_spi_print, |
| 510 | .make_new = pyb_spi_make_new, |
| 511 | .locals_dict = (mp_obj_t)&pyb_spi_locals_dict, |
| 512 | }; |