nRF52840 I2C could not start SCL signal after setting TASKS_STARTTX to 1

I was using Seeed Xiao BLE Sense as the I2C master and Arduino's Wire library. The basic I2C communication was working but it could not be stopped for switching the pins for other purpose. So I turned to direct TWI register access hoping I could resolve that problem. But the basic I2C communication could not start with this method. The SCL clock wasn't toggling after setting TASKS_STARTTX to 1. Here is my code:

#define SCL_PIN 5
#define SDA_PIN 4

void I2C_init(void)
{ 
    NRF_TWI1->ENABLE = 0x00000000;

    NRF_GPIO->PIN_CNF[SCL_PIN] = 
        (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
      | (GPIO_PIN_CNF_DRIVE_S0D1     << GPIO_PIN_CNF_DRIVE_Pos)
      | (GPIO_PIN_CNF_PULL_Pullup    << GPIO_PIN_CNF_PULL_Pos)
      | (GPIO_PIN_CNF_INPUT_Connect  << GPIO_PIN_CNF_INPUT_Pos)
      | (GPIO_PIN_CNF_DIR_Input      << GPIO_PIN_CNF_DIR_Pos);    

    NRF_GPIO->PIN_CNF[SDA_PIN] = 
        (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
      | (GPIO_PIN_CNF_DRIVE_S0D1     << GPIO_PIN_CNF_DRIVE_Pos)
      | (GPIO_PIN_CNF_PULL_Pullup    << GPIO_PIN_CNF_PULL_Pos)
      | (GPIO_PIN_CNF_INPUT_Connect  << GPIO_PIN_CNF_INPUT_Pos)
      | (GPIO_PIN_CNF_DIR_Input      << GPIO_PIN_CNF_DIR_Pos); 	

    NRF_TWI1->PSELSCL=SCL_PIN;
    NRF_TWI1->PSELSDA=SDA_PIN;

    NRF_TWI1->FREQUENCY = 0x01980000;   //100 KHz /
    //NRF_TWI1->FREQUENCY = 0x04000000;     //250 Khz
    //NRF_TWI1->FREQUENCY = 0x06680000;     //400 Khz

    NRF_TWI1->ENABLE = 0x00000005;
}

void I2C_disable(void)
{
    NRF_TWI1->ENABLE = 0x00000000;
}


void I2C_startTX(unsigned char addr) 
{ 
    cprintf("I2C_startTX addr: 0x%x 0x%x\n", &(NRF_TWI1->ADDRESS), addr);
    NRF_TWI1->ADDRESS=addr;
    NRF_TWI1->TASKS_STARTTX=1; 
}

I tried both TWI0 and TWI1, all 3 different clock speeds. From a logic analyzer, the SDA start signal (high->low) was there, but SCL was not there.

To be clear, the above experiments were performed with Arduino. Any help will be highly appreciated.

Parents
  • Maybe try this:

    // Set sizes to suit
    static uint8_t TxBuffer[] = "Test";
    static volatile uint8_t RxBuffer[3];
    
    void I2C_init(void)
    {
        NRF_TWI1->ENABLE = 0;
    
        NRF_GPIO->PIN_CNF[SCL_PIN] =
            (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
          | (GPIO_PIN_CNF_DRIVE_H0D1     << GPIO_PIN_CNF_DRIVE_Pos)
          | (GPIO_PIN_CNF_PULL_Pullup    << GPIO_PIN_CNF_PULL_Pos)
          | (GPIO_PIN_CNF_INPUT_Connect  << GPIO_PIN_CNF_INPUT_Pos)
          | (GPIO_PIN_CNF_DIR_Input      << GPIO_PIN_CNF_DIR_Pos);
    
        NRF_GPIO->PIN_CNF[SDA_PIN] =
            (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
          | (GPIO_PIN_CNF_DRIVE_H0D1     << GPIO_PIN_CNF_DRIVE_Pos)
          | (GPIO_PIN_CNF_PULL_Pullup    << GPIO_PIN_CNF_PULL_Pos)
          | (GPIO_PIN_CNF_INPUT_Connect  << GPIO_PIN_CNF_INPUT_Pos)
          | (GPIO_PIN_CNF_DIR_Input      << GPIO_PIN_CNF_DIR_Pos);
    
        NRF_TWI1->PSELSCL=SCL_PIN;
        NRF_TWI1->PSELSDA=SDA_PIN;
    
        NRF_TWI1->FREQUENCY = 0x01980000;   //100 KHz /
        //NRF_TWI1->FREQUENCY = 0x04000000;     //250 Khz
        //NRF_TWI1->FREQUENCY = 0x06680000;     //400 Khz
    
        NRF_TWI1->SHORTS = TWIM_SHORTS_LASTTX_STOP_Msk;
        // Device address and clock speed
        // Transmit data
        NRF_TWI1->TXD.MAXCNT = sizeof(TxBuffer);
        NRF_TWI1->TXD.PTR = (uint32_t)&TxBuffer[0];
        // 1-255 bytes receive data
        NRF_TWI1->RXD.MAXCNT = 0;
        NRF_TWI1->RXD.PTR = (uint32_t)&RxBuffer[0];
        // Clear all events
        NRF_TWI1->EVENTS_ERROR     = 0;
        NRF_TWI1->EVENTS_SUSPENDED = 0;
        NRF_TWI1->EVENTS_TXSTARTED = 0;
        NRF_TWI1->EVENTS_RXSTARTED = 0;
        NRF_TWI1->EVENTS_STOPPED   = 0;
        NRF_TWI1->EVENTS_LASTRX    = 0;
        NRF_TWI1->EVENTS_LASTTX    = 0;
        // Disable all interrupts
        NRF_TWI1->INTENCLR = 0xFFFFFFFFUL;
        __DSB();
    
        // Enable TWIM - note 6 not 5 for TWIM
        NRF_TWI1->ENABLE = 6;
    }
    
    void I2C_startTX(unsigned char addr) 
    {
        uint32_t Timeout = 2000; // Some big number of uSecs
        cprintf("I2C_startTX addr: 0x%x 0x%x\n", &(NRF_TWI1->ADDRESS), addr);
        NRF_TWI1->ADDRESS=addr;
        NRF_TWI1->TASKS_STARTTX = 1;
        __DSB();
        // Wait until the transfer start event is indicated
        while (NRF_TWI1->EVENTS_TXSTARTED == 0) ;
    
        // Wait until the transfer end event or error is indicated
        while ((NRF_TWIM0->EVENTS_LASTTX == 0) && (NRF_TWI1->EVENTS_STOPPED == 0) && (NRF_TWI1->EVENTS_ERROR == 0) && (--Timeout > 0))
        {
            __DSB();
            nrf_delay_us(500);
        }
        if ((NRF_TWI1->ERRORSRC) || (NRF_TWI1->EVENTS_ERROR == 1))
        {
            NRF_TWI1->TASKS_STOP = 1;
            NRF_TWI1->ERRORSRC = NRF_TWI1->ERRORSRC;
            while(NRF_TWI1->EVENTS_STOPPED == 0) ;
        }
        // Disable TWIM
        NRF_TWI1->ENABLE = 0;
    }

    Edit: Note also changing S0D1 to H0D1 for more reliable performance

  • Thanks for the reply. I'd like to give it a try. Just want to know,

    1. Has TWI been phased out and I2C is only supported by TWIM? I noticed TWI and TWIM share the same base address.

    2. In you example, should the NRF_TWIM0 be NRF_TWI1 in order to operate on TWI1/TWIM1? Is it a typo?

       while ((NRF_TWIM0->EVENTS_LASTTX == 0) && (NRF_TWI1->EVENTS_STOPPED == 0) && (NRF_TWI1->EVENTS_ERROR == 0) && (--Timeout > 0))

    Please clarify. Thanks!

  • Thanks. Just tried the code. Still, SCL line doesn't have anything.

  • Just noticed this:

        NRF_GPIO->PIN_CNF[SCL_PIN] =
            (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
          | (GPIO_PIN_CNF_DRIVE_H0D1     << GPIO_PIN_CNF_DRIVE_Pos)
          | (GPIO_PIN_CNF_PULL_Pullup    << GPIO_PIN_CNF_PULL_Pos)
          | (GPIO_PIN_CNF_INPUT_Connect  << GPIO_PIN_CNF_INPUT_Pos)
          | (GPIO_PIN_CNF_DIR_Input      << GPIO_PIN_CNF_DIR_Pos);
    should be
        NRF_GPIO->PIN_CNF[SCL_PIN] =
            (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
          | (GPIO_PIN_CNF_DRIVE_H0D1     << GPIO_PIN_CNF_DRIVE_Pos)
          | (GPIO_PIN_CNF_PULL_Pullup    << GPIO_PIN_CNF_PULL_Pos)
          | (GPIO_PIN_CNF_INPUT_Connect  << GPIO_PIN_CNF_INPUT_Pos)
          | (GPIO_PIN_CNF_DIR_Output     << GPIO_PIN_CNF_DIR_Pos);  <<== !
    

  • SCL can go from high to low this time, but nothing after that. No clock was generated. Also, SDA should go low before SCL to start I2C according the protocol, but the logic analyzer shows SCL goes to low before SDA in with this configuration.

  • Maybe post full code including order of setup in main() in case something is missing .. assuming there isn't another peripheral also using the port pin

  • I found in my code there was a extra line added by mistake which set the SCL to an internal I2C pin after I2C_init(). After removing that line, I can see the clock now. I'll continue the tests to see if it fully works. Thanks!

Reply Children
No Data
Related