micropython/ports/nrf/modules/machine/spi.c
glennrub 002f7d1ad7 nrf: Replace custom-HAL with nrfx-HAL
Summarized this squashed PR replaces the hal/ folder in the port. This has been replaced the official
HAL layer from Nordic Semiconductor; https://github.com/NordicSemiconductor/nrfx.

A Git submodule has been added under lib/nrfx, for the nrfx dependency.

The drivers / modules has been updated to use this new HAL layer; nrfx at v1.0.0.

Also, header files and system files for nrf51/nrf52x chip variants has been deleted from the device/ folder, only keeping back the startup files written in C. All other files are now fetched from nrfx.

3 new header files in the ports/nrf/ folder has been added to configure nrfx (nrfx_config.h), logging (nrfx_log.h) and glue nrfx together with the drivers and modules from micropython (nrfx_glue.h).

The PR has been a joint effort from @aykevl (Ayke van Laethem) and @glennrub.

For reference, the commit log will be kept to get an overview of the changes done:

* ports/nrf: Initial commit for moving hal to Nordic Semiconductor BSD-3 licensed nrfx-hal.

* ports/nrf: Adding nrfx, Nordic Semiconductor BSD-3 hal layer, as git submodule checked out at lib/nrfx.

* ports/nrf/modules/machine/uart: Fixing bug which set hwfc to parity excluded, always resulting in no flow control, hence corrupted output. Also adding an extra loop on uart_tx_char to prevent any tx when any ongoing tx is in progress.

* ports/nrf/i2c: Moving I2C over to nrfx driver.

* ports/nrf/modules/machine/i2c: Alignment. Renaming print function param 'o' to 'self_in'

* ports/nrf/spi: Updating SPI machine module to use nrfx drivers.

* ports/nrf: Renaming modules/machine/rtc.c/.h to rtcounter.c/.h to not confuse the peripheral with Real-Time Clock:

* ports/nrf: Updating various files after renaming machine module RTC to RTCounter.

* ports/nrf: Renaming RTC to RTCounter in modmachine globals dict table. Also updating object type name to reflect new module name.

* ports/nrf: Fixing leftovers after renaming rtc to rtcounter.

* ports/nrf: Early untested adoption of nrfx_rtc in RTCounter. Untested.

* nrf/modules/machine/i2c: Improve keyword argument handling

* ports/nrf/modules/temp: Updating Temp machine module to use nrfx defined hal nrf_temp.h. Moving logic of BLE stack awareness to machine module.

* ports/nrf/boards/pca10040: Enable machine Temp module.

* nrf/modules/machine/rtcounter: Remove magic constants.

* ports/nrf: Adding base support for nrfx module logging. Adding option to disable logging of UART as it might log its own setup over UART while the peripheral is not yet set up. Logging of UART could make sense if other transport of log is used.

* ports/nrf: updating nrfx_log.h with more correct parenthisis on macro grouping.

* ports/nrf: Updating nrfx logging with configuration to disable logging of UART module. The pattern can be used to turn off other modules as well. However, for now UART is the only module locking itself by logging before the peripheral is configured. Logging is turned off by default, can be enabled in nrfx_config.h by setting NRFX_LOG_ENABLED=1.

* ports/nrf/modules/random: Updating modrandom to use nrfx hal for rng. Not using nrfx-driver for this peripheral as its blocking mode would do the trick on RNG. Moving softdevice aware code from legacy hal to modrandom.c.

* nrf: Enable Peripheral Resource Sharing.

This enables TWI and SPI to be enabled at the same time.

* nrf/Makefile: Define MCU sub variant (e.g. NRF51822/NRF51422)

* nrf: Port TIMER peripheral to nrfx HAL.

* nrf/modules/machine/uart: Optimize UART module

For a nRF51, this results in a size reduction of:
.text: -68 bytes
.data: -56 bytes

* nrf/modules/machine/uart: Don't use magic index numbers.

* nrf/modules/machine/uart: Fix off-by-one error.

For nrf51:
.text: -40 bytes

* nrf/modules/machine/rtcounter: Update for nrfx HAL.

* nrf/modules/machine/i2c: Reduce RAM consumption.

Reductions for the nrf51:
flash: -108 bytes
RAM:   -72 bytes

* nrf/mpconfigport: Avoid unnecessary root pointers.

This saves 92 bytes of RAM.

* nrf: Support SoftDevice with nrfx HAL.

* nrf: Add NVMC peripheral (microbitfs) support.

There is no support yet for a SoftDevice.

It also fixes a potentially serious bug in start_index generation.

* nrf/modules/machine/spi: Optimize SPI peripheral.

nrf51:
text: -340 bytes
data: -72  bytes

nrf52:
text: -352 bytes
data: -108 bytes

* nrf/modules/random: Forgot to commit header file.

* nrf: Make nrfx_config.h universal for all boards.

* nrf: Use SoftDevice API for flash access when built for SD

* nrf/drivers/bluetooth: Remove legacy HAL driver includes.

These were not used anymore so can be removed.

* ports/nrf/microbit: Port microbit targets to nrfx HAL

Initial port of microbit modules to use nrfx HAL layer.
Tested display/image and modmusic on micro:bit to verify that
softpwm and ticker for nrf51 is working as expected.

Changing IRQ priority on timer to priority 2, as 1 might collide if
used side by side of SD110 BLE stack.

The patch reserves Timer1 peripheral compile time. This is not ideal
and should be resolved in seperate task.

* nrf/boards/microbit: Remove custom nrfx_config.h from microbit target, adding disablement of timer1 if softpwm is enabled.

* nrf/adc: Update ADC module to use nrfx

* nrf/modules/machine/pwm: Updating machine PWM module to use nrfx HAL driver.

examples/nrf52_pwm.py and examples/nrf52_servo.py tested on pca10040.

* nrf: Removing hal folder and boards nrf5x_hal_conf.h headers.

* nrf/nrfx_glue: Adding direct NVIC access for S110 BLE stack

If SoftDevice s110 has not yet been initialized, the IRQ will not be forwarded to
the application using the sd_nvic* function calls. Hence, direct access to cmsi
nvic functions are used instead if SoftDevice is not enabled.

* nrf/drivers/ticker: Setting IRQ priority 3 on Timer1

SoftDevice fails to initilize if Timer1 has been configured to priority
level 2 before enabling the SD. The timer is set to priority 1, higher than BLE
stack in order to provide better quality of music rendering when used with the
music module. This might be too high, time will show.

* nrf/examples: Updating ubluepy_temp after moving RTCounter to nrfx.

* nrf: delete duplicate files from device folder which can be located in nrfx/mdk.

* nrf/Makefile: Fetch system files from nrfx.

Testing on each device sub-variant to figure out which system file to
use. Reason for this is that nrf52.c is actually defining nrf52832.

Removing NRF_DEFINES parameter setting the device in use into the
same sub-variant test, as NRF52 is unique to nrf52832 when using nrfx.
Without this exclusion of -DNRF52 in compilation for nrf52840, the
device will be interpreted as a nrf52, hence nrf52832.

Also, changing name on variable SRC_NRF_HAL to SRC_NRFX_HAL to
explicitly tell the origin of the file.

* nrf: Updating device #ifdefs to be more open to non-nrf51 targets.

* nrf/modules/machine/uart: Removing second instance of UART for nrf52840 as it only has one non-DMA variant.

* nrf/device: Removing system files as these are now used from nrfx/mdk

* nrf: Moving startup files in device one level up as there is no need for deep hierarchy.

* nrf: Use NRF52_SERIES defined in nrfx/mdk/nrf.h as define value when testing for both nrf52(832) and nrf52840 variants.

* nrf/modules/machine/uart: Enable UART RX by default

Enable rx by default after intiialization of the peripheral.
Else, the nrfx driver will re-enable rx for each byte read
on uart REPL, clearing the EVENT_RXDRDY before second byte,
which again will make second byte get lost and read will get stuck.

This happens if the bytes are transmitted nrf(51) while still
processing the previous byte. Not seen on nrf52, but should
also become an issue at higher speeds.

This patch sets rx to always be enabled. Hence, not clearing the event
between read bytes, and it will be able to detect next byte recieved
upon finishing the first.

* nrf/modules/machine/timer: Fixing defines excluding Timer1 if ticker/softpwm is used.

* nrf: Switching import form mpconfigboard.h to mpconfigport.h in nrfx_config.h as mpconfigboard.h might define default values for defines not set by board specific header.

* nrf/modules/machine/i2c: nrfx integration fixes

Increasing speed to 400K.

Returning Address NACK's as MP error code; MP_ENODEV.

Returning MP_ETIMEOUT on all other error codes from TWI nrfx driver
except the ANACK.

Enabling and disabling the TWI peripheral before and after each transaction.

* nrf/examples: Updating ssd1306_mod.py to split framebuffer transfer into multiple chunks

* nrf/modules/machine/i2c: Return MP_EIO error if Data NACK occurs.

* nrf: Addressing review comments.

* nrf: Updating git submodule and users to nrfx v1.0.0.

* nrf/modules/machine/adc: Update adc module to follow v1.0.0 nrfx API.

* nrf/modules/machine/spi: Implement init and deinit functions

Extending SPI objects with a config member such that
configuration can be kept between new() and init().

Moving initialization done in new() to common init
function shared between the module functions.

If SPI is already configured, the SPI peripheral will
be uninitialized before initalized again.

Adding logic to handle initialization of polarity and
phase. As well, updating default speed to 1M from 500K.

* nrf/modules/machine: Removing unused nrfx includes in machine module header files
2018-07-18 17:12:26 +10:00

411 lines
15 KiB
C

/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 Damien P. George
* Copyright (c) 2016 Glenn Ruben Bakke
* Copyright (c) 2018 Ayke van Laethem
*
* 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.
*/
#include <stdio.h>
#include <string.h>
#include "py/nlr.h"
#include "py/runtime.h"
#include "py/mphal.h"
#include "extmod/machine_spi.h"
#include "pin.h"
#include "genhdr/pins.h"
#include "spi.h"
#include "nrfx_spi.h"
#if MICROPY_PY_MACHINE_HW_SPI
/// \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
/// spi = SPI(1, SPI.MASTER, baudrate=600000, polarity=1, phase=0, crc=0x7)
///
/// Only required parameter is mode, SPI.MASTER or SPI.SLAVE. Polarity can be
/// 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.
///
/// 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
typedef struct _machine_hard_spi_obj_t {
mp_obj_base_t base;
const nrfx_spi_t * p_spi; // Driver instance
nrfx_spi_config_t * p_config; // pointer to volatile part
} machine_hard_spi_obj_t;
STATIC const nrfx_spi_t machine_spi_instances[] = {
NRFX_SPI_INSTANCE(0),
NRFX_SPI_INSTANCE(1),
#if NRF52
NRFX_SPI_INSTANCE(2),
#if NRF52840_XXAA
NRFX_SPI_INSTANCE(3),
#endif // NRF52840_XXAA
#endif // NRF52
};
STATIC nrfx_spi_config_t configs[MP_ARRAY_SIZE(machine_spi_instances)];
STATIC const machine_hard_spi_obj_t machine_hard_spi_obj[] = {
{{&machine_hard_spi_type}, .p_spi = &machine_spi_instances[0], .p_config = &configs[0]},
{{&machine_hard_spi_type}, .p_spi = &machine_spi_instances[1], .p_config = &configs[1]},
#if NRF52
{{&machine_hard_spi_type}, .p_spi = &machine_spi_instances[2], .p_config = &configs[2]},
#if NRF52840_XXAA
{{&machine_hard_spi_type}, .p_spi = &machine_spi_instances[3], .p_config = &configs[3]},
#endif // NRF52840_XXAA
#endif // NRF52
};
void spi_init0(void) {
}
STATIC int spi_find(mp_obj_t id) {
if (MP_OBJ_IS_STR(id)) {
// given a string id
const char *port = mp_obj_str_get_str(id);
if (0) {
#ifdef MICROPY_HW_SPI0_NAME
} else if (strcmp(port, MICROPY_HW_SPI0_NAME) == 0) {
return 1;
#endif
}
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
"SPI(%s) does not exist", port));
} else {
// given an integer id
int spi_id = mp_obj_get_int(id);
if (spi_id >= 0 && spi_id < MP_ARRAY_SIZE(machine_hard_spi_obj)) {
return spi_id;
}
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
"SPI(%d) does not exist", spi_id));
}
}
STATIC void spi_transfer(const machine_hard_spi_obj_t * self, size_t len, const void * src, void * dest) {
nrfx_spi_xfer_desc_t xfer_desc = {
.p_tx_buffer = src,
.tx_length = len,
.p_rx_buffer = dest,
.rx_length = len
};
nrfx_spi_xfer(self->p_spi, &xfer_desc, 0);
}
/******************************************************************************/
/* MicroPython bindings for machine API */
// for make_new
enum {
ARG_NEW_id,
ARG_NEW_baudrate,
ARG_NEW_polarity,
ARG_NEW_phase,
ARG_NEW_bits,
ARG_NEW_firstbit,
ARG_NEW_sck,
ARG_NEW_mosi,
ARG_NEW_miso
};
// for init
enum {
ARG_INIT_baudrate,
ARG_INIT_polarity,
ARG_INIT_phase,
ARG_INIT_bits,
ARG_INIT_firstbit
};
STATIC mp_obj_t machine_hard_spi_make_new(mp_arg_val_t *args);
STATIC void machine_hard_spi_init(mp_obj_t self, mp_arg_val_t *args);
STATIC void machine_hard_spi_deinit(mp_obj_t self);
/* common code for both soft and hard implementations *************************/
STATIC mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_id, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(-1)} },
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 1000000} },
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
{ MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0 /* SPI_FIRSTBIT_MSB */} },
{ MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
};
// parse args
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if (args[ARG_NEW_id].u_obj == MP_OBJ_NEW_SMALL_INT(-1)) {
// TODO: implement soft SPI
// return machine_soft_spi_make_new(args);
return mp_const_none;
} else {
// hardware peripheral id given
return machine_hard_spi_make_new(args);
}
}
STATIC mp_obj_t machine_spi_init(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000000} },
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
{ MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
};
// parse args
mp_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);
// dispatch to specific implementation
if (mp_obj_get_type(self) == &machine_hard_spi_type) {
machine_hard_spi_init(self, args);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_spi_init_obj, 1, machine_spi_init);
STATIC mp_obj_t machine_spi_deinit(mp_obj_t self) {
// dispatch to specific implementation
if (mp_obj_get_type(self) == &machine_hard_spi_type) {
machine_hard_spi_deinit(self);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_spi_deinit_obj, machine_spi_deinit);
STATIC const mp_rom_map_elem_t machine_spi_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_spi_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_spi_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_machine_spi_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_machine_spi_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_machine_spi_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&mp_machine_spi_write_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_MSB), MP_ROM_INT(NRF_SPI_BIT_ORDER_MSB_FIRST) },
{ MP_ROM_QSTR(MP_QSTR_LSB), MP_ROM_INT(NRF_SPI_BIT_ORDER_LSB_FIRST) },
};
STATIC MP_DEFINE_CONST_DICT(machine_spi_locals_dict, machine_spi_locals_dict_table);
/* code for hard implementation ***********************************************/
STATIC void machine_hard_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_hard_spi_obj_t *self = self_in;
mp_printf(print, "SPI(%u)", self->p_spi->drv_inst_idx);
}
STATIC mp_obj_t machine_hard_spi_make_new(mp_arg_val_t *args) {
// get static peripheral object
int spi_id = spi_find(args[ARG_NEW_id].u_obj);
const machine_hard_spi_obj_t *self = &machine_hard_spi_obj[spi_id];
// here we would check the sck/mosi/miso pins and configure them
if (args[ARG_NEW_sck].u_obj != MP_OBJ_NULL
&& args[ARG_NEW_mosi].u_obj != MP_OBJ_NULL
&& args[ARG_NEW_miso].u_obj != MP_OBJ_NULL) {
self->p_config->sck_pin = ((const pin_obj_t *)args[ARG_NEW_sck].u_obj)->pin;
self->p_config->mosi_pin = ((const pin_obj_t *)args[ARG_NEW_mosi].u_obj)->pin;
self->p_config->miso_pin = ((const pin_obj_t *)args[ARG_NEW_miso].u_obj)->pin;
} else {
self->p_config->sck_pin = (&MICROPY_HW_SPI0_SCK)->pin;
self->p_config->mosi_pin = (&MICROPY_HW_SPI0_MOSI)->pin;
self->p_config->miso_pin = (&MICROPY_HW_SPI0_MISO)->pin;
}
// Manually trigger slave select from upper layer.
self->p_config->ss_pin = NRFX_SPI_PIN_NOT_USED;
#ifdef NRF51
self->p_config->irq_priority = 3;
#else
self->p_config->irq_priority = 6;
#endif
mp_obj_t self_obj = MP_OBJ_FROM_PTR(self);
machine_hard_spi_init(self_obj, &args[1]); // Skip instance id param.
return self_obj;
}
STATIC void machine_hard_spi_init(mp_obj_t self_in, mp_arg_val_t *args) {
const machine_hard_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
int baudrate = args[ARG_INIT_baudrate].u_int;
if (baudrate <= 125000) {
self->p_config->frequency = NRF_SPI_FREQ_125K;
} else if (baudrate <= 250000) {
self->p_config->frequency = NRF_SPI_FREQ_250K;
} else if (baudrate <= 500000) {
self->p_config->frequency = NRF_SPI_FREQ_500K;
} else if (baudrate <= 1000000) {
self->p_config->frequency = NRF_SPI_FREQ_1M;
} else if (baudrate <= 2000000) {
self->p_config->frequency = NRF_SPI_FREQ_2M;
} else if (baudrate <= 4000000) {
self->p_config->frequency = NRF_SPI_FREQ_4M;
} else if (baudrate <= 8000000) {
self->p_config->frequency = NRF_SPI_FREQ_8M;
#if NRF52840_XXAA
} else if (baudrate <= 16000000) {
self->p_config->frequency = SPIM_FREQUENCY_FREQUENCY_M16; // Temporary value until SPIM support is addressed (EasyDMA)
} else if (baudrate <= 32000000) {
self->p_config->frequency = SPIM_FREQUENCY_FREQUENCY_M32; // Temporary value until SPIM support is addressed (EasyDMA)
#endif
} else { // Default
self->p_config->frequency = NRF_SPI_FREQ_1M;
}
// Active high
if (args[ARG_INIT_polarity].u_int == 0) {
if (args[ARG_INIT_phase].u_int == 0) {
// First clock edge
self->p_config->mode = NRF_SPI_MODE_0;
} else {
// Second clock edge
self->p_config->mode = NRF_SPI_MODE_1;
}
// Active low
} else {
if (args[ARG_INIT_phase].u_int == 0) {
// First clock edge
self->p_config->mode = NRF_SPI_MODE_2;
} else {
// Second clock edge
self->p_config->mode = NRF_SPI_MODE_3;
}
}
self->p_config->orc = 0xFF; // Overrun character
self->p_config->bit_order = (args[ARG_INIT_firstbit].u_int == 0) ? NRF_SPI_BIT_ORDER_MSB_FIRST : NRF_SPI_BIT_ORDER_LSB_FIRST;
// Set context to this instance of SPI
nrfx_err_t err_code = nrfx_spi_init(self->p_spi, self->p_config, NULL, (void *)self);
if (err_code == NRFX_ERROR_INVALID_STATE) {
// Instance already initialized, deinitialize first.
nrfx_spi_uninit(self->p_spi);
// Initialize again.
nrfx_spi_init(self->p_spi, self->p_config, NULL, (void *)self);
}
}
STATIC void machine_hard_spi_deinit(mp_obj_t self_in) {
const machine_hard_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
nrfx_spi_uninit(self->p_spi);
}
STATIC void machine_hard_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
const machine_hard_spi_obj_t *self = (machine_hard_spi_obj_t*)self_in;
spi_transfer(self, len, src, dest);
}
STATIC mp_obj_t mp_machine_spi_read(size_t n_args, const mp_obj_t *args) {
vstr_t vstr;
vstr_init_len(&vstr, mp_obj_get_int(args[1]));
memset(vstr.buf, n_args == 3 ? mp_obj_get_int(args[2]) : 0, vstr.len);
spi_transfer(args[0], vstr.len, vstr.buf, vstr.buf);
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj, 2, 3, mp_machine_spi_read);
STATIC mp_obj_t mp_machine_spi_readinto(size_t n_args, const mp_obj_t *args) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE);
memset(bufinfo.buf, n_args == 3 ? mp_obj_get_int(args[2]) : 0, bufinfo.len);
spi_transfer(args[0], bufinfo.len, bufinfo.buf, bufinfo.buf);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj, 2, 3, mp_machine_spi_readinto);
STATIC mp_obj_t mp_machine_spi_write(mp_obj_t self, mp_obj_t wr_buf) {
mp_buffer_info_t src;
mp_get_buffer_raise(wr_buf, &src, MP_BUFFER_READ);
spi_transfer(self, src.len, (const uint8_t*)src.buf, NULL);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(mp_machine_spi_write_obj, mp_machine_spi_write);
STATIC mp_obj_t mp_machine_spi_write_readinto(mp_obj_t self, mp_obj_t wr_buf, mp_obj_t rd_buf) {
mp_buffer_info_t src;
mp_get_buffer_raise(wr_buf, &src, MP_BUFFER_READ);
mp_buffer_info_t dest;
mp_get_buffer_raise(rd_buf, &dest, MP_BUFFER_WRITE);
if (src.len != dest.len) {
mp_raise_ValueError("buffers must be the same length");
}
spi_transfer(self, src.len, src.buf, dest.buf);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_3(mp_machine_spi_write_readinto_obj, mp_machine_spi_write_readinto);
STATIC const mp_machine_spi_p_t machine_hard_spi_p = {
.transfer = machine_hard_spi_transfer,
};
const mp_obj_type_t machine_hard_spi_type = {
{ &mp_type_type },
.name = MP_QSTR_SPI,
.print = machine_hard_spi_print,
.make_new = machine_spi_make_new,
.protocol = &machine_hard_spi_p,
.locals_dict = (mp_obj_dict_t*)&machine_spi_locals_dict,
};
#endif // MICROPY_PY_MACHINE_HW_SPI