diff --git a/os/hal/ports/AVR/XMEGA/LLD/SPIv1/driver.mk b/os/hal/ports/AVR/XMEGA/LLD/SPIv1/driver.mk new file mode 100644 index 000000000..07d9088c3 --- /dev/null +++ b/os/hal/ports/AVR/XMEGA/LLD/SPIv1/driver.mk @@ -0,0 +1,9 @@ +ifeq ($(USE_SMART_BUILD),yes) +ifneq ($(findstring HAL_USE_SPI TRUE,$(HALCONF)),) +PLATFORMSRC += $(CHIBIOS)/os/hal/ports/AVR/XMEGA/LLD/SPIv1/hal_spi_lld.c +endif +else +PLATFORMSRC += $(CHIBIOS)/os/hal/ports/AVR/XMEGA/LLD/SPIv1/hal_spi_lld.c +endif + +PLATFORMINC += $(CHIBIOS)/os/hal/ports/AVR/XMEGA/LLD/SPIv1 diff --git a/os/hal/ports/AVR/XMEGA/LLD/SPIv1/hal_spi_lld.c b/os/hal/ports/AVR/XMEGA/LLD/SPIv1/hal_spi_lld.c new file mode 100644 index 000000000..12c953a10 --- /dev/null +++ b/os/hal/ports/AVR/XMEGA/LLD/SPIv1/hal_spi_lld.c @@ -0,0 +1,409 @@ +/* + 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 SPIv1/hal_spi_lld.c + * @brief AVR/MEGA SPI subsystem low level driver source. + * + * @addtogroup SPI + * @{ + */ + +#include "hal.h" + +#if HAL_USE_SPI || defined(__DOXYGEN__) + +/*==========================================================================*/ +/* Driver local definitions. */ +/*==========================================================================*/ + +#define DUMMY_SPI_SEND_VALUE 0xFF + +/*==========================================================================*/ +/* Driver exported variables. */ +/*==========================================================================*/ + +/** + * @brief SPI1 driver identifier. + */ +#if AVR_SPI_USE_SPI1 || defined(__DOXYGEN__) +SPIDriver SPID1; +#endif + +/** + * @brief SPI2 driver identifier. + */ +#if AVR_SPI_USE_SPI2 || defined(__DOXYGEN__) +SPIDriver SPID2; +#endif + +/*==========================================================================*/ +/* Driver local variables and types. */ +/*==========================================================================*/ + +/*==========================================================================*/ +/* Driver local functions. */ +/*==========================================================================*/ +/* +// Configure the speed of the SPI interface. +static void spi_set_speed(uint8_t ds) { + // We must make a test to see if we are in master mode. + + // ds = double speed; + if (ds == SPI_SPEED_DOUBLE) { + SPIC.CTRL |= (1 << SPI_CLK2X_bp); // double speed. + } + else { + SPIC.CTRL &= ~(1 << SPI_CLK2X_bp); // simple speed. + } +} +*/ + + +// Enable the SPI module. +//static void spi_lld_enable(SPIDriver *spip) { +// spip->spi->CTRL |= (1 << SPI_ENABLE_bp); +//} + + +/* +// Disable the SPI interface. +static void spi_disable(void) { + SPIC.CTRL &= ~(1 << SPI_ENABLE_bp); +} +*/ + +/* +// Configure the SPI bit order, LSB/MSB first. +static void spi_set_bit_order(uint8_t bo) { + // bo = bit order + if (bo == SPI_MSB_FIRST) + SPIC.CTRL &= ~(1 << SPI_DORD_bp); + else + SPIC.CTRL |= (1 << SPI_DORD_bp); +} + +// Configure the SPI interface to Master or slave. +static void spi_set_mode(uint8_t mode) { + if (mode == SPI_MODE_SLAVE) + SPIC.CTRL &= ~(1 << SPI_MASTER_bp); + else + SPIC.CTRL |= (1 << SPI_MASTER_bp); +} + +static void spi_set_stransfer_mode(SPI_MODE_t mode) { + switch(mode) { + case SPI_TRANSFER_MODE0: + SPIC.CTRL = (SPIC.CTRL & ~SPI_MODE_gm) | SPI_MODE_0_gc; + break; + + case SPI_TRANSFER_MODE1: + SPIC.CTRL = (SPIC.CTRL & ~SPI_MODE_gm) | SPI_MODE_1_gc; + break; + + case SPI_TRANSFER_MODE2: + SPIC.CTRL = (SPIC.CTRL & ~SPI_MODE_gm) | SPI_MODE_2_gc; + break; + + case SPI_TRANSFER_MODE3: + SPIC.CTRL = (SPIC.CTRL & ~SPI_MODE_gm) | SPI_MODE_3_gc; + break; + + default: + SPIC.CTRL = (SPIC.CTRL & ~SPI_MODE_gm) | SPI_MODE_0_gc; + break; + } +}*/ + +/** + * @brief Configure the SPI colck from the System clock. + * + * @param[in] prescaler the prescaler used to divide the system clock + *//* +static void spi_set_clock_prescaler(SPI_PRESCALER_t prescaler) { + switch(prescaler) { + case SPI_PRESCALER_4: + SPIC.CTRL = (SPIC.CTRL & ~SPI_PRESCALER_gm) | SPI_PRESCALER_DIV4_gc; + break; + + case SPI_PRESCALER_16: + SPIC.CTRL = (SPIC.CTRL & ~SPI_PRESCALER_gm) | SPI_PRESCALER_DIV4_gc; + break; + + case SPI_PRESCALER_64: + SPIC.CTRL = (SPIC.CTRL & ~SPI_PRESCALER_gm) | SPI_PRESCALER_DIV4_gc; + break; + + case SPI_PRESCALER_128: + SPIC.CTRL = (SPIC.CTRL & ~SPI_PRESCALER_gm) | SPI_PRESCALER_DIV4_gc; + break; + + default: + break; + } +} +*/ + +/** + * @brief Enable/Disable the interruption and set the interrupt level. + * + * @param[in] il the interrupt level + */ +/*static void spi_set_irq_level(SPI_INTLVL_t il) { + switch(il) { + case SPI_INT_DISABLE: + SPIC.INTCTRL = (SPIC.INTCTRL & ~SPI_INTLVL_gm) | SPI_INTLVL_OFF_gc; + break; + + case SPI_INT_LEVEL_LOW: + SPIC.INTCTRL = (SPIC.INTCTRL & ~SPI_INTLVL_gm) | SPI_INTLVL_LO_gc; + break; + + case SPI_INT_LEVEL_MEDIUM: + SPIC.INTCTRL = (SPIC.INTCTRL & ~SPI_INTLVL_gm) | SPI_INTLVL_MED_gc; + break; + + case SPI_INT_LEVEL_HIGH: + SPIC.INTCTRL = (SPIC.INTCTRL & ~SPI_INTLVL_gm) | SPI_INTLVL_HI_gc; + break; + + default: + SPIC.INTCTRL = (SPIC.INTCTRL & ~SPI_INTLVL_gm) | SPI_INTLVL_OFF_gc; + break; + } +} + + +void spi_send_byte(uint8_t data) { + SPIC.DATA = data; + while(!(SPIC.STATUS & SPI_IF_bm)); +} +*/ +/* + spi_set_speed(SPI_SPEED_SIMPLE); // Configure the speed double. + spi_set_bit_order(SPI_MSB_FIRST); // Configure the bit order. + spi_set_mode(SPI_MODE_MASTER); // Configure the mode to master. + spi_set_stransfer_mode(SPI_TRANSFER_MODE0); // Configure the transfer mode. + spi_set_clock_prescaler(SPI_PRESCALER_4); // Configure the clock prescaler. + spi_set_irq_level(SPI_INT_LEVEL_LOW); // Configure the irq level. + spi_enable(); // Enable the SPI interface. + + //spi_select(); // For the chip select. // TODO + spi_send_byte(0xAA); // For the chip select. + //spi_deselect(); // for the chip select. // TODO + */ +/*==========================================================================*/ +/* Driver interrupt handlers. */ +/*==========================================================================*/ + +#if AVR_SPI_USE_SPI1 || defined(__DOXYGEN__) +/** + * @brief SPI event interrupt handler. + * + * @notapi + *//* +OSAL_IRQ_HANDLER(SPI_STC_vect) { + OSAL_IRQ_PROLOGUE(); + + SPIDriver *spip = &SPID1; + + // A new value has arrived, store it if we are interested in it. + if (spip->rxbuf) spip->rxbuf[spip->exidx] = SPDR; + + // Check if we are done. + if (++(spip->exidx) >= spip->exbytes) { + _spi_isr_code(spip); + } else { // If not done send the next byte. + if (spip->txbuf) { // If there is a buffer with values to be send then use it. + SPDR = spip->txbuf[spip->exidx]; + } else { // If there isn't a buffer with values to be send then send a the dummy value. + SPDR = DUMMY_SPI_SEND_VALUE; + } + } + OSAL_IRQ_EPILOGUE(); +}*/ +#endif /* AVR_SPI_USE_SPI1 */ + +/*==========================================================================*/ +/* Driver exported functions. */ +/*==========================================================================*/ + +/** + * @brief Low level SPI driver initialization. + * + * @notapi + */ +void spi_lld_init(void) { + +#if AVR_SPI_USE_SPI1 + /* Driver initialization. */ + spiObjectInit(&SPID1); + SPID1.spi = &SPIC; +#endif /* AVR_SPI_USE_SPI1 */ + +#if AVR_SPI_USE_SPI2 + /* Driver initialization. */ + spiObjectInit(&SPID2); + SPID2.spi = &SPID; +#endif /* AVR_SPI_USE_SPI2 */ +} + +/** + * @brief Configures and activates the SPI peripheral. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_start(SPIDriver *spip) { + + uint8_t dummy; + + if (&SPID1 == spip) { + // Configures the peripheral. + // Note that some bits are forced: + // SPI interrupt disabled, + // SPI enabled, + // SPI master enabled. + + spip->spi->INTCTRL = SPI_INTLVL_OFF_gc; + + spip->spi->CTRL = (spip->config->clk2x ? SPI_CLK2X_bm : 0) | + (SPI_ENABLE_bm) | + (spip->config->dord ? SPI_DORD_bm : 0) | + (spip->config->master ? SPI_MASTER_bm : 0) | + (spip->config->mode) | + (spip->config->prescaler); + + // Dummy reads before enabling interrupt. + dummy = spip->spi->STATUS; + dummy = spip->spi->DATA; + (void) dummy; // Suppress warning about unused variable. + + // Enable SPI interrupts. + spip->spi->INTCTRL = spip->config->irqlevel; + } +} + +/** + * @brief Deactivates the SPI peripheral. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_stop(SPIDriver *spip) { + if (spip->state == SPI_READY) { + // Disable the peripheral. + spip->spi->CTRL &= ~(1 << SPI_ENABLE_bp); + } +} + +/** + * @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) { + + /** + * NOTE: This should only be called in master mode. + */ + //spip->config->ssport->out &= ~(1 << spip->config->sspad); + +} + +/** + * @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) { + + /** + * NOTE: This should only be called in master mode. + */ + //spip->config->ssport->out |= (1 << spip->config->sspad); + +} + + +/** + * @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 = txbuf; + spip->rxbuf = rxbuf; + spip->exidx = 0; + spip->exbytes = n; + spip->spi->DATA = (spip->txbuf ? spip->txbuf[0] : DUMMY_SPI_SEND_VALUE); +} + + +/** + * @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) { + + uint8_t spdr = 0; + uint8_t dummy; + (void)spip; + + // Disable interrupt. + spip->spi->INTCTRL = SPI_INTLVL_OFF_gc; + + spip->spi->DATA = frame; + while (!(spip->spi->STATUS & SPI_IF_bm)) ; + spdr = spip->spi->DATA; + + dummy = spip->spi->STATUS; + dummy = spip->spi->DATA; + (void) dummy; // Suppress warning about unused variable. + spip->spi->INTCTRL = SPI_INTLVL_LO_gc; + + return spdr; +} + +#endif /* HAL_USE_SPI */ + +/** @} */ diff --git a/os/hal/ports/AVR/XMEGA/LLD/SPIv1/hal_spi_lld.h b/os/hal/ports/AVR/XMEGA/LLD/SPIv1/hal_spi_lld.h new file mode 100644 index 000000000..11f806ff9 --- /dev/null +++ b/os/hal/ports/AVR/XMEGA/LLD/SPIv1/hal_spi_lld.h @@ -0,0 +1,309 @@ +/* + ChibiOS - Copyright (C) 2016..2018 Theodore Ateba + + 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 SPIv1/hal_spi_lld.h + * @brief AVR/XMEGA 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. */ +/*==========================================================================*/ + +/** + * @name SPI Configuration Register + * @{ + */ +/*#define SPI_CR_SPIE (1 << SPIE) + +#define SPI_CR_SPE (1 << SPE) + +#define SPI_CR_DORD_MSB_FIRST (0 << DORD) +#define SPI_CR_DORD_LSB_FIRST (1 << DORD) + +#define SPI_CR_MSTR (1 << MSTR) + +#define SPI_CR_CPOL_CPHA_MODE(n) ((n) << CPHA) + +#define SPI_CR_SCK_FOSC_2 (0 << SPR0) +#define SPI_CR_SCK_FOSC_4 (0 << SPR0) +#define SPI_CR_SCK_FOSC_8 (1 << SPR0) +#define SPI_CR_SCK_FOSC_16 (1 << SPR0) +#define SPI_CR_SCK_FOSC_32 (2 << SPR0) +#define SPI_CR_SCK_FOSC_64 (2 << SPR0) +#define SPI_CR_SCK_FOSC_128 (3 << SPR0) +*/ + + +#define SPI_SPEED_SIMPLE 0 +#define SPI_SPEED_DOUBLE 1 + +#define SPI_MODE_MASTER 1 +#define SPI_MODE_SLAVE 0 + +#define SPI_LSB_FIRST 1 +#define SPI_MSB_FIRST 0 + +#define SPI_TRANSFER_MODE0 (0x00 << 2) +#define SPI_TRANSFER_MODE1 (0x01 << 2) +#define SPI_TRANSFER_MODE2 (0x02 << 2) +#define SPI_TRANSFER_MODE3 (0x03 << 2) + +#define SPI_PRESCALER_4 (0x00 << 0) +#define SPI_PRESCALER_16 (0x01 << 0) +#define SPI_PRESCALER_64 (0x02 << 0) +#define SPI_PRESCALER_128 (0x03 << 0) + +#define SPI_INT_DISABLE (0x00 << 0) +#define SPI_INT_LEVEL_LOW (0x01 << 0) +#define SPI_INT_LEVEL_MEDIUM (0x02 << 0) +#define SPI_INT_LEVEL_HIGH (0x03 << 0) + +/** @} */ + +/** + * @name SPI Status Register + * { + *//* +#define SPI_SR_SPIF (1 << SPIF) + +#define SPI_SR_WCOL (1 << WCOL) + +#define SPI_SR_SCK_FOSC_2 (1 << SPI2X) +#define SPI_SR_SCK_FOSC_4 (0 << SPI2X) +#define SPI_SR_SCK_FOSC_8 (1 << SPI2X) +#define SPI_SR_SCK_FOSC_16 (0 << SPI2X) +#define SPI_SR_SCK_FOSC_32 (1 << SPI2X) +#define SPI_SR_SCK_FOSC_64 (0 << SPI2X) +#define SPI_SR_SCK_FOSC_128 (0 << SPI2X) +*/ +/** @} */ + +/*==========================================================================*/ +/* Driver pre-compile time settings. */ +/*==========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief SPI driver enable switch. + * @details If set to @p TRUE the support for SPI1 is included. + */ // FIXME: Correct this define by using XMEGA insted of AVR +#if !defined(AVR_SPI_USE_SPI1) || defined(__DOXYGEN__) +#define AVR_SPI_USE_SPI1 FALSE +#endif +/** @} */ + +/*==========================================================================*/ +/* Derived constants and error checks. */ +/*==========================================================================*/ + +/*==========================================================================*/ +/* Driver data structures and types. */ +/*==========================================================================*/ + +/** + * @brief Type of a structure representing an SPI driver. + */ +typedef struct SPIDriver SPIDriver; + +/** + * @brief SPI notification callback type. + * + * @param[in] spip pointer to the @p SPIDriver object triggering the + * callback + */ +typedef void (*spicallback_t)(SPIDriver *spip); + +/** + * @brief Driver configuration structure. + * @note Implementations may extend this structure to contain more, + * architecture dependent, fields. + */ +typedef struct { + /** + * @brief Operation complete callback. + */ + spicallback_t end_cb; + /* End of the mandatory fields. */ + /** + * @brief Port used of Slave Select + */ + ioportid_t ssport; + /** + * @brief Pad used of Slave Select + */ + uint8_t sspad; + /** + * @brief SPI Control Register initialization data. + */ + //uint8_t spcr; + + SPI_PRESCALER_t prescaler; // Clock divider + SPI_MODE_t mode; // SPI mode 1, 2, ... + bool master; // Master or slave + bool dord; // Data order, LSB or MSB first + bool clk2x; // SPI double speed mode. + SPI_INTLVL_t irqlevel; // SPI interrupt service level +} SPIConfig; + +/** + * @brief Structure representing an SPI driver. + * @note Implementations may extend this structure to contain more, + * architecture dependent, fields. + */ +struct SPIDriver { + /** + * @brief Driver state. + */ + spistate_t state; + /** + * @brief Current configuration data. + */ + const SPIConfig *config; +#if SPI_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif /* SPI_USE_WAIT */ +#if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the bus. + */ + mutex_t mutex; +#endif /* SPI_USE_MUTUAL_EXCLUSION */ +#if defined(SPI_DRIVER_EXT_FIELDS) + SPI_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields. */ + /** + * @brief Pointer to the SPI register module. + */ + SPI_t *spi; + /** + * @brief Pointer to the buffer with data to send. + */ + const uint8_t *txbuf; + /** + * @brief Pointer to the buffer to store received data. + */ + uint8_t *rxbuf; + /** + * @brief Number of bytes of data to exchange. + */ + size_t exbytes; + /** + * @brief Current index in buffer when exchanging data. + */ + size_t exidx; +}; + +/*==========================================================================*/ +/* Driver macros. */ +/*==========================================================================*/ + +/** + * @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 + */ +#define spi_lld_ignore(spip, n) spi_lld_exchange(spip, n, NULL, NULL) + +/** + * @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 + */ +#define spi_lld_send(spip, n, txbuf) spi_lld_exchange(spip, n, txbuf, NULL) + +/** + * @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 + */ +#define spi_lld_receive(spip, n, rxbuf) spi_lld_exchange(spip, n, NULL, rxbuf) + +/*==========================================================================*/ +/* External declarations. */ +/*==========================================================================*/ + +#if AVR_SPI_USE_SPI1 && !defined(__DOXYGEN__) +extern SPIDriver SPID1; +#endif + +#if AVR_SPI_USE_SPI2 && !defined(__DOXYGEN__) +extern SPIDriver SPID2; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void spi_lld_init(void); + void spi_lld_start(SPIDriver *spip); + void spi_lld_stop(SPIDriver *spip); + void spi_lld_select(SPIDriver *spip); + void spi_lld_unselect(SPIDriver *spip); + void spi_lld_exchange(SPIDriver *spip, size_t n, + const void *txbuf, void *rxbuf); + +//#if AVR_SPI_USE_16BIT_POLLED_EXCHANGE +// uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame); +//#else + uint8_t spi_lld_polled_exchange(SPIDriver *spip, uint8_t frame); +//#endif + +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_SPI */ + +#endif /* HAL_SPI_LLD_H */ + +/** @} */