nrf52840 3-wire spi implementation

Hi Team,

Currently we'd like to use nrf52840 to verify 3-wire spi with our device, in HW side, no connection for MISO pin, I check default nrfSDK, fine spi related example, but haven't seen 3-wire spi configuration or struct related yet. Could you please guide me how to change the underlying driver, thanks a lot!

Best regards.

Parents
  • 3-wire spi is possible on the nRF52840; I posted a bare-metal example a few years ago using the SDK in this post 3-wire-spi

  • Hi Hmolesworth,

    Thanks for your update, I could find your mentioned above example, sorry I'm not deep familiar with nrf SDK, so I should additionally add these codes in main.c file, right?

    but in default main.c file, related spi configuration already existed as below

    nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
    spi_config.ss_pin = SPI_SS_PIN;
    spi_config.miso_pin = SPI_MISO_PIN;
    spi_config.mosi_pin = SPI_MOSI_PIN;
    spi_config.sck_pin = SPI_SCK_PIN;

  • The bare-metal code replaces all the existing spi code in main as it handles both pin initialisations and transmit/receive of data. To retain the existing spi code in main.c the bare-metal code would not be used but the sequence of switching the tx/rx pin from MOSI mode to MISO mode after the first byte has been sent would have to be added. I don't have a code snippet to hand in that format which does this; maybe replace the existing code with the snippet to verify the 3-wire spi works on the sensor then go back to the older spi code and try changing to replicate that sequence.

Reply
  • The bare-metal code replaces all the existing spi code in main as it handles both pin initialisations and transmit/receive of data. To retain the existing spi code in main.c the bare-metal code would not be used but the sequence of switching the tx/rx pin from MOSI mode to MISO mode after the first byte has been sent would have to be added. I don't have a code snippet to hand in that format which does this; maybe replace the existing code with the snippet to verify the 3-wire spi works on the sensor then go back to the older spi code and try changing to replicate that sequence.

Children
  • Hi Hmolesworth,

    Thanks a lot, I will have a try.

  • in your provided code snippet, spi3wTXBuf is not undeclared and clk is not defined to NRF_SPIM0->PSEL.SCK

  • The code was only a guide; here is a more complete snippet which is designed to test an accelerometer in 3-wire SPI mode. This accelerometer works in both 4-wire and 3-wire modes an must be told to switch to 3-wire mode; other 3-wire only sensors would not require that step. This should be the only code in main(), don't enable anything like nrfx spi stuff

    #define CSPIN   ACC_CS_PIN   // 11  //spi chip select
    #define SCKPIN  ACC_SCK_PIN  // 12  //spi clock
    #define MOSIPIN ACC_MOSI_PIN // 13  //spi mosi
    #define MISOPIN ACC_MISO_PIN // 14  //spi miso
    
    #define LIS2DH12_WHO_AM_I          0x0F                          // Read as 0x33
    #define I_AM_LIS3DH                0x33
    
    uint8_t spi3wTXBuf[] = {0x80|LIS2DH12_WHO_AM_I, 0xFF};
    uint8_t spi3wRXBuf[sizeof(spi3wTXBuf)] = {0,0};
    static const uint8_t m3wRxTxBufLength = sizeof(spi3wTXBuf);
    
    #define LIS2DH12_CTRL_REG0         0x1E // 0x90 // Disconnect SDO pull-up
    
    // Configure control registers REG0, TEMP, REG1-6
    static uint8_t mACC_ControlRegisterRequiredSettings[] =
      {0x40 | LIS2DH12_CTRL_REG0, // Write from CTRL_REG0
       0x90, // 0x1E CTRL_REG0     Disconnect SDO pull-up
       0x00, // 0x1F TEMP_CFG_REG  Temp sensor disabled
       0x77, // 0x20 CTRL_REG1     XZY enable 0x77 400Hz low power
       0x85, // 0x21 CTRL_REG2     Enable filters - No high-pass filter 0x00
       0x80, // 0x22 CTRL_REG3     Enable click INT1 pin
       0x01, // 0x23 CTRL_REG4     self test options, set BDU, 3-wire SPI
       0x00, // 0x24 CTRL_REG5     0x00 no latch interrupt
       0x00  // 0x25 CTRL_REG6     Disable INT2 pin, INT1 active low
     //0x00  // 0x26 LIS2DH12_REFERENCE DC reference
    };
    
    volatile uint32_t mSpiInterruptCounter = 0UL;
    
    void notSPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void)
    {
      NRF_SPIM0->EVENTS_END = 0;
      mSpiInterruptCounter++;
    }
    
    static void Test3WireSPI(void)
    {
      NRF_GPIO->PIN_CNF[CSPIN]   = 0x301;  // output, high drive high and low H0H1
      NRF_GPIO->PIN_CNF[SCKPIN]  = 0x301;  // output, high drive high and low H0H1
      NRF_GPIO->PIN_CNF[MOSIPIN] = 1;      // output, standard drive S0S1
      NRF_GPIO->PIN_CNF[MISOPIN] = 0;      // input pin, input buffer connected, no pull, S0S1, sense disabled
      NRF_GPIO->OUTSET = 1 << CSPIN;       // deactivate by setting chip select high
      NRF_SPIM0->PSEL.SCK = SCKPIN;
      NRF_SPIM0->PSEL.MOSI = MOSIPIN;
      NRF_SPIM0->PSEL.MISO = 0xFFFFFFFFUL; // MISOPIN;
      NRF_SPIM0->CONFIG = 0;               // CPOL 0 -- clock polarity active high, CPHA 1 -- sample on trailing clock edge, send Msb first
      NRF_SPIM0->FREQUENCY = 0x80000000UL; // 8 Mbps
      NRF_SPIM0->ORC =0;                   // Unused Tx bytes, set all low
    
      // Configure registers for 3-wire mode
      NRF_SPIM0->PSEL.MOSI = MOSIPIN;
      NRF_SPIM0->PSEL.MISO = 0xFFFFFFFFUL;  // MISOPIN;
      NRF_GPIO->PIN_CNF[MOSIPIN] = 1;       // output
    
      NRF_SPIM0->EVENTS_ENDTX = 0;
      //NRF_SPIM0->EVENTS_ENDRX = 0;
    
      // Disable all interrupts
      NRF_SPIM0->INTENCLR = 0xFFFFFFFFUL;
      // Enable selected interrupts
      NRF_SPIM0->INTENSET = 0x40;   // END
    
      // Set interrupt priority and enable interrupt
      NVIC_SetPriority(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn, 6);
      NVIC_ClearPendingIRQ(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn);
      NVIC_EnableIRQ(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn);
    
      NRF_SPIM0->ENABLE = 7;               // enable SPI
      // Transfer registers to set 3-wire SPI mode on target
      NRF_SPIM0->TXD.PTR = (uint32_t)mACC_ControlRegisterRequiredSettings;
      NRF_SPIM0->TXD.MAXCNT = sizeof(mACC_ControlRegisterRequiredSettings);
      NRF_SPIM0->RXD.PTR = NULL;
      NRF_SPIM0->RXD.MAXCNT = 0;
    
      NRF_GPIO->OUTCLR = 1 << CSPIN;  //drive cs low to initiate spi comm
      NRF_SPIM0->TASKS_START = 1;
      while(!NRF_SPIM0->EVENTS_ENDTX);  //last byte transmitted
      NRF_SPIM0->TASKS_STOP = 1;
      NRF_GPIO->OUTSET = 1 << CSPIN;
    
      // Send 3-wire whoami command byte (1 byte only)
      // Connect output drive, connect 1-wire rx/tx output to MOSI
      NRF_SPIM0->PSEL.MISO = 0xFFFFFFFF; // MISOPIN;
      NRF_SPIM0->PSEL.MOSI = MOSIPIN;
      NRF_GPIO->PIN_CNF[MOSIPIN] = 1;    // output
      NRF_SPIM0->TXD.PTR = (uint32_t)spi3wTXBuf;
      NRF_SPIM0->TXD.MAXCNT = 1;
      NRF_SPIM0->RXD.PTR = NULL;
      NRF_SPIM0->RXD.MAXCNT = 0;
      NRF_SPIM0->EVENTS_ENDTX = 0;
      NRF_GPIO->OUTCLR = 1 << CSPIN;       // drive cs low to initiate spi comm
      NRF_SPIM0->TASKS_START = 1;
      while(!NRF_SPIM0->EVENTS_ENDTX);     // last byte transmitted
    
      // Manual says disable SPI before changing pins, but actually doesn't matter
      NRF_SPIM0->ENABLE = 0;               // disable SPI
      // Disconnect output drive, connect 1-wire rx/tx input to MISO
      NRF_SPIM0->PSEL.MOSI = 0xFFFFFFFFUL; // MOSIPIN;
      NRF_GPIO->PIN_CNF[MOSIPIN] = 0;      // input pin, input buffer connected, no pull, S0S1, sense disabled
      NRF_SPIM0->PSEL.MISO = MOSIPIN;      // MISOPIN;
      NRF_SPIM0->ENABLE = 7;               // enable SPI
      spi3wRXBuf[0] = 99; spi3wRXBuf[1] = 98;
      NRF_SPIM0->TXD.PTR = NULL;
      NRF_SPIM0->TXD.MAXCNT = 0;
      NRF_SPIM0->RXD.PTR = (uint32_t)spi3wRXBuf;
      NRF_SPIM0->RXD.MAXCNT = 1;
      NRF_SPIM0->EVENTS_ENDRX = 0;
      NRF_SPIM0->TASKS_START = 1;
      while(!NRF_SPIM0->EVENTS_ENDRX);    // last byte received
    
      NRF_SPIM0->TASKS_STOP = 1;
      while(!NRF_SPIM0->EVENTS_STOPPED);
      NRF_GPIO->OUTSET = 1 << CSPIN;
      // Check result
      if (spi3wRXBuf[1] == I_AM_LIS3DH) mSpiCorrectResponseCounter++;
      while(1);
    }

Related