|
@@ -28,6 +28,7 @@
|
|
|
#include "bits.h"
|
|
|
#include "config.h"
|
|
|
#include "spi.h"
|
|
|
+#include "uart.h"
|
|
|
|
|
|
#define SSP_TFE 0 // Transmit FIFO empty
|
|
|
#define SSP_TNF 1 // Transmit FIFO not full
|
|
@@ -35,10 +36,6 @@
|
|
|
#define SSP_RFF 3 // Receive FIFO full
|
|
|
#define SSP_BSY 4 // Busy
|
|
|
|
|
|
-// FIXME: Move to config.h!
|
|
|
-#define SSP_CLK_DIVISOR_FAST 4
|
|
|
-#define SSP_CLK_DIVISOR_SLOW 250
|
|
|
-
|
|
|
// #define SSP_REGS LPC_SSP1
|
|
|
// #define SSP_PCLKREG PCLKSEL0
|
|
|
/* SSP0: PCLKSEL1
|
|
@@ -54,118 +51,173 @@
|
|
|
SSP1: 3 */
|
|
|
|
|
|
typedef struct {
|
|
|
- LPC_SSP_TypeDef *SSP_REGS;
|
|
|
- LPC_GPDMA_TypeDef *SSP_DMAC;
|
|
|
- __IO uint32_t SSP_PCLKREG;
|
|
|
- int SSP_PCLKBIT;
|
|
|
- int SSP_DMAID_TX;
|
|
|
- int SSP_DMAID_RX;
|
|
|
+ LPC_SSP_TypeDef *SSP_REGS;
|
|
|
+ LPC_GPDMACH_TypeDef *SSP_DMACH;
|
|
|
+ uint32_t *SSP_PCLKREG;
|
|
|
+ int SSP_PCLKBIT;
|
|
|
+ int SSP_DMAID_TX;
|
|
|
+ int SSP_DMAID_RX;
|
|
|
} ssp_props;
|
|
|
|
|
|
|
|
|
static ssp_props SSP_SEL[2] = {
|
|
|
- { LPC_SSP0, LPC_GPDMA0, PCLKSEL1, 10, 0, 1 }, /* SSP0 */
|
|
|
- { LPC_SSP1, LPC_GPDMA1, PCLKSEL0, 20, 2, 3 }
|
|
|
+ { LPC_SSP0, LPC_GPDMACH0, (uint32_t*)&(LPC_SC->PCLKSEL1), 10, 0, 1 }, /* SSP0 */
|
|
|
+ { LPC_SSP1, LPC_GPDMACH1, (uint32_t*)&(LPC_SC->PCLKSEL0), 20, 2, 3 } /* SSP1 */
|
|
|
};
|
|
|
|
|
|
-void spi_init(spi_speed_t speed) {
|
|
|
+void spi_preinit(int device) {
|
|
|
+ /* select interface */
|
|
|
+ ssp_props *ssp = &(SSP_SEL[device]);
|
|
|
+
|
|
|
/* Set clock prescaler to 1:1 */
|
|
|
- BITBAND(LPC_SC->SSP_PCLKREG, SSP_PCLKBIT) = 1;
|
|
|
+ BITBAND(*(ssp->SSP_PCLKREG), ssp->SSP_PCLKBIT) = 1;
|
|
|
+}
|
|
|
+
|
|
|
+void spi_init(spi_speed_t speed, int device) {
|
|
|
+
|
|
|
+ /* select interface */
|
|
|
+ ssp_props *ssp = &(SSP_SEL[device]);
|
|
|
|
|
|
/* configure data format - 8 bits, SPI, CPOL=0, CPHA=0, 1 clock per bit */
|
|
|
- SSP_REGS->CR0 = (8-1);
|
|
|
+ ssp->SSP_REGS->CR0 = (8-1);
|
|
|
|
|
|
/* set clock prescaler */
|
|
|
if (speed == SPI_SPEED_FAST) {
|
|
|
- SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST;
|
|
|
+ ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST;
|
|
|
+ } else if (speed == SPI_SPEED_SLOW) {
|
|
|
+ ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW;
|
|
|
+ } else if (speed == SPI_SPEED_FPGA_FAST) {
|
|
|
+ ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_FAST;
|
|
|
} else {
|
|
|
- SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW;
|
|
|
+ ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_SLOW;
|
|
|
}
|
|
|
|
|
|
/* Enable SSP */
|
|
|
- SSP_REGS->CR1 = BV(1);
|
|
|
+ ssp->SSP_REGS->CR1 = BV(1);
|
|
|
|
|
|
/* Enable DMA controller, little-endian mode */
|
|
|
BITBAND(LPC_SC->PCONP, 29) = 1;
|
|
|
LPC_GPDMA->DMACConfig = 1;
|
|
|
}
|
|
|
|
|
|
-void spi_tx_byte(uint8_t data) {
|
|
|
+void spi_tx_sync(int device) {
|
|
|
+ /* select interface */
|
|
|
+ ssp_props *ssp = &(SSP_SEL[device]);
|
|
|
+
|
|
|
+ /* Wait until TX fifo is flushed */
|
|
|
+ while (BITBAND(ssp->SSP_REGS->SR, SSP_BSY)) ;
|
|
|
+}
|
|
|
+
|
|
|
+void spi_tx_byte(uint8_t data, int device) {
|
|
|
+
|
|
|
+ /* select interface */
|
|
|
+ ssp_props *ssp = &(SSP_SEL[device]);
|
|
|
+
|
|
|
/* Wait until TX fifo can accept data */
|
|
|
- while (!BITBAND(SSP_REGS->SR, SSP_TNF)) ;
|
|
|
+ while (!BITBAND(ssp->SSP_REGS->SR, SSP_TNF)) ;
|
|
|
|
|
|
/* Send byte */
|
|
|
- SSP_REGS->DR = data;
|
|
|
+ ssp->SSP_REGS->DR = data;
|
|
|
}
|
|
|
|
|
|
-uint8_t spi_rx_byte(void) {
|
|
|
+uint8_t spi_txrx_byte(uint8_t data, int device) {
|
|
|
+ /* select interface */
|
|
|
+ ssp_props *ssp = &(SSP_SEL[device]);
|
|
|
+
|
|
|
/* Wait until SSP is not busy */
|
|
|
- while (BITBAND(SSP_REGS->SR, SSP_BSY)) ;
|
|
|
+ while (BITBAND(ssp->SSP_REGS->SR, SSP_BSY)) ;
|
|
|
|
|
|
/* Clear RX fifo */
|
|
|
- while (BITBAND(SSP_REGS->SR, SSP_RNE))
|
|
|
- (void) SSP_REGS->DR;
|
|
|
+ while (BITBAND(ssp->SSP_REGS->SR, SSP_RNE))
|
|
|
+ (void) ssp->SSP_REGS->DR;
|
|
|
|
|
|
/* Transmit a single dummy byte */
|
|
|
- SSP_REGS->DR = 0xff;
|
|
|
+ ssp->SSP_REGS->DR = data;
|
|
|
|
|
|
/* Wait until answer has been received */
|
|
|
- while (!BITBAND(SSP_REGS->SR, SSP_RNE)) ;
|
|
|
+ while (!BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) ;
|
|
|
|
|
|
- return SSP_REGS->DR;
|
|
|
+ return ssp->SSP_REGS->DR;
|
|
|
}
|
|
|
|
|
|
-void spi_tx_block(const void *ptr, unsigned int length) {
|
|
|
+uint8_t spi_rx_byte(int device) {
|
|
|
+
|
|
|
+ /* select interface */
|
|
|
+ ssp_props *ssp = &(SSP_SEL[device]);
|
|
|
+
|
|
|
+ /* Wait until SSP is not busy */
|
|
|
+ while (BITBAND(ssp->SSP_REGS->SR, SSP_BSY)) ;
|
|
|
+
|
|
|
+ /* Clear RX fifo */
|
|
|
+ while (BITBAND(ssp->SSP_REGS->SR, SSP_RNE))
|
|
|
+ (void) ssp->SSP_REGS->DR;
|
|
|
+
|
|
|
+ /* Transmit a single dummy byte */
|
|
|
+ ssp->SSP_REGS->DR = 0xff;
|
|
|
+
|
|
|
+ /* Wait until answer has been received */
|
|
|
+ while (!BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) ;
|
|
|
+
|
|
|
+ return ssp->SSP_REGS->DR;
|
|
|
+}
|
|
|
+
|
|
|
+void spi_tx_block(const void *ptr, unsigned int length, int device) {
|
|
|
const uint8_t *data = (const uint8_t *)ptr;
|
|
|
|
|
|
+ /* select interface */
|
|
|
+ ssp_props *ssp = &(SSP_SEL[device]);
|
|
|
+
|
|
|
while (length--) {
|
|
|
/* Wait until TX fifo can accept data */
|
|
|
- while (!BITBAND(SSP_REGS->SR, SSP_TNF)) ;
|
|
|
+ while (!BITBAND(ssp->SSP_REGS->SR, SSP_TNF)) ;
|
|
|
|
|
|
- SSP_REGS->DR = *data++;
|
|
|
+ ssp->SSP_REGS->DR = *data++;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void spi_rx_block(void *ptr, unsigned int length) {
|
|
|
+void spi_rx_block(void *ptr, unsigned int length, int device) {
|
|
|
uint8_t *data = (uint8_t *)ptr;
|
|
|
unsigned int txlen = length;
|
|
|
|
|
|
+ /* select interface */
|
|
|
+ ssp_props *ssp = &(SSP_SEL[device]);
|
|
|
+
|
|
|
/* Wait until SSP is not busy */
|
|
|
- while (BITBAND(SSP_REGS->SR, SSP_BSY)) ;
|
|
|
+ while (BITBAND(ssp->SSP_REGS->SR, SSP_BSY)) ;
|
|
|
|
|
|
/* Clear RX fifo */
|
|
|
- while (BITBAND(SSP_REGS->SR, SSP_RNE))
|
|
|
- (void) SSP_REGS->DR;
|
|
|
+ while (BITBAND(ssp->SSP_REGS->SR, SSP_RNE))
|
|
|
+ (void) ssp->SSP_REGS->DR;
|
|
|
|
|
|
if ((length & 3) != 0 || ((uint32_t)ptr & 3) != 0) {
|
|
|
/* Odd length or unaligned buffer */
|
|
|
while (length > 0) {
|
|
|
/* Wait until TX or RX FIFO are ready */
|
|
|
- while (txlen > 0 && !BITBAND(SSP_REGS->SR, SSP_TNF) &&
|
|
|
- !BITBAND(SSP_REGS->SR, SSP_RNE)) ;
|
|
|
+ while (txlen > 0 && !BITBAND(ssp->SSP_REGS->SR, SSP_TNF) &&
|
|
|
+ !BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) ;
|
|
|
|
|
|
/* Try to receive data */
|
|
|
- while (length > 0 && BITBAND(SSP_REGS->SR, SSP_RNE)) {
|
|
|
- *data++ = SSP_REGS->DR;
|
|
|
+ while (length > 0 && BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) {
|
|
|
+ *data++ = ssp->SSP_REGS->DR;
|
|
|
length--;
|
|
|
}
|
|
|
|
|
|
/* Send dummy data until TX full or RX ready */
|
|
|
- while (txlen > 0 && BITBAND(SSP_REGS->SR, SSP_TNF) && !BITBAND(SSP_REGS->SR, SSP_RNE)) {
|
|
|
+ while (txlen > 0 && BITBAND(ssp->SSP_REGS->SR, SSP_TNF) && !BITBAND(ssp->SSP_REGS->SR, SSP_RNE)) {
|
|
|
txlen--;
|
|
|
- SSP_REGS->DR = 0xff;
|
|
|
+ ssp->SSP_REGS->DR = 0xff;
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
/* Clear interrupt flags of DMA channels 0 */
|
|
|
- LPC_GPDMA->DMACIntTCClear = BV(0);
|
|
|
- LPC_GPDMA->DMACIntErrClr = BV(0);
|
|
|
+ LPC_GPDMA->DMACIntTCClear = BV(device);
|
|
|
+ LPC_GPDMA->DMACIntErrClr = BV(device);
|
|
|
|
|
|
/* Set up RX DMA channel */
|
|
|
- LPC_GPDMACH0->DMACCSrcAddr = (uint32_t)&SSP_REGS->DR;
|
|
|
- LPC_GPDMACH0->DMACCDestAddr = (uint32_t)ptr;
|
|
|
- LPC_GPDMACH0->DMACCLLI = 0; // no linked list
|
|
|
- LPC_GPDMACH0->DMACCControl = length
|
|
|
+ ssp->SSP_DMACH->DMACCSrcAddr = (uint32_t)&ssp->SSP_REGS->DR;
|
|
|
+ ssp->SSP_DMACH->DMACCDestAddr = (uint32_t)ptr;
|
|
|
+ ssp->SSP_DMACH->DMACCLLI = 0; // no linked list
|
|
|
+ ssp->SSP_DMACH->DMACCControl = length
|
|
|
| (0 << 12) // source burst size 1 (FIXME: Check if larger possible/useful)
|
|
|
| (0 << 15) // destination burst size 1
|
|
|
| (0 << 18) // source transfer width 1 byte
|
|
@@ -173,45 +225,52 @@ void spi_rx_block(void *ptr, unsigned int length) {
|
|
|
| (0 << 26) // source address not incremented
|
|
|
| (1 << 27) // destination address incremented
|
|
|
;
|
|
|
- LPC_GPDMACH0->DMACCConfig = 1 // enable channel
|
|
|
- | (SSP_DMAID_RX << 1) // data source SSP RX
|
|
|
+ ssp->SSP_DMACH->DMACCConfig = 1 // enable channel
|
|
|
+ | (ssp->SSP_DMAID_RX << 1) // data source SSP RX
|
|
|
| (2 << 11) // transfer from peripheral to memory
|
|
|
;
|
|
|
|
|
|
/* Enable RX FIFO DMA */
|
|
|
- SSP_REGS->DMACR = 1;
|
|
|
+ ssp->SSP_REGS->DMACR = 1;
|
|
|
|
|
|
/* Write <length> bytes into TX FIFO */
|
|
|
// FIXME: Any value in doing this using DMA too?
|
|
|
while (txlen > 0) {
|
|
|
- while (txlen > 0 && BITBAND(SSP_REGS->SR, SSP_TNF)) {
|
|
|
+ while (txlen > 0 && BITBAND(ssp->SSP_REGS->SR, SSP_TNF)) {
|
|
|
txlen--;
|
|
|
- SSP_REGS->DR = 0xff;
|
|
|
+ ssp->SSP_REGS->DR = 0xff;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Wait until DMA channel disables itself */
|
|
|
- while (LPC_GPDMACH0->DMACCConfig & 1) ;
|
|
|
+ while (ssp->SSP_DMACH->DMACCConfig & 1) ;
|
|
|
|
|
|
/* Disable RX FIFO DMA */
|
|
|
- SSP_REGS->DMACR = 0;
|
|
|
+ ssp->SSP_REGS->DMACR = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void spi_set_speed(spi_speed_t speed) {
|
|
|
+void spi_set_speed(spi_speed_t speed, int device) {
|
|
|
+ /* select interface */
|
|
|
+ ssp_props *ssp = &(SSP_SEL[device]);
|
|
|
+
|
|
|
/* Wait until TX fifo is empty */
|
|
|
- while (!BITBAND(SSP_REGS->SR, 0)) ;
|
|
|
+ while (!BITBAND(ssp->SSP_REGS->SR, 0)) ;
|
|
|
|
|
|
/* Disable SSP (FIXME: Is this required?) */
|
|
|
- SSP_REGS->CR1 = 0;
|
|
|
+ ssp->SSP_REGS->CR1 = 0;
|
|
|
|
|
|
/* Change clock divisor */
|
|
|
if (speed == SPI_SPEED_FAST) {
|
|
|
- SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST;
|
|
|
+ ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FAST;
|
|
|
+ } else if (speed == SPI_SPEED_SLOW) {
|
|
|
+ ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW;
|
|
|
+ } else if (speed == SPI_SPEED_FPGA_FAST) {
|
|
|
+ ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_FAST;
|
|
|
} else {
|
|
|
- SSP_REGS->CPSR = SSP_CLK_DIVISOR_SLOW;
|
|
|
+ ssp->SSP_REGS->CPSR = SSP_CLK_DIVISOR_FPGA_SLOW;
|
|
|
}
|
|
|
|
|
|
/* Enable SSP */
|
|
|
- SSP_REGS->CR1 = BV(1);
|
|
|
+ ssp->SSP_REGS->CR1 = BV(1);
|
|
|
}
|