Author Topic: Cannot read I2C device inside Timer interrupt with SimbleeBLE initialized?!  (Read 301 times)

frogie

  • RFduino Newbie
  • *
  • Posts: 5
  • Karma: +0/-0
    • View Profile
Hi, I've been trying to figure this out: my Simblee freezes while trying to read from an I2C device while inside an interrupt (Timer2 interrupt in this case), but only if I have the radio enabled (with SimbleeBLE.begin()).

If I either:
- do not turn on the radio, or
- read from the device from the main loop(), or
- only perform I2C writes inside the timer interrupt
then the chip doesn't freeze.

The device I'm attempting to read from is an accelerometer (MMA8452Q).
Does anyone have a clue why this could be going on?

Below is my test code. If I comment out the setupBLE() line then I see pin0 toggle. If I instead comment out the setupTimer() line and make calls to readAccel() inside loop() it also works!

Code: [Select]
#include <SimbleeBLE.h>
#include <Wire.h>

#define SCLpin 21
#define SDApin 24

#define TIMER2_PRESCALER (8)
#define TIMER2_RESOLUTION ((1<<TIMER2_PRESCALER)/16) // TimerTick = 16M/2^8 = 16 us
// Note: at 16us per tick, the longest delay that can be requested is roughly 1s

#define READ_ACCEL_INTERVAL (10000) // 10ms
#define READ_ACCEL_TICKS (READ_ACCEL_INTERVAL / TIMER2_RESOLUTION)

#define OUT_X_MSB (0x01) // first register of acceleration data

void readAccel()
{
    digitalWrite(1, HIGH);
    // Read the accelerator values
    byte rawData[6];  // x/y/z accel register data stored here
    Wire.beginTransmission(0x1C); // Address of the Accelerometer
    Wire.write(OUT_X_MSB);
    Wire.endTransmission(false); //endTransmission but keep the connection active
    Wire.requestFrom(0x1C, 6); //Ask for bytes, once done, bus is released by default
    while (Wire.available() < 6); //Hang out until we get the # of bytes we expect
 
    for (int x = 0; x < 6; x++)
      rawData[x] = Wire.read();

    // Here I would convert the 6 bytes into 3 words for x, y and z
    digitalWrite(1, LOW);
}

void timer()
{
    if (NRF_TIMER2->EVENTS_COMPARE[0] != 0)
    {
      // Clear interrupt
      NRF_TIMER2->EVENTS_COMPARE[0] = 0;

      // Update the compare value for next multiple
      NRF_TIMER2->CC[0] += READ_ACCEL_TICKS;

      readAccel();
    }
}

void setupTimer()
{
  NRF_TIMER2->TASKS_STOP = 1; // Stop timer
  NRF_TIMER2->MODE = TIMER_MODE_MODE_Timer;  // taken from Nordic dev zone
  NRF_TIMER2->BITMODE = TIMER_BITMODE_BITMODE_16Bit;
  NRF_TIMER2->PRESCALER = TIMER2_PRESCALER; // 16MHz / (2^8) = 16 us resolution
  NRF_TIMER2->CC[0] = READ_ACCEL_TICKS;
  NRF_TIMER2->INTENSET = TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos;
  NVIC_SetPriority(TIMER2_IRQn, 3);
  dynamic_attachInterrupt(TIMER2_IRQn, timer); // Read accelerometer!
  NRF_TIMER2->TASKS_CLEAR = 1; // Clear timer
  NRF_TIMER2->TASKS_START = 1;  // Start TIMER
}

void setupBLE()
{
  // start the BLE stack
  SimbleeBLE.advertisementData = "Test";
  SimbleeBLE.deviceName = "Test";
  SimbleeBLE.txPowerLevel = 0;
  SimbleeBLE.begin();
}

void setup()
{
  // Setup I2C
  Wire.speed = 400;
  Wire.beginOnPins(SCLpin, SDApin);

  // Setup pins for debugging
  pinMode(0, OUTPUT);
  pinMode(1, OUTPUT);
  digitalWrite(0, LOW);
  digitalWrite(1, LOW);

  setupBLE();
  setupTimer();
}

void loop()
{
  digitalWrite(0, HIGH);
  delay(1);
 
  digitalWrite(0, LOW);
  delay(1);
}


Wayne

  • RFduino Jr. Member
  • **
  • Posts: 45
  • Karma: +0/-0
    • View Profile
Is GPIO pins 0,1 for serial programming.. They connect to the USB program module that way?
I never used GPIO 0 nor GPIO 1 at all.. I Leave em alone..

frogie

  • RFduino Newbie
  • *
  • Posts: 5
  • Karma: +0/-0
    • View Profile
Yes, and they're also used for UART, but if you don't use UART they can be used as GPIOs.
In my setup I only have those pins easily accessible to hook up to a scope (and check whether the main loop() is still running) so that's why I use them.
I also use I2C on specific pins (21, 24), again because of the specific layout of the board I'm working with. Neither of these *should* have any impact though.

frogie

  • RFduino Newbie
  • *
  • Posts: 5
  • Karma: +0/-0
    • View Profile
Re: Cannot read I2C device inside Timer interrupt with SimbleeBLE initialized?!
« Reply #3 on: September 01, 2017, 07:53:18 AM »
After doing as much research on the Nordic forums as I could, I think this has something to do with interrupt priority inversion, something along the lines of the PPI internally used by the TWI library having a different interrupt priority if you're using the Soft Device (i.e. when radio is enabled) or not. Sadly I don't really know for sure.

In the meantime, I have gotten around the problem by delegating all my I2C operations to the main loop (using a small message queue of sorts). It means I can't really put anything else inside loop() (like main app logic) since it has to sit there and wait for messages like "Read Accelerator". A small inconvenience but I don't know of a better way right now.

I still would like to know for sure what exactly is going on though!