New LPC214x SPI device driver.
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@2264 35acf78f-673a-0410-8e92-d51de3d6d3f4
This commit is contained in:
parent
3b5571076e
commit
a7af0ac06b
|
@ -5,7 +5,7 @@
|
|||
|
||||
# Compiler options here.
|
||||
ifeq ($(USE_OPT),)
|
||||
USE_OPT = -O2 -ggdb -fomit-frame-pointer -mabi=apcs-gnu -falign-functions=16
|
||||
USE_OPT = -O0 -ggdb -fomit-frame-pointer -mabi=apcs-gnu -falign-functions=16
|
||||
endif
|
||||
|
||||
# C++ specific options here (added to USE_OPT).
|
||||
|
|
|
@ -43,6 +43,7 @@ static bool_t fs_ready = FALSE;
|
|||
|
||||
/* Maximum speed SPI configuration (18MHz, CPHA=0, CPOL=0).*/
|
||||
static SPIConfig hs_spicfg = {
|
||||
NULL,
|
||||
IOPORT1,
|
||||
PA_SSEL1,
|
||||
CR0_DSS8BIT | CR0_FRFSPI | CR0_CLOCKRATE(0),
|
||||
|
@ -52,6 +53,7 @@ static SPIConfig hs_spicfg = {
|
|||
|
||||
/* Low speed SPI configuration (281.250KHz, CPHA=0, CPOL=0).*/
|
||||
static SPIConfig ls_spicfg = {
|
||||
NULL,
|
||||
IOPORT1,
|
||||
PA_SSEL1,
|
||||
CR0_DSS8BIT | CR0_FRFSPI | CR0_CLOCKRATE(0),
|
||||
|
|
|
@ -390,6 +390,7 @@ typedef struct {
|
|||
#define SSPMIS (SSPBase->SSP_MIS)
|
||||
#define SSPICR (SSPBase->SSP_ICR)
|
||||
|
||||
#define CR0_DSSMASK 0x0F
|
||||
#define CR0_DSS4BIT 3
|
||||
#define CR0_DSS5BIT 4
|
||||
#define CR0_DSS6BIT 5
|
||||
|
|
|
@ -28,13 +28,13 @@
|
|||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if CH_HAL_USE_SPI || defined(__DOXYGEN__)
|
||||
#if LPC214x_SPI_USE_SSP || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if USE_LPC214x_SPI1 || defined(__DOXYGEN__)
|
||||
#if LPC214x_SPI_USE_SSP || defined(__DOXYGEN__)
|
||||
/** @brief SPI1 driver identifier.*/
|
||||
SPIDriver SPID1;
|
||||
#endif
|
||||
|
@ -48,38 +48,92 @@ SPIDriver SPID1;
|
|||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Synchronous SSP transfer.
|
||||
* @brief Preloads the transmit FIFO.
|
||||
*
|
||||
* @param[in] n number of bytes to be exchanged
|
||||
* @param[in] txbuf the pointer to the transmit buffer or @p NULL
|
||||
* @param[out] rxbuf the pointer to the receive buffer or @p NULL
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*/
|
||||
static void rw8(size_t n, const uint8_t *txbuf, uint8_t *rxbuf) {
|
||||
size_t ntx = n;
|
||||
static void ssp_fifo_preload(SPIDriver *spip) {
|
||||
SSP *ssp = spip->spd_ssp;
|
||||
uint32_t n = spip->spd_txcnt > LPC214x_SSP_FIFO_DEPTH ?
|
||||
LPC214x_SSP_FIFO_DEPTH : spip->spd_txcnt;
|
||||
|
||||
while (n > 0) {
|
||||
uint32_t sr = SSPBase->SSP_SR;
|
||||
if (sr & SR_RNE) {
|
||||
uint8_t w = SSPBase->SSP_DR;
|
||||
if (rxbuf != NULL)
|
||||
*rxbuf++ = w;
|
||||
n--;
|
||||
continue; /* Priority over transmission. */
|
||||
}
|
||||
if ((ntx > 0) && (sr & SR_TNF)) {
|
||||
if (txbuf != NULL)
|
||||
SSPBase->SSP_DR = *txbuf++;
|
||||
while(((ssp->SSP_SR & SR_TNF) != 0) && (n > 0)) {
|
||||
if (spip->spd_txptr != NULL) {
|
||||
if ((ssp->SSP_CR0 & CR0_DSSMASK) > CR0_DSS8BIT)
|
||||
ssp->SSP_DR = *(uint16_t *)spip->spd_txptr++;
|
||||
else
|
||||
SSPBase->SSP_DR = 0xFFFFFFFF;
|
||||
ntx--;
|
||||
ssp->SSP_DR = *(uint8_t *)spip->spd_txptr++;
|
||||
}
|
||||
else
|
||||
ssp->SSP_DR = 0xFFFFFFFF;
|
||||
n--;
|
||||
spip->spd_txcnt--;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__GNUC__)
|
||||
__attribute__((noinline))
|
||||
#endif
|
||||
/**
|
||||
* @brief Common IRQ handler.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*/
|
||||
static void serve_interrupt(SPIDriver *spip) {
|
||||
SSP *ssp = spip->spd_ssp;
|
||||
|
||||
if ((ssp->SSP_MIS & MIS_ROR) != 0) {
|
||||
/* The overflow condition should never happen because priority is given
|
||||
to receive but a hook macro is provided anyway...*/
|
||||
LPC214x_SPI_SSP_ERROR_HOOK();
|
||||
}
|
||||
ssp->SSP_ICR = ICR_RT | ICR_ROR;
|
||||
while ((ssp->SSP_SR & SR_RNE) != 0) {
|
||||
if (spip->spd_rxptr != NULL) {
|
||||
if ((ssp->SSP_CR0 & CR0_DSSMASK) > CR0_DSS8BIT)
|
||||
*(uint16_t *)spip->spd_rxptr++ = ssp->SSP_DR;
|
||||
else
|
||||
*(uint8_t *)spip->spd_rxptr++ = ssp->SSP_DR;
|
||||
}
|
||||
else
|
||||
(void)ssp->SSP_DR;
|
||||
if (--spip->spd_rxcnt == 0) {
|
||||
chDbgAssert(spip->spd_txcnt == 0,
|
||||
"chSemResetI(), #1", "counter out of synch");
|
||||
/* Stops the IRQ sources.*/
|
||||
ssp->SSP_IMSC = 0;
|
||||
/* Portable SPI ISR code defined in the high level driver, note, it is
|
||||
a macro.*/
|
||||
_spi_isr_code(spip);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ssp_fifo_preload(spip);
|
||||
if (spip->spd_txcnt == 0)
|
||||
ssp->SSP_IMSC = IMSC_ROR | IMSC_RT | IMSC_RX;
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if LPC214x_SPI_USE_SSP || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief SPI1 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(SPI1IrqHandler) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_interrupt(&SPID1);
|
||||
VICVectAddr = 0;
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
@ -91,8 +145,10 @@ static void rw8(size_t n, const uint8_t *txbuf, uint8_t *rxbuf) {
|
|||
*/
|
||||
void spi_lld_init(void) {
|
||||
|
||||
#if USE_LPC214x_SPI1
|
||||
#if LPC214x_SPI_USE_SSP
|
||||
spiObjectInit(&SPID1);
|
||||
SPID1.spd_ssp = SSPBase;
|
||||
SetVICVector(SPI1IrqHandler, LPC214x_SPI_SSP_IRQ_PRIORITY, SOURCE_SPI1);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -107,16 +163,22 @@ void spi_lld_start(SPIDriver *spip) {
|
|||
|
||||
if (spip->spd_state == SPI_STOP) {
|
||||
/* Clock activation.*/
|
||||
PCONP = (PCONP & PCALL) | PCSPI1;
|
||||
#if LPC214x_SPI_USE_SSP
|
||||
if (&SPID1 == spip) {
|
||||
PCONP = (PCONP & PCALL) | PCSPI1;
|
||||
VICIntEnable = INTMASK(SOURCE_SPI1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* Configuration.*/
|
||||
SSPBase->SSP_CR1 = 0;
|
||||
/* Emptying the receive FIFO, it happens to not be empty while debugging.*/
|
||||
while (SSPBase->SSP_SR & SR_RNE)
|
||||
(void) SSPBase->SSP_DR;
|
||||
SSPBase->SSP_CR0 = spip->spd_config->spc_cr0;
|
||||
SSPBase->SSP_CPSR = spip->spd_config->spc_cpsr;
|
||||
SSPBase->SSP_CR1 = spip->spd_config->spc_cr1 | CR1_SSE;
|
||||
while (spip->spd_ssp->SSP_SR & SR_RNE)
|
||||
(void) spip->spd_ssp->SSP_DR;
|
||||
spip->spd_ssp->SSP_ICR = ICR_RT | ICR_ROR;
|
||||
spip->spd_ssp->SSP_CR0 = spip->spd_config->spc_cr0;
|
||||
spip->spd_ssp->SSP_CPSR = spip->spd_config->spc_cpsr;
|
||||
spip->spd_ssp->SSP_CR1 = spip->spd_config->spc_cr1 | CR1_SSE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,6 +191,12 @@ void spi_lld_start(SPIDriver *spip) {
|
|||
void spi_lld_stop(SPIDriver *spip) {
|
||||
|
||||
if (spip->spd_state != SPI_STOP) {
|
||||
#if LPC214x_SPI_USE_SSP
|
||||
if (&SPID1 == spip) {
|
||||
PCONP = (PCONP & PCALL) & ~PCSPI1;
|
||||
VICIntEnClear = INTMASK(SOURCE_SPI1);
|
||||
}
|
||||
#endif
|
||||
SSPBase->SSP_CR1 = 0;
|
||||
SSPBase->SSP_CR0 = 0;
|
||||
SSPBase->SSP_CPSR = 0;
|
||||
|
@ -174,14 +242,20 @@ void spi_lld_unselect(SPIDriver *spip) {
|
|||
*/
|
||||
void spi_lld_ignore(SPIDriver *spip, size_t n) {
|
||||
|
||||
(void)spip;
|
||||
rw8(n, NULL, NULL);
|
||||
spip->spd_rxptr = NULL;
|
||||
spip->spd_txptr = NULL;
|
||||
spip->spd_rxcnt = spip->spd_txcnt = n;
|
||||
ssp_fifo_preload(spip);
|
||||
spip->spd_ssp->SSP_IMSC = IMSC_ROR | IMSC_RT | IMSC_TX | IMSC_RX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges data on the SPI bus.
|
||||
* @details This function performs a simultaneous transmit/receive operation.
|
||||
* @note The buffers are organized as uint8_t arrays.
|
||||
* @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
|
||||
|
@ -193,13 +267,19 @@ void spi_lld_ignore(SPIDriver *spip, size_t n) {
|
|||
void spi_lld_exchange(SPIDriver *spip, size_t n,
|
||||
const void *txbuf, void *rxbuf) {
|
||||
|
||||
(void)spip;
|
||||
rw8(n, txbuf, rxbuf);
|
||||
spip->spd_rxptr = rxbuf;
|
||||
spip->spd_txptr = txbuf;
|
||||
spip->spd_rxcnt = spip->spd_txcnt = n;
|
||||
ssp_fifo_preload(spip);
|
||||
spip->spd_ssp->SSP_IMSC = IMSC_ROR | IMSC_RT | IMSC_TX | IMSC_RX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends data ever the SPI bus.
|
||||
* @note The buffers are organized as uint8_t arrays.
|
||||
* @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
|
||||
|
@ -209,13 +289,19 @@ void spi_lld_exchange(SPIDriver *spip, size_t n,
|
|||
*/
|
||||
void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) {
|
||||
|
||||
(void)spip;
|
||||
rw8(n, txbuf, NULL);
|
||||
spip->spd_rxptr = NULL;
|
||||
spip->spd_txptr = txbuf;
|
||||
spip->spd_rxcnt = spip->spd_txcnt = n;
|
||||
ssp_fifo_preload(spip);
|
||||
spip->spd_ssp->SSP_IMSC = IMSC_ROR | IMSC_RT | IMSC_TX | IMSC_RX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receives data from the SPI bus.
|
||||
* @note The buffers are organized as uint8_t arrays.
|
||||
* @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
|
||||
|
@ -225,8 +311,11 @@ void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) {
|
|||
*/
|
||||
void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) {
|
||||
|
||||
(void)spip;
|
||||
rw8(n, NULL, rxbuf);
|
||||
spip->spd_rxptr = rxbuf;
|
||||
spip->spd_txptr = NULL;
|
||||
spip->spd_rxcnt = spip->spd_txcnt = n;
|
||||
ssp_fifo_preload(spip);
|
||||
spip->spd_ssp->SSP_IMSC = IMSC_ROR | IMSC_RT | IMSC_TX | IMSC_RX;
|
||||
}
|
||||
|
||||
#endif /* CH_HAL_USE_SPI */
|
||||
|
|
|
@ -34,31 +34,76 @@
|
|||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Hardware FIFO depth.
|
||||
*/
|
||||
#define LPC214x_SSP_FIFO_DEPTH 8
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief SPI1 (SSP) driver enable switch.
|
||||
* @details If set to @p TRUE the support for SPI0 is included.
|
||||
* @brief SPI1 driver enable switch.
|
||||
* @details If set to @p TRUE the support for SSP is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(USE_LPC214x_SPI1) || defined(__DOXYGEN__)
|
||||
#define USE_LPC214x_SPI1 TRUE
|
||||
#if !defined(LPC214x_SPI_USE_SSP) || defined(__DOXYGEN__)
|
||||
#define LPC214x_SPI_USE_SSP TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SSP interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI1_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define LPC214x_SPI_SSP_IRQ_PRIORITY 4
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Overflow error hook.
|
||||
* @details The default action is to stop the system.
|
||||
*/
|
||||
#if !defined(LPC214x_SPI_SSP_ERROR_HOOK) || defined(__DOXYGEN__)
|
||||
#define LPC214x_SPI_SSP_ERROR_HOOK() chSysHalt()
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if !LPC214x_SPI_USE_SSP
|
||||
#error "SPI driver activated but no SPI peripheral assigned"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* 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.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Operation complete callback or @p NULL.
|
||||
* @note In order to use synchronous functions this field must be set to
|
||||
* @p NULL, callbacks and synchronous operations are mutually
|
||||
* exclusive.
|
||||
*/
|
||||
spicallback_t spc_endcb;
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief The chip select line port.
|
||||
*/
|
||||
|
@ -84,11 +129,21 @@ typedef struct {
|
|||
/**
|
||||
* @brief Structure representing a SPI driver.
|
||||
*/
|
||||
typedef struct {
|
||||
struct SPIDriver {
|
||||
/**
|
||||
* @brief Driver state.
|
||||
*/
|
||||
spistate_t spd_state;
|
||||
/**
|
||||
* @brief Current configuration data.
|
||||
*/
|
||||
const SPIConfig *spd_config;
|
||||
#if SPI_USE_WAIT || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Waiting thread.
|
||||
*/
|
||||
Thread *spd_thread;
|
||||
#endif /* SPI_USE_WAIT */
|
||||
#if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
|
||||
#if CH_USE_MUTEXES || defined(__DOXYGEN__)
|
||||
/**
|
||||
|
@ -99,12 +154,31 @@ typedef struct {
|
|||
Semaphore spd_semaphore;
|
||||
#endif
|
||||
#endif /* SPI_USE_MUTUAL_EXCLUSION */
|
||||
/**
|
||||
* @brief Current configuration data.
|
||||
*/
|
||||
const SPIConfig *spd_config;
|
||||
#if defined(SPI_DRIVER_EXT_FIELDS)
|
||||
SPI_DRIVER_EXT_FIELDS
|
||||
#endif
|
||||
/* End of the mandatory fields.*/
|
||||
} SPIDriver;
|
||||
/**
|
||||
* @brief Pointer to the SSP registers block.
|
||||
*/
|
||||
SSP *spd_ssp;
|
||||
/**
|
||||
* @brief Number of bytes yet to be received.
|
||||
*/
|
||||
uint32_t spd_rxcnt;
|
||||
/**
|
||||
* @brief Receive pointer or @p NULL.
|
||||
*/
|
||||
void *spd_rxptr;
|
||||
/**
|
||||
* @brief Number of bytes yet to be transmitted.
|
||||
*/
|
||||
uint32_t spd_txcnt;
|
||||
/**
|
||||
* @brief Transmit pointer or @p NULL.
|
||||
*/
|
||||
const void *spd_txptr;
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
|
@ -114,7 +188,7 @@ typedef struct {
|
|||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if USE_LPC214x_SPI1 && !defined(__DOXYGEN__)
|
||||
#if LPC214x_SPI_USE_SSP && !defined(__DOXYGEN__)
|
||||
extern SPIDriver SPID1;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
(backported to 2.0.6).
|
||||
- FIX: Incorrect AT91SAM7X initialization, thanks Leszek (bug 3075354)
|
||||
(backported to 2.0.5).
|
||||
- FIX: Fixed race condition in function chSchGoSleepTimeoutS, thanks Balázs
|
||||
- FIX: Fixed race condition in function chSchGoSleepTimeoutS(), thanks Balázs
|
||||
(bug 3074984)(backported to 2.0.5).
|
||||
- FIX: Fixed race condition in threads creation (bug 3069854)(backported
|
||||
to 2.0.5).
|
||||
|
@ -114,6 +114,8 @@
|
|||
- NEW: Added to the UART driver the capability to return the number of
|
||||
not yet transferred frames when stopping an operation.
|
||||
- NEW: Added more compile-time checks to the various STM32 device drivers.
|
||||
- NEW: Improved LPC214x SPI driver, now it uses IRQs and implements the
|
||||
new SPI device driver model.
|
||||
- NEW: Added a simple STM32 ADC demo under ./testhal/STM32/ADC.
|
||||
- NEW: Added a simple STM32 CAN demo under ./testhal/STM32/CAN.
|
||||
- NEW: Added a simple STM32 PWM demo under ./testhal/STM32/PWM.
|
||||
|
|
Loading…
Reference in New Issue