summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKeith Packard <keithp@keithp.com>2014-12-22 17:27:09 -0800
committerKeith Packard <keithp@keithp.com>2014-12-22 17:27:09 -0800
commit2a053d3d157e00b6a6406f4f78ddb8e298b6c4b7 (patch)
tree4c70f6bb4c5d99509ef1dbf454f65cddb89dfbc9 /src
parent213a6604ec225e843f30244e6f94d0ebb3d27e46 (diff)
altos/lpc: Switch LPC SPI driver to interrupt-drivenbdale-altosdroid
This improves performance for SPI transfers, while allowing other tasks to get work done during longer SPI transfers. Signed-off-by: Keith Packard <keithp@keithp.com>
Diffstat (limited to 'src')
-rw-r--r--src/lpc/ao_arch.h3
-rw-r--r--src/lpc/ao_arch_funcs.h2
-rw-r--r--src/lpc/ao_spi_lpc.c143
3 files changed, 115 insertions, 33 deletions
diff --git a/src/lpc/ao_arch.h b/src/lpc/ao_arch.h
index 5fbb8dfa..42faf06f 100644
--- a/src/lpc/ao_arch.h
+++ b/src/lpc/ao_arch.h
@@ -130,12 +130,15 @@ ao_serial_init(void);
/* SPI definitions */
#define AO_SPI_SPEED_12MHz 4
+#define AO_SPI_SPEED_8MHz 6
#define AO_SPI_SPEED_6MHz 8
#define AO_SPI_SPEED_4MHz 12
#define AO_SPI_SPEED_2MHz 24
#define AO_SPI_SPEED_1MHz 48
#define AO_SPI_SPEED_500kHz 96
#define AO_SPI_SPEED_250kHz 192
+#define AO_SPI_SPEED_125kHz 384
+#define AO_SPI_SPEED_62500Hz 768
#define AO_SPI_SPEED_FAST AO_SPI_SPEED_12MHz
diff --git a/src/lpc/ao_arch_funcs.h b/src/lpc/ao_arch_funcs.h
index 69f2cbfb..fbe641d8 100644
--- a/src/lpc/ao_arch_funcs.h
+++ b/src/lpc/ao_arch_funcs.h
@@ -224,8 +224,6 @@ ao_spi_recv(void *block, uint16_t len, uint8_t spi_index);
void
ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index);
-extern uint16_t ao_spi_speed[LPC_NUM_SPI];
-
void
ao_spi_init(void);
diff --git a/src/lpc/ao_spi_lpc.c b/src/lpc/ao_spi_lpc.c
index bc8f9c69..5a358919 100644
--- a/src/lpc/ao_spi_lpc.c
+++ b/src/lpc/ao_spi_lpc.c
@@ -19,63 +19,130 @@
static uint8_t ao_spi_mutex[LPC_NUM_SPI];
+struct ao_lpc_ssp_state {
+ int tx_count;
+ const uint8_t *tx;
+ int tx_inc;
+ int rx_count;
+ uint8_t *rx;
+ int rx_inc;
+};
+
+static struct ao_lpc_ssp_state ao_lpc_ssp_state[LPC_NUM_SPI];
+
static struct lpc_ssp * const ao_lpc_ssp[LPC_NUM_SPI] = { &lpc_ssp0, &lpc_ssp1 };
-#define tx_busy(lpc_ssp) (lpc_ssp->sr & ((1 << LPC_SSP_SR_BSY) | (1 << LPC_SSP_SR_TNF))) != (1 << LPC_SSP_SR_TNF)
-#define rx_busy(lpc_ssp) (lpc_ssp->sr & ((1 << LPC_SSP_SR_BSY) | (1 << LPC_SSP_SR_RNE))) != (1 << LPC_SSP_SR_RNE)
-
-#define spi_loop(len, put, get) do { \
- while (len--) { \
- /* Wait for space in the fifo */ \
- while (tx_busy(lpc_ssp)) \
- ; \
- \
- /* send a byte */ \
- lpc_ssp->dr = put; \
- \
- /* Wait for byte to appear in the fifo */ \
- while (rx_busy(lpc_ssp)) \
- ; \
- \
- /* recv a byte */ \
- get lpc_ssp->dr; \
- } \
- } while (0)
+static inline void
+ao_lpc_ssp_recv(struct lpc_ssp *lpc_ssp, struct ao_lpc_ssp_state *state)
+{
+ while ((lpc_ssp->sr & (1 << LPC_SSP_SR_RNE)) &&
+ state->rx_count)
+ {
+ /* RX ready, read a byte */
+ *state->rx = lpc_ssp->dr;
+ state->rx += state->rx_inc;
+ state->rx_count--;
+ }
+}
+
+static void
+ao_lpc_ssp_isr(struct lpc_ssp *lpc_ssp, struct ao_lpc_ssp_state *state)
+{
+ ao_lpc_ssp_recv(lpc_ssp, state);
+ while ((lpc_ssp->sr & (1 << LPC_SSP_SR_TNF)) &&
+ state->tx_count)
+ {
+ /* TX ready, write a byte */
+ lpc_ssp->dr = *state->tx;
+ state->tx += state->tx_inc;
+ state->tx_count--;
+ ao_lpc_ssp_recv(lpc_ssp, state);
+ }
+ if (!state->rx_count) {
+ lpc_ssp->imsc &= ~(1 << LPC_SSP_IMSC_TXIM);
+ ao_wakeup(state);
+ }
+}
+
+void
+lpc_ssp0_isr(void)
+{
+ ao_lpc_ssp_isr(&lpc_ssp0, &ao_lpc_ssp_state[0]);
+}
+
+void
+lpc_ssp1_isr(void)
+{
+ ao_lpc_ssp_isr(&lpc_ssp1, &ao_lpc_ssp_state[1]);
+}
+
+static void
+ao_spi_run(struct lpc_ssp *lpc_ssp, struct ao_lpc_ssp_state *state)
+{
+ ao_arch_block_interrupts();
+ lpc_ssp->imsc = (1 << LPC_SSP_IMSC_TXIM);
+ while (state->rx_count)
+ ao_sleep(state);
+ ao_arch_release_interrupts();
+}
+
+static uint8_t ao_spi_tx_dummy = 0xff;
+static uint8_t ao_spi_rx_dummy;
void
ao_spi_send(const void *block, uint16_t len, uint8_t id)
{
- const uint8_t *b = block;
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
+ struct ao_lpc_ssp_state *state = &ao_lpc_ssp_state[id];
- spi_loop(len, *b++, (void));
+ state->tx_count = state->rx_count = len;
+ state->tx = block;
+ state->tx_inc = 1;
+ state->rx = &ao_spi_rx_dummy;
+ state->rx_inc = 0;
+ ao_spi_run(lpc_ssp, state);
}
void
ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t id)
{
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
+ struct ao_lpc_ssp_state *state = &ao_lpc_ssp_state[id];
- spi_loop(len, value, (void));
+ state->tx_count = state->rx_count = len;
+ state->tx = &value;
+ state->tx_inc = 0;
+ state->rx = &ao_spi_rx_dummy;
+ state->rx_inc = 0;
+ ao_spi_run(lpc_ssp, state);
}
void
ao_spi_recv(void *block, uint16_t len, uint8_t id)
{
- uint8_t *b = block;
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
+ struct ao_lpc_ssp_state *state = &ao_lpc_ssp_state[id];
- spi_loop(len, 0xff, *b++ =);
+ state->tx_count = state->rx_count = len;
+ state->tx = &ao_spi_tx_dummy;
+ state->tx_inc = 0;
+ state->rx = block;
+ state->rx_inc = 1;
+ ao_spi_run(lpc_ssp, state);
}
void
ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t id)
{
- const uint8_t *o = out;
- uint8_t *i = in;
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
+ struct ao_lpc_ssp_state *state = &ao_lpc_ssp_state[id];
- spi_loop(len, *o++, *i++ =);
+ state->tx_count = state->rx_count = len;
+ state->tx = out;
+ state->tx_inc = 1;
+ state->rx = in;
+ state->rx_inc = 1;
+ ao_spi_run(lpc_ssp, state);
}
void
@@ -84,7 +151,7 @@ ao_spi_get(uint8_t id, uint32_t speed)
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
ao_mutex_get(&ao_spi_mutex[id]);
-
+
/* Set the clock prescale */
lpc_ssp->cpsr = speed;
}
@@ -101,6 +168,11 @@ ao_spi_channel_init(uint8_t id)
struct lpc_ssp *lpc_ssp = ao_lpc_ssp[id];
uint8_t d;
+ /* Clear interrupt registers */
+ lpc_ssp->imsc = 0;
+ lpc_ssp->ris = 0;
+ lpc_ssp->mis = 0;
+
lpc_ssp->cr0 = ((LPC_SSP_CR0_DSS_8 << LPC_SSP_CR0_DSS) |
(LPC_SSP_CR0_FRF_SPI << LPC_SSP_CR0_FRF) |
(0 << LPC_SSP_CR0_CPOL) |
@@ -151,7 +223,11 @@ ao_spi_init(void)
lpc_scb.presetctrl &= ~(1 << LPC_SCB_PRESETCTRL_SSP0_RST_N);
lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_SSP0_RST_N);
ao_spi_channel_init(0);
-#endif
+
+ /* Configure NVIC */
+ lpc_nvic_set_enable(LPC_ISR_SSP0_POS);
+ lpc_nvic_set_priority(LPC_ISR_SSP0_POS, 0);
+#endif
#if HAS_SPI_1
@@ -190,7 +266,7 @@ ao_spi_init(void)
#ifndef HAS_MOSI1
#error "No pin specified for MOSI1"
#endif
-
+
/* Enable the device */
lpc_scb.sysahbclkctrl |= (1 << LPC_SCB_SYSAHBCLKCTRL_SSP1);
@@ -201,5 +277,10 @@ ao_spi_init(void)
lpc_scb.presetctrl &= ~(1 << LPC_SCB_PRESETCTRL_SSP1_RST_N);
lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_SSP1_RST_N);
ao_spi_channel_init(1);
+
+ /* Configure NVIC */
+ lpc_nvic_set_enable(LPC_ISR_SSP1_POS);
+ lpc_nvic_set_priority(LPC_ISR_SSP1_POS, 0);
+
#endif /* HAS_SPI_1 */
}