Introduce SPI_ENABLE_SYNC/SPI_DISABLE_SYNC to simplify enable/disable control

pull/6697/head
ccli8 2018-03-26 10:34:22 +08:00 committed by adbridge
parent 54ce719b71
commit 24496cb162
4 changed files with 176 additions and 93 deletions

View File

@ -60,6 +60,34 @@ static struct nu_spi_var spi2_var = {
#endif #endif
}; };
/* Synchronous version of SPI_ENABLE()/SPI_DISABLE() macros
*
* The SPI peripheral clock is asynchronous with the system clock. In order to make sure the SPI
* control logic is enabled/disabled, this bit indicates the real status of SPI controller.
*
* NOTE: All configurations shall be ready before calling SPI_ENABLE_SYNC().
* NOTE: Before changing the configurations of SPIx_CTL, SPIx_CLKDIV, SPIx_SSCTL and SPIx_FIFOCTL registers,
* user shall clear the SPIEN (SPIx_CTL[0]) and confirm the SPIENSTS (SPIx_STATUS[15]) is 0
* (by SPI_DISABLE_SYNC here).
*/
__STATIC_INLINE void SPI_ENABLE_SYNC(SPI_T *spi_base)
{
if (! (spi_base->CTL & SPI_CTL_SPIEN_Msk)) {
SPI_ENABLE(spi_base);
}
while (! (spi_base->STATUS & SPI_STATUS_SPIENSTS_Msk));
}
__STATIC_INLINE void SPI_DISABLE_SYNC(SPI_T *spi_base)
{
if (spi_base->CTL & SPI_CTL_SPIEN_Msk) {
// NOTE: SPI H/W may get out of state without the busy check.
while (SPI_IS_BUSY(spi_base));
SPI_DISABLE(spi_base);
}
while (spi_base->STATUS & SPI_STATUS_SPIENSTS_Msk);
}
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable); static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable);
static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable); static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable);
@ -177,10 +205,7 @@ void spi_format(spi_t *obj, int bits, int mode, int slave)
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
// NOTE 1: All configurations should be ready before enabling SPI peripheral. SPI_DISABLE_SYNC(spi_base);
// NOTE 2: Re-configuration is allowed only as SPI peripheral is idle.
while (SPI_IS_BUSY(spi_base));
SPI_DISABLE(spi_base);
SPI_Open(spi_base, SPI_Open(spi_base,
slave ? SPI_SLAVE : SPI_MASTER, slave ? SPI_SLAVE : SPI_MASTER,
@ -207,15 +232,14 @@ void spi_format(spi_t *obj, int bits, int mode, int slave)
} }
// NOTE: M451's SPI_Open() will enable SPI transfer (SPI_CTL_SPIEN_Msk). This will violate judgement of spi_active(). Disable it. // NOTE: M451's SPI_Open() will enable SPI transfer (SPI_CTL_SPIEN_Msk). This will violate judgement of spi_active(). Disable it.
SPI_DISABLE(spi_base); SPI_DISABLE_SYNC(spi_base);
} }
void spi_frequency(spi_t *obj, int hz) void spi_frequency(spi_t *obj, int hz)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
while (SPI_IS_BUSY(spi_base)); SPI_DISABLE_SYNC(spi_base);
SPI_DISABLE(spi_base);
SPI_SetBusClock((SPI_T *) NU_MODBASE(obj->spi.spi), hz); SPI_SetBusClock((SPI_T *) NU_MODBASE(obj->spi.spi), hz);
} }
@ -226,7 +250,7 @@ int spi_master_write(spi_t *obj, int value)
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
// NOTE: Data in receive FIFO can be read out via ICE. // NOTE: Data in receive FIFO can be read out via ICE.
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
// Wait for tx buffer empty // Wait for tx buffer empty
while(! spi_writeable(obj)); while(! spi_writeable(obj));
@ -236,7 +260,7 @@ int spi_master_write(spi_t *obj, int value)
while (! spi_readable(obj)); while (! spi_readable(obj));
int value2 = SPI_READ_RX(spi_base); int value2 = SPI_READ_RX(spi_base);
SPI_DISABLE(spi_base); SPI_DISABLE_SYNC(spi_base);
return value2; return value2;
} }
@ -261,7 +285,7 @@ int spi_slave_receive(spi_t *obj)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
return spi_readable(obj); return spi_readable(obj);
}; };
@ -270,7 +294,7 @@ int spi_slave_read(spi_t *obj)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
// Wait for rx buffer full // Wait for rx buffer full
while (! spi_readable(obj)); while (! spi_readable(obj));
@ -282,7 +306,7 @@ void spi_slave_write(spi_t *obj, int value)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
// Wait for tx buffer empty // Wait for tx buffer empty
while(! spi_writeable(obj)); while(! spi_writeable(obj));
@ -316,7 +340,7 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
spi_enable_event(obj, event, 1); spi_enable_event(obj, event, 1);
spi_buffer_set(obj, tx, tx_length, rx, rx_length); spi_buffer_set(obj, tx, tx_length, rx, rx_length);
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
if (obj->spi.dma_usage == DMA_USAGE_NEVER) { if (obj->spi.dma_usage == DMA_USAGE_NEVER) {
// Interrupt way // Interrupt way
@ -426,9 +450,8 @@ void spi_abort_asynch(spi_t *obj)
spi_enable_vector_interrupt(obj, 0, 0); spi_enable_vector_interrupt(obj, 0, 0);
spi_master_enable_interrupt(obj, 0); spi_master_enable_interrupt(obj, 0);
// FIXME: SPI H/W may get out of state without the busy check. /* Necessary for accessing FIFOCTL below */
while (SPI_IS_BUSY(spi_base)); SPI_DISABLE_SYNC(spi_base);
SPI_DISABLE(spi_base);
SPI_ClearRxFIFO(spi_base); SPI_ClearRxFIFO(spi_base);
SPI_ClearTxFIFO(spi_base); SPI_ClearTxFIFO(spi_base);

View File

@ -71,6 +71,34 @@ static struct nu_spi_var spi4_var = {
#endif #endif
}; };
/* Synchronous version of SPI_ENABLE()/SPI_DISABLE() macros
*
* The SPI peripheral clock is asynchronous with the system clock. In order to make sure the SPI
* control logic is enabled/disabled, this bit indicates the real status of SPI controller.
*
* NOTE: All configurations shall be ready before calling SPI_ENABLE_SYNC().
* NOTE: Before changing the configurations of SPIx_CTL, SPIx_CLKDIV, SPIx_SSCTL and SPIx_FIFOCTL registers,
* user shall clear the SPIEN (SPIx_CTL[0]) and confirm the SPIENSTS (SPIx_STATUS[15]) is 0
* (by SPI_DISABLE_SYNC here).
*/
__STATIC_INLINE void SPI_ENABLE_SYNC(SPI_T *spi_base)
{
if (! (spi_base->CTL & SPI_CTL_SPIEN_Msk)) {
SPI_ENABLE(spi_base);
}
while (! (spi_base->STATUS & SPI_STATUS_SPIENSTS_Msk));
}
__STATIC_INLINE void SPI_DISABLE_SYNC(SPI_T *spi_base)
{
if (spi_base->CTL & SPI_CTL_SPIEN_Msk) {
// NOTE: SPI H/W may get out of state without the busy check.
while (SPI_IS_BUSY(spi_base));
SPI_DISABLE(spi_base);
}
while (spi_base->STATUS & SPI_STATUS_SPIENSTS_Msk);
}
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable); static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable);
static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable); static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable);
@ -184,10 +212,7 @@ void spi_format(spi_t *obj, int bits, int mode, int slave)
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
// NOTE 1: All configurations should be ready before enabling SPI peripheral. SPI_DISABLE_SYNC(spi_base);
// NOTE 2: Re-configuration is allowed only as SPI peripheral is idle.
while (SPI_IS_BUSY(spi_base));
SPI_DISABLE(spi_base);
SPI_Open(spi_base, SPI_Open(spi_base,
slave ? SPI_SLAVE : SPI_MASTER, slave ? SPI_SLAVE : SPI_MASTER,
@ -212,15 +237,14 @@ void spi_format(spi_t *obj, int bits, int mode, int slave)
} }
// NOTE: M451's/M480's SPI_Open() will enable SPI transfer (SPI_CTL_SPIEN_Msk). This will violate judgement of spi_active(). Disable it. // NOTE: M451's/M480's SPI_Open() will enable SPI transfer (SPI_CTL_SPIEN_Msk). This will violate judgement of spi_active(). Disable it.
SPI_DISABLE(spi_base); SPI_DISABLE_SYNC(spi_base);
} }
void spi_frequency(spi_t *obj, int hz) void spi_frequency(spi_t *obj, int hz)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
while (SPI_IS_BUSY(spi_base)); SPI_DISABLE_SYNC(spi_base);
SPI_DISABLE(spi_base);
SPI_SetBusClock((SPI_T *) NU_MODBASE(obj->spi.spi), hz); SPI_SetBusClock((SPI_T *) NU_MODBASE(obj->spi.spi), hz);
} }
@ -231,7 +255,7 @@ int spi_master_write(spi_t *obj, int value)
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
// NOTE: Data in receive FIFO can be read out via ICE. // NOTE: Data in receive FIFO can be read out via ICE.
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
// Wait for tx buffer empty // Wait for tx buffer empty
while(! spi_writeable(obj)); while(! spi_writeable(obj));
@ -241,7 +265,7 @@ int spi_master_write(spi_t *obj, int value)
while (! spi_readable(obj)); while (! spi_readable(obj));
int value2 = SPI_READ_RX(spi_base); int value2 = SPI_READ_RX(spi_base);
SPI_DISABLE(spi_base); SPI_DISABLE_SYNC(spi_base);
return value2; return value2;
} }
@ -266,7 +290,7 @@ int spi_slave_receive(spi_t *obj)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
return spi_readable(obj); return spi_readable(obj);
}; };
@ -275,7 +299,7 @@ int spi_slave_read(spi_t *obj)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
// Wait for rx buffer full // Wait for rx buffer full
while (! spi_readable(obj)); while (! spi_readable(obj));
@ -287,7 +311,7 @@ void spi_slave_write(spi_t *obj, int value)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
// Wait for tx buffer empty // Wait for tx buffer empty
while(! spi_writeable(obj)); while(! spi_writeable(obj));
@ -320,7 +344,7 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
spi_enable_event(obj, event, 1); spi_enable_event(obj, event, 1);
spi_buffer_set(obj, tx, tx_length, rx, rx_length); spi_buffer_set(obj, tx, tx_length, rx, rx_length);
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
if (obj->spi.dma_usage == DMA_USAGE_NEVER) { if (obj->spi.dma_usage == DMA_USAGE_NEVER) {
// Interrupt way // Interrupt way
@ -428,9 +452,8 @@ void spi_abort_asynch(spi_t *obj)
spi_enable_vector_interrupt(obj, 0, 0); spi_enable_vector_interrupt(obj, 0, 0);
spi_master_enable_interrupt(obj, 0); spi_master_enable_interrupt(obj, 0);
// NOTE: SPI H/W may get out of state without the busy check. /* Necessary for accessing FIFOCTL below */
while (SPI_IS_BUSY(spi_base)); SPI_DISABLE_SYNC(spi_base);
SPI_DISABLE(spi_base);
SPI_ClearRxFIFO(spi_base); SPI_ClearRxFIFO(spi_base);
SPI_ClearTxFIFO(spi_base); SPI_ClearTxFIFO(spi_base);

View File

@ -75,6 +75,38 @@ static struct nu_spi_var spi2_var = {
#endif #endif
}; };
/* Synchronous version of SPI_ENABLE()/SPI_DISABLE() macros
*
* The SPI peripheral clock is asynchronous with the system clock. In order to make sure the SPI
* control logic is enabled/disabled, this bit indicates the real status of SPI controller.
*
* NOTE: All configurations shall be ready before calling SPI_ENABLE_SYNC().
* NOTE: Before changing the configurations of SPIx_CTL, SPIx_CLKDIV, SPIx_SSCTL and SPIx_FIFOCTL registers,
* user shall clear the SPIEN (SPIx_CTL[0]) and confirm the SPIENSTS (SPIx_STATUS[15]) is 0
* (by SPI_DISABLE_SYNC here).
*
* NOTE:
* 1. NUC472/M453/M487: SPI_CTL.SPIEN is controlled by software (in FIFO mode).
* 2. NANO130: SPI_CTL.GO_BUSY is controlled by hardware in FIFO mode.
*/
__STATIC_INLINE void SPI_ENABLE_SYNC(SPI_T *spi_base)
{
/* NOTE: On NANO130, FIFO mode defaults to disabled. */
if (! (spi_base->CTL & SPI_CTL_FIFOM_Msk)) {
SPI_EnableFIFO(spi_base, NU_SPI_FIFO_DEPTH / 2, NU_SPI_FIFO_DEPTH / 2);
}
}
__STATIC_INLINE void SPI_DISABLE_SYNC(SPI_T *spi_base)
{
/* NOTE: On NANO130, SPI_CTL.GO_BUSY always reads as 1 in slave/FIFO mode. So disable FIFO first. */
if (spi_base->CTL & SPI_CTL_FIFOM_Msk) {
SPI_DisableFIFO(spi_base);
}
/* NOTE: SPI H/W may get out of state without the busy check */
while (SPI_IS_BUSY(spi_base));
}
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable); static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable);
static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable, uint32_t mask); static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable, uint32_t mask);
@ -191,13 +223,7 @@ void spi_format(spi_t *obj, int bits, int mode, int slave)
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
// NOTE: All configurations should be ready before enabling SPI peripheral. SPI_DISABLE_SYNC(spi_base);
// NOTE: Re-configuration is allowed only as SPI peripheral is idle.
// NOTE:
// NANO130, SPI_CTL.GO_BUSY always reads as 1 in slave/FIFO mode. So disable FIFO first.
SPI_DisableFIFO(spi_base);
while (SPI_IS_BUSY(spi_base));
SPI_Open(spi_base, SPI_Open(spi_base,
slave ? SPI_SLAVE : SPI_MASTER, slave ? SPI_SLAVE : SPI_MASTER,
@ -240,25 +266,15 @@ void spi_format(spi_t *obj, int bits, int mode, int slave)
// NANO130: Configure slave select signal to edge-trigger rather than level-trigger // NANO130: Configure slave select signal to edge-trigger rather than level-trigger
spi_base->SSR |= SPI_SSR_SS_LTRIG_Msk; spi_base->SSR |= SPI_SSR_SS_LTRIG_Msk;
} }
// NOTE:
// NANO130: FIFO mode defaults to disabled.
SPI_EnableFIFO(spi_base, NU_SPI_FIFO_DEPTH / 2, NU_SPI_FIFO_DEPTH / 2);
} }
void spi_frequency(spi_t *obj, int hz) void spi_frequency(spi_t *obj, int hz)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
// NANO130, SPI_CTL.GO_BUSY always reads as 1 in slave/FIFO mode. So disable FIFO first. SPI_DISABLE_SYNC(spi_base);
SPI_DisableFIFO(spi_base);
while (SPI_IS_BUSY(spi_base));
SPI_SetBusClock((SPI_T *) NU_MODBASE(obj->spi.spi), hz); SPI_SetBusClock((SPI_T *) NU_MODBASE(obj->spi.spi), hz);
// NOTE:
// NANO130: FIFO mode defaults to disabled.
SPI_EnableFIFO(spi_base, NU_SPI_FIFO_DEPTH / 2, NU_SPI_FIFO_DEPTH / 2);
} }
@ -270,6 +286,7 @@ int spi_master_write(spi_t *obj, int value)
// NOTE: // NOTE:
// NUC472/M453/M487: SPI_CTL.SPIEN is controlled by software (in FIFO mode). // NUC472/M453/M487: SPI_CTL.SPIEN is controlled by software (in FIFO mode).
// NANO130: SPI_CTL.GO_BUSY is controlled by hardware in FIFO mode. // NANO130: SPI_CTL.GO_BUSY is controlled by hardware in FIFO mode.
SPI_ENABLE_SYNC(spi_base);
// Wait for tx buffer empty // Wait for tx buffer empty
while(! spi_writeable(obj)); while(! spi_writeable(obj));
@ -303,9 +320,9 @@ int spi_master_block_write(spi_t *obj, const char *tx_buffer, int tx_length,
#if DEVICE_SPISLAVE #if DEVICE_SPISLAVE
int spi_slave_receive(spi_t *obj) int spi_slave_receive(spi_t *obj)
{ {
// NOTE: SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
// NUC472/M453/M487: SPI_CTL.SPIEN is controlled by software (in FIFO mode).
// NANO130: SPI_CTL.GO_BUSY is controlled by hardware in FIFO mode. SPI_ENABLE_SYNC(spi_base);
return spi_readable(obj); return spi_readable(obj);
}; };
@ -314,9 +331,7 @@ int spi_slave_read(spi_t *obj)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
// NOTE: SPI_ENABLE_SYNC(spi_base);
// NUC472/M453/M487: SPI_CTL.SPIEN is controlled by software (in FIFO mode).
// NANO130: SPI_CTL.GO_BUSY is controlled by hardware in FIFO mode.
// Wait for rx buffer full // Wait for rx buffer full
while (! spi_readable(obj)); while (! spi_readable(obj));
@ -329,9 +344,7 @@ void spi_slave_write(spi_t *obj, int value)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
// NOTE: SPI_ENABLE_SYNC(spi_base);
// NUC472/M453/M487: SPI_CTL.SPIEN is controlled by software (in FIFO mode).
// NANO130: SPI_CTL.GO_BUSY is controlled by hardware in FIFO mode.
// Wait for tx buffer empty // Wait for tx buffer empty
while(! spi_writeable(obj)); while(! spi_writeable(obj));
@ -365,9 +378,7 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
spi_enable_event(obj, event, 1); spi_enable_event(obj, event, 1);
spi_buffer_set(obj, tx, tx_length, rx, rx_length); spi_buffer_set(obj, tx, tx_length, rx, rx_length);
// NOTE: SPI_ENABLE_SYNC(spi_base);
// NUC472/M453/M487: SPI_CTL.SPIEN is controlled by software (in FIFO mode).
// NANO130: SPI_CTL.GO_BUSY is controlled by hardware in FIFO mode.
if (obj->spi.dma_usage == DMA_USAGE_NEVER) { if (obj->spi.dma_usage == DMA_USAGE_NEVER) {
// Interrupt way // Interrupt way
@ -473,8 +484,8 @@ void spi_abort_asynch(spi_t *obj)
spi_enable_vector_interrupt(obj, 0, 0); spi_enable_vector_interrupt(obj, 0, 0);
spi_master_enable_interrupt(obj, 0, SPI_FIFO_RX_INTEN_MASK | SPI_FIFO_TX_INTEN_MASK); spi_master_enable_interrupt(obj, 0, SPI_FIFO_RX_INTEN_MASK | SPI_FIFO_TX_INTEN_MASK);
// NOTE: SPI H/W may get out of state without the busy check. /* Necessary for accessing FIFOCTL below */
while (SPI_IS_BUSY(spi_base)); SPI_DISABLE_SYNC(spi_base);
SPI_ClearRxFIFO(spi_base); SPI_ClearRxFIFO(spi_base);
SPI_ClearTxFIFO(spi_base); SPI_ClearTxFIFO(spi_base);

View File

@ -66,6 +66,34 @@ static struct nu_spi_var spi3_var = {
#endif #endif
}; };
/* Synchronous version of SPI_ENABLE()/SPI_DISABLE() macros
*
* The SPI peripheral clock is asynchronous with the system clock. In order to make sure the SPI
* control logic is enabled/disabled, this bit indicates the real status of SPI controller.
*
* NOTE: All configurations shall be ready before calling SPI_ENABLE_SYNC().
* NOTE: Before changing the configurations of SPIx_CTL, SPIx_CLKDIV, SPIx_SSCTL and SPIx_FIFOCTL registers,
* user shall clear the SPIEN (SPIx_CTL[0]) and confirm the SPIENSTS (SPIx_STATUS[15]) is 0
* (by SPI_DISABLE_SYNC here).
*/
__STATIC_INLINE void SPI_ENABLE_SYNC(SPI_T *spi_base)
{
if (! (spi_base->CTL & SPI_CTL_SPIEN_Msk)) {
SPI_ENABLE(spi_base);
}
while (! (spi_base->STATUS & SPI_STATUS_SPIENSTS_Msk));
}
__STATIC_INLINE void SPI_DISABLE_SYNC(SPI_T *spi_base)
{
if (spi_base->CTL & SPI_CTL_SPIEN_Msk) {
// NOTE: SPI H/W may get out of state without the busy check.
while (SPI_IS_BUSY(spi_base));
SPI_DISABLE(spi_base);
}
while (spi_base->STATUS & SPI_STATUS_SPIENSTS_Msk);
}
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable); static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable);
static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable); static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable);
@ -181,10 +209,7 @@ void spi_format(spi_t *obj, int bits, int mode, int slave)
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
// NOTE 1: All configurations should be ready before enabling SPI peripheral. SPI_DISABLE_SYNC(spi_base);
// NOTE 2: Re-configuration is allowed only as SPI peripheral is idle.
while (SPI_IS_BUSY(spi_base));
SPI_DISABLE(spi_base);
SPI_Open(spi_base, SPI_Open(spi_base,
slave ? SPI_SLAVE : SPI_MASTER, slave ? SPI_SLAVE : SPI_MASTER,
@ -211,14 +236,16 @@ void spi_format(spi_t *obj, int bits, int mode, int slave)
spi_base->SSCTL &= ~SPI_SSCTL_SSACTPOL_Msk; spi_base->SSCTL &= ~SPI_SSCTL_SSACTPOL_Msk;
// NOTE: SPI_SS0 is defined as the slave select input in Slave mode. // NOTE: SPI_SS0 is defined as the slave select input in Slave mode.
} }
// NOTE: M451's/M480's SPI_Open() will enable SPI transfer (SPI_CTL_SPIEN_Msk). This will violate judgement of spi_active(). Disable it.
SPI_DISABLE_SYNC(spi_base);
} }
void spi_frequency(spi_t *obj, int hz) void spi_frequency(spi_t *obj, int hz)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
while (SPI_IS_BUSY(spi_base)); SPI_DISABLE_SYNC(spi_base);
SPI_DISABLE(spi_base);
SPI_SetBusClock((SPI_T *) NU_MODBASE(obj->spi.spi), hz); SPI_SetBusClock((SPI_T *) NU_MODBASE(obj->spi.spi), hz);
} }
@ -229,7 +256,7 @@ int spi_master_write(spi_t *obj, int value)
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
// NOTE: Data in receive FIFO can be read out via ICE. // NOTE: Data in receive FIFO can be read out via ICE.
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
// Wait for tx buffer empty // Wait for tx buffer empty
while(! spi_writeable(obj)); while(! spi_writeable(obj));
@ -239,7 +266,7 @@ int spi_master_write(spi_t *obj, int value)
while (! spi_readable(obj)); while (! spi_readable(obj));
int value2 = SPI_READ_RX(spi_base); int value2 = SPI_READ_RX(spi_base);
SPI_DISABLE(spi_base); SPI_DISABLE_SYNC(spi_base);
return value2; return value2;
} }
@ -264,7 +291,7 @@ int spi_slave_receive(spi_t *obj)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
return spi_readable(obj); return spi_readable(obj);
}; };
@ -273,7 +300,7 @@ int spi_slave_read(spi_t *obj)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
// Wait for rx buffer full // Wait for rx buffer full
while (! spi_readable(obj)); while (! spi_readable(obj));
@ -285,7 +312,7 @@ void spi_slave_write(spi_t *obj, int value)
{ {
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
// Wait for tx buffer empty // Wait for tx buffer empty
while(! spi_writeable(obj)); while(! spi_writeable(obj));
@ -319,7 +346,7 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
spi_enable_event(obj, event, 1); spi_enable_event(obj, event, 1);
spi_buffer_set(obj, tx, tx_length, rx, rx_length); spi_buffer_set(obj, tx, tx_length, rx, rx_length);
SPI_ENABLE(spi_base); SPI_ENABLE_SYNC(spi_base);
if (obj->spi.dma_usage == DMA_USAGE_NEVER) { if (obj->spi.dma_usage == DMA_USAGE_NEVER) {
// Interrupt way // Interrupt way
@ -425,9 +452,8 @@ void spi_abort_asynch(spi_t *obj)
spi_enable_vector_interrupt(obj, 0, 0); spi_enable_vector_interrupt(obj, 0, 0);
spi_master_enable_interrupt(obj, 0); spi_master_enable_interrupt(obj, 0);
// FIXME: SPI H/W may get out of state without the busy check. /* Necessary for accessing FIFOCTL below */
while (SPI_IS_BUSY(spi_base)); SPI_DISABLE_SYNC(spi_base);
SPI_DISABLE(spi_base);
SPI_ClearRxFIFO(spi_base); SPI_ClearRxFIFO(spi_base);
SPI_ClearTxFIFO(spi_base); SPI_ClearTxFIFO(spi_base);