From 05e2b8becc7b0f6740be0fbda7bfb7141d27758a Mon Sep 17 00:00:00 2001 From: Rocco Marco Guglielmi Date: Tue, 22 Oct 2019 08:22:46 +0000 Subject: [PATCH] Added SPI IRQ based implementation (Still not totally tested) git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@13134 27425a3e-05d8-49a3-a47f-9c15f0e5edd8 --- os/hal/ports/ADUCM/ADUCM36x/hal_spi_lld.c | 411 ++++++++++++++++++++++ os/hal/ports/ADUCM/ADUCM36x/hal_spi_lld.h | 231 ++++++++++++ os/hal/ports/ADUCM/ADUCM36x/platform.mk | 1 + 3 files changed, 643 insertions(+) create mode 100644 os/hal/ports/ADUCM/ADUCM36x/hal_spi_lld.c create mode 100644 os/hal/ports/ADUCM/ADUCM36x/hal_spi_lld.h diff --git a/os/hal/ports/ADUCM/ADUCM36x/hal_spi_lld.c b/os/hal/ports/ADUCM/ADUCM36x/hal_spi_lld.c new file mode 100644 index 000000000..9edeb9247 --- /dev/null +++ b/os/hal/ports/ADUCM/ADUCM36x/hal_spi_lld.c @@ -0,0 +1,411 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file ADUCM36x/hal_spi_lld.c + * @brief ADUCM SPI subsystem low level driver source. + * + * @addtogroup SPI + * @{ + */ + +#include "hal.h" + +#if HAL_USE_SPI || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define ADUCM_SPI_DIV_MASK 0x003FU + +#define ADUCM_SPI_STA_IRQ 0x0001U +#define ADUCM_SPI_STA_TX 0x0020U + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief SPI0 driver identifier.*/ +#if ADUCM_SPI_USE_SPI0 || defined(__DOXYGEN__) +SPIDriver SPID0; +#endif + +/** @brief SPI1 driver identifier.*/ +#if ADUCM_SPI_USE_SPI1 || defined(__DOXYGEN__) +SPIDriver SPID1; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Shared interrupt service routine. + * + * @param[in] spip pointer to the @p SPIDriver object + */ +static void spi_lld_serve_interrupt(SPIDriver *spip) { + + uint32_t sta = spip->spi->SPISTA; + uint8_t dummy_rx; + uint8_t dummy_tx = 0xFFU; + + if((sta & ADUCM_SPI_STA_TX) && (spip->size > 0)) { + /* Decreasing the size. */ + (spip->size)--; + + /* Retrieving the RX. */ + if(spip->rxbuf != NULL) { + *(spip->rxbuf) = spip->spi->SPIRX; + (spip->rxbuf)++; + } + else { + dummy_rx = spip->spi->SPIRX; + } + + /* Pushing the new TX: this will start a new transfert. */ + if(spip->txbuf != NULL) { + spip->spi->SPITX = *(spip->txbuf); + (spip->txbuf)++; + } + else { + spip->spi->SPITX = dummy_tx; + } + + (void)dummy_rx; + } + + if(spip->size == 0) { + /* Portable SPI ISR code defined in the high level driver, note, it is + a macro.*/ + _spi_isr_code(spip); + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if ADUCM_SPI_USE_SPI0 || defined(__DOXYGEN__) +#if !defined(ADUCM_SPI0_HANDLER) +#error "ADUCM_SPI0_HANDLER not defined" +#endif +/** + * @brief SPI0 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(ADUCM_SPI0_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + spi_lld_serve_interrupt(&SPID0); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if ADUCM_SPI_USE_SPI1 || defined(__DOXYGEN__) +#if !defined(ADUCM_SPI1_HANDLER) +#error "ADUCM_SPI1_HANDLER not defined" +#endif +/** + * @brief SPI1 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(ADUCM_SPI1_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + spi_lld_serve_interrupt(&SPID1); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level SPI driver initialization. + * + * @notapi + */ +void spi_lld_init(void) { + +#if ADUCM_SPI_USE_SPI0 + spiObjectInit(&SPID0); + SPID0.spi = pADI_SPI0; + SPID0.rxbuf = NULL; + SPID0.txbuf = NULL; + SPID0.size = 0; +#endif + +#if ADUCM_SPI_USE_SPI1 + spiObjectInit(&SPID1); + SPID1.spi = pADI_SPI1; + SPID1.rxbuf = NULL; + SPID1.txbuf = NULL; + SPID1.size = 0; +#endif +} + +/** + * @brief Configures and activates the SPI peripheral. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_start(SPIDriver *spip) { + + /* If in stopped state then enables the SPI and DMA clocks.*/ + if (spip->state == SPI_STOP) { +#if ADUCM_SPI_USE_SPI0 + if (&SPID0 == spip) { + /* Enabling peripheral clock branch. */ + ccEnableSPI0(); + + /* Enabling peripheral interrupt. */ + nvicEnableVector(ADUCM_SPI0_NUMBER, ADUCM_SPI_SPI0_IRQ_PRIORITY); + } +#endif +#if ADUCM_SPI_USE_SPI1 + if (&SPID1 == spip) { + /* Enabling peripheral clock branch. */ + ccEnableSPI1(); + + /* Enabling peripheral interrupt. */ + nvicEnableVector(ADUCM_SPI1_NUMBER, ADUCM_SPI_SPI1_IRQ_PRIORITY); + } +#endif + } + + /* SPI clock divider configuration.*/ + spip->spi->SPIDIV = (spip->config->div & ADUCM_SPI_DIV_MASK); + + /* SPI enabling and configuration. Note that some configuration are + enforced to ensure the IRQ proper behavior. */ + spip->spi->SPICON = spip->config->con | ADUCM_SPI_CON_TIM | + ADUCM_SPI_CON_MASEN; + spip->spi->SPICON &= ~ADUCM_SPI_CON_MOD_MASK; + spip->spi->SPICON |= ADUCM_SPI_CON_ENABLE; +} + +/** + * @brief Deactivates the SPI peripheral. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_stop(SPIDriver *spip) { + + /* If in ready state then disables the SPI clock.*/ + if (spip->state == SPI_READY) { + + /* SPI disable.*/ + spip->spi->SPICON &= ~ADUCM_SPI_CON_ENABLE; + spip->spi->SPICON = 0; + spip->spi->SPIDIV = 0; + spip->rxbuf = NULL; + spip->txbuf = NULL; + spip->size = 0; + +#if ADUCM_SPI_USE_SPI0 + if (&SPID0 == spip) + ccDisableSPI0(); +#endif +#if ADUCM_SPI_USE_SPI1 + if (&SPID1 == spip) + ccDisableSPI1(); +#endif + } +} + +#if (SPI_SELECT_MODE == SPI_SELECT_MODE_LLD) || defined(__DOXYGEN__) +/** + * @brief Asserts the slave select signal and prepares for transfers. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_select(SPIDriver *spip) { + + /* No implementation on ADUCM.*/ +} + +/** + * @brief Deasserts the slave select signal. + * @details The previously selected peripheral is unselected. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_unselect(SPIDriver *spip) { + + /* No implementation on ADUCM.*/ +} +#endif + +/** + * @brief Ignores data on the SPI bus. + * @details This asynchronous function starts the transmission of a series of + * idle words on the SPI bus and ignores the received data. + * @post At the end of the operation the configured callback is invoked. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to be ignored + * + * @notapi + */ +void spi_lld_ignore(SPIDriver *spip, size_t n) { + uint8_t dummy_tx = 0xFFU; + + spip->txbuf = NULL; + spip->rxbuf = NULL; + spip->size = n; + spip->spi->SPITX = dummy_tx; +} + +/** + * @brief Exchanges data on the SPI bus. + * @details This asynchronous function starts a simultaneous transmit/receive + * operation. + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below or + * equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to be exchanged + * @param[in] txbuf the pointer to the transmit buffer + * @param[out] rxbuf the pointer to the receive buffer + * + * @notapi + */ +void spi_lld_exchange(SPIDriver *spip, size_t n, + const void *txbuf, void *rxbuf) { + spip->txbuf = (uint8_t*)txbuf; + spip->rxbuf = (uint8_t*)rxbuf; + spip->size = n; + spip->spi->SPITX = *(spip->txbuf); +} + +/** + * @brief Sends data over the SPI bus. + * @details This asynchronous function starts a transmit operation. + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below or + * equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to send + * @param[in] txbuf the pointer to the transmit buffer + * + * @notapi + */ +void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) { + spip->txbuf = (uint8_t*)txbuf; + spip->rxbuf = NULL; + spip->size = n; + spip->spi->SPITX = *(spip->txbuf); +} + +/** + * @brief Receives data from the SPI bus. + * @details This asynchronous function starts a receive operation. + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below or + * equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to receive + * @param[out] rxbuf the pointer to the receive buffer + * + * @notapi + */ +void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) { + uint8_t dummy_tx = 0xFFU; + + spip->txbuf = NULL; + spip->rxbuf = (uint8_t*) rxbuf; + spip->size = n; + spip->spi->SPITX = dummy_tx; +} + +/** + * @brief Exchanges one frame using a polled wait. + * @details This synchronous function exchanges one frame using a polled + * synchronization method. This function is useful when exchanging + * small amount of data on high speed channels, usually in this + * situation is much more efficient just wait for completion using + * polling than suspending the thread waiting for an interrupt. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] frame the data frame to send over the SPI bus + * @return The received data frame from the SPI bus. + */ +uint8_t spi_lld_polled_exchange(SPIDriver *spip, uint8_t frame) { + uint32_t sta = spip->spi->SPISTA; + +#if ADUCM_SPI_USE_SPI0 + if (&SPID0 == spip) { + /* Disabling ISR. */ + nvicDisableVector(ADUCM_SPI0_NUMBER); + } +#endif + +#if ADUCM_SPI_USE_SPI1 + if (&SPID1 == spip) { + /* Disabling ISR. */ + nvicDisableVector(ADUCM_SPI1_NUMBER); + } +#endif + + spip->spi->SPITX = frame; + while((sta & (ADUCM_SPI_STA_TX | ADUCM_SPI_STA_IRQ))) { + sta = spip->spi->SPISTA; + } + +#if ADUCM_SPI_USE_SPI0 + if (&SPID0 == spip) { + /* Re-enabling peripheral interrupt. */ + nvicEnableVector(ADUCM_SPI0_NUMBER, ADUCM_SPI_SPI0_IRQ_PRIORITY); + } +#endif + +#if ADUCM_SPI_USE_SPI1 + if (&SPID1 == spip) { + /* Re-enabling peripheral interrupt. */ + nvicEnableVector(ADUCM_SPI1_NUMBER, ADUCM_SPI_SPI1_IRQ_PRIORITY); + } +#endif + + return spip->spi->SPIRX; +} + +#endif /* HAL_USE_SPI */ + +/** @} */ diff --git a/os/hal/ports/ADUCM/ADUCM36x/hal_spi_lld.h b/os/hal/ports/ADUCM/ADUCM36x/hal_spi_lld.h new file mode 100644 index 000000000..56c2c24b7 --- /dev/null +++ b/os/hal/ports/ADUCM/ADUCM36x/hal_spi_lld.h @@ -0,0 +1,231 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file ADUCM36x/hal_spi_lld.h + * @brief ADUCM SPI subsystem low level driver header. + * + * @addtogroup SPI + * @{ + */ + +#ifndef HAL_SPI_LLD_H +#define HAL_SPI_LLD_H + +#if HAL_USE_SPI || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Circular mode support flag. + */ +#define SPI_SUPPORTS_CIRCULAR FALSE + +/** + * @name Register helpers not found in ADI headers + * @{ + */ +#define ADUCM_SPI_CON_ENABLE 0x0001U +#define ADUCM_SPI_CON_MASEN 0x0002U +#define ADUCM_SPI_CON_CPHA 0x0004U +#define ADUCM_SPI_CON_CPOL 0x0008U +#define ADUCM_SPI_CON_WOM 0x0010U +#define ADUCM_SPI_CON_LSB 0x0020U +#define ADUCM_SPI_CON_TIM 0x0040U +#define ADUCM_SPI_CON_ZEN 0x0080U +#define ADUCM_SPI_CON_RXOF 0x0100U +#define ADUCM_SPI_CON_OEN 0x0200U +#define ADUCM_SPI_CON_LOOPBACK 0x0400U +#define ADUCM_SPI_CON_CON 0x0800U +#define ADUCM_SPI_CON_RFLUSH 0x1000U +#define ADUCM_SPI_CON_TFLUSH 0x2000U +#define ADUCM_SPI_CON_MOD_MASK 0xC000U +#define ADUCM_SPI_CON_MOD_TX1RX1 0x0000U +#define ADUCM_SPI_CON_MOD_TX2RX2 0x4000U +#define ADUCM_SPI_CON_MOD_TX3RX3 0x8000U +#define ADUCM_SPI_CON_MOD_TX4RX4 0xC000U + +#define ADUCM_SPI_DIV_0 0x0001U +#define ADUCM_SPI_DIV_1 0x0002U +#define ADUCM_SPI_DIV_2 0x0004U +#define ADUCM_SPI_DIV_3 0x0008U +#define ADUCM_SPI_DIV_4 0x00F0U +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief SPI0 driver enable switch. + * @details If set to @p TRUE the support for SPI0 is included. + * @note The default is @p FALSE. + */ +#if !defined(ADUCM_SPI_USE_SPI0) || defined(__DOXYGEN__) +#define ADUCM_SPI_USE_SPI0 FALSE +#endif + +/** + * @brief SPI1 driver enable switch. + * @details If set to @p TRUE the support for SPI1 is included. + * @note The default is @p FALSE. + */ +#if !defined(ADUCM_SPI_USE_SPI1) || defined(__DOXYGEN__) +#define ADUCM_SPI_USE_SPI1 FALSE +#endif + +/** + * @brief SPI0 interrupt priority level setting. + */ +#if !defined(ADUCM_SPI_SPI0_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define ADUCM_SPI_SPI0_IRQ_PRIORITY 5 +#endif + +/** + * @brief SPI1 interrupt priority level setting. + */ +#if !defined(ADUCM_SPI_SPI1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define ADUCM_SPI_SPI1_IRQ_PRIORITY 5 +#endif + +/** + * @brief Enable SPI DMA support + */ +#if !defined(ADUCM_SPI_USE_DMA) || defined(__DOXYGEN__) +#define ADUCM_SPI_USE_DMA FALSE +#endif + +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if !ADUCM_SPI_USE_SPI0 && !ADUCM_SPI_USE_SPI1 +#error "SPI driver activated but no SPI peripheral assigned" +#endif + +#if ADUCM_SPI_USE_SPI0 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(ADUCM_SPI_SPI0_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI0" +#endif + +#if ADUCM_SPI_USE_SPI1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(ADUCM_SPI_SPI1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI1" +#endif + +#if ADUCM_SPI_USE_DMA +#error "ADuCM SPI driver implementation does not supports DMA yet." +#endif + +#if ADUCM_SPI_USE_DMA && ADUCM_SPI_USE_SPI0 +#error "ADuCM SPI0 does not supports DMA." +#endif + +#if (ADUCM_SPI_USE_DMA == TRUE) && !defined(ADUCM_DMA_REQUIRED) +#define ADUCM_DMA_REQUIRED +#endif + +#if (SPI_USE_CIRCULAR == TRUE) && (SPI_SUPPORTS_CIRCULAR == FALSE) +#error "ADuCM SPI does not support circular mode." +#endif + +#if SPI_SELECT_MODE == SPI_SELECT_MODE_LLD +#error "SPI_SELECT_MODE_LLD not supported by this driver" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Low level fields of the SPI driver structure. + */ +#if ADUCM_SPI_USE_DMA +#define spi_lld_driver_fields \ + /* Empty placeholder for DMA based implementation. */ +#else +#define spi_lld_driver_fields \ + /* Pointer to the SPIx registers block.*/ \ + ADI_SPI_TypeDef *spi; \ + /* Receive buffer pointer.*/ \ + uint8_t *rxbuf; \ + /* Transmission buffer pointer.*/ \ + uint8_t *txbuf; \ + /* Transfert size.*/ \ + uint32_t size; +#endif /* ADUCM_SPI_USE_DMA */ + +/** + * @brief Low level fields of the SPI configuration structure. + */ +#define spi_lld_config_fields \ + /* SPIxCON register initialization data.*/ \ + uint16_t con; \ + /* SPIxDIV register initialization data.*/ \ + uint16_t div + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if ADUCM_SPI_USE_SPI0 && !defined(__DOXYGEN__) +extern SPIDriver SPID0; +#endif + +#if ADUCM_SPI_USE_SPI1 && !defined(__DOXYGEN__) +extern SPIDriver SPID1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void spi_lld_init(void); + void spi_lld_start(SPIDriver *spip); + void spi_lld_stop(SPIDriver *spip); +#if (SPI_SELECT_MODE == SPI_SELECT_MODE_LLD) || defined(__DOXYGEN__) + void spi_lld_select(SPIDriver *spip); + void spi_lld_unselect(SPIDriver *spip); +#endif + void spi_lld_ignore(SPIDriver *spip, size_t n); + void spi_lld_exchange(SPIDriver *spip, size_t n, + const void *txbuf, void *rxbuf); + void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf); + void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf); +#if (SPI_SUPPORTS_CIRCULAR == TRUE) || defined(__DOXYGEN__) + void spi_lld_abort(SPIDriver *spip); +#endif + uint8_t spi_lld_polled_exchange(SPIDriver *spip, uint8_t frame); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_SPI */ + +#endif /* HAL_SPI_LLD_H */ + +/** @} */ diff --git a/os/hal/ports/ADUCM/ADUCM36x/platform.mk b/os/hal/ports/ADUCM/ADUCM36x/platform.mk index 52a1beb61..6f7debbe7 100644 --- a/os/hal/ports/ADUCM/ADUCM36x/platform.mk +++ b/os/hal/ports/ADUCM/ADUCM36x/platform.mk @@ -4,6 +4,7 @@ PLATFORMSRC := $(CHIBIOS)/os/hal/ports/common/ARMCMx/nvic.c \ $(CHIBIOS)/os/hal/ports/ADUCM/ADUCM36x/hal_lld.c \ $(CHIBIOS)/os/hal/ports/ADUCM/ADUCM36x/hal_pal_lld.c \ $(CHIBIOS)/os/hal/ports/ADUCM/ADUCM36x/hal_serial_lld.c \ + $(CHIBIOS)/os/hal/ports/ADUCM/ADUCM36x/hal_spi_lld.c \ $(CHIBIOS)/os/hal/ports/ADUCM/ADUCM36x/hal_st_lld.c # Required include directories.