Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
lock attach
Attachments are accessible only for community members.
cadi_1014291
Level 6
Level 6
25 likes received 10 likes received 10 likes given

Greetings,

I was getting familiar with the SPI Master interrupts, so i started a project to send data via SPI taking advantage of the SPI TX FIFO and interrupts, so that way i don't stall the CPU waiting for the transfer to be completed, in the example the cpu triggers the SPI transfer and leave the interrupts to handle it, meanwhile the CPU toogles a digital output (silly but illustrate the point ;P), this can be useful when using PSoCs with no DMA.

To send just 3 bytes (less than the FIFO depth) using the spi_xfer_async function:

int main(void)

{

    const uint8_t data_spi[10] = {0x01, 0x02, 0x03, 0x04, 0x05,

                              0x06, 0x07, 0x08, 0x09, 0x0A};

   

    isr_SPI_Tx_StartEx(SPI_tx_handler);

   

    // Enable interrupts

    CyGlobalIntEnable;

   

    // Configure and enable the peripherals

    SPI_Start();

    UART_Start();

   

    UART_PutChar(0x0C); // clear the screen

    UART_PutString("Test SPI with interrupts.\r\n");

    while (1) {

        if (TX_IDLE == tx_state) {

            spi_xfer_asynch(data_spi, 3);

        }

        CyDelayUs(10);

        LED_Write(~LED_Read());

    }

}

The data transfer captured with a LA (you can see the LED toogling on the top trace):

async_spi_3.png

To send just 10 bytes (more than the FIFO depth):

int main(void)

{

    const uint8_t data_spi[10] = {0x01, 0x02, 0x03, 0x04, 0x05,

                              0x06, 0x07, 0x08, 0x09, 0x0A};

   

    isr_SPI_Tx_StartEx(SPI_tx_handler);

   

    // Enable interrupts

    CyGlobalIntEnable;

   

    // Configure and enable the peripherals

    SPI_Start();

    UART_Start();

   

    UART_PutChar(0x0C); // clear the screen

    UART_PutString("Test SPI with interrupts.\r\n");

    while (1) {

        if (TX_IDLE == tx_state) {

            spi_xfer_asynch(data_spi, 10);

        }

        CyDelayUs(10);

        LED_Write(~LED_Read());

    }

}

The data transfer captured with a LA (you can see the LED toogling on the top trace):

async_spi_10.png

Hope is useful to you (find the project attached, it use SPI based on UDB, but the principle apply to SCB as well), if you see something wrong let me know.

Regards,

Carlos

0 Likes
1 Solution
lock attach
Attachments are accessible only for community members.
cadi_1014291
Level 6
Level 6
25 likes received 10 likes received 10 likes given

I worked a bit more and updated the buffer_t struct, now it just have a pointer to a uint8_t instead of the array of uint8_t, so now we must have an array of uint8_t with global scope to work with it, this avoid using memcpy (on the spi_xfer_async function) from the previous implementation.

This is the new main.c file

#include "project.h"

enum {

    MAX_ITEMS = 10,

};

typedef enum  {

    TX_IDLE,

    TX_TRANSFERING,

} tx_state_tag;

volatile tx_state_tag tx_state = TX_IDLE;

typedef struct {

    uint8_t *const buffer;

    uint8_t cnt;

    uint8_t left_to_xfer;

} buffer_t;

uint8_t data_spi[MAX_ITEMS] = {0x01, 0x02, 0x03, 0x04, 0x05,

                              0x06, 0x07, 0x08, 0x09, 0x0A};

buffer_t tx_buffer = {

    .buffer = data_spi,

    .cnt = 0,

    .left_to_xfer = 0,

};

void spi_xfer_async(uint8_t *data, const size_t size);

void SPI_handler(void);

int main(void)

{

    isr_SPI_StartEx(SPI_handler);

   

    CyGlobalIntEnable;

   

    SPI_Start();

    UART_Start();

   

    UART_PutChar(0x0C); // clear the screen

    UART_PutString("Test SPI with interrupts.\r\n");

    while (1) {

       

        if (TX_IDLE == tx_state) {

            spi_xfer_async(tx_buffer.buffer, 3);

        }

       

        CyDelayUs(50);

        LED_Write(~LED_Read());

       

        if (TX_IDLE == tx_state) {

            spi_xfer_async(tx_buffer.buffer, 10);

        }

       

        CyDelayUs(100);

        LED_Write(~LED_Read());

    }

}

The spi_xfer_async function:

void spi_xfer_async(uint8_t *data, const size_t size)

{

    if (NULL != data) {

        CyGlobalIntDisable;

        if (TX_IDLE == tx_state) {

            tx_state = TX_TRANSFERING;

           

            if (size <= SPI_FIFO_SIZE) {

                // fill the spi tx fifo

                for (uint8_t i = 0; i < size; i++) {

                    SPI_WriteTxData(data);

                }

            } else {

                tx_buffer.cnt = 2;

                tx_buffer.left_to_xfer = size;

                // put at least 2 bytes into the spi tx fifo

                for (uint8_t i = 0; i < 2; i++) {

                    SPI_WriteTxData(data);

                }

            }

           

        }

        CyGlobalIntEnable;

    }

}

The interrupt handler:

void SPI_handler(void)

{

    // clear interrupt flag

    volatile uint8_t sts = SPI_ReadTxStatus();

   

    // TODO: Get the data from MISO

    (void)SPI_ReadRxStatus();

    if (TX_IDLE != tx_state) {

        if (SPI_STS_BYTE_COMPLETE & sts) {

            // do we still have data to send?

            if (tx_buffer.left_to_xfer > tx_buffer.cnt) {

                SPI_WriteTxData(tx_buffer.buffer[tx_buffer.cnt]);

                tx_buffer.cnt++;

            }

        }

        // the interrupt was triggered by the SPI_DONE flag

        if (SPI_STS_SPI_IDLE & sts) {

            tx_state = TX_IDLE;

        }

    }

}

We should be careful when updating the global data_spi array doing it when a transfer is not in process.

The project is attached (PSoC Creator 4.1).

Regards,

Carlos

View solution in original post

0 Likes
5 Replies