diff --git a/sw/airborne/arch/lpc21/mcu_periph/spi_arch.c b/sw/airborne/arch/lpc21/mcu_periph/spi_arch.c index 64f8dde558..d7b5f4a15f 100644 --- a/sw/airborne/arch/lpc21/mcu_periph/spi_arch.c +++ b/sw/airborne/arch/lpc21/mcu_periph/spi_arch.c @@ -304,6 +304,63 @@ __attribute__ ((always_inline)) static inline void SpiAutomaton(struct spi_perip } } +__attribute__ ((always_inline)) static inline void SpiSlaveStart(struct spi_periph* p, struct spi_transaction* t) { + p->status = SPIRunning; + t->status = SPITransRunning; + + // callback function before transaction + if (t->before_cb != 0) t->before_cb(t); + + // start spi transaction + SpiEnable(p); + SpiInitBuf(p,t); + SpiEnableTxi(p); // enable tx fifo half empty interrupt + //SpiEnableRti(p); // enable rx timeout interrupt +} + +__attribute__ ((always_inline)) static inline void SpiSlaveAutomaton(struct spi_periph* p) { + struct spi_transaction* trans = p->trans[p->trans_extract_idx]; + + /* Tx fifo is half empty */ + if (bit_is_set(((sspRegs_t *)(p->reg_addr))->mis, TXMIS)) { + SpiTransmit(p, trans); + SpiReceive(p, trans); + SpiEnableRti(p); + } + + /* Rx fifo is not empty and no receive took place in the last 32 bits period */ + if (bit_is_set(((sspRegs_t *)(p->reg_addr))->mis, RTMIS)) { + SpiReceive(p, trans); + SpiClearRti(p); /* clear interrupt */ + SpiDisableRti(p); + + // callback function after transaction + if (trans->after_cb != 0) trans->after_cb(trans); + + SpiDisable(p); + // end transaction with success + trans->status = SPITransSuccess; + p->status = SPIIdle; + } + +} + +/* SSP (SPI1) pins (UM10120_1.pdf page 76) + P0.17 SCK PINSEL1 2 << 2 + P0.18 MISO PINSEL1 2 << 4 + P0.19 MOSI PINSEL1 2 << 6 + P0.20 SS PINSEL1 2 << 8 +*/ +#define SSP_PINSEL1_SCK (2 << 2) +#define SSP_PINSEL1_MISO (2 << 4) +#define SSP_PINSEL1_MOSI (2 << 6) +#define SSP_PINSEL1_SSEL (2 << 8) + +/** default initial settings */ +#ifndef SPI1_VIC_SLOT +#define SPI1_VIC_SLOT 7 +#endif + /* * * SPI Master code @@ -311,7 +368,7 @@ __attribute__ ((always_inline)) static inline void SpiAutomaton(struct spi_perip * */ -#ifdef SPI_MASTER +#if SPI_MASTER #if USE_SPI0 @@ -339,34 +396,18 @@ void spi0_arch_init(void) { #if USE_SPI1 -/** default initial settings */ -#ifndef SPI1_VIC_SLOT -#define SPI1_VIC_SLOT 7 -#endif - -/* SSP (SPI1) pins (UM10120_1.pdf page 76) - P0.17 SCK PINSEL1 2 << 2 - P0.18 MISO PINSEL1 2 << 4 - P0.19 MOSI PINSEL1 2 << 6 - P0.20 SS PINSEL1 2 << 8 -*/ -#define PINSEL1_SCK (2 << 2) -#define PINSEL1_MISO (2 << 4) -#define PINSEL1_MOSI (2 << 6) -#define PINSEL1_SSEL (2 << 8) - /* SSPCR0 settings */ -#define SSP_DSS 0x07 << 0 /* data size : 8 bits */ -#define SSP_FRF 0x00 << 4 /* frame format : SPI */ -#define SSP_CPOL 0x00 << 6 /* clock polarity : SCK idles low */ -#define SSP_CPHA 0x00 << 7 /* clock phase : data captured on first clock transition */ -#define SSP_SCR 0x0F << 8 /* serial clock rate : divide by 16 */ +#define MASTER_SSP_DSS 0x07 << 0 /* data size : 8 bits */ +#define MASTER_SSP_FRF 0x00 << 4 /* frame format : SPI */ +#define MASTER_SSP_CPOL 0x00 << 6 /* clock polarity : SCK idles low */ +#define MASTER_SSP_CPHA 0x00 << 7 /* clock phase : data captured on first clock transition */ +#define MASTER_SSP_SCR 0x0F << 8 /* serial clock rate : divide by 16 */ /* SSPCR1 settings */ -#define SSP_LBM 0x00 << 0 /* loopback mode : disabled */ -#define SSP_SSE 0x00 << 1 /* SSP enable : disabled */ -#define SSP_MS 0x00 << 2 /* master slave mode : master */ -#define SSP_SOD 0x00 << 3 /* slave output disable : don't care when master */ +#define MASTER_SSP_LBM 0x00 << 0 /* loopback mode : disabled */ +#define MASTER_SSP_SSE 0x00 << 1 /* SSP enable : disabled */ +#define MASTER_SSP_MS 0x00 << 2 /* master slave mode : master */ +#define MASTER_SSP_SOD 0x00 << 3 /* slave output disable : don't care when master */ /** Clock prescaler. * SPI clock rate = PCLK / (CPSR*(SCR+1)) @@ -396,11 +437,11 @@ void spi1_arch_init(void) { spi1.init_struct = (void*)(&spi1_vic_slot); /* setup pins for SSP (SCK, MISO, MOSI) */ - PINSEL1 |= PINSEL1_SCK | PINSEL1_MISO | PINSEL1_MOSI; + PINSEL1 |= SSP_PINSEL1_SCK | SSP_PINSEL1_MISO | SSP_PINSEL1_MOSI; /* setup SSP */ - SSPCR0 = SSP_DSS | SSP_FRF | SSP_CPOL | SSP_CPHA | SSP_SCR; - SSPCR1 = SSP_LBM | SSP_MS | SSP_SOD; + SSPCR0 = MASTER_SSP_DSS | MASTER_SSP_FRF | MASTER_SSP_CPOL | MASTER_SSP_CPHA | MASTER_SSP_SCR; + SSPCR1 = MASTER_SSP_LBM | MASTER_SSP_MS | MASTER_SSP_SOD; SSPCPSR = SSPCPSR_VAL; /* Prescaler */ /* initialize interrupt vector */ @@ -516,12 +557,7 @@ bool_t spi_resume(struct spi_periph* p, uint8_t slave) { * FIXME it is probably not working at all right now * */ -#ifdef SPI_SLAVE - -volatile uint8_t spi_tx_idx; -volatile uint8_t spi_rx_idx; - -void SPI1_ISR(void) __attribute__((naked)); +#if SPI_SLAVE /* set SSP input clock, PCLK / CPSDVSR = 468.75kHz */ @@ -542,57 +578,90 @@ void SPI1_ISR(void) __attribute__((naked)); #endif #endif +#if USE_SPI0_SLAVE +#error SPI0 in slave mode is not implemented yet, sorry +#endif + +#if USE_SPI1_SLAVE + /* SSPCR0 settings */ -#define SSP_DSS 0x07 << 0 /* data size : 8 bits */ -#define SSP_FRF 0x00 << 4 /* frame format : SPI */ -#define SSP_CPOL 0x00 << 6 /* clock polarity : idle low */ -#define SSP_CPHA 0x01 << 7 /* clock phase : 1 */ -#define SSP_SCR 0x0F << 8 /* serial clock rate : 29.3kHz, SSP input clock / 16 */ +#define SLAVE_SSP_DSS 0x07 << 0 /* data size : 8 bits */ +#define SLAVE_SSP_FRF 0x00 << 4 /* frame format : SPI */ +#define SLAVE_SSP_CPOL 0x00 << 6 /* clock polarity : idle low */ +#define SLAVE_SSP_CPHA 0x01 << 7 /* clock phase : 1 */ +#define SLAVE_SSP_SCR 0x0F << 8 /* serial clock rate : 29.3kHz, SSP input clock / 16 */ /* SSPCR1 settings */ -#define SSP_LBM 0x00 << 0 /* loopback mode : disabled */ -#define SSP_SSE 0x00 << 1 /* SSP enable : disabled */ -#define SSP_MS 0x01 << 2 /* master slave mode : slave */ -#define SSP_SOD 0x00 << 3 /* slave output disable : disabled */ +#define SLAVE_SSP_LBM 0x00 << 0 /* loopback mode : disabled */ +#define SLAVE_SSP_SSE 0x00 << 1 /* SSP enable : disabled */ +#define SLAVE_SSP_MS 0x01 << 2 /* master slave mode : slave */ +#define SLAVE_SSP_SOD 0x00 << 3 /* slave output disable : disabled */ + +void spi1_slave_ISR(void) __attribute__((naked)); + +void spi1_slave_ISR(void) { + ISR_ENTRY(); + + SpiSlaveAutomaton(&spi1); + + VICVectAddr = 0x00000000; /* clear this interrupt from the VIC */ + ISR_EXIT(); +} + +void spi1_slave_arch_init(void) { + + spi1.reg_addr = SPI1; -void spi_slave_init( void ) { /* setup pins for SSP (SCK, MISO, MOSI, SS) */ - PINSEL1 |= PINSEL1_SCK | PINSEL1_MISO | PINSEL1_MOSI | PINSEL1_SSEL; + PINSEL1 |= SSP_PINSEL1_SCK | SSP_PINSEL1_MISO | SSP_PINSEL1_MOSI | SSP_PINSEL1_SSEL; /* setup SSP */ - SSPCR0 = SSP_DSS | SSP_FRF | SSP_CPOL | SSP_CPHA | SSP_SCR; - SSPCR1 = SSP_LBM | SSP_MS | SSP_SOD; + SSPCR0 = SLAVE_SSP_DSS | SLAVE_SSP_FRF | SLAVE_SSP_CPOL | SLAVE_SSP_CPHA | SLAVE_SSP_SCR; + SSPCR1 = SLAVE_SSP_LBM | SLAVE_SSP_MS | SLAVE_SSP_SOD; SSPCPSR = CPSDVSR; /* Prescaler, UM10120_1.pdf page 167 */ /* initialize interrupt vector */ - VICIntSelect &= ~VIC_BIT(VIC_SPI1); // SPI1 selected as IRQ - VICIntEnable = VIC_BIT(VIC_SPI1); // SPI1 interrupt enabled - VICVectCntl7 = VIC_ENABLE | VIC_SPI1; - VICVectAddr7 = (uint32_t)SPI1_ISR; // address of the ISR + VICIntSelect &= ~VIC_BIT(VIC_SPI1); /* SPI1 selected as IRQ */ + VICIntEnable = VIC_BIT(VIC_SPI1); /* SPI1 interrupt enabled */ + _VIC_CNTL(SPI1_VIC_SLOT) = VIC_ENABLE | VIC_SPI1; + _VIC_ADDR(SPI1_VIC_SLOT) = (uint32_t)spi1_slave_ISR; /* address of the ISR */ - /* enable SPI */ - // SpiEnable(); } -void SPI1_ISR(void) { - ISR_ENTRY(); +#endif - if (bit_is_set(SSPMIS, TXMIS)) { /* Tx half empty */ - SpiTransmit(); - SpiReceive(); - SpiEnableRti(); - } +/* Register one (and only one) transaction to use spi as slave */ +bool_t spi_slave_register(struct spi_periph* p, struct spi_transaction* t) { - if ( bit_is_set(SSPMIS, RTMIS)) { /* Rx timeout */ - SpiReceive(); - SpiClearRti(); /* clear interrupt */ - SpiDisableRti(); - SpiDisable(); - spi_message_received = TRUE; - } + if (p->trans_insert_idx >= 1) { + t->status = SPITransFailed; + return FALSE; + } + t->status = SPITransPending; + p->status = SPIIdle; + p->trans[p->trans_insert_idx] = t; // No need to disable interrupts, only one transaction + p->trans_insert_idx = 1; - VICVectAddr = 0x00000000; /* clear this interrupt from the VIC */ - ISR_EXIT(); + // handle spi options (CPOL, CPHA, data size,...) + if (t->cpol == SPICpolIdleHigh) SpiSetCPOL(p); + else SpiClearCPOL(p); + + if (t->cpha == SPICphaEdge2) SpiSetCPHA(p); + else SpiClearCPHA(p); + + SpiSetDataSize(p, t->dss); + + return TRUE; +} + +bool_t spi_slave_wait(struct spi_periph* p) { + if (p->trans_insert_idx == 0) { + // no transaction registered + return FALSE; + } + // Start waiting + SpiSlaveStart(p, p->trans[p->trans_extract_idx]); + return TRUE; } #endif /* SPI_SLAVE */ diff --git a/sw/airborne/arch/lpc21/mcu_periph/spi_arch.h b/sw/airborne/arch/lpc21/mcu_periph/spi_arch.h index 139febe516..50750a70f9 100644 --- a/sw/airborne/arch/lpc21/mcu_periph/spi_arch.h +++ b/sw/airborne/arch/lpc21/mcu_periph/spi_arch.h @@ -33,8 +33,6 @@ #include "LPC21xx.h" #include BOARD_CONFIG - - // SSP is on SPI1 on lpc #if defined USE_SSP & !USE_SPI1 #define USE_SP11 1 @@ -44,19 +42,4 @@ #define SPI1_VIC_SLOT SSP_VIC_SLOT #endif -#ifdef SPI_SLAVE - -extern volatile uint8_t spi_tx_idx; -extern volatile uint8_t spi_rx_idx; - -#endif /* SPI_SLAVE */ - - - -#ifdef SPI_MASTER - - -#endif /* SPI_MASTER */ - - #endif /* SPI_ARCH_H */ diff --git a/sw/airborne/mcu.c b/sw/airborne/mcu.c index 90fb8b0c3f..e1a96bdbdf 100644 --- a/sw/airborne/mcu.c +++ b/sw/airborne/mcu.c @@ -46,7 +46,7 @@ #ifdef USE_USB_SERIAL #include "mcu_periph/usb_serial.h" #endif -#if USE_SPI0 || USE_SPI1 || USE_SPI2 +#if USE_SPI0 || USE_SPI1 || USE_SPI2 || USE_SPI0_SLAVE || USE_SPI1_SLAVE || USE_SPI2_SLAVE #include "mcu_periph/spi.h" #endif #ifdef USE_DAC @@ -109,6 +109,15 @@ void mcu_init(void) { #if USE_SPI2 spi2_init(); #endif +#if USE_SPI0_SLAVE + spi0_slave_init(); +#endif +#if USE_SPI1_SLAVE + spi1_slave_init(); +#endif +#if USE_SPI2_SLAVE + spi2_slave_init(); +#endif #ifdef USE_DAC dac_init(); #endif diff --git a/sw/airborne/mcu_periph/spi.c b/sw/airborne/mcu_periph/spi.c index 3891e0cc05..ad6184104d 100644 --- a/sw/airborne/mcu_periph/spi.c +++ b/sw/airborne/mcu_periph/spi.c @@ -28,7 +28,7 @@ #include "std.h" #include "mcu_periph/spi.h" -#ifdef SPI_MASTER +#if SPI_MASTER #if USE_SPI0 struct spi_periph spi0; @@ -64,16 +64,51 @@ void spi_init(struct spi_periph* p) { p->trans_insert_idx = 0; p->trans_extract_idx = 0; p->status = SPIIdle; + p->mode = SPIMaster; p->suspend = FALSE; } #endif /* SPI_MASTER */ -#ifdef SPI_SLAVE +#if SPI_SLAVE -uint8_t* spi_buffer_input; -uint8_t* spi_buffer_output; -uint8_t spi_buffer_length; -volatile bool_t spi_message_received; +#if USE_SPI0_SLAVE +struct spi_periph spi0; + +void spi0_slave_init(void) { + spi_slave_init(&spi0); + spi0_slave_arch_init(); +} #endif + +#if USE_SPI1_SLAVE +struct spi_periph spi1; + +void spi1_slave_init(void) { + spi_slave_init(&spi1); + spi1_slave_arch_init(); +} + +#endif + +#if USE_SPI2_SLAVE +struct spi_periph spi2; + +void spi2_slave_init(void) { + spi_slave_init(&spi2); + spi2_slave_arch_init(); +} + +#endif + +extern void spi_slave_init(struct spi_periph* p) { + p->trans_insert_idx = 0; + p->trans_extract_idx = 0; + p->status = SPIIdle; + p->mode = SPISlave; + p->suspend = FALSE; +} + +#endif /* SPI_SLAVE */ + diff --git a/sw/airborne/mcu_periph/spi.h b/sw/airborne/mcu_periph/spi.h index b6e278404c..03693ff80b 100644 --- a/sw/airborne/mcu_periph/spi.h +++ b/sw/airborne/mcu_periph/spi.h @@ -167,7 +167,7 @@ struct spi_periph { volatile uint8_t suspend; }; -#ifdef SPI_MASTER +#if SPI_MASTER #define SPI_SLAVE0 0 #define SPI_SLAVE1 1 @@ -260,16 +260,65 @@ extern bool_t spi_resume(struct spi_periph* p, uint8_t slave); #endif /* SPI_MASTER */ -#ifdef SPI_SLAVE +#if SPI_SLAVE -extern uint8_t* spi_buffer_input; -extern uint8_t* spi_buffer_output; -extern uint8_t spi_buffer_length; +#if USE_SPI0_SLAVE -extern volatile bool_t spi_message_received; +extern struct spi_periph spi0; +extern void spi0_slave_init(void); -void spi_slave_init(void); +/** Architecture dependant SPI1 initialization. + * Must be implemented by underlying architecture + */ +extern void spi0_slave_arch_init(void); #endif +#if USE_SPI1_SLAVE + +extern struct spi_periph spi1; +extern void spi1_slave_init(void); + +/** Architecture dependant SPI1 initialization. + * Must be implemented by underlying architecture + */ +extern void spi1_slave_arch_init(void); + +#endif + +#if USE_SPI2_SLAVE + +extern struct spi_periph spi2; +extern void spi2_slave_init(void); + +/** Architecture dependant SPI1 initialization. + * Must be implemented by underlying architecture + */ +extern void spi2_slave_arch_init(void); + +#endif + +/** Initialize a spi peripheral in slave mode. + * @param p spi peripheral to be configured + */ +extern void spi_slave_init(struct spi_periph* p); + +/** Register a spi transaction in slave mode (only one transaction can be registered). + * Must be implemented by the underlying architecture + * @param p spi peripheral to be used + * @param t spi transaction + * @return return true if registered with success + */ +extern bool_t spi_slave_register(struct spi_periph* p, struct spi_transaction* t); + +/** Initialized and wait for the next transaction. + * If a transaction is registered for this peripheral, the spi will be + * waiting for a communication from the master + * @param p spi peripheral to be used + * @return return true if a transaction was register for this peripheral + */ +extern bool_t spi_slave_wait(struct spi_periph* p); + +#endif /* SPI_SLAVE */ + #endif /* SPI_H */