circuitpython/ports/nrf/common-hal/busio/SPI.c

263 lines
7.7 KiB
C

/*
* SPI Master library for nRF5x.
* Copyright (c) 2015 Arduino LLC
* Copyright (c) 2016 Sandeep Mistry All right reserved.
* Copyright (c) 2017 hathach
* Copyright (c) 2018 Artur Pacholec
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "shared-bindings/busio/SPI.h"
#include "py/mperrno.h"
#include "py/runtime.h"
#include "nrfx_spim.h"
#include "nrf_gpio.h"
#if NRFX_SPIM3_ENABLED
#define INST_NO 3
#else
#define INST_NO 2
#endif
#define MAX_XFER_SIZE ((1U << NRFX_CONCAT_3(SPIM, INST_NO, _EASYDMA_MAXCNT_SIZE)) - 1)
// Convert frequency to clock-speed-dependent value
static nrf_spim_frequency_t baudrate_to_spim_frequency(const uint32_t baudrate) {
if (baudrate <= 125000)
return NRF_SPIM_FREQ_125K;
if (baudrate <= 250000)
return NRF_SPIM_FREQ_250K;
if (baudrate <= 500000)
return NRF_SPIM_FREQ_500K;
if (baudrate <= 1000000)
return NRF_SPIM_FREQ_1M;
if (baudrate <= 2000000)
return NRF_SPIM_FREQ_2M;
if (baudrate <= 4000000)
return NRF_SPIM_FREQ_4M;
if (baudrate <= 8000000)
return NRF_SPIM_FREQ_8M;
#ifdef SPIM_FREQUENCY_FREQUENCY_M16
if (baudrate <= 16000000)
return NRF_SPIM_FREQ_16M;
#endif
#ifdef SPIM_FREQUENCY_FREQUENCY_M32
return NRF_SPIM_FREQ_32M;
#else
return NRF_SPIM_FREQ_8M;
#endif
}
void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t * clock, const mcu_pin_obj_t * mosi, const mcu_pin_obj_t * miso) {
const nrfx_spim_t instance = NRFX_SPIM_INSTANCE(INST_NO);
self->spim = instance;
nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG;
config.frequency = NRF_SPIM_FREQ_8M;
config.sck_pin = clock->number;
self->clock_pin_number = clock->number;
claim_pin(clock);
if (mosi != (mcu_pin_obj_t*)&mp_const_none_obj) {
config.mosi_pin = mosi->number;
self->MOSI_pin_number = mosi->number;
claim_pin(mosi);
} else {
self->MOSI_pin_number = NO_PIN;
}
if (miso != (mcu_pin_obj_t*)&mp_const_none_obj) {
config.miso_pin = miso->number;
self->MISO_pin_number = mosi->number;
claim_pin(miso);
} else {
self->MISO_pin_number = NO_PIN;
}
nrfx_err_t err = nrfx_spim_init(&self->spim, &config, NULL, NULL);
// A soft reset doesn't uninit the driver so we might end up with a invalid state
if (err == NRFX_ERROR_INVALID_STATE) {
nrfx_spim_uninit(&self->spim);
err = nrfx_spim_init(&self->spim, &config, NULL, NULL);
}
if (err != NRFX_SUCCESS) {
common_hal_busio_spi_deinit(self);
mp_raise_OSError(MP_EIO);
}
}
bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) {
return self->clock_pin_number == NO_PIN;
}
void common_hal_busio_spi_deinit(busio_spi_obj_t *self) {
if (common_hal_busio_spi_deinited(self))
return;
nrfx_spim_uninit(&self->spim);
reset_pin_number(self->clock_pin_number);
reset_pin_number(self->MOSI_pin_number);
reset_pin_number(self->MISO_pin_number);
}
bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) {
// nrf52 does not support 16 bit
if (bits != 8)
return false;
nrf_spim_frequency_set(self->spim.p_reg, baudrate_to_spim_frequency(baudrate));
nrf_spim_mode_t mode = NRF_SPIM_MODE_0;
if (polarity) {
mode = (phase) ? NRF_SPIM_MODE_3 : NRF_SPIM_MODE_2;
} else {
mode = (phase) ? NRF_SPIM_MODE_1 : NRF_SPIM_MODE_0;
}
nrf_spim_configure(self->spim.p_reg, mode, NRF_SPIM_BIT_ORDER_MSB_FIRST);
return true;
}
bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) {
bool grabbed_lock = false;
// CRITICAL_SECTION_ENTER()
// if (!self->has_lock) {
grabbed_lock = true;
self->has_lock = true;
// }
// CRITICAL_SECTION_LEAVE();
return grabbed_lock;
}
bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) {
return self->has_lock;
}
void common_hal_busio_spi_unlock(busio_spi_obj_t *self) {
self->has_lock = false;
}
bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size_t len) {
if (len == 0)
return true;
const uint32_t parts = len / MAX_XFER_SIZE;
const uint32_t remainder = len % MAX_XFER_SIZE;
for (uint32_t i = 0; i < parts; ++i) {
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TX(data + i * MAX_XFER_SIZE, MAX_XFER_SIZE);
if (nrfx_spim_xfer(&self->spim, &xfer, 0) != NRFX_SUCCESS)
return false;
}
if (remainder > 0) {
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TX(data + parts * MAX_XFER_SIZE, remainder);
if (nrfx_spim_xfer(&self->spim, &xfer, 0) != NRFX_SUCCESS)
return false;
}
return true;
}
bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value) {
if (len == 0)
return true;
const uint32_t parts = len / MAX_XFER_SIZE;
const uint32_t remainder = len % MAX_XFER_SIZE;
for (uint32_t i = 0; i < parts; ++i) {
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_RX(data + i * MAX_XFER_SIZE, MAX_XFER_SIZE);
if (nrfx_spim_xfer(&self->spim, &xfer, 0) != NRFX_SUCCESS)
return false;
}
if (remainder > 0) {
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_RX(data + parts * MAX_XFER_SIZE, remainder);
if (nrfx_spim_xfer(&self->spim, &xfer, 0) != NRFX_SUCCESS)
return false;
}
return true;
}
bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, uint8_t *data_out, uint8_t *data_in, size_t len) {
if (len == 0)
return true;
const uint32_t parts = len / MAX_XFER_SIZE;
const uint32_t remainder = len % MAX_XFER_SIZE;
for (uint32_t i = 0; i < parts; ++i) {
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(data_out + i * MAX_XFER_SIZE, MAX_XFER_SIZE,
data_in + i * MAX_XFER_SIZE, MAX_XFER_SIZE);
if (nrfx_spim_xfer(&self->spim, &xfer, 0) != NRFX_SUCCESS)
return false;
}
if (remainder > 0) {
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(data_out + parts * MAX_XFER_SIZE, remainder,
data_in + parts * MAX_XFER_SIZE, remainder);
if (nrfx_spim_xfer(&self->spim, &xfer, 0) != NRFX_SUCCESS)
return false;
}
return true;
}
uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t* self) {
switch (self->spim.p_reg->FREQUENCY) {
case NRF_SPIM_FREQ_125K:
return 125000;
case NRF_SPIM_FREQ_250K:
return 250000;
case NRF_SPIM_FREQ_500K:
return 500000;
case NRF_SPIM_FREQ_1M:
return 1000000;
case NRF_SPIM_FREQ_2M:
return 2000000;
case NRF_SPIM_FREQ_4M:
return 4000000;
case NRF_SPIM_FREQ_8M:
return 8000000;
#ifdef SPIM_FREQUENCY_FREQUENCY_M16
case NRF_SPIM_FREQ_16M:
return 16000000;
#endif
#ifdef SPIM_FREQUENCY_FREQUENCY_M32
case NRF_SPIM_FREQ_32M:
return 32000000;
#endif
default:
return 0;
}
}