Subversion Repositories Code-Repo

Rev

Rev 226 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "defines.h"
#include "SPI4.h"

static SPI4_DATA *spi_data_ptr;

void SPI4_Init(SPI4_DATA *data) {
    spi_data_ptr = data;
    spi_data_ptr->buffer_out_ind = 0;
    spi_data_ptr->buffer_out_len = 0;
    spi_data_ptr->buffer_in_ind = 0;
    spi_data_ptr->buffer_in_len = 0;
    spi_data_ptr->write_blank = 0;

    INTDisableInterrupts();

    // Note: FIFO enhanced buffer depth is 4/8/16 for 32/16/8 bit widths

    // FSCK = FPB / (2 * (SPIxBRG + 1))
    IEC1CLR     = 0x00000700;   // Disable all SPI interrupts
    SPI4CON     = 0;            // Stops and resets the SPI4.
    uint32_t tmp     = SPI4BUF;      // Clears the receive buffer
    IFS1CLR     = 0x00000700;   // Clear any existing event
    IPC8CLR     = 0x0000001F;   // Clear the priority
    IPC8SET     = 0x0000001A;   // Set IPL=6, Subpriority 2
    SPI4BRG     = 0x4;          // Use FPB/10 clock frequency
    SPI4STATCLR = 0x40;         // Clear the Overflow
    IEC1SET = 0x00000300;       // Enable RX and Error interrupts

    // Enhanced buffer, SPI on, 8 bits transfer, SMP=1, Master mode
    // SPIxTXIF set on buffer empty, SPIxRXIF set on buffer not empty
    SPI4CON     = 0x18225;

    INTEnableInterrupts();
}

uint8_t SPI4_Read(uint32_t length, void (*rx_callback)(uint8_t, uint8_t *)) {
    spi_data_ptr->rx_callback = rx_callback;

    // Ensure that the receiving buffer is large enough
    if (length > SPI4_BUFFER_IN_SIZE)
        return 0;
    
    // Ensure that no previous transactions are in progress
    if (spi_data_ptr->buffer_in_len != 0)
        return 0;

    spi_data_ptr->write_blank = 1;
    spi_data_ptr->buffer_in_len = length;
    spi_data_ptr->buffer_in_ind = 0;
    
    SPI4_Write(NULL, length, NULL);
    return 1;
}

uint8_t SPI4_Write(uint8_t *array, uint32_t length, void (*tx_callback)(void)) {
    spi_data_ptr->tx_callback = tx_callback;

    // We only care about the transmit length if we are sending data
    if (length > SPI4_BUFFER_OUT_SIZE && !spi_data_ptr->write_blank)
        return 0;

    // Ensure that no previous transactions are in progress
    if (spi_data_ptr->buffer_out_len != 0)
        return 0;

    // Put the data to send into the outbound buffer
    spi_data_ptr->buffer_out_len = length;
    spi_data_ptr->buffer_out_ind = 0;

    // Copy only if we are actually going to transmit data
    if (!spi_data_ptr->write_blank) {
        int32_t i;
        for (i = 0; i < length; i++) {
            spi_data_ptr->buffer_out[i] = array[i];
        }
    }
    IEC1SET = 0x00000400; // Enable TX interrupt
    return 1;
}

void __ISR(_SPI_4_VECTOR, ipl6) __SPI_4_Interrupt_Handler(void) {
    // Process SPI4 error flag
    if (IFS1bits.SPI4EIF) {
        // Clear the receive overflow bit if it is set
        if (SPI4STATbits.SPIROV) {
            SPI4STATbits.SPIROV = 0;
        }
        IFS1CLR = 0x00000100; // Clear the error flag
    }

    // Process SPI4 receive flag
    if (IFS1bits.SPI4RXIF) {
        uint32_t i;
        // Read the data received from the last transfer
        uint32_t rxBufferCount = SPI4STATbits.RXBUFELM;
        for (i = 0; i < rxBufferCount; i++) {
            int8_t c = SPI4BUF;
            // Put the received data into the buffer
            if (spi_data_ptr->buffer_in_len != 0) {
                spi_data_ptr->buffer_in[spi_data_ptr->buffer_in_ind] = c;
                spi_data_ptr->buffer_in_ind++;
                // If done acquiring requested length, reset
                if (spi_data_ptr->buffer_in_ind == spi_data_ptr->buffer_in_len) {
                    // Call the RX callback function on the received data
                    if (spi_data_ptr->rx_callback != NULL)
                        (*spi_data_ptr->rx_callback)(spi_data_ptr->buffer_in_len, spi_data_ptr->buffer_in);
                    spi_data_ptr->buffer_in_len = 0;
                }
            }
        }
        IFS1CLR = 0x00000200; // Clear the RX flag
    }

    // Process SPI4 transmit flag
    if (IFS1bits.SPI4TXIF && IEC1bits.SPI4TXIE) {
        int32_t i;
        // Disable the transmit interrupt if all data has been sent
        if (spi_data_ptr->buffer_out_len == 0) {
            IEC1CLR=0x00000400;
            spi_data_ptr->write_blank = 0;
            // Call the TX callback function at end of transmission
            if (spi_data_ptr->tx_callback != NULL)
                (*spi_data_ptr->tx_callback)();
        } else {
            // Start transmitting the data in the buffer
            int32_t txBufferFree = 16 - SPI4STATbits.TXBUFELM;
            if (spi_data_ptr->buffer_out_len > txBufferFree) {
                for (i = 0; i < txBufferFree; i++) {
                    if (spi_data_ptr->write_blank) {
                        SPI4BUF = 0x00;
                    } else {
                        SPI4BUF = spi_data_ptr->buffer_out[spi_data_ptr->buffer_out_ind];
                        spi_data_ptr->buffer_out_ind++;
                    }
                }
                spi_data_ptr->buffer_out_len -= txBufferFree;
            } else {
                for (i = 0; i < spi_data_ptr->buffer_out_len; i++) {
                    if (spi_data_ptr->write_blank) {
                        SPI4BUF = 0x00;
                    } else {
                        SPI4BUF = spi_data_ptr->buffer_out[spi_data_ptr->buffer_out_ind];
                        spi_data_ptr->buffer_out_ind++;
                    }
                }
                spi_data_ptr->buffer_out_len = 0;
            }
        }
        IFS1CLR = 0x00000400; // Clear the TX flag
    }
}