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:
gdisirio 2010-10-17 09:13:43 +00:00
parent 3b5571076e
commit a7af0ac06b
6 changed files with 223 additions and 55 deletions

View File

@ -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).

View File

@ -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),

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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.