1 Reply Latest reply on Sep 20, 2020 10:38 AM by EdHa_4455331

    Weird SPI Oversampling Lock-Up

    EdHa_4455331

      This is a long shot, but I am at my wit's end with this bug.

       

      I am using a simple task to share the SPI bus in my system. Other tasks place a request in the task's input queue. The task wake up, does the SPI exchange over the bus, and then sends the response back to the calling task's response queue.

       

      This has been working fine for months using an SPI oversampling of 16. Unfortunately, this relatively slow SPI speed makes the LCD look slow and clunky (and the client really is dissatisfied). Now here is the weird part. I can reduce the oversampling to 8 and the system still works fine. But if I go down to an oversampling of 4, the task locks up.

       

      Now here is was really has me puzzled. The task will work fine for several hundred exchanges with an oversample of 4, but then locks up. Now, given that the SPI bus speed was faster, I expected the problem to have something to do with the Cy_SCB_SPI_Transfer. But... that doesn't seem to be the case. When the task locks up the inUse flag is false (its only purpose is to tell me if  the CY_SCB_SPI_Transfer has locked up). Breakpoint on the error cases in the call back routine are never hit. exchangeOK is always true. So it sure seems like CY_SCB_SPI_Transfer isn't bothered by the higher bus speed. But by careful placement of breakpoints, I am fairly certain that the problem is that the when the task is blocked it suddenly stops waking up when a new request is put in the input queue. And I have never been able to pin this wake-up failure on anything in particular.

       

      With a faster bus speed the SPI task should be able to service requests faster than before, so that rules out things like the queues filling up (unlikely in any case).

       

      So... Has anybody else seen problem like this? Or can anybody spot a flaw in the code? (The initialization code that creates the queue and semaphore is not shown).

       

      Thanks,

       

      Ed H.

       


      /**************************************************************************//**
      @brief Callback function registered with the interrupt service routine (ISR)
      for the SPI channel. Used by the ISR to pass event notifications back
      to this task.

      @param theEvent - see #defines in cy_scb_i2c.h
      ******************************************************************************/
      static void spiCallback( uint32_t theEvent )
      {
        (void)theEvent;
       
        higherPriorityTaskWoken = false;
       
        switch( theEvent )
        {
        case CY_SCB_SPI_TRANSFER_IN_FIFO_EVENT :
         
          // All bytes loaded into the TX FIFO. Nice to know, but we really just
          // want to know when it is finished.
          break;
         
        case CY_SCB_SPI_TRANSFER_CMPLT_EVENT:  
         
          // Exchange operation is complete. Pass the news on to the task.
          exchangeOk = true;
          xSemaphoreGiveFromISR( spiSemaphore, &higherPriorityTaskWoken);
          break;

        case CY_SCB_SPI_TRANSFER_ERR_EVENT:
             
          // Bad news.
          exchangeOk =false;
          xSemaphoreGiveFromISR( spiSemaphore, &higherPriorityTaskWoken);
          break;
         
        default:
         
          // Should never arrive here.
          exchangeOk = false;
          xSemaphoreGiveFromISR( spiSemaphore, &higherPriorityTaskWoken);
          break;
        }
       
        portYIELD_FROM_ISR( higherPriorityTaskWoken );
      }

       


      /**************************************************************************//**
      @brief Process any requests for a write/read/exchange of data on the SPI
      channel that have been placed in the SPI request queue. Once the exchange
      has completed, send the final status of the exchange back to the response
      queue that was included as part of the request.

      @param args - not used but mandated by FreeRTOS's prototype for a task.
      ******************************************************************************/

      void SpiMasterTask( void* args)
      {
        SpiRequest activeRequest;
        bool inUse;
       
        UNUSED(args);
       
        spiSemaphore = xSemaphoreCreateBinary();
       
        // Use the auto-generated code to initialize the SPI Channel
        SpiMaster_Start();
       
        // Connect the auto-generated interrupt service routine for the
        // SPI channel to this task.
        Cy_SCB_SPI_RegisterCallback( SpiMaster_HW,  spiCallback, &SpiMaster_context );
       
        spiMasterTaskUpAndRunning = true;

        while( true )
        {
          xQueueReceive( spiRequestQueue, &activeRequest, portMAX_DELAY );
         
          inUse = true;
         
          // Assume we are going to fail (comm error or timeout).
          exchangeOk = false;
       
          // Select either the LCD, flash memory or the motor controller IC.
          Cy_SCB_SPI_SetActiveSlaveSelect( SpiMaster_HW,
                                           (cy_en_scb_spi_slave_select_t)(activeRequest.slave));

          // Exchange the data with the selected target.
          Cy_SCB_SPI_Transfer( SpiMaster_HW, activeRequest.writeBytes, activeRequest.readBytes,
                                activeRequest.bytesToExchange, &SpiMaster_context);
       
          // Wait for the exchange to complete.
          xSemaphoreTake( spiSemaphore, portMAX_DELAY);
         
          xQueueSendToBack( activeRequest.answerBackQueue, (void*)&exchangeOk,
                            portMAX_DELAY);
         
          inUse = false;
        }   
      }