TWIM0 not starting in custom driver

I am trying to write a low level driver for the TWIM system in a nrf52840 and cannot seem to get the hardware to transfer the TX data out of memory. For many reasons, I do not wish to use the provided libraries for this function although I did refer to them when creating this custom code:

step 1: set up the I/O

// I2C pin assignment here
(not shown is the configuration of these pins to be inputs with pullups)
    NRF_TWIM0->PSEL.SCL = 11;           // selects GPIO 0.11 as SCL
    NRF_TWIM0->PSEL.SDA = 12;           // selects GPIO 0.12 as SDA

step 2: initialize TWIM0:

int twi_init (void)
{
    /* I2C setup here. Setup of the I2C connected sensors are done in other functions AFTER this has been run
    and AFTER the configure_gpio() function*/

    NRF_TWIM0->FREQUENCY = NRF_TWI_FREQ_100K;   // sets the I2C frequency to 100kHz
    NRF_TWIM0->INTEN = 0x0;             // interrupts will not be used
    NRF_TWIM0->SHORTS = 0x1200;         // this connects the LASTTX and LASTRX events to the STOPTX task. See data sheet for reason
    NRF_TWIM0->RXD.PTR = (uint32_t)&I2C_RX_data;    // buffer for rx data
    NRF_TWIM0->TXD.PTR = (uint32_t)&I2C_TX_data;    // buffer for tx data
    NRF_TWIM0->ENABLE = 1;              // enable the I2C system and connect to the IO pins
    return OK;                      // no errors expected here
}

step 3: send out the data to the peripheral:

int TWI_write(uint8_t address)
{
    int timeout = 5;                    // 5 millisecond timeout should more than enough for most sensors
    if (NRF_TWIM0->TXD.MAXCNT == 0)
        return ERR;                     // nothing to send!
   
    NRF_TWIM0->TXD.PTR = (uint32_t)&I2C_TX_data;    // reset the pointer to the start of the buffer
    NRF_TWIM0->ADDRESS = address;       // put the peripherals address into the TWI register

    NRF_TWIM0->EVENTS_LASTTX = 0;       // clear the LASTTX event flag - just in case a previous transfer ended poorly
    NRF_TWIM0->EVENTS_ERROR = 0;        // clear the ERROR event flag - just in case a previous transfer ended poorly
    NRF_TWIM0->TASKS_STARTTX = 1;       // starts the transmission of the data in the tx buffer
    while ((NRF_TWIM0->EVENTS_LASTTX == 0) && (timeout != 0))   // LASTTX flag is true when the last byte has started transferring out
    {
        timeout --;
        delay_1ms();                // wait 1 mS
    }

    if ((timeout == 0) || (NRF_TWIM0->EVENTS_ERROR == 1))   // timeout or TWI system error
        return ERR;
    else
    {
        return OK;  // 
    }
The Problem:
In the TWI_write function, setting the "TASKS_STARTTX" bit seems to do nothing...I do not see any of the event flags change nor do I see a change in the TXD_AMOUNT count. I can view the TWIM0 registers and everything seems to be OK (it is showing as being enabled, and the data pointer for the DMA looks OK and the frequency is set to 100KHz, and the TXD_MAXCOUNT has a non-zero value). Am I missing some fundamental setup with this hardware? This a a brand new design and PCB so there is a chance that the peripheral connected to the system is "dead" but would that prevent the TASKS_STARTTX flag from being set? Maybe I am missing something extra that needs to be done for the DMA system? Note that I did not include pullups on the PCB for the two lines as I was hoping that the internal pullups would be strong enough at the 100KHz speed.
Parents
  • Hi!

    I strongly recommend using the provided libraries and drivers

    That said, if you want to try something very bare-metal, try something like this:

    #include <nrf.h>
    
    #define DEVICE_ADDRESS (0x34)
    #define PIN_SCL        (27)
    #define PIN_SDA        (26)
    
    void i2c_write(uint8_t addr, uint8_t data)
    {
      uint8_t tx_buf[2];
      NRF_TWIM0->SHORTS = TWIM_SHORTS_LASTTX_STOP_Msk;
    
      tx_buf[0] = addr;
      tx_buf[1] = data;
      NRF_TWIM0->TXD.MAXCNT = sizeof(tx_buf);
      NRF_TWIM0->TXD.PTR = (uint32_t)&tx_buf[0];
    
      NRF_TWIM0->EVENTS_STOPPED = 0;
      NRF_TWIM0->TASKS_STARTTX = 1;
      while (NRF_TWIM0->EVENTS_STOPPED == 0);
    }
    
    uint8_t i2c_read(uint8_t addr)
    {
      uint8_t tx_buf[1];
      uint8_t rx_buf[1];
      NRF_TWIM0->SHORTS = TWIM_SHORTS_LASTTX_STARTRX_Msk | TWIM_SHORTS_LASTRX_STOP_Msk;
    
      tx_buf[0] = addr;
      NRF_TWIM0->TXD.MAXCNT = sizeof(tx_buf);
      NRF_TWIM0->TXD.PTR = (uint32_t)&tx_buf[0];
    
      NRF_TWIM0->RXD.MAXCNT = 1;
      NRF_TWIM0->RXD.PTR = (uint32_t)&rx_buf[0];
    
      NRF_TWIM0->EVENTS_STOPPED = 0;
      NRF_TWIM0->TASKS_STARTTX = 1;
      while (NRF_TWIM0->EVENTS_STOPPED == 0);
    
      return rx_buf[0];
    }
    
    int main(void)
    {
      volatile uint8_t data;
    
      NRF_TWIM0->PSEL.SCL = PIN_SCL;
      NRF_TWIM0->PSEL.SDA = PIN_SDA;
    
      NRF_TWIM0->ADDRESS = DEVICE_ADDRESS;
      NRF_TWIM0->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K400 << TWIM_FREQUENCY_FREQUENCY_Pos;
      NRF_TWIM0->SHORTS = 0;
    
      NRF_TWIM0->ENABLE = TWIM_ENABLE_ENABLE_Enabled << TWIM_ENABLE_ENABLE_Pos;
    
      i2c_write(0x0, 0xFF);
    
      data = i2c_read(0x0);
      data; // to get rid of set but unused warning
    
      while (1)
      {
        __WFE();
      }
    }

  • After much editing of various config files and finding a bad connection on my usb cable, I now am getting a "NACK" error recorded on the TWIM registers. Since this is a brand new PCB that I manually assembled, there is a good chance that the IC connected to the I2C bus is not soldered properly so I will try fixing that next and report back if it completely fixes the issue.

Reply
  • After much editing of various config files and finding a bad connection on my usb cable, I now am getting a "NACK" error recorded on the TWIM registers. Since this is a brand new PCB that I manually assembled, there is a good chance that the IC connected to the I2C bus is not soldered properly so I will try fixing that next and report back if it completely fixes the issue.

Children
No Data
Related