SPI Slave Tx Buffer Problem (PSOC 5)

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.
PatrickU
Level 1
Level 1
First reply posted First question asked Welcome!

I am using a PSOC 5 (CY8CKIT-059) as a SPI slave in a project. I am trying put a status byte in the slave SPI tx buffer to serve as the default data read by the master when there is no data to transmit from the slave.  A ready byte when the slave is not processing, a busy byte when it is (or actual data from the slave).
The psoc's rx buffer is handled via dma. When the dma irq triggers for the single command byte, it clears the tx buffer (hardware), and places the busy byte in the tx buffer while it figures out what to do with the command received in the main loop. When it done executing the command, it clears the tx fifo once again and places the ready byte in the tx buffer. The problem I am running into is that at higher spi speeds (200khz in this case), the ready byte does not appear to be placed in the tx fifo. Instead, the busy byte is always returned. At lower speeds, it works as expected. I have included a bare-bones project showing the problem. Single bytes come in from the master at approx a 1 sec interval so there should not be a timing problem replacing the busy byte with the ready byte in the tx buffer. Nevertheless, the response received by the master will change based on the bus speed (at speeds around 190000 and higher, the slave will always respond with busy). Any idea what is going on here?

 

 

 

#define    PSPI_READY      0xAD
#define    PSPI_BUSY       0xAA

static uint8_t _PSPI_DMA_TD = 0x00;
static uint8_t _PSPI_DMA_Channel = 0x00;

volatile uint16 _PSPICMDFlags = 0x00;
volatile uint8_t _PSPICurCMD = 0x00;
uint8_t _PSPIInputBuffer[PSPI_BUF_SIZE] = {0};

volatile static uint32_t _PSPIRecvCnt = 0;

void Init_PSPIDMA(void) {
    
    // we are going to grab 1 byte from the SPI at a time
    _PSPI_DMA_Channel = PSPI_DMA_DmaInitialize(1,1, HI16((uint32)PSPI_RXDATA_PTR), HI16((uint32)(&_PSPIInputBuffer[0])) );
     
    /* Allocate TD - _PSPI_DMA_TD */
    _PSPI_DMA_TD = CyDmaTdAllocate();    
    
    /* Set the source of TD_rx as SPIS_RXDATA_PTR Address and the destination SPI slave Rx Buffer */	
    CyDmaTdSetAddress(_PSPI_DMA_TD, LO16(((uint32)PSPI_RXDATA_PTR)), LO16(((uint32)&_PSPIInputBuffer[0])));  

    // Set _PSPI_DMA_TD as the initial TD associated with _PSPI_DMA_Channdel
    CyDmaChSetInitialTd(_PSPI_DMA_Channel, _PSPI_DMA_TD);    
            
    PSPI_RX_DMA_Done_ISR_Start();
}

// set up DMA to transfer burstCnt bursts of 1 byte from the PSPI rx 
//    our DMA irq will trigger letting us know when the data is available
void Set_PSPICmdDMATransfer(uint16_t burstCnt, uint8_t respByte, uint16_t status) {
    
    // assuming the SPI device will echo the last byte in the tx fifo, we will add a busy
    //   byte so the master will know when we are done processing the last command 
    //     this will happen when we change the tx fifo byte to PSPI_READY

    //PSPI_ClearFIFO();
    PSPI_ClearTxBuffer();
    PSPI_WriteByte(respByte);
 
    _PSPICMDFlags = status;
    CyDmaTdSetConfiguration(_PSPI_DMA_TD, burstCnt, DMA_DISABLE_TD, TD_INC_DST_ADR | PSPI_DMA__TD_TERMOUT_EN);
    CyDmaChEnable(_PSPI_DMA_Channel, 1);     
}

void PSPI_RX_DMA_Done_ISR_Interrupt_InterruptCallback(void) {
   
    _PSPIRecvCnt = 0;
  
    PSPI_ClearTxBuffer();
    PSPI_WriteByte(PSPI_BUSY);
           
    // This DMA interrupt will trigger in three cases
    //  In the first, the DMA transfer of a PSPI command byte has finished
    if (_PSPICMDFlags == PSPI_IDLE) {
        _PSPICMDFlags  = PSPI_CMD_READY;
        _PSPICurCMD = _PSPIInputBuffer[0];
        
        // setting up dma here appears results in the ready byte being in the tx buffer
      // Set_PSPICmdDMATransfer(1, PSPI_READY, PSPI_IDLE);
        
    }
}

void Init_System(void) {  
    
    // turn on the PSPI connection
    PSPI_Start();
    
    // start the irq that handles possible lockups
   // PSPI_SS_IRQ_Start();
    
    // prepare the PSPI DMA for incoming PSPI data
    Init_PSPIDMA();    

    // and set up the dma transfer for the command byte
    Set_PSPICmdDMATransfer(1, PSPI_READY, PSPI_IDLE); 
}


int main() {
    CyGlobalIntEnable; /* Enable global interrupts. */
    Init_System();
    
    //Handle_LCDDemo();
    
    while(true) {
        
       // we received a command byte so figure out what we need to to do
       if (_PSPICMDFlags == PSPI_CMD_READY) {
            //Handle_PSPICommand();
            
            // setting up dma here does not place ready byte into tx buffer
            Set_PSPICmdDMATransfer(1, PSPI_READY, PSPI_IDLE);
        }       
    }
}

 

 

0 Likes
4 Replies
Hari
Moderator
Moderator
Moderator
750 replies posted 500 replies posted 250 solutions authored

Hi @PatrickU 

 

I went through the code and it seems like the only place where you are writing into the Tx buffer is in the Rx interrupt callback function. And the command you are writing is the busy flag always. 

Please let me know if I missed any other call for the Tx as this logic is set to always send the busy flag.

 

Best regards, 
Hari

0 Likes
PatrickU
Level 1
Level 1
First reply posted First question asked Welcome!

Hi, thanks so much for looking at this.

The busy byte does gets added to the tx buffer in the Rx interrupt (and that is the only place it is added).  It is cleared/replaced with the Ready byte in the main loop via:

 

Set_PSPICmdDMATransfer(1, PSPI_READY, PSPI_IDLE);

 

 

void Set_PSPICmdDMATransfer(uint16_t burstCnt, uint8_t respByte, uint16_t status) {

    PSPI_ClearTxBuffer();
    PSPI_WriteByte(respByte);
 
    _PSPICMDFlags = status;
    CyDmaTdSetConfiguration(_PSPI_DMA_TD, burstCnt, DMA_DISABLE_TD, TD_INC_DST_ADR | PSPI_DMA__TD_TERMOUT_EN);
    CyDmaChEnable(_PSPI_DMA_Channel, 1);     
}

 

 

This function will set up dma to receive another byte from the master, clear the tx buffer, and then add the ready byte to the tx buffer to be transmitted.   And this is the crux of the problem I am running into.   This function should run immediately after the rx inturrupt when control returns to the main loop.   The function does run, but the result of clearing and writing a new byte to the tx fifo seems to depend on the speed of the spi bus.

 

    PSPI_ClearTxBuffer();
    PSPI_WriteByte(respByte);    // respByte will always be PSPI_READY in this example

 

This problem occurs even when data is coming in very slowly (1 byte per second) so there would be no chance that the next byte is transmitted by the master between the time between the rx irq is executed and the tx buffer is cleared/the ready byte is added (via Set_PSPICmdDMATransfer).

 Thanks!

0 Likes
lock attach
Attachments are accessible only for community members.
PatrickU
Level 1
Level 1
First reply posted First question asked Welcome!

If it is helpful, I am attaching some screen captures from a logic analyzer to show:  1) Sufficient time between bytes  2) The slave is returning valid data.  In both cases.  3 separate transmissions of 1 byte (0x33) are sent 1-2 seconds apart.  With the 200000hz bus, the slave code above always returns busy (0xAA)  and in the 150000hz bus, it returns the ready byte as expected (0xAD).

0 Likes
Hari
Moderator
Moderator
Moderator
750 replies posted 500 replies posted 250 solutions authored

Hi @PatrickU 

 

Can you please check if the interrupt (PSPI_RX_DMA_Done_ISR_Interrupt_InterruptCallback) is getting triggered at the higher speed? Because, following the logic flow, the cmd ready should be set after the interrupt callback sets the _PSPICMDFlags to PSPI_CMD_READY. 

You can toggle a GPIO whenever it enters the ISR to check.

 

Best regards, 
Hari

0 Likes