SPI DMA functions.

Adds 6 new functions: DMA Transfer, DMA 8bit send, DMA 16bit send, write
16 bit int. and mode change to change between 8bit and 16 bit transfer.
This commit is contained in:
victorpv 2015-05-21 22:35:40 -05:00
parent 331a02e296
commit e13b9a0709
2 changed files with 170 additions and 18 deletions

View File

@ -183,6 +183,19 @@ void SPIClass::setBitOrder(BitOrder bitOrder)
this->begin();
}
/* Victor Perez. Added to test changing datasize from 8 to 16 bit modes on the fly.
* Input parameter should be SPI_CR1_DFF set to 0 or 1 on a 32bit word.
*
*/
void SPIClass::setDataSize(uint32 datasize)
{
uint32 cr1 = this->spi_d->regs->CR1;
datasize &= SPI_CR1_DFF;
cr1 &= ~(SPI_CR1_DFF);
cr1 |= datasize;
this->spi_d->regs->CR1 = cr1;
}
void SPIClass::setDataMode(uint8_t dataMode)
{
/* Notes. As far as I can tell, the AVR numbers for dataMode appear to match the numbers required by the STM32
@ -297,6 +310,20 @@ void SPIClass::read(uint8 *buf, uint32 len) {
}
}
void SPIClass::write(uint16 data) {
// this->write(&data, 1);
/* Added for 16bit data Victor Perez. Roger Clark
* Improved speed by just directly writing the single byte to the SPI data reg and wait for completion, * by taking the Tx code from transfer(byte)
* The original method, of calling write(*data, length) .
* This almost doubles the speed of this function.
*/
spi_tx_reg(this->spi_d, data); // "2. Write the first data item to be transmitted into the SPI_DR register (this clears the TXE flag)."
while (spi_is_tx_empty(this->spi_d) == 0); // "5. Wait until TXE=1 ..."
while (spi_is_busy(this->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI."
}
void SPIClass::write(uint8 byte) {
// this->write(&byte, 1);
@ -329,35 +356,116 @@ uint8 SPIClass::transfer(uint8 byte) {
while (spi_is_busy(this->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI."
return b;
}
uint8 SPIClass::DMATransfer(uint8 *transmitBuf, uint8 *receiveBuf, uint32 length) {
/* Roger Clark and Victor Perez, 2015
* Performs a DMA SPI transfer with at least a receive buffer.
* If a TX buffer is not provided, FF is sent over and over for the lenght of the transfer.
* On exit TX buffer is not modified, and RX buffer cotains the received data.
* Still in progress.
*/
uint8 SPIClass::dmaTransfer(uint8 *transmitBuf, uint8 *receiveBuf, uint16 length) {
if (length == 0) return 0;
uint8 b;
if (spi_is_rx_nonempty(this->spi_d) == 1) b = spi_rx_reg(this->spi_d); //Clear the RX buffer in case a byte is waiting on it.
dma1_ch3_Active=true;
dma_init(DMA1);
dma_attach_interrupt(DMA1, DMA_CH2, &SPIClass::DMA1_CH3_Event);
dma_attach_interrupt(DMA1, DMA_CH3, &SPIClass::DMA1_CH3_Event);
// RX
spi_rx_dma_enable(SPI1);
dma_setup_transfer(DMA1, DMA_CH2, &SPI1->regs->DR, DMA_SIZE_8BITS,
receiveBuf, DMA_SIZE_8BITS, (DMA_MINC_MODE | DMA_TRNS_CMPLT | DMA_TRNS_ERR));// receive buffer DMA
receiveBuf, DMA_SIZE_8BITS, (DMA_MINC_MODE | DMA_TRNS_CMPLT));// receive buffer DMA
dma_set_num_transfers(DMA1, DMA_CH2, length);
// TX
spi_tx_dma_enable(SPI1);
spi_tx_dma_enable(SPI1);
if (!transmitBuf) {
static uint8_t ff = 0XFF;
transmitBuf = &ff;
dma_setup_transfer(DMA1, DMA_CH3, &SPI1->regs->DR, DMA_SIZE_8BITS,
transmitBuf, DMA_SIZE_8BITS, (DMA_FROM_MEM | DMA_TRNS_CMPLT));// Transmit FF repeatedly
}
else {
dma_setup_transfer(DMA1, DMA_CH3, &SPI1->regs->DR, DMA_SIZE_8BITS,
transmitBuf, DMA_SIZE_8BITS, (DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT));// Transmit buffer DMA
dma_set_num_transfers(DMA1, DMA_CH3, length);
transmitBuf, DMA_SIZE_8BITS, (DMA_MINC_MODE | DMA_FROM_MEM | DMA_TRNS_CMPLT));// Transmit buffer DMA
}
dma_set_num_transfers(DMA1, DMA_CH3, length);
dma_enable(DMA1, DMA_CH2);// enable receive
dma_enable(DMA1, DMA_CH3);// enable transmit
while (dma1_ch3_Active);
// while (dma1_ch3_Active);
// if (receiveBuf) {
uint32_t m = millis();
while (dma1_ch3_Active) {
if ((millis() - m) > 100) {
dma1_ch3_Active = 0;
b = 2;
break;
}
}
// }
while (spi_is_tx_empty(this->spi_d) == 0); // "5. Wait until TXE=1 ..."
while (spi_is_busy(this->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI."
while (spi_is_busy(this->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI."
dma_disable(DMA1, DMA_CH3);
dma_disable(DMA1, DMA_CH2);
spi_rx_dma_disable(SPI1);
spi_tx_dma_disable(SPI1);
return b;
}
/* Roger Clark and Victor Perez, 2015
* Performs a DMA SPI send using a TX buffer.
* On exit TX buffer is not modified.
* Still in progress.
*/
uint8 SPIClass::dmaSend(uint8 *transmitBuf, uint16 length, bool minc) {
if (length == 0) return 0;
uint32 flags = ((DMA_MINC_MODE * minc) | DMA_FROM_MEM | DMA_TRNS_CMPLT);
uint8 b;
dma1_ch3_Active=true;
dma_init(DMA1);
dma_attach_interrupt(DMA1, DMA_CH3, &SPIClass::DMA1_CH3_Event);
// TX
spi_tx_dma_enable(SPI1);
dma_setup_transfer(DMA1, DMA_CH3, &SPI1->regs->DR, DMA_SIZE_8BITS,
transmitBuf, DMA_SIZE_8BITS, flags);// Transmit buffer DMA
dma_set_num_transfers(DMA1, DMA_CH3, length);
dma_enable(DMA1, DMA_CH3);// enable transmit
while (dma1_ch3_Active);
while (spi_is_rx_nonempty(this->spi_d) == 0); // "4. Wait until RXNE=1 ..."
b = spi_rx_reg(this->spi_d); // "... and read the last received data."
while (spi_is_tx_empty(this->spi_d) == 0); // "5. Wait until TXE=1 ..."
while (spi_is_busy(this->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI."
dma_disable(DMA1, DMA_CH3);
spi_tx_dma_disable(SPI1);
return b;
}
uint8 SPIClass::dmaSend(uint16 *transmitBuf, uint16 length, bool minc) {
if (length == 0) return 0;
uint32 flags = ((DMA_MINC_MODE * minc) | DMA_FROM_MEM | DMA_TRNS_CMPLT);
uint8 b;
dma1_ch3_Active=true;
dma_init(DMA1);
dma_attach_interrupt(DMA1, DMA_CH3, &SPIClass::DMA1_CH3_Event);
// TX
spi_tx_dma_enable(SPI1);
dma_setup_transfer(DMA1, DMA_CH3, &SPI1->regs->DR, DMA_SIZE_16BITS,
transmitBuf, DMA_SIZE_16BITS, flags);// Transmit buffer DMA
dma_set_num_transfers(DMA1, DMA_CH3, length);
dma_enable(DMA1, DMA_CH3);// enable transmit
while (dma1_ch3_Active);
while (spi_is_rx_nonempty(this->spi_d) == 0); // "4. Wait until RXNE=1 ..."
b = spi_rx_reg(this->spi_d); // "... and read the last received data."
while (spi_is_tx_empty(this->spi_d) == 0); // "5. Wait until TXE=1 ..."
while (spi_is_busy(this->spi_d) != 0); // "... and then wait until BSY=0 before disabling the SPI."
dma_disable(DMA1, DMA_CH3);
spi_tx_dma_disable(SPI1);
return b;
}

View File

@ -183,6 +183,12 @@ public:
void attachInterrupt(void);
void detachInterrupt(void);
/* Victor Perez. Added to change datasize from 8 to 16 bit modes on the fly.
* Input parameter should be SPI_CR1_DFF set to 0 or 1 on a 32bit word.
* Requires an added function spi_data_size on STM32F1 / cores / maple / libmaple / spi.c
*/
void setDataSize(uint32 ds);
/*
* I/O
@ -211,6 +217,12 @@ public:
*/
void write(uint8 data);
/**
* @brief Transmit a half word.
* @param data to transmit.
*/
void write(uint16 data);
/**
* @brief Transmit multiple bytes.
* @param buffer Bytes to transmit.
@ -227,8 +239,40 @@ public:
* @return Next unread byte.
*/
uint8 transfer(uint8 data);
/**
* @brief Sets up a DMA Transfer for "length" bytes.
*
* This function transmits and receives to buffers.
*
* @param transmitBuf buffer Bytes to transmit. If passed as 0, it sends FF repeatedly for "length" bytes
* @param receiveBuf buffer Bytes to save received data.
* @param length Number of bytes in buffer to transmit.
*/
uint8 dmaTransfer(uint8 *transmitBuf, uint8 *receiveBuf, uint16 length);
uint8 DMATransfer(uint8 *transmitBuf, uint8 *receiveBuf, uint32 length);
/**
* @brief Sets up a DMA Transmit for bytes.
*
* This function transmits and does not care about the RX fifo.
*
* @param transmitBuf buffer Bytes to transmit,
* @param length Number of bytes in buffer to transmit.
* @param minc Set to use Memory Increment mode, clear to use Circular mode.
*/
uint8 dmaSend(uint8 *transmitBuf, uint16 length, bool minc = 1);
/**
* @brief Sets up a DMA Transmit for half words.
* SPI PERFIPHERAL MUST BE SET TO 16 BIT MODE BEFORE
*
* This function transmits and does not care about the RX fifo.
*
* @param data buffer half words to transmit,
* @param length Number of bytes in buffer to transmit.
* @param minc Set to use Memory Increment mode (default if blank), clear to use Circular mode.
*/
uint8 dmaSend(uint16 *transmitBuf, uint16 length, bool minc = 1);
/*
* Pin accessors
@ -302,8 +346,8 @@ private:
static inline void DMA1_CH3_Event() {
dma1_ch3_Active = 0;
dma_disable(DMA1, DMA_CH3);
dma_disable(DMA1, DMA_CH2);
// dma_disable(DMA1, DMA_CH3);
// dma_disable(DMA1, DMA_CH2);
// To Do. Need to wait for
}