Modifications and bug fixes in SPI APIs' implementations.

pull/1214/head
vimalrajr 2015-06-25 16:59:37 +05:30 committed by Karthik Purushothaman
parent 5d1b574d8b
commit 6fd1a45011
1 changed files with 133 additions and 86 deletions

View File

@ -68,6 +68,7 @@ enum spi_mode {
# define SPI_TIMEOUT 10000 # define SPI_TIMEOUT 10000
extern uint8_t g_sys_init; extern uint8_t g_sys_init;
uint16_t dummy_fill_word = 0xFFFF;
#if DEVICE_SPI_ASYNCH #if DEVICE_SPI_ASYNCH
/* Global variables */ /* Global variables */
@ -93,7 +94,7 @@ const uint32_t _sercom_handlers[SERCOM_INST_NUM] = {
MREPEAT(SERCOM_INST_NUM, _SERCOM_SPI_INTERRUPT_HANDLER_DECLR, ~) MREPEAT(SERCOM_INST_NUM, _SERCOM_SPI_INTERRUPT_HANDLER_DECLR, ~)
}; };
uint32_t _sercom_callbacks[SERCOM_INST_NUM] = {0}; uint32_t _sercom_callbacks[SERCOM_INST_NUM] = {0};
#endif #endif /* DEVICE_SPI_ASYNCH */
static inline bool spi_is_syncing(spi_t *obj) static inline bool spi_is_syncing(spi_t *obj)
{ {
@ -172,13 +173,13 @@ static inline bool spi_write(spi_t *obj, uint16_t tx_data)
/* Check if the data register has been copied to the shift register */ /* Check if the data register has been copied to the shift register */
if (!spi_is_ready_to_write(obj)) { if (!spi_is_ready_to_write(obj)) {
/* Data register has not been copied to the shift register, return */ /* Data register has not been copied to the shift register, return */
return 0; return false;
} }
/* Write the character to the DATA register */ /* Write the character to the DATA register */
_SPI(obj).DATA.reg = tx_data & SERCOM_SPI_DATA_MASK; _SPI(obj).DATA.reg = tx_data & SERCOM_SPI_DATA_MASK;
return 1; return true;
} }
static inline bool spi_read(spi_t *obj, uint16_t *rx_data) static inline bool spi_read(spi_t *obj, uint16_t *rx_data)
@ -189,7 +190,7 @@ static inline bool spi_read(spi_t *obj, uint16_t *rx_data)
/* Check if data is ready to be read */ /* Check if data is ready to be read */
if (!spi_is_ready_to_read(obj)) { if (!spi_is_ready_to_read(obj)) {
/* No data has been received, return */ /* No data has been received, return */
return 0; return false;
} }
/* Check if data is overflown */ /* Check if data is overflown */
@ -205,7 +206,7 @@ static inline bool spi_read(spi_t *obj, uint16_t *rx_data)
*rx_data = (uint8_t)_SPI(obj).DATA.reg; *rx_data = (uint8_t)_SPI(obj).DATA.reg;
} }
return 1; return true;
} }
/** /**
@ -234,8 +235,8 @@ void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel
g_sys_init = 1; g_sys_init = 1;
} }
// TODO: Calculate SERCOM instance from pins /* TODO: Calculate SERCOM instance from pins */
// TEMP: Giving our own value for testing /* TEMP: Giving external SPI module value of SAMR21 for now */
pSPI_SERCOM(obj) = EXT1_SPI_MODULE; pSPI_SERCOM(obj) = EXT1_SPI_MODULE;
/* Disable SPI */ /* Disable SPI */
@ -291,13 +292,10 @@ void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel
_SPI(obj).CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x3); _SPI(obj).CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x3);
pSPI_S(obj)->mode = SPI_MODE_MASTER; pSPI_S(obj)->mode = SPI_MODE_MASTER;
// TODO: Do pin muxing here /* TODO: Do pin muxing here */
struct system_pinmux_config pin_conf; struct system_pinmux_config pin_conf;
system_pinmux_get_config_defaults(&pin_conf); system_pinmux_get_config_defaults(&pin_conf);
pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT;
//if(config->mode == SPI_MODE_SLAVE) {
//pin_conf.input_pull = SYSTEM_PINMUX_PIN_PULL_NONE;
//}
uint32_t pad_pinmuxes[] = { uint32_t pad_pinmuxes[] = {
EXT1_SPI_SERCOM_PINMUX_PAD0, EXT1_SPI_SERCOM_PINMUX_PAD1, EXT1_SPI_SERCOM_PINMUX_PAD0, EXT1_SPI_SERCOM_PINMUX_PAD1,
@ -324,7 +322,7 @@ void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel
_SPI(obj).BAUD.reg = (uint8_t)baud; _SPI(obj).BAUD.reg = (uint8_t)baud;
/* Set MUX setting */ /* Set MUX setting */
ctrla |= EXT1_SPI_SERCOM_MUX_SETTING; // TODO: Change this to appropriate Settings ctrla |= EXT1_SPI_SERCOM_MUX_SETTING; /* TODO: Change this to appropriate Settings */
/* Set SPI character size */ /* Set SPI character size */
ctrlb |= SERCOM_SPI_CTRLB_CHSIZE(0); ctrlb |= SERCOM_SPI_CTRLB_CHSIZE(0);
@ -397,6 +395,8 @@ void spi_format(spi_t *obj, int bits, int mode, int slave)
/* Already in SPI master mode */ /* Already in SPI master mode */
} }
/* TODO: Change MUX settings to appropriate value */
/* Set SPI Frame size - only 8-bit and 9-bit supported now */ /* Set SPI Frame size - only 8-bit and 9-bit supported now */
_SPI(obj).CTRLB.bit.CHSIZE = (bits > 8)? 1 : 0; _SPI(obj).CTRLB.bit.CHSIZE = (bits > 8)? 1 : 0;
@ -612,18 +612,20 @@ static void _spi_write_async(spi_t *obj)
/* Do nothing if we are at the end of buffer */ /* Do nothing if we are at the end of buffer */
if (obj->tx_buff.pos < obj->tx_buff.length) { if (obj->tx_buff.pos < obj->tx_buff.length) {
/* Write value will be at least 8-bits long */ /* Write value will be at least 8-bits long */
if (tx_buffer)
data_to_send = tx_buffer[obj->tx_buff.pos]; data_to_send = tx_buffer[obj->tx_buff.pos];
/* Increment 8-bit index */ /* Increment 8-bit index */
obj->tx_buff.pos++; obj->tx_buff.pos++;
if (_SPI(obj).CTRLB.bit.CHSIZE == 1) { if (_SPI(obj).CTRLB.bit.CHSIZE == 1) {
if (tx_buffer)
data_to_send |= (tx_buffer[obj->tx_buff.pos] << 8); data_to_send |= (tx_buffer[obj->tx_buff.pos] << 8);
/* Increment 8-bit index */ /* Increment 8-bit index */
obj->tx_buff.pos++; obj->tx_buff.pos++;
} }
} else { } else {
/* Write a dummy packet */ /* Write a dummy packet */
data_to_send = ~0; data_to_send = dummy_fill_word;
} }
/* Write the data to send*/ /* Write the data to send*/
@ -631,7 +633,7 @@ static void _spi_write_async(spi_t *obj)
/* Check for error */ /* Check for error */
if ((_SPI(obj).INTFLAG.reg & SERCOM_SPI_INTFLAG_ERROR) && (obj->spi.mask & SPI_EVENT_ERROR)) { if ((_SPI(obj).INTFLAG.reg & SERCOM_SPI_INTFLAG_ERROR) && (obj->spi.mask & SPI_EVENT_ERROR)) {
obj->spi.event != SPI_EVENT_ERROR; obj->spi.event |= SPI_EVENT_ERROR;
} }
} }
@ -654,7 +656,7 @@ static void _spi_read_async(spi_t *obj)
_SPI(obj).STATUS.reg |= SERCOM_SPI_STATUS_BUFOVF; _SPI(obj).STATUS.reg |= SERCOM_SPI_STATUS_BUFOVF;
if (obj->spi.mask & SPI_EVENT_RX_OVERFLOW) { if (obj->spi.mask & SPI_EVENT_RX_OVERFLOW) {
/* Set overflow error */ /* Set overflow error */
obj->spi.event != SPI_EVENT_RX_OVERFLOW; obj->spi.event |= SPI_EVENT_RX_OVERFLOW;
return; return;
} }
} }
@ -663,7 +665,7 @@ static void _spi_read_async(spi_t *obj)
uint16_t received_data = (_SPI(obj).DATA.reg & SERCOM_SPI_DATA_MASK); uint16_t received_data = (_SPI(obj).DATA.reg & SERCOM_SPI_DATA_MASK);
/* Do nothing if we are at the end of buffer */ /* Do nothing if we are at the end of buffer */
if (obj->rx_buff.pos >= obj->rx_buff.length) { if ((obj->rx_buff.pos >= obj->rx_buff.length) && rx_buffer) {
return; return;
} }
@ -681,15 +683,35 @@ static void _spi_read_async(spi_t *obj)
/* Check for error */ /* Check for error */
if ((_SPI(obj).INTFLAG.reg & SERCOM_SPI_INTFLAG_ERROR) && (obj->spi.mask & SPI_EVENT_ERROR)) { if ((_SPI(obj).INTFLAG.reg & SERCOM_SPI_INTFLAG_ERROR) && (obj->spi.mask & SPI_EVENT_ERROR)) {
obj->spi.event != SPI_EVENT_ERROR; obj->spi.event |= SPI_EVENT_ERROR;
} }
} }
/**
* \internal
* Clears all interrupt flags of SPI
*
* \param[in,out] module Pointer to SPI software instance struct
*/
static void _spi_clear_interrupts(spi_t *obj)
{
uint8_t sercom_index = _sercom_get_sercom_inst_index(obj->spi.spi);
/* Clear all interrupts */
_SPI(obj).INTENCLR.reg =
SERCOM_SPI_INTFLAG_DRE |
SERCOM_SPI_INTFLAG_TXC |
SERCOM_SPI_INTFLAG_RXC |
SERCOM_SPI_INTFLAG_ERROR;
NVIC_DisableIRQ(SERCOM0_IRQn + sercom_index);
NVIC_SetVector((SERCOM0_IRQn + sercom_index), (uint32_t)NULL);
}
/** /**
* \internal * \internal
* Starts transceive of buffers with a given length * Starts transceive of buffers with a given length
* *
* \param[in] obj Pointer to SPI software instance struct * \param[in,out] obj Pointer to SPI software instance struct
* *
*/ */
static void _spi_transceive_buffer(spi_t *obj) static void _spi_transceive_buffer(spi_t *obj)
@ -698,30 +720,45 @@ static void _spi_transceive_buffer(spi_t *obj)
MBED_ASSERT(obj); MBED_ASSERT(obj);
void (*callback_func)(void); void (*callback_func)(void);
uint8_t sercom_index = _sercom_get_sercom_inst_index(obj->spi.spi); uint8_t sercom_index = _sercom_get_sercom_inst_index(obj->spi.spi);
uint16_t interrupt_status = _SPI(obj).INTFLAG.reg; uint16_t interrupt_status = _SPI(obj).INTFLAG.reg;
interrupt_status &= _SPI(obj).INTENSET.reg; interrupt_status &= _SPI(obj).INTENSET.reg;
if (interrupt_status & SERCOM_SPI_INTFLAG_DRE) { if (interrupt_status & SERCOM_SPI_INTFLAG_DRE) {
/* Clear DRE interrupt */
_SPI(obj).INTENCLR.reg = SERCOM_SPI_INTFLAG_DRE;
/* Write data */
_spi_write_async(obj); _spi_write_async(obj);
/* Set TXC interrupt */
_SPI(obj).INTENSET.reg |= SERCOM_SPI_INTFLAG_TXC;
} }
if (interrupt_status & SERCOM_SPI_INTFLAG_TXC) {
if (interrupt_status & SERCOM_SPI_INTFLAG_RXC) { /* Clear TXC interrupt */
_SPI(obj).INTENCLR.reg = SERCOM_SPI_INTFLAG_TXC;
if ((obj->rx_buff.buffer) && (obj->rx_buff.pos < obj->rx_buff.length)) {
while (!spi_is_ready_to_read(obj));
_spi_read_async(obj); _spi_read_async(obj);
if ((obj->tx_buff.pos >= obj->tx_buff.length) && (obj->tx_buff.length < obj->rx_buff.length)) {
obj->tx_buff.length = obj->rx_buff.length;
obj->tx_buff.buffer = 0;
}
}
if (obj->tx_buff.pos < obj->tx_buff.length) {
/* Set DRE interrupt */
_SPI(obj).INTENSET.reg |= SERCOM_SPI_INTFLAG_DRE;
}
} }
if (obj->spi.event & (SPI_EVENT_ERROR | SPI_EVENT_RX_OVERFLOW)) { if (obj->spi.event & (SPI_EVENT_ERROR | SPI_EVENT_RX_OVERFLOW) || (interrupt_status & SERCOM_SPI_INTFLAG_ERROR)) {
/* Disable all interrupts */ /* Clear all interrupts */
_SPI(obj).INTENCLR.reg = _spi_clear_interrupts(obj);
SERCOM_SPI_INTFLAG_DRE |
SERCOM_SPI_INTFLAG_TXC |
SERCOM_SPI_INTFLAG_RXC |
SERCOM_SPI_INTFLAG_ERROR;
NVIC_DisableIRQ(SERCOM0_IRQn + sercom_index);
/* Transfer complete, invoke the callback function */ if (interrupt_status & SERCOM_SPI_INTFLAG_ERROR) {
obj->spi.event = STATUS_ERR_BAD_DATA;
}
/* Transfer interrupted, invoke the callback function */
if (obj->spi.event & SPI_EVENT_RX_OVERFLOW) { if (obj->spi.event & SPI_EVENT_RX_OVERFLOW) {
obj->spi.status = STATUS_ERR_OVERFLOW; obj->spi.status = STATUS_ERR_OVERFLOW;
} else { } else {
@ -731,31 +768,22 @@ static void _spi_transceive_buffer(spi_t *obj)
if (callback_func && (obj->spi.mask & (SPI_EVENT_ERROR | SPI_EVENT_RX_OVERFLOW))) { if (callback_func && (obj->spi.mask & (SPI_EVENT_ERROR | SPI_EVENT_RX_OVERFLOW))) {
callback_func(); callback_func();
} }
obj->spi.status = STATUS_OK;
return; return;
} }
//if (interrupt_status & (SERCOM_SPI_INTFLAG_TXC | SERCOM_SPI_INTFLAG_RXC)) { if ((obj->tx_buff.pos >= obj->tx_buff.length) && (obj->rx_buff.pos >= obj->rx_buff.length) && (interrupt_status & SERCOM_SPI_INTFLAG_TXC)) {
if ((obj->tx_buff.pos >= obj->tx_buff.length) && (obj->rx_buff.pos >= obj->rx_buff.length)) {
/* Clear all interrupts */ /* Clear all interrupts */
_SPI(obj).INTENCLR.reg = _spi_clear_interrupts(obj);
SERCOM_SPI_INTFLAG_DRE |
SERCOM_SPI_INTFLAG_TXC |
SERCOM_SPI_INTFLAG_RXC |
SERCOM_SPI_INTFLAG_ERROR;
NVIC_DisableIRQ(SERCOM0_IRQn + sercom_index);
NVIC_SetVector((SERCOM0_IRQn + sercom_index), (uint32_t)NULL);
/* Transfer complete, invoke the callback function */ /* Transfer complete, invoke the callback function */
obj->spi.event |= SPI_EVENT_COMPLETE; obj->spi.event = SPI_EVENT_INTERNAL_TRANSFER_COMPLETE;
obj->spi.status = STATUS_OK;
callback_func = _sercom_callbacks[sercom_index]; callback_func = _sercom_callbacks[sercom_index];
if (callback_func && (obj->spi.mask & SPI_EVENT_COMPLETE)) { if (callback_func && (obj->spi.mask & SPI_EVENT_COMPLETE)) {
callback_func(); callback_func();
} }
obj->spi.status = STATUS_OK;
return; return;
} }
//}
} }
/** Begin the SPI transfer. Buffer pointers and lengths are specified in tx_buff and rx_buff /** Begin the SPI transfer. Buffer pointers and lengths are specified in tx_buff and rx_buff
@ -763,15 +791,16 @@ static void _spi_transceive_buffer(spi_t *obj)
* @param[in] obj The SPI object which holds the transfer information * @param[in] obj The SPI object which holds the transfer information
* @param[in] tx The buffer to send * @param[in] tx The buffer to send
* @param[in] tx_length The number of words to transmit * @param[in] tx_length The number of words to transmit
* @param[in] rx The buffer to receive * @param[out]rx The buffer to receive
* @param[in] rx_length The number of words to receive * @param[in] rx_length The number of words to receive
* @param[in] bit_width The bit width of buffer words * @param[in] bit_width The bit width of buffer words
* @param[in] event The logical OR of events to be registered * @param[in] event The logical OR of events to be registered
* @param[in] handler SPI interrupt handler * @param[in] handler SPI interrupt handler
* @param[in] hint A suggestion for how to use DMA with this transfer **< DMA currently not implemented >** * @param[in] hint A suggestion for how to use DMA with this transfer **< DMA currently not implemented >**
*/ */
void spi_master_transfer(spi_t *obj, void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{ {
uint16_t dummy_read;
/* Sanity check arguments */ /* Sanity check arguments */
MBED_ASSERT(obj); MBED_ASSERT(obj);
@ -780,12 +809,29 @@ void spi_master_transfer(spi_t *obj, void *tx, size_t tx_length, void *rx, size_
obj->spi.tx_buffer = tx; obj->spi.tx_buffer = tx;
obj->tx_buff.buffer = tx; obj->tx_buff.buffer = tx;
obj->tx_buff.pos = 0; obj->tx_buff.pos = 0;
if (tx) {
obj->tx_buff.length = tx_length * (bit_width>>3); obj->tx_buff.length = tx_length * (bit_width>>3);
} else {
obj->tx_buff.length = 0;
}
obj->spi.rx_buffer = rx; obj->spi.rx_buffer = rx;
obj->rx_buff.buffer = rx; obj->rx_buff.buffer = rx;
obj->rx_buff.pos = 0; obj->rx_buff.pos = 0;
if (rx) {
obj->rx_buff.length = rx_length * (bit_width>>3); obj->rx_buff.length = rx_length * (bit_width>>3);
} else {
/* Disable RXEN */
spi_disable(obj);
_SPI(obj).CTRLB.bit.RXEN = 0;
spi_enable(obj);
obj->rx_buff.length = 0;
}
/* Clear data buffer if there is anything pending to read */
while (spi_is_ready_to_read(obj)) {
dummy_read = _SPI(obj).DATA.reg;
}
_sercom_callbacks[sercom_index] = handler; _sercom_callbacks[sercom_index] = handler;
obj->spi.mask = event; obj->spi.mask = event;
@ -810,9 +856,6 @@ void spi_master_transfer(spi_t *obj, void *tx, size_t tx_length, void *rx, size_
if (tx) { if (tx) {
irq_mask |= SERCOM_SPI_INTFLAG_DRE; irq_mask |= SERCOM_SPI_INTFLAG_DRE;
} }
if (rx) {
irq_mask |= SERCOM_SPI_INTFLAG_RXC;
}
if (event & SPI_EVENT_ERROR) { if (event & SPI_EVENT_ERROR) {
irq_mask |= SERCOM_SPI_INTFLAG_ERROR; irq_mask |= SERCOM_SPI_INTFLAG_ERROR;
} }
@ -836,39 +879,47 @@ uint32_t spi_irq_handler_asynch(spi_t *obj)
if (obj->spi.dma_usage == DMA_USAGE_NEVER) { if (obj->spi.dma_usage == DMA_USAGE_NEVER) {
/* IRQ method */ /* IRQ method */
if (obj->spi.event & SPI_EVENT_INTERNAL_TRANSFER_COMPLETE) {
obj->spi.event |= SPI_EVENT_COMPLETE;
transfer_event = obj->spi.event;
} else {
/* Data is still remaining to be transferred! */
obj->spi.status = STATUS_BUSY; obj->spi.status = STATUS_BUSY;
if ((obj->tx_buff.pos < obj->tx_buff.length) || (obj->rx_buff.pos < obj->rx_buff.length)) {
bytes_to_transfer = (obj->tx_buff.length > obj->rx_buff.length)? obj->tx_buff.length : obj->rx_buff.length; /* Read any pending data in RX buffer */
while (bytes_to_transfer) { while (spi_is_ready_to_read(obj)) {
if (spi_is_ready_to_write(obj)) { _spi_read_async(obj);
_spi_write_async(obj);
} }
while (obj->tx_buff.pos < obj->tx_buff.length) {
/* Write data */
_spi_write_async(obj);
/* Read if any */
if ((obj->rx_buff.buffer) && (obj->rx_buff.pos < obj->rx_buff.length)) {
if (spi_is_ready_to_read(obj)) { if (spi_is_ready_to_read(obj)) {
_spi_read_async(obj); _spi_read_async(obj);
} }
if (obj->spi.event & (SPI_EVENT_ERROR | SPI_EVENT_RX_OVERFLOW)) { /* Extend TX buffer (with dummy) if there is more to receive */
if ((obj->tx_buff.pos >= obj->tx_buff.length) && (obj->tx_buff.length < obj->rx_buff.length)) {
obj->tx_buff.length = obj->rx_buff.length;
obj->tx_buff.buffer = 0;
}
}
if (obj->spi.event & SPI_EVENT_ERROR) {
transfer_event = obj->spi.event; transfer_event = obj->spi.event;
obj->spi.status = (obj->spi.event & SPI_EVENT_RX_OVERFLOW)? STATUS_ERR_OVERFLOW : STATUS_ERR_BAD_DATA; obj->spi.status = STATUS_ERR_BAD_DATA;
break; break;
} }
bytes_to_transfer--;
} }
} if ((obj->tx_buff.pos >= obj->tx_buff.length) && (obj->rx_buff.pos >= obj->rx_buff.length)) {
if (bytes_to_transfer == 0) { transfer_event = (SPI_EVENT_INTERNAL_TRANSFER_COMPLETE | SPI_EVENT_COMPLETE);
transfer_event |= SPI_EVENT_COMPLETE;
/* Clear all interrupts */
_SPI(obj).INTENCLR.reg =
SERCOM_SPI_INTFLAG_DRE |
SERCOM_SPI_INTFLAG_TXC |
SERCOM_SPI_INTFLAG_RXC |
SERCOM_SPI_INTFLAG_ERROR;
NVIC_DisableIRQ(SERCOM0_IRQn + sercom_index);
NVIC_SetVector((SERCOM0_IRQn + sercom_index), (uint32_t)NULL);
obj->spi.status = STATUS_OK; obj->spi.status = STATUS_OK;
} }
transfer_event &= obj->spi.mask;
} }
transfer_event &= (obj->spi.mask | SPI_EVENT_INTERNAL_TRANSFER_COMPLETE);
/* Clear all interrupts */
_spi_clear_interrupts(obj);
}
return transfer_event; return transfer_event;
} }
@ -878,12 +929,8 @@ uint32_t spi_irq_handler_asynch(spi_t *obj)
*/ */
uint8_t spi_active(spi_t *obj) uint8_t spi_active(spi_t *obj)
{ {
if (obj->spi.status == STATUS_BUSY) {
/* Check if the SPI module is busy with a job */ /* Check if the SPI module is busy with a job */
return 1; return (obj->spi.status == STATUS_BUSY);
} else {
return 0;
}
} }
/** Abort an SPI transfer /** Abort an SPI transfer
@ -911,4 +958,4 @@ void spi_abort_asynch(spi_t *obj)
obj->spi.status = STATUS_ABORTED; obj->spi.status = STATUS_ABORTED;
} }
#endif #endif /* DEVICE_SPI_ASYNCH */