Forums - Need help getting SPI master working

6 posts / 0 new
Last post
Need help getting SPI master working
david_charlap
Join Date: 3 Apr 19
Posts: 17
Posted: Tue, 2019-05-28 06:48

I'm trying to send data to an SPI-attached device and it's not working as expected.

My device is wired to the QCA 4020 developer kit board as follows:

 

  • 3.3v power - attached to J1, pin 4 (Arduino pin 3V3)
  • Ground - attached to J1, pin 7 (Arduino pin AGND)
  • SPI CLK - attached to J5 pin 4 (GPIO25_BE, which the schematic says is SCK for an SPI master)
  • SPI MOSI - attached to J5 pin 6 (GPIO26_BE, which the schematic says is MOSI for an SPI master)
  • SPI MISO - attached to J5 pin 8 (GPIO27_BE, which the schematic says is MISO for an SPI master)
  • Chip select - Attached to J5 pin 2 (GPIO24_BE, which the schematic says is SS for an SPI master)

 

On the software side, I made a copy of the Hello World demo app and replaced the code in its main thread with some code that appears to be correct for sending bytes to the SPI bus, but it doesn't seem to work. The device doesn't respond and the callback function is never called.

I've seen this same symptom (no callback function) from my Wi-Fi tests as well. It would appears that some system thread is not running, but I have not yet figured out what I need to do in order to start that thread. I have not yet found anything in the documentation or the QCLI_demo application to do this.

Does anyone know what I'm missing? What is the secret magic I need to do to start the background threads that SPI (and WLAN) use? Is there a function I need to call? Do I need to change the auto-generated files in the src/export directory? Do I need to make changs in the build/gcc folder (Makefile? app.config? Something else?)

Here's an excerpt from my code, which is a modification of the Hello World demo application. The values sent over SPI are to initialize an ST7789 TFT display controller chip, but that shouldn't be relevant to the problem here. There is additional work for driving the display, but that code (toggling a GPIO pin) is working, so I omitted it from this excerpt.  If I can send any bytes over the SPI bus at all, the rest won't be a problem. (Printf is my function for sending text to the UART console - you can safely ignore it):

 

// Globals
void               *spi_handle = NULL;
qapi_SPIM_Config_t  spi_config = {0};

// SPI transfers are asynchronous, so we need a callback function and signal
// so we can block until completion
#define SPI_COMPLETION_MASK (0x10)
qurt_signal_t spi_completion_signal;

void init_spi(void) {
    qapi_Status_t status;

    status = qapi_SPIM_Open(QAPI_SPIM_INSTANCE_1_E, &spi_handle);
    if (status) {
        Printf("Can't open SPI Master instance 1.  Status=%ld\r\n", status);
        return;
    }

    status = qapi_SPIM_Enable(spi_handle);
    if (status) {
        Printf("Can't enable SPI master (handle=%p).  Status=%ld\r\n", spi_handle, status);
        return;
    }

    qurt_signal_create(&spi_completion_signal);

    spi_config.SPIM_Clk_Polarity = QAPI_SPIM_CLK_IDLE_LOW_E;
    spi_config.SPIM_Shift_Mode = QAPI_SPIM_INPUT_FIRST_MODE_E;
    spi_config.SPIM_CS_Polarity = QAPI_SPIM_CS_ACTIVE_LOW_E;
    spi_config.SPIM_CS_Mode = QAPI_SPIM_CS_DEASSERT_E;
    spi_config.SPIM_Clk_Always_On = QAPI_SPIM_CLK_NORMAL_E;
    spi_config.SPIM_Bits_Per_Word = 8;
    spi_config.SPIM_Slave_Index = 0;
    spi_config.min_Slave_Freq_Hz =   500000;  // Minimum 500kHz
    spi_config.max_Slave_Freq_Hz = 16000000;  // Maximum 16MHz
    spi_config.deassertion_Time_Ns = 1000;
    spi_config.loopback_Mode = 0;
    spi_config.hs_Mode = 0;
}

void spi_completion_callback_fn(uint32_t status, void *context) {
    if (status) {
        Printf("SPI callback failed with status %ld\r\n", status);
    }
    qurt_signal_set(&spi_completion_signal, SPI_COMPLETION_MASK);
}

void wait_for_spi_completion(void) {
    qurt_signal_wait(&spi_completion_signal, SPI_COMPLETION_MASK,
                     QURT_SIGNAL_ATTR_WAIT_ANY | QURT_SIGNAL_ATTR_CLEAR_MASK);
}

void write_buffer(uint8_t *buffer, size_t len) {
    qapi_Status_t        status;
    qapi_SPIM_Transfer_t tx_info = { 0 };

    tx_info.buf_phys_addr = buffer;
    tx_info.buf_len       = len;

    status = qapi_SPIM_Full_Duplex(spi_handle, &spi_config, &tx_info, NULL,
        spi_completion_callback_fn, NULL);
    if (status) {
        Printf("Failed to send %d bytes over SPI.  Status = %ld\r\n", len, status);
    } else {
        wait_for_spi_completion();
    }
}

void write_byte(uint8_t value) {
    write_buffer(&value, 1);
}

enum {
    SWRESET = 0x01,    // Software reset
    SLPOUT  = 0x11,    // Sleep out
    NORON   = 0x13,    // Normal display mode on
    INVON   = 0x21,    // Inverse video mode on
    DISPON  = 0x29,    // Display on
    CASET   = 0x2A,    // Column address set
    RASET   = 0x2B,    // Row address set
    RAMWR   = 0x2C,    // RAM Write
    MADCTL  = 0x36,    // Memory and data access control
    COLMOD  = 0x3A     // Color mode (interface pixel format)
} commands;

send_spi_commands() {
    write_byte(SWRESET );
    Sleep(200);
    write_byte(SLPOUT);
    Sleep(200);
    write_byte(COLMOD);
    write_byte(0x55);
    Sleep(10);
    write_byte(MADCTL);
    write_byte(0);
    Sleep(10);
    write_byte(INVON);
    Sleep(10);
    write_byte(NORON);
    Sleep(10);
    write_byte(DISPON);
    Sleep(2000);
}

static void MainAppThread(void *Param)  // Replaces HelloWorld_Thread
{
    init_spi();

    send_spi_commands();

    while(true)
    {
        Sleep(10000);  // Will be replaced with other code later
    }
}
  • Up0
  • Down0
david_charlap
Join Date: 3 Apr 19
Posts: 17
Posted: Tue, 2019-05-28 14:16

I still have had no luck getting any kind of callback from SPI, but I think I learned a little bit more about what I need to do.

It appears that there are no QAPI calls to specify which GPIO pins are used for each of the 4 SPI master instances, but I think I need to edit the various export/DevCfg_master_*.xml files.  Looking at the comments in the XML files, I think I need to edit the hex values in the "spi1" and "spi2" devices.  For example, in DevCfg_master_devcfg_out.xml:

...
<driver name="spi1">
<!-- GPIO configuration calculations
GPIO DIR values
   GPIO_INPUT = 0x0
   GPIO_OUTPUT = 0x1
GPIO_PULL values
   GPIO_NO_PULL    = 0,    
   GPIO_PULL_DOWN  = 0x1,  
   GPIO_PULL_UP    = 0x2,  
GPIO_DRV_STRENGTH values
   GPIO_1P6MA   = 0,       
   GPIO_2P7MA   = 0x1,     
   GPIO_4P0MA   = 0x2,     
GPIO configuration =  (GPIO_NUM           & 0xFF) << 0x10 |
                      (GPIO_FS_VAL        &  0xF) <<  0xC |
                      (GPIO_DRV_STRENGTH  &  0xF) <<  0x8 |
                      (GPIO_PULL          &  0xF) <<  0x4 |
                      (GPIO_DIR           &  0xF)
-->
   <device id="0x02000010">
      <props helptext="SPI Master Core Configuration" id="0x21001" id_name="SPI Core Configuration" oem_configurable="false" type="0x00000012"> spi_first_port  </props>
	  <props helptext="GPIO encoding {Format 0xXX (LSB), 0xXX, 0xXX, 0xXX(MSB)} : 1st 8 bits =  pull[7:4] -> 0 - NO_PULL, 1 - PULL_DOWN, 2 - PULL_UP | direction[3:0] -> 0 - INPUT, 1 = OUTPUT , 2nd 8 bits = drive_strength[7:4] -> 0 - 1.6mA, 1 - 2.7mA, 2 - 4mA | function[3:0], 3rd 8 bits = gpio pin[7:0] -> pin number, 4th 8 bits {unused[7:0] -> Reserved" id="0x2100A" id_name="SPI Controller Board Specific Configuration" oem_configurable="true" type="0x00000008">
         <!-- MOSI GPIO key               = --> 0x01, 0x00, 0x06, 0x00,
         <!-- MISO GPIO key               = --> 0x00, 0x00, 0x05, 0x00,
         <!-- CS GPIO key                 = --> 0x01, 0x00, 0x07, 0x00,
         <!-- CLK GPIO key                = --> 0x01, 0x00, 0x04, 0x00,
	 <!-- PU MOSI GPIO encoding       = --> 0x01, 0x12, 0x1A, 0x00, 
         <!-- PU MISO GPIO encoding       = --> 0x00, 0x12, 0x1B, 0x00,
         <!-- PU CS GPIO encoding         = --> 0x01, 0x12, 0x18, 0x00,
         <!-- PU CLK GPIO encoding        = --> 0x01, 0x12, 0x19, 0x00,
         <!-- PD MOSI GPIO encoding       = --> 0x10, 0x00, 0x1A, 0x00,
         <!-- PD MISO GPIO encoding       = --> 0x10, 0x00, 0x1B, 0x00,
         <!-- PD CS GPIO encoding         = --> 0x20, 0x00, 0x18, 0x00,
         <!-- PD CLK GPIO encoding        = --> 0x10, 0x00, 0x19, 0x00,
         <!-- CS 1 GPIO key               = --> 0x01, 0x00, 0x0E, 0x00,
         <!-- PU CS 1 GPIO encoding       = --> 0x00, 0x00, 0x00, 0x00,
         <!-- PD CS 1 GPIO encoding       = --> 0x20, 0x00, 0x00, 0x00,
	     end
	  </props>
      <props helptext="SPI BAM mode enable" id="0x2100B" id_name="SPI BAM Configuration" oem_configurable="true" type="0x00000002"> 1 </props>
   </device>
</driver>

 

In the above example, I think I am configuring SPI instance 1 so GPIO6 (pin J5-5)  is MOSI, GPIO5 (pin J5-3) is MISO, GPIO7 (pin J5-7)is CS, GPIO4 (pin J5-1) is CLK and GPIO14 (pin J5-21) is CS for the secondary device on the SPI bus.

I made this change to all four XML files (and, of course, re-wired my device to use those GPIO pins), but the above code still doesn't get any callback.  I'm still clueless about what I need to do in order to make the SPI master code work.

Has anybody else successfully run an SPI master on this board?  I can't possibly be the only one.

  • Up0
  • Down0
c_rpedad
Profile picture
Join Date: 18 Jun 18
Location: San Jose
Posts: 317
Posted: Wed, 2019-06-05 20:52
The issue appears to be know issue with your Printf() function that has a 1K buffer as a local variable. It must be overflowing the stack of the main thread resulting crash. When changed that buffer to be static, the application may work as expected
  • Up0
  • Down0
david_charlap
Join Date: 3 Apr 19
Posts: 17
Posted: Thu, 2019-06-06 05:36

Yes.  We have discovered that with the updated Printf function, the callback happens.  I still need to figure out how to assign the GPIO pins I want to use for the SPI bus.  I'll post a followup question when I learn more.

  • Up0
  • Down0
david_charlap
Join Date: 3 Apr 19
Posts: 17
Posted: Thu, 2019-06-20 08:25

I've managed to make a bit of progress.  I can now move data to my display using the SPIM API.  As it turns out, there was one additional problem, in addition to the stack overflow from my "Printf" function.  I had the SPIM shift mode set wrong for my device.  Changing it to QAPI_SPIM_OUTPUT_FIRST_MODE_E makes it work.

So that first task is completed.  The problem now is that the CDB uses the same GPIO pins for SPI and for JTAG.  So whenever I run my test app, I need to remove jumpers J30, J31, J37, J38, J39, and J40.  Then when I edit my code and want to re-flash it, I need to put them all back.  This is really a pain in the neck.

So I'm now trying to see if there is any way to reassign the SPI interface to different GPIO pins.

Looking at the schematics, it appears that GPIOs 4, 5, 6 and 7 are not used by any hardware devices.  I would like to use them for SPI CLK, MISO, MOSI and SS, respectively.

I edited DevCfg_master_devcfg_out_cdb.xml in the "spi1" driver section, replacng the GPIO numbers to match the above assignments (0x18 -> 0x07, 0x19 -> 0x04, 0x1A -> 0x06, 0x1B -> 0x05):

<driver name="spi1">
<!-- GPIO configuration calculations
GPIO DIR values
   GPIO_INPUT = 0x0
   GPIO_OUTPUT = 0x1
GPIO_PULL values
   GPIO_NO_PULL    = 0,    
   GPIO_PULL_DOWN  = 0x1,  
   GPIO_PULL_UP    = 0x2,  
GPIO_DRV_STRENGTH values
   GPIO_1P6MA   = 0,       
   GPIO_2P7MA   = 0x1,     
   GPIO_4P0MA   = 0x2,     
GPIO configuration =  (GPIO_NUM           & 0xFF) << 0x10 |
                      (GPIO_FS_VAL        &  0xF) <<  0xC |
                      (GPIO_DRV_STRENGTH  &  0xF) <<  0x8 |
                      (GPIO_PULL          &  0xF) <<  0x4 |
                      (GPIO_DIR           &  0xF)
-->
   <device id="0x02000010">
      <props helptext="SPI Master Core Configuration" id="0x21001" id_name="SPI Core Configuration" oem_configurable="false" type="0x00000012"> spi_first_port  </props>
	  <props helptext="GPIO encoding {Format 0xXX (LSB), 0xXX, 0xXX, 0xXX(MSB)} : 1st 8 bits =  pull[7:4] -> 0 - NO_PULL, 1 - PULL_DOWN, 2 - PULL_UP | direction[3:0] -> 0 - INPUT, 1 = OUTPUT , 2nd 8 bits = drive_strength[7:4] -> 0 - 1.6mA, 1 - 2.7mA, 2 - 4mA | function[3:0], 3rd 8 bits = gpio pin[7:0] -> pin number, 4th 8 bits {unused[7:0] -> Reserved" id="0x2100A" id_name="SPI Controller Board Specific Configuration" oem_configurable="true" type="0x00000008">
         <!-- MOSI GPIO key               = --> 0x00, 0x00, 0x00, 0x00,
         <!-- MISO GPIO key               = --> 0x00, 0x00, 0x00, 0x00,
         <!-- CS GPIO key                 = --> 0x00, 0x00, 0x00, 0x00,
         <!-- CLK GPIO key                = --> 0x00, 0x00, 0x00, 0x00,
	 <!-- PU MOSI GPIO encoding       = --> 0x01, 0x12, 0x06, 0x00, 
         <!-- PU MISO GPIO encoding       = --> 0x00, 0x12, 0x05, 0x00,
         <!-- PU CS GPIO encoding         = --> 0x01, 0x12, 0x07, 0x00,
         <!-- PU CLK GPIO encoding        = --> 0x01, 0x12, 0x04, 0x00,
         <!-- PD MOSI GPIO encoding       = --> 0x10, 0x00, 0x06, 0x00,
         <!-- PD MISO GPIO encoding       = --> 0x10, 0x00, 0x05, 0x00,
         <!-- PD CS GPIO encoding         = --> 0x20, 0x00, 0x07, 0x00,
         <!-- PD CLK GPIO encoding        = --> 0x10, 0x00, 0x04, 0x00,
         <!-- CS 1 GPIO key               = --> 0x00, 0x00, 0x00, 0x00,
         <!-- PU CS 1 GPIO encoding       = --> 0x00, 0x00, 0x0E, 0x00,
         <!-- PD CS 1 GPIO encoding       = --> 0x20, 0x00, 0x0E, 0x00,
	     end
	  </props>
      <props helptext="SPI BAM mode enable" id="0x2100B" id_name="SPI BAM Configuration" oem_configurable="true" type="0x00000002"> 1 </props>
   </device>
</driver>

 

But doing only that didn't work.  The app starts, but the SPIM calls don't seem to work.  The SPI callback function never gets called, so something has either crashed or is blocked indefinitely.

Is it possible to reassign an SPI master to a different set of pins?  And if it is possible, what do I need to do differently?  Do I need to change something else?  Do I need to initialize something?  Maybe use the GPIO calls to set up the pins such that the SPI library can use them?

  • Up0
  • Down0
scarfys
Join Date: 11 Oct 17
Posts: 7
Posted: Fri, 2019-06-21 02:23

Hi,

You can also remap the JTAG pins to other ports.

It can be done via GPIO_9, GPIO_25 and GPIO_18

GPIO_9=0, GPIO_25=0, GPIO_18=0 means NO JTAG

GPIO_9=0, GPIO_25=0, GPIO_18=1 means JTAG in GPIO [53:50]

GPIO_9=0, GPIO_25=1, GPIO_18=0 means JTAG in GPIO [11:8]

 

GPIO_9=0, GPIO_25=1, GPIO_18=1 means JTAG in GPIO [27:24]

Other option is that you can flash via USB, its quicker.

  • Up0
  • Down0
or Register

Opinions expressed in the content posted here are the personal opinions of the original authors, and do not necessarily reflect those of Qualcomm Incorporated or its subsidiaries (“Qualcomm”). The content is provided for informational purposes only and is not meant to be an endorsement or representation by Qualcomm or any other party. This site may also provide links or references to non-Qualcomm sites and resources. Qualcomm makes no representations, warranties, or other commitments whatsoever about any non-Qualcomm sites or third-party resources that may be referenced, accessible from, or linked to this site.