mirror of https://github.com/ARMmbed/mbed-os.git
[STM32F4] SPI ASYNCH support
This commit is highly derived from an existing work from salkinium. It adds SPI ASYNC support to STM32 F4 devices. This required a small rework of the structure to have SYNCH and ASYNCH co-exist.pull/2542/head
parent
5fdce5fbf7
commit
478168c712
|
@ -69,6 +69,8 @@ struct serial_s {
|
|||
};
|
||||
|
||||
struct spi_s {
|
||||
SPI_HandleTypeDef handle;
|
||||
IRQn_Type spiIRQ;
|
||||
SPIName spi;
|
||||
uint32_t bits;
|
||||
uint32_t cpol;
|
||||
|
@ -80,6 +82,11 @@ struct spi_s {
|
|||
PinName pin_mosi;
|
||||
PinName pin_sclk;
|
||||
PinName pin_ssel;
|
||||
#ifdef DEVICE_SPI_ASYNCH
|
||||
uint32_t event;
|
||||
uint8_t module;
|
||||
uint8_t transfer_type;
|
||||
#endif
|
||||
};
|
||||
|
||||
#include "gpio_object.h"
|
||||
|
|
|
@ -32,42 +32,69 @@
|
|||
#include "spi_api.h"
|
||||
|
||||
#if DEVICE_SPI
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "cmsis.h"
|
||||
#include "pinmap.h"
|
||||
#include "PeripheralPins.h"
|
||||
#include "mbed_error.h"
|
||||
|
||||
static SPI_HandleTypeDef SpiHandle;
|
||||
#if DEVICE_SPI_ASYNCH
|
||||
#define SPI_INST(obj) ((SPI_TypeDef *)(obj->spi.spi))
|
||||
#else
|
||||
#define SPI_INST(obj) ((SPI_TypeDef *)(obj->spi))
|
||||
#endif
|
||||
|
||||
#if DEVICE_SPI_ASYNCH
|
||||
#define SPI_S(obj) (( struct spi_s *)(&(obj->spi)))
|
||||
#else
|
||||
#define SPI_S(obj) (( struct spi_s *)(obj))
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_STDIO
|
||||
# define DEBUG_STDIO 0
|
||||
#endif
|
||||
|
||||
#if DEBUG_STDIO
|
||||
# include <stdio.h>
|
||||
# define DEBUG_PRINTF(...) do { printf(__VA_ARGS__); } while(0)
|
||||
#else
|
||||
# define DEBUG_PRINTF(...) {}
|
||||
#endif
|
||||
|
||||
static void init_spi(spi_t *obj)
|
||||
{
|
||||
SpiHandle.Instance = (SPI_TypeDef *)(obj->spi);
|
||||
struct spi_s *spiobj = SPI_S(obj);
|
||||
SPI_HandleTypeDef *handle = &(spiobj->handle);
|
||||
|
||||
__HAL_SPI_DISABLE(&SpiHandle);
|
||||
handle->Instance = SPI_INST(obj);
|
||||
__HAL_SPI_DISABLE(handle);
|
||||
|
||||
SpiHandle.Init.Mode = obj->mode;
|
||||
SpiHandle.Init.BaudRatePrescaler = obj->br_presc;
|
||||
SpiHandle.Init.Direction = SPI_DIRECTION_2LINES;
|
||||
SpiHandle.Init.CLKPhase = obj->cpha;
|
||||
SpiHandle.Init.CLKPolarity = obj->cpol;
|
||||
SpiHandle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;
|
||||
SpiHandle.Init.CRCPolynomial = 7;
|
||||
SpiHandle.Init.DataSize = obj->bits;
|
||||
SpiHandle.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
||||
SpiHandle.Init.NSS = obj->nss;
|
||||
SpiHandle.Init.TIMode = SPI_TIMODE_DISABLED;
|
||||
DEBUG_PRINTF("init_spi: instance=0x%8X\r\n", (int)handle->Instance);
|
||||
|
||||
if (HAL_SPI_Init(&SpiHandle) != HAL_OK) {
|
||||
handle->Init.Mode = spiobj->mode;
|
||||
handle->Init.BaudRatePrescaler = spiobj->br_presc;
|
||||
handle->Init.Direction = SPI_DIRECTION_2LINES;
|
||||
handle->Init.CLKPhase = spiobj->cpha;
|
||||
handle->Init.CLKPolarity = spiobj->cpol;
|
||||
handle->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;
|
||||
handle->Init.CRCPolynomial = 7;
|
||||
handle->Init.DataSize = (spiobj->bits == 16) ? SPI_DATASIZE_16BIT : SPI_DATASIZE_8BIT;
|
||||
handle->Init.FirstBit = SPI_FIRSTBIT_MSB;
|
||||
handle->Init.NSS = spiobj->nss;
|
||||
handle->Init.TIMode = SPI_TIMODE_DISABLED;
|
||||
|
||||
if (HAL_SPI_Init(handle) != HAL_OK) {
|
||||
error("Cannot initialize SPI");
|
||||
}
|
||||
|
||||
__HAL_SPI_ENABLE(&SpiHandle);
|
||||
__HAL_SPI_ENABLE(handle);
|
||||
}
|
||||
|
||||
void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel)
|
||||
{
|
||||
struct spi_s *spiobj = SPI_S(obj);
|
||||
|
||||
// Determine the SPI to use
|
||||
SPIName spi_mosi = (SPIName)pinmap_peripheral(mosi, PinMap_SPI_MOSI);
|
||||
SPIName spi_miso = (SPIName)pinmap_peripheral(miso, PinMap_SPI_MISO);
|
||||
|
@ -77,39 +104,45 @@ void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel
|
|||
SPIName spi_data = (SPIName)pinmap_merge(spi_mosi, spi_miso);
|
||||
SPIName spi_cntl = (SPIName)pinmap_merge(spi_sclk, spi_ssel);
|
||||
|
||||
obj->spi = (SPIName)pinmap_merge(spi_data, spi_cntl);
|
||||
MBED_ASSERT(obj->spi != (SPIName)NC);
|
||||
spiobj->spi = (SPIName)pinmap_merge(spi_data, spi_cntl);
|
||||
MBED_ASSERT(spiobj->spi != (SPIName)NC);
|
||||
|
||||
// Enable SPI clock
|
||||
if (obj->spi == SPI_1) {
|
||||
if (spiobj->spi == SPI_1) {
|
||||
__HAL_RCC_SPI1_CLK_ENABLE();
|
||||
spiobj->spiIRQ = SPI1_IRQn;
|
||||
}
|
||||
|
||||
if (obj->spi == SPI_2) {
|
||||
if (spiobj->spi == SPI_2) {
|
||||
__HAL_RCC_SPI2_CLK_ENABLE();
|
||||
spiobj->spiIRQ = SPI2_IRQn;
|
||||
}
|
||||
|
||||
#if defined SPI3_BASE
|
||||
if (obj->spi == SPI_3) {
|
||||
if (spiobj->spi == SPI_3) {
|
||||
__HAL_RCC_SPI3_CLK_ENABLE();
|
||||
spiobj->spiIRQ = SPI3_IRQn;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined SPI4_BASE
|
||||
if (obj->spi == SPI_4) {
|
||||
if (spiobj->spi == SPI_4) {
|
||||
__HAL_RCC_SPI4_CLK_ENABLE();
|
||||
spiobj->spiIRQ = SPI4_IRQn;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined SPI5_BASE
|
||||
if (obj->spi == SPI_5) {
|
||||
if (spiobj->spi == SPI_5) {
|
||||
__HAL_RCC_SPI5_CLK_ENABLE();
|
||||
spiobj->spiIRQ = SPI5_IRQn;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined SPI6_BASE
|
||||
if (obj->spi == SPI_6) {
|
||||
if (spiobj->spi == SPI_6) {
|
||||
__HAL_RCC_SPI6_CLK_ENABLE();
|
||||
spiobj->spiIRQ = SPI6_IRQn;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -118,21 +151,23 @@ void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel
|
|||
pinmap_pinout(miso, PinMap_SPI_MISO);
|
||||
pinmap_pinout(sclk, PinMap_SPI_SCLK);
|
||||
|
||||
// Save new values
|
||||
obj->bits = SPI_DATASIZE_8BIT;
|
||||
obj->cpol = SPI_POLARITY_LOW;
|
||||
obj->cpha = SPI_PHASE_1EDGE;
|
||||
obj->br_presc = SPI_BAUDRATEPRESCALER_256;
|
||||
// Save default values
|
||||
spiobj->bits = 8;
|
||||
spiobj->mode = SPI_MODE_MASTER;
|
||||
|
||||
obj->pin_miso = miso;
|
||||
obj->pin_mosi = mosi;
|
||||
obj->pin_sclk = sclk;
|
||||
obj->pin_ssel = ssel;
|
||||
spiobj->cpol = SPI_POLARITY_LOW;
|
||||
spiobj->cpha = SPI_PHASE_1EDGE;
|
||||
spiobj->br_presc = SPI_BAUDRATEPRESCALER_256;
|
||||
|
||||
spiobj->pin_miso = miso;
|
||||
spiobj->pin_mosi = mosi;
|
||||
spiobj->pin_sclk = sclk;
|
||||
spiobj->pin_ssel = ssel;
|
||||
|
||||
if (ssel != NC) {
|
||||
pinmap_pinout(ssel, PinMap_SPI_SSEL);
|
||||
} else {
|
||||
obj->nss = SPI_NSS_SOFT;
|
||||
spiobj->nss = SPI_NSS_SOFT;
|
||||
}
|
||||
|
||||
init_spi(obj);
|
||||
|
@ -140,20 +175,28 @@ void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel
|
|||
|
||||
void spi_free(spi_t *obj)
|
||||
{
|
||||
struct spi_s *spiobj = SPI_S(obj);
|
||||
SPI_HandleTypeDef *handle = &(spiobj->handle);
|
||||
|
||||
DEBUG_PRINTF("spi_free\r\n");
|
||||
|
||||
__HAL_SPI_DISABLE(handle);
|
||||
HAL_SPI_DeInit(handle);
|
||||
|
||||
// Reset SPI and disable clock
|
||||
if (obj->spi == SPI_1) {
|
||||
if (spiobj->spi == SPI_1) {
|
||||
__HAL_RCC_SPI1_FORCE_RESET();
|
||||
__HAL_RCC_SPI1_RELEASE_RESET();
|
||||
__HAL_RCC_SPI1_CLK_DISABLE();
|
||||
}
|
||||
|
||||
if (obj->spi == SPI_2) {
|
||||
if (spiobj->spi == SPI_2) {
|
||||
__HAL_RCC_SPI2_FORCE_RESET();
|
||||
__HAL_RCC_SPI2_RELEASE_RESET();
|
||||
__HAL_RCC_SPI2_CLK_DISABLE();
|
||||
}
|
||||
#if defined SPI3_BASE
|
||||
if (obj->spi == SPI_3) {
|
||||
if (spiobj->spi == SPI_3) {
|
||||
__HAL_RCC_SPI3_FORCE_RESET();
|
||||
__HAL_RCC_SPI3_RELEASE_RESET();
|
||||
__HAL_RCC_SPI3_CLK_DISABLE();
|
||||
|
@ -161,7 +204,7 @@ void spi_free(spi_t *obj)
|
|||
#endif
|
||||
|
||||
#if defined SPI4_BASE
|
||||
if (obj->spi == SPI_4) {
|
||||
if (spiobj->spi == SPI_4) {
|
||||
__HAL_RCC_SPI4_FORCE_RESET();
|
||||
__HAL_RCC_SPI4_RELEASE_RESET();
|
||||
__HAL_RCC_SPI4_CLK_DISABLE();
|
||||
|
@ -169,7 +212,7 @@ void spi_free(spi_t *obj)
|
|||
#endif
|
||||
|
||||
#if defined SPI5_BASE
|
||||
if (obj->spi == SPI_5) {
|
||||
if (spiobj->spi == SPI_5) {
|
||||
__HAL_RCC_SPI5_FORCE_RESET();
|
||||
__HAL_RCC_SPI5_RELEASE_RESET();
|
||||
__HAL_RCC_SPI5_CLK_DISABLE();
|
||||
|
@ -177,7 +220,7 @@ void spi_free(spi_t *obj)
|
|||
#endif
|
||||
|
||||
#if defined SPI6_BASE
|
||||
if (obj->spi == SPI_6) {
|
||||
if (spiobj->spi == SPI_6) {
|
||||
__HAL_RCC_SPI6_FORCE_RESET();
|
||||
__HAL_RCC_SPI6_RELEASE_RESET();
|
||||
__HAL_RCC_SPI6_CLK_DISABLE();
|
||||
|
@ -185,45 +228,47 @@ void spi_free(spi_t *obj)
|
|||
#endif
|
||||
|
||||
// Configure GPIOs
|
||||
pin_function(obj->pin_miso, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0));
|
||||
pin_function(obj->pin_mosi, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0));
|
||||
pin_function(obj->pin_sclk, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0));
|
||||
pin_function(obj->pin_ssel, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0));
|
||||
pin_function(spiobj->pin_miso, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0));
|
||||
pin_function(spiobj->pin_mosi, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0));
|
||||
pin_function(spiobj->pin_sclk, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0));
|
||||
if (spiobj->nss != SPI_NSS_SOFT) {
|
||||
pin_function(spiobj->pin_ssel, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void spi_format(spi_t *obj, int bits, int mode, int slave)
|
||||
{
|
||||
struct spi_s *spiobj = SPI_S(obj);
|
||||
|
||||
DEBUG_PRINTF("spi_format, bits:%d, mode:%d, slave?:%d\r\n", bits, mode, slave);
|
||||
|
||||
// Save new values
|
||||
if (bits == 16) {
|
||||
obj->bits = SPI_DATASIZE_16BIT;
|
||||
} else {
|
||||
obj->bits = SPI_DATASIZE_8BIT;
|
||||
}
|
||||
spiobj->bits = bits;
|
||||
|
||||
switch (mode) {
|
||||
case 0:
|
||||
obj->cpol = SPI_POLARITY_LOW;
|
||||
obj->cpha = SPI_PHASE_1EDGE;
|
||||
spiobj->cpol = SPI_POLARITY_LOW;
|
||||
spiobj->cpha = SPI_PHASE_1EDGE;
|
||||
break;
|
||||
case 1:
|
||||
obj->cpol = SPI_POLARITY_LOW;
|
||||
obj->cpha = SPI_PHASE_2EDGE;
|
||||
spiobj->cpol = SPI_POLARITY_LOW;
|
||||
spiobj->cpha = SPI_PHASE_2EDGE;
|
||||
break;
|
||||
case 2:
|
||||
obj->cpol = SPI_POLARITY_HIGH;
|
||||
obj->cpha = SPI_PHASE_1EDGE;
|
||||
spiobj->cpol = SPI_POLARITY_HIGH;
|
||||
spiobj->cpha = SPI_PHASE_1EDGE;
|
||||
break;
|
||||
default:
|
||||
obj->cpol = SPI_POLARITY_HIGH;
|
||||
obj->cpha = SPI_PHASE_2EDGE;
|
||||
spiobj->cpol = SPI_POLARITY_HIGH;
|
||||
spiobj->cpha = SPI_PHASE_2EDGE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (obj->nss != SPI_NSS_SOFT) {
|
||||
obj->nss = (slave) ? SPI_NSS_HARD_INPUT : SPI_NSS_HARD_OUTPUT;
|
||||
if (spiobj->nss != SPI_NSS_SOFT) {
|
||||
spiobj->nss = (slave) ? SPI_NSS_HARD_INPUT : SPI_NSS_HARD_OUTPUT;
|
||||
}
|
||||
|
||||
obj->mode = (slave) ? SPI_MODE_SLAVE : SPI_MODE_MASTER;
|
||||
spiobj->mode = (slave) ? SPI_MODE_SLAVE : SPI_MODE_MASTER;
|
||||
|
||||
init_spi(obj);
|
||||
}
|
||||
|
@ -239,11 +284,14 @@ static const uint16_t baudrate_prescaler_table[] = {SPI_BAUDRATEPRESCALER_2,
|
|||
|
||||
void spi_frequency(spi_t *obj, int hz)
|
||||
{
|
||||
struct spi_s *spiobj = SPI_S(obj);
|
||||
int spi_hz = 0;
|
||||
uint8_t prescaler_rank = 0;
|
||||
|
||||
DEBUG_PRINTF("spi_frequency:%d\r\n", hz);
|
||||
|
||||
/* Get source clock depending on SPI instance */
|
||||
switch ((int)obj->spi) {
|
||||
switch ((int)spiobj->spi) {
|
||||
case SPI_1:
|
||||
#if defined SPI4_BASE
|
||||
case SPI_4:
|
||||
|
@ -275,7 +323,7 @@ void spi_frequency(spi_t *obj, int hz)
|
|||
}
|
||||
|
||||
if (prescaler_rank <= sizeof(baudrate_prescaler_table)/sizeof(baudrate_prescaler_table[0])) {
|
||||
obj->br_presc = baudrate_prescaler_table[prescaler_rank-1];
|
||||
spiobj->br_presc = baudrate_prescaler_table[prescaler_rank-1];
|
||||
} else {
|
||||
error("Couldn't setup requested SPI frequency");
|
||||
}
|
||||
|
@ -286,31 +334,35 @@ void spi_frequency(spi_t *obj, int hz)
|
|||
static inline int ssp_readable(spi_t *obj)
|
||||
{
|
||||
int status;
|
||||
SpiHandle.Instance = (SPI_TypeDef *)(obj->spi);
|
||||
struct spi_s *spiobj = SPI_S(obj);
|
||||
SPI_HandleTypeDef *handle = &(spiobj->handle);
|
||||
|
||||
// Check if data is received
|
||||
status = ((__HAL_SPI_GET_FLAG(&SpiHandle, SPI_FLAG_RXNE) != RESET) ? 1 : 0);
|
||||
status = ((__HAL_SPI_GET_FLAG(handle, SPI_FLAG_RXNE) != RESET) ? 1 : 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline int ssp_writeable(spi_t *obj)
|
||||
{
|
||||
int status;
|
||||
SpiHandle.Instance = (SPI_TypeDef *)(obj->spi);
|
||||
struct spi_s *spiobj = SPI_S(obj);
|
||||
SPI_HandleTypeDef *handle = &(spiobj->handle);
|
||||
|
||||
// Check if data is transmitted
|
||||
status = ((__HAL_SPI_GET_FLAG(&SpiHandle, SPI_FLAG_TXE) != RESET) ? 1 : 0);
|
||||
status = ((__HAL_SPI_GET_FLAG(handle, SPI_FLAG_TXE) != RESET) ? 1 : 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline void ssp_write(spi_t *obj, int value)
|
||||
{
|
||||
SPI_TypeDef *spi = (SPI_TypeDef *)(obj->spi);
|
||||
SPI_TypeDef *spi = SPI_INST(obj);
|
||||
while (!ssp_writeable(obj));
|
||||
spi->DR = (uint16_t)value;
|
||||
}
|
||||
|
||||
static inline int ssp_read(spi_t *obj)
|
||||
{
|
||||
SPI_TypeDef *spi = (SPI_TypeDef *)(obj->spi);
|
||||
SPI_TypeDef *spi = SPI_INST(obj);
|
||||
while (!ssp_readable(obj));
|
||||
return (int)spi->DR;
|
||||
}
|
||||
|
@ -318,8 +370,10 @@ static inline int ssp_read(spi_t *obj)
|
|||
static inline int ssp_busy(spi_t *obj)
|
||||
{
|
||||
int status;
|
||||
SpiHandle.Instance = (SPI_TypeDef *)(obj->spi);
|
||||
status = ((__HAL_SPI_GET_FLAG(&SpiHandle, SPI_FLAG_BSY) != RESET) ? 1 : 0);
|
||||
struct spi_s *spiobj = SPI_S(obj);
|
||||
SPI_HandleTypeDef *handle = &(spiobj->handle);
|
||||
|
||||
status = ((__HAL_SPI_GET_FLAG(handle, SPI_FLAG_BSY) != RESET) ? 1 : 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -336,14 +390,14 @@ int spi_slave_receive(spi_t *obj)
|
|||
|
||||
int spi_slave_read(spi_t *obj)
|
||||
{
|
||||
SPI_TypeDef *spi = (SPI_TypeDef *)(obj->spi);
|
||||
SPI_TypeDef *spi = SPI_INST(obj);
|
||||
while (!ssp_readable(obj));
|
||||
return (int)spi->DR;
|
||||
}
|
||||
|
||||
void spi_slave_write(spi_t *obj, int value)
|
||||
{
|
||||
SPI_TypeDef *spi = (SPI_TypeDef *)(obj->spi);
|
||||
SPI_TypeDef *spi = SPI_INST(obj);
|
||||
while (!ssp_writeable(obj));
|
||||
spi->DR = (uint16_t)value;
|
||||
}
|
||||
|
@ -353,4 +407,221 @@ int spi_busy(spi_t *obj)
|
|||
return ssp_busy(obj);
|
||||
}
|
||||
|
||||
#ifdef DEVICE_SPI_ASYNCH
|
||||
typedef enum {
|
||||
SPI_TRANSFER_TYPE_NONE = 0,
|
||||
SPI_TRANSFER_TYPE_TX = 1,
|
||||
SPI_TRANSFER_TYPE_RX = 2,
|
||||
SPI_TRANSFER_TYPE_TXRX = 3,
|
||||
} transfer_type_t;
|
||||
|
||||
|
||||
/// @returns the number of bytes transferred, or `0` if nothing transferred
|
||||
static int spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer_type, const void *tx, void *rx, size_t length)
|
||||
{
|
||||
struct spi_s *spiobj = SPI_S(obj);
|
||||
SPI_HandleTypeDef *handle = &(spiobj->handle);
|
||||
bool is16bit = (handle->Init.DataSize == SPI_DATASIZE_16BIT);
|
||||
// the HAL expects number of transfers instead of number of bytes
|
||||
// so for 16 bit transfer width the count needs to be halved
|
||||
size_t words;
|
||||
|
||||
DEBUG_PRINTF("SPI inst=0x%8X Start: %u, %u\r\n", (int)handle->Instance, transfer_type, length);
|
||||
|
||||
obj->spi.transfer_type = transfer_type;
|
||||
|
||||
if (is16bit) words = length / 2;
|
||||
else words = length;
|
||||
|
||||
// enable the interrupt
|
||||
IRQn_Type irq_n = spiobj->spiIRQ;
|
||||
NVIC_ClearPendingIRQ(irq_n);
|
||||
NVIC_DisableIRQ(irq_n);
|
||||
NVIC_SetPriority(irq_n, 1);
|
||||
NVIC_EnableIRQ(irq_n);
|
||||
|
||||
// enable the right hal transfer
|
||||
static uint16_t sink;
|
||||
int rc = 0;
|
||||
switch(transfer_type) {
|
||||
case SPI_TRANSFER_TYPE_TXRX:
|
||||
rc = HAL_SPI_TransmitReceive_IT(handle, (uint8_t*)tx, (uint8_t*)rx, words);
|
||||
break;
|
||||
case SPI_TRANSFER_TYPE_TX:
|
||||
// TODO: we do not use `HAL_SPI_Transmit_IT`, since it has some unknown bug
|
||||
// and makes the HAL keep some state and then that fails successive transfers
|
||||
// rc = HAL_SPI_Transmit_IT(handle, (uint8_t*)tx, words);
|
||||
rc = HAL_SPI_TransmitReceive_IT(handle, (uint8_t*)tx, (uint8_t*)&sink, 1);
|
||||
length = is16bit ? 2 : 1;
|
||||
break;
|
||||
case SPI_TRANSFER_TYPE_RX:
|
||||
// the receive function also "transmits" the receive buffer so in order
|
||||
// to guarantee that 0xff is on the line, we explicitly memset it here
|
||||
memset(rx, SPI_FILL_WORD, length);
|
||||
rc = HAL_SPI_Receive_IT(handle, (uint8_t*)rx, words);
|
||||
break;
|
||||
default:
|
||||
length = 0;
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
DEBUG_PRINTF("SPI: RC=%u\n", rc);
|
||||
length = 0;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// asynchronous API
|
||||
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
|
||||
{
|
||||
struct spi_s *spiobj = SPI_S(obj);
|
||||
SPI_HandleTypeDef *handle = &(spiobj->handle);
|
||||
|
||||
// TODO: DMA usage is currently ignored
|
||||
(void) hint;
|
||||
|
||||
// check which use-case we have
|
||||
bool use_tx = (tx != NULL && tx_length > 0);
|
||||
bool use_rx = (rx != NULL && rx_length > 0);
|
||||
bool is16bit = (handle->Init.DataSize == SPI_DATASIZE_16BIT);
|
||||
|
||||
// don't do anything, if the buffers aren't valid
|
||||
if (!use_tx && !use_rx)
|
||||
return;
|
||||
|
||||
// copy the buffers to the SPI object
|
||||
obj->tx_buff.buffer = (void *) tx;
|
||||
obj->tx_buff.length = tx_length;
|
||||
obj->tx_buff.pos = 0;
|
||||
obj->tx_buff.width = is16bit ? 16 : 8;
|
||||
|
||||
obj->rx_buff.buffer = rx;
|
||||
obj->rx_buff.length = rx_length;
|
||||
obj->rx_buff.pos = 0;
|
||||
obj->rx_buff.width = obj->tx_buff.width;
|
||||
|
||||
obj->spi.event = event;
|
||||
|
||||
DEBUG_PRINTF("SPI: Transfer: %u, %u\n", tx_length, rx_length);
|
||||
|
||||
// register the thunking handler
|
||||
IRQn_Type irq_n = spiobj->spiIRQ;
|
||||
NVIC_SetVector(irq_n, (uint32_t)handler);
|
||||
|
||||
// enable the right hal transfer
|
||||
if (use_tx && use_rx) {
|
||||
// transfer with the min(tx, rx), then later either transmit _or_ receive the remainder
|
||||
size_t size = (tx_length < rx_length)? tx_length : rx_length;
|
||||
spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_TXRX, tx, rx, size);
|
||||
} else if (use_tx) {
|
||||
spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_TX, tx, NULL, tx_length);
|
||||
} else if (use_rx) {
|
||||
spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_RX, NULL, rx, rx_length);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t spi_irq_handler_asynch(spi_t *obj)
|
||||
{
|
||||
// use the right instance
|
||||
struct spi_s *spiobj = SPI_S(obj);
|
||||
SPI_HandleTypeDef *handle = &spiobj->handle;
|
||||
int event = 0;
|
||||
|
||||
// call the CubeF4 handler, this will update the handle
|
||||
HAL_SPI_IRQHandler(handle);
|
||||
|
||||
if (HAL_SPI_GetState(handle) == HAL_SPI_STATE_READY) {
|
||||
// adjust buffer positions
|
||||
size_t tx_size = (handle->TxXferSize - handle->TxXferCount);
|
||||
size_t rx_size = (handle->RxXferSize - handle->RxXferCount);
|
||||
// 16 bit transfers need to be doubled to get bytes
|
||||
if (handle->Init.DataSize == SPI_DATASIZE_16BIT) {
|
||||
tx_size *= 2;
|
||||
rx_size *= 2;
|
||||
}
|
||||
// adjust buffer positions
|
||||
if (obj->spi.transfer_type != SPI_TRANSFER_TYPE_RX) {
|
||||
obj->tx_buff.pos += tx_size;
|
||||
}
|
||||
if (obj->spi.transfer_type != SPI_TRANSFER_TYPE_TX) {
|
||||
obj->rx_buff.pos += rx_size;
|
||||
}
|
||||
|
||||
if (handle->TxXferCount > 0) {DEBUG_PRINTF("SPI: TxXferCount: %u\n", handle->TxXferCount);}
|
||||
if (handle->RxXferCount > 0) {DEBUG_PRINTF("SPI: RxXferCount: %u\n", handle->RxXferCount);}
|
||||
|
||||
int error = HAL_SPI_GetError(handle);
|
||||
if(error != HAL_SPI_ERROR_NONE) {
|
||||
// something went wrong and the transfer has definitely completed
|
||||
event = SPI_EVENT_ERROR | SPI_EVENT_INTERNAL_TRANSFER_COMPLETE;
|
||||
|
||||
if (error & HAL_SPI_ERROR_OVR) {
|
||||
// buffer overrun
|
||||
event |= SPI_EVENT_RX_OVERFLOW;
|
||||
}
|
||||
} else {
|
||||
// figure out if we need to transfer more data:
|
||||
if (obj->tx_buff.pos < obj->tx_buff.length) {
|
||||
//DEBUG_PRINTF("t%u ", obj->tx_buff.pos);
|
||||
// we need to transfer more data
|
||||
spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_TX,
|
||||
obj->tx_buff.buffer + obj->tx_buff.pos, // offset the initial buffer by the position
|
||||
NULL, // there is no receive buffer
|
||||
obj->tx_buff.length - obj->tx_buff.pos); // transfer the remaining bytes only
|
||||
} else if (obj->rx_buff.pos < obj->rx_buff.length) {
|
||||
//DEBUG_PRINTF("r%u ", obj->rx_buff.pos);
|
||||
// we need to receive more data
|
||||
spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_RX,
|
||||
NULL, // there is no transmit buffer
|
||||
obj->rx_buff.buffer + obj->rx_buff.pos, // offset the initial buffer by the position
|
||||
obj->rx_buff.length - obj->rx_buff.pos); // transfer one byte at a time, until we received everything
|
||||
} else {
|
||||
// everything is ok, nothing else needs to be transferred
|
||||
event = SPI_EVENT_COMPLETE | SPI_EVENT_INTERNAL_TRANSFER_COMPLETE;
|
||||
DEBUG_PRINTF("SPI: Done: %u, %u\n", obj->tx_buff.pos, obj->rx_buff.pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event) DEBUG_PRINTF("SPI: Event: 0x%x\n", event);
|
||||
|
||||
return (event & (obj->spi.event | SPI_EVENT_INTERNAL_TRANSFER_COMPLETE));
|
||||
}
|
||||
|
||||
uint8_t spi_active(spi_t *obj)
|
||||
{
|
||||
struct spi_s *spiobj = SPI_S(obj);
|
||||
SPI_HandleTypeDef *handle = &(spiobj->handle);
|
||||
HAL_SPI_StateTypeDef state = HAL_SPI_GetState(handle);
|
||||
|
||||
switch(state) {
|
||||
case HAL_SPI_STATE_RESET:
|
||||
case HAL_SPI_STATE_READY:
|
||||
case HAL_SPI_STATE_ERROR:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void spi_abort_asynch(spi_t *obj)
|
||||
{
|
||||
struct spi_s *spiobj = SPI_S(obj);
|
||||
SPI_HandleTypeDef *handle = &(spiobj->handle);
|
||||
|
||||
// disable interrupt
|
||||
IRQn_Type irq_n = spiobj->spiIRQ;
|
||||
NVIC_ClearPendingIRQ(irq_n);
|
||||
NVIC_DisableIRQ(irq_n);
|
||||
|
||||
// clean-up
|
||||
__HAL_SPI_DISABLE(handle);
|
||||
HAL_SPI_DeInit(handle);
|
||||
HAL_SPI_Init(handle);
|
||||
__HAL_SPI_ENABLE(handle);
|
||||
}
|
||||
|
||||
#endif //DEVICE_SPI_ASYNCH
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue