mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2025-01-18 03:52:11 +01:00
spi: Add support for hardware CRC generation/checking
PIOS SPI devices may now make use of automatic CRC generation and checking on block transfers. Only supports CRC8 for now. Since the SPI interface CRC calculation continues across message boundaries (ie. not reset on every transfer), we must manually reset the CRC registers for every transfer to allow the two sides of the link to resynchronize. Unfortunately, resetting the CRC registers requires disabling the SPI peripheral which must now be done on every block transfer. Note: The last byte of the tx buffer is never sent and is assumed to be a place holder for the tx CRC8. Note: The last byte of the rx buffer is expected to hold the rx CRC8. git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@1011 ebee16cc-31ac-478f-84a7-5cbb03baadba
This commit is contained in:
parent
c12b7e3fed
commit
a3a2dbd634
@ -49,6 +49,7 @@ static const struct pios_spi_cfg pios_spi_op_cfg = {
|
||||
.SPI_CPOL = SPI_CPOL_High,
|
||||
.SPI_CPHA = SPI_CPHA_2Edge,
|
||||
},
|
||||
.use_crc = TRUE,
|
||||
.dma = {
|
||||
.ahb_clk = RCC_AHBPeriph_DMA1,
|
||||
|
||||
|
@ -147,6 +147,7 @@ const struct pios_spi_cfg pios_spi_ahrs_cfg = {
|
||||
.SPI_CPHA = SPI_CPHA_2Edge,
|
||||
.SPI_BaudRatePrescaler = 7 << 3, /* Maximum divider (ie. slowest clock rate) */
|
||||
},
|
||||
.use_crc = TRUE,
|
||||
.dma = {
|
||||
.ahb_clk = RCC_AHBPeriph_DMA1,
|
||||
|
||||
|
@ -8,12 +8,6 @@
|
||||
* @see The GNU Public License (GPL) Version 3
|
||||
* @defgroup PIOS_SPI SPI Functions
|
||||
* @notes
|
||||
* J19 provides two SSEL (alias Chip Select) lines.<BR>
|
||||
* It's a software emulated SPI, therefore the selected spi_prescaler has no
|
||||
* effect! Bytes are transfered so fast as possible. The usage of
|
||||
* PIOS_SPI_PIN_DRIVER_STRONG is strongly recommended ;)<BR>
|
||||
* DMA transfers are not supported by the emulation, so that
|
||||
* PIOS_SPI_TransferBlock() will consume CPU time (but the callback handling does work).
|
||||
*
|
||||
* Note that additional chip select lines can be easily added by using
|
||||
* the remaining free GPIOs of the core module. Shared SPI ports should be
|
||||
@ -90,7 +84,8 @@ int32_t PIOS_SPI_Init(void)
|
||||
}
|
||||
break;
|
||||
case SPI_NSS_Hard:
|
||||
GPIO_Init(spi_dev->cfg->ssel.gpio, &(spi_dev->cfg->ssel.init));
|
||||
/* FIXME: Should this also call SPI_SSOutputCmd()? */
|
||||
GPIO_Init(spi_dev->cfg->ssel.gpio, &(spi_dev->cfg->ssel.init));
|
||||
break;
|
||||
default:
|
||||
PIOS_DEBUG_Assert(0);
|
||||
@ -131,6 +126,13 @@ int32_t PIOS_SPI_Init(void)
|
||||
/* Initialize the SPI block */
|
||||
SPI_Init(spi_dev->cfg->regs, &(spi_dev->cfg->init));
|
||||
|
||||
/* Configure CRC calculation */
|
||||
if (spi_dev->cfg->use_crc) {
|
||||
SPI_CalculateCRC(spi_dev->cfg->regs, ENABLE);
|
||||
} else {
|
||||
SPI_CalculateCRC(spi_dev->cfg->regs, DISABLE);
|
||||
}
|
||||
|
||||
/* Enable SPI */
|
||||
SPI_Cmd(spi_dev->cfg->regs, ENABLE);
|
||||
|
||||
@ -299,15 +301,23 @@ int32_t PIOS_SPI_TransferBlock(uint8_t spi, const uint8_t *send_buffer, uint8_t
|
||||
|
||||
|
||||
/* Exit if ongoing transfer */
|
||||
if(spi_dev->cfg->dma.rx.channel->CNDTR) {
|
||||
if (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel)) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Disable the SPI peripheral */
|
||||
SPI_Cmd(spi_dev->cfg->regs, DISABLE);
|
||||
|
||||
/* Disable the DMA channels */
|
||||
DMA_Cmd(spi_dev->cfg->dma.rx.channel, DISABLE);
|
||||
DMA_Cmd(spi_dev->cfg->dma.tx.channel, DISABLE);
|
||||
|
||||
/* Set callback function */
|
||||
spi_dev->callback = callback;
|
||||
|
||||
/* Configure Rx channel */
|
||||
DMA_Cmd(spi_dev->cfg->dma.rx.channel, DISABLE);
|
||||
/*
|
||||
* Configure Rx channel
|
||||
*/
|
||||
|
||||
/* Start with the default configuration for this peripheral */
|
||||
dma_init = spi_dev->cfg->dma.rx.init;
|
||||
@ -318,16 +328,21 @@ int32_t PIOS_SPI_TransferBlock(uint8_t spi, const uint8_t *send_buffer, uint8_t
|
||||
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
||||
} else {
|
||||
/* Disable memory addr. increment - bytes written into dummy buffer */
|
||||
spi_dev->rx_dummy_byte = 0xff;
|
||||
spi_dev->rx_dummy_byte = 0xFF;
|
||||
dma_init.DMA_MemoryBaseAddr = (uint32_t)&spi_dev->rx_dummy_byte;
|
||||
dma_init.DMA_MemoryInc = DMA_MemoryInc_Disable;
|
||||
}
|
||||
if (spi_dev->cfg->use_crc) {
|
||||
/* Make sure the CRC error flag is cleared before we start */
|
||||
SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR);
|
||||
}
|
||||
|
||||
dma_init.DMA_BufferSize = len;
|
||||
DMA_Init(spi_dev->cfg->dma.rx.channel, &(dma_init));
|
||||
DMA_Cmd(spi_dev->cfg->dma.rx.channel, ENABLE);
|
||||
|
||||
/* Configure Tx channel */
|
||||
DMA_Cmd(spi_dev->cfg->dma.tx.channel, DISABLE);
|
||||
/*
|
||||
* Configure Tx channel
|
||||
*/
|
||||
|
||||
/* Start with the default configuration for this peripheral */
|
||||
dma_init = spi_dev->cfg->dma.tx.init;
|
||||
@ -338,21 +353,64 @@ int32_t PIOS_SPI_TransferBlock(uint8_t spi, const uint8_t *send_buffer, uint8_t
|
||||
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
||||
} else {
|
||||
/* Disable memory addr. increment - bytes written into dummy buffer */
|
||||
spi_dev->tx_dummy_byte = 0xff;
|
||||
spi_dev->tx_dummy_byte = 0xFF;
|
||||
dma_init.DMA_MemoryBaseAddr = (uint32_t)&spi_dev->tx_dummy_byte;
|
||||
dma_init.DMA_MemoryInc = DMA_MemoryInc_Disable;
|
||||
}
|
||||
dma_init.DMA_BufferSize = len;
|
||||
|
||||
if (spi_dev->cfg->use_crc) {
|
||||
/* The last byte of the payload will be replaced with the CRC8 */
|
||||
dma_init.DMA_BufferSize = len - 1;
|
||||
} else {
|
||||
dma_init.DMA_BufferSize = len;
|
||||
}
|
||||
|
||||
DMA_Init(spi_dev->cfg->dma.tx.channel, &(dma_init));
|
||||
|
||||
/* Enable DMA interrupt if callback function active */
|
||||
DMA_ITConfig(spi_dev->cfg->dma.rx.channel, DMA_IT_TC, (callback != NULL) ? ENABLE : DISABLE);
|
||||
|
||||
/* Start DMA Tx transfer */
|
||||
/* Flush out the CRC registers */
|
||||
SPI_CalculateCRC(spi_dev->cfg->regs, DISABLE);
|
||||
(void) SPI_GetCRC(spi_dev->cfg->regs, SPI_CRC_Rx);
|
||||
SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR);
|
||||
|
||||
/* Make sure to flush out the receive buffer */
|
||||
(void) SPI_I2S_ReceiveData(spi_dev->cfg->regs);
|
||||
|
||||
if (spi_dev->cfg->use_crc) {
|
||||
/* Need a 0->1 transition to reset the CRC logic */
|
||||
SPI_CalculateCRC(spi_dev->cfg->regs, ENABLE);
|
||||
}
|
||||
|
||||
/* Start DMA transfers */
|
||||
DMA_Cmd(spi_dev->cfg->dma.rx.channel, ENABLE);
|
||||
DMA_Cmd(spi_dev->cfg->dma.tx.channel, ENABLE);
|
||||
|
||||
/* Reenable the SPI device */
|
||||
SPI_Cmd(spi_dev->cfg->regs, ENABLE);
|
||||
|
||||
if (callback) {
|
||||
/* User has requested a callback, don't wait for the transfer to complete. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wait until all bytes have been transmitted/received */
|
||||
while(DMA_GetCurrDataCounter(spi_dev->cfg->dma.tx.channel));
|
||||
while(DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel));
|
||||
|
||||
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */
|
||||
while(!(SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_TXE)));
|
||||
|
||||
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */
|
||||
while(SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_BSY));
|
||||
|
||||
/* Check the CRC on the transfer if enabled. */
|
||||
if (spi_dev->cfg->use_crc) {
|
||||
/* Check the SPI CRC error flag */
|
||||
if (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_FLAG_CRCERR)) {
|
||||
return -4;
|
||||
}
|
||||
}
|
||||
|
||||
/* No error */
|
||||
return 0;
|
||||
@ -367,8 +425,22 @@ void PIOS_SPI_IRQ_Handler(uint8_t spi)
|
||||
|
||||
DMA_ClearFlag(spi_dev->cfg->dma.irq.flags);
|
||||
|
||||
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */
|
||||
while(!(SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_TXE)));
|
||||
|
||||
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */
|
||||
while(SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_BSY));
|
||||
|
||||
if(spi_dev->callback != NULL) {
|
||||
spi_dev->callback();
|
||||
bool crc_ok = TRUE;
|
||||
uint8_t crc_val;
|
||||
|
||||
if (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_FLAG_CRCERR)) {
|
||||
crc_ok = FALSE;
|
||||
SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR);
|
||||
}
|
||||
crc_val = SPI_GetCRC(spi_dev->cfg->regs, SPI_CRC_Rx);
|
||||
spi_dev->callback(crc_ok, crc_val);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@
|
||||
struct pios_spi_cfg {
|
||||
SPI_TypeDef * regs;
|
||||
SPI_InitTypeDef init;
|
||||
bool * master;
|
||||
bool use_crc;
|
||||
struct stm32_dma dma;
|
||||
struct stm32_gpio ssel;
|
||||
struct stm32_gpio sclk;
|
||||
@ -43,7 +43,7 @@ struct pios_spi_cfg {
|
||||
|
||||
struct pios_spi_dev {
|
||||
const struct pios_spi_cfg * const cfg;
|
||||
void (*callback)(void);
|
||||
void (*callback)(uint8_t, uint8_t);
|
||||
uint8_t tx_dummy_byte;
|
||||
uint8_t rx_dummy_byte;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user