noInterrupts() allowing some interrupts - LIS3DH, NRF24L01+, Teensy 3.6

Status
Not open for further replies.

Rotario

Member
Hi,
I've got a rather complex project using both LIS3DH and NRF24L1+ with the Teensy 3.6 with two interrupt pins for the chips respectively.
the accelerometer(LIS3DH) is being read at around 1.6kHz with the interrupts, and the radio is sending this data at the same rate. These two chips are both using the SPI bus

Another set of chips are also read at 5 second intervals, controlled by the loop(), during this time, noInterrupts() is called to read from the chips and send that data through the radio so as to not corrupt anything if an interrupt is called halfway through a variable write.

After an amount of time, sometimes 30 seconds, sometimes 10 minutes of working correctly, the LIS3DH starts to give random values, from -32768 to +32768.

I originally thought this was an issue with the shared SPI bus, where, in the accel_ready() ISR using both lis.read and radio.startWrite somehow messed up the communication. However, when I remove the radio.startWrite from the 5 second interval data sender, the program runs fine overnight without any issues with the accel. This made me investigate further, and I have a flag variable called interrupt which is set to 1 or 2 depending on which ISR is called. this is set to 0 just after noInterrupts() is called in the 5 second interval and printed just before interrupts() is called after all processing has been done in the function.

This should, in theory, always produce zeroes right? However, my Serial output is below:
Code:
10:10:20
1
10:10:25
0
10:10:30
0
10:10:35
0
10:10:40
0
10:10:45
0
10:10:50
0
10:10:55
0
10:11:00
0
10:11:05
0
10:11:10
1
10:11:15
0
10:11:20
0
10:11:25
0
10:11:30
1
10:11:35
0
10:11:40
1

This data, among other things, is sent via a websocket and drawn on a website, just for a visual indication of the output of the LIS3DH accel in working order:
Capture.PNG

This is the LIS3DH output once it fails:
Capture.PNG

Program:
Code:
/*
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 version 2 as published by the Free Software Foundation.
 
 Created Dec 2014 - TMRh20
 */

/**
 * Example of using interrupts
 *
 * This is a very simple example of using two devices to communicate using interrupts.
 * With multiple devices, each device would need to have a separate reading pipe
 */

 // Basic demo for accelerometer readings from Adafruit LIS3DH

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_LIS3DH.h>
#include <Adafruit_Sensor.h>
#include "DHT.h"
#include <TimeLib.h>
#include <SPI.h>
#include "RF24.h"
#include <printf.h>

#include <DS1307RTC.h>

// Used for software SPI
#define LIS3DH_CLK 13
#define LIS3DH_MISO 12
#define LIS3DH_MOSI 11
// Used for hardware & software SPI
#define LIS3DH_CS 15
#define DHTPIN 27
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
// software SPI
//Adafruit_LIS3DH lis = Adafruit_LIS3DH(LIS3DH_CS, LIS3DH_MOSI, LIS3DH_MISO, LIS3DH_CLK);
// hardware SPI
Adafruit_LIS3DH lis = Adafruit_LIS3DH(LIS3DH_CS);
// I2C
//Adafruit_LIS3DH lis = Adafruit_LIS3DH();

#if defined(ARDUINO_ARCH_SAMD)
// for Zero, output on USB Serial console, remove line below if using programming port to program the Zero!
   #define Serial SerialUSB
#endif
volatile int interrupt;

unsigned long reFuel=0;

int range;
int i=0;
volatile uint8_t crash=0;
float crash_max = 15; // in m/s^2
uint16_t max_tobits; // converts crash_max to bit integer
#define size 100

#include "EmonLib.h"                   // Include Emon Library
EnergyMonitor emon1;                   // Create an instance
DHT dht(DHTPIN, DHTTYPE);              // humidity sensor setup
// Hardware configuration
// Set up nRF24L01 radio on SPI bus plus pins 8 + 9
RF24 radio(8, 9);
float Irms;
float temp;



// Use the same address for both devices
int fiveSecWait = 1;
int dailyWait = 1;
uint8_t address[][6] = { "1Node", "2Node" };


int fails= 0;
char mode = 'd';


float mcurrent;
struct fiveSecDataSet{
  uint8_t table = 111;
  uint8_t hours;
  uint8_t minutes;
  uint8_t seconds;
  uint8_t temp;  
  float current;
  int crashx;
  int crashy;
  int crashz;
};
struct dailyDataSet{
uint8_t table = 111;
float temp;
float humidity;
};
struct liveDataSet{
  uint8_t table = 111;
 float current;
  int x;
  int y;
  int z;
};
fiveSecDataSet fiveSecData;
liveDataSet liveData;
dailyDataSet dayData;




/********************** Setup *********************/

void setup(){

  Serial.begin(115200);
  Serial.println(F("Simple pingpair example"));
  Serial.println(F("Send a 'T' via Serial to transmit a single 'ping' "));

    
  // Setup and configure rf radio
  radio.begin();
  radio.setChannel(0x4c);
  radio.setAutoAck(1);
   radio.setPALevel(RF24_PA_MAX);
  // Use dynamic payloads to improve response time
  radio.enableAckPayload();
  radio.enableDynamicPayloads();
  radio.openWritingPipe(address[0]);             // communicate back and forth.  One listens on it, the other talks to it.
  radio.openReadingPipe(1,address[1]);
  radio.setDataRate(RF24_1MBPS); 
  //radio.startListening();
  
  radio.printDetails();                             // Dump the configuration of the rf unit for debugging

  
  attachInterrupt(5, check_radio, LOW);             // Attach interrupt handler to interrupt #0 (using pin 2) on BOTH the sender and receiver
  init_data();

}



/********************** Main Loop *********************/
void loop() {
 /* if (Serial.available()>0){
    char c = Serial.read();
      mode = c;
   * }*/

datapacket();
}

void datapacket(){
  
  liveData.current = currentData();//take present current value

if (liveData.current > mcurrent){//test if present current value greater than max measured this loop
  mcurrent = liveData.current; //if greater, overwrite max with present
}

if (second() % 5){
  //once past packet_time set time reset to 0
    //last_minute = minute();
    fiveSecWait=1;
} else if (fiveSecWait==1) {

cli()
//detachInterrupt(35);
//detachInterrupt(5);
//debug
interrupt = 0;


fiveSecWait = 0;
fiveSecData.hours = hour();
fiveSecData.minutes = minute();
fiveSecData.seconds = second();
fiveSecData.temp = tempData();
fiveSecData.current = mcurrent;
//if (liveData.x == 0 || fiveSecData.crashz > abs(32760)){ //check if LIS has malfunctioned
  //Serial.println(lis.getDataRate(), HEX);
  //lis.read();
//}
mcurrent = 0; // reset max current measured

liveData.x = 0;
liveData.y = 0;
liveData.z = 0;
//Serial.println(F("Sending FiveSec Data"));
timestamp();
//Serial.println(fiveSecData.temp);
//Serial.println(fiveSecData.current);
//Serial.println(fiveSecData.crashz);
//radio.stopListening();

radio.startFastWrite( &fiveSecData, sizeof(fiveSecData), 1);
fiveSecData.crashx = 0;
fiveSecData.crashy = 0;
fiveSecData.crashz = 0;
mode = 'a';

Serial.println(interrupt);
//attachInterrupt(35, accelReady, RISING);
//attachInterrupt(5, check_radio, LOW);             // Attach interrupt handler to interrupt #0 (using pin 2) on BOTH the sender and receiver
lis.read();
sei();
//radio.txStandBy();
//radio.startListening();
} 
}



/********************** Interrupt *********************/

void check_radio(void)                                // Receiver role: Does nothing!  All the work is in IRQ
{
  interrupt=2;
  
  bool tx,fail,rx;
  radio.whatHappened(tx,fail,rx);                     // What happened?
if ( tx ) {                                         // Have we successfully transmitted?
     
  }
  
  if ( fail ) {                                       // Have we failed to transmit?
     fails ++;

  }
  
  if ( rx || radio.available()){                 // Did we receive a message?
    
                      // If we're the sender, we've received an ack payload
        //radio.read(&message_count,sizeof(message_count));
        //Serial.print(F("Ack: "));
        //Serial.print(message_count);
        
    }
}







void init_data() {
 
 Serial.println("Serial Opened");
 
 setSyncProvider(RTC.get);   // the function to get the time from the RTC
  if(timeStatus()!= timeSet) 
     Serial.println("Unable to sync with the RTC");
  else
     Serial.println("RTC has set the system time"); 
 Serial.println("four modes - t sends temp, c sends current, a sends accel, d sends data packets every 30 secs");
 emon1.current(A12, 29.4);             // Current: input pin, calibration. was 29.4
 dht.begin();                          //humidity + temp chip
 setupLIS();
 Serial.print("current time is");
 timestamp();
}

float currentData(){
  Irms = emon1.calcIrms(1480);  // Calculate Irms only
  return Irms;
}

float tempData(){
  int analog = analogRead(A9);
  float voltage = analog * (3.3/1023) ;
  temp = (voltage - 1.25) / 0.005 ;
  return temp;
}

void setupLIS(){
  if (! lis.begin(0x18)) {   // change this to 0x19 for alternative i2c address
   Serial.println("Couldnt start");
    return;
  }
 Serial.println("LIS3DH found!");
  
  lis.setRange(LIS3DH_RANGE_2_G);   // 2, 4, 8 or 16 lG!

  //1, 10, 25, 50, 100, 200, 400_HZ, LOWPOWER_1K6HZ, LOWPOWER_5KHZ
  lis.setDataRate(LIS3DH_DATARATE_LOWPOWER_5KHZ); 

  //get the range for acceleration in m/s^2 for further calcs
  range = (2 << lis.getRange()) * 9.81;
  max_tobits = (crash_max*32768)/range;
  Serial.print("Data rate = "); 
  Serial.println(lis.getDataRate());
  Serial.print("Range = "); Serial.print(2 << lis.getRange());  
  Serial.println("G");
  Serial.println(range);
  Serial.println();
  pinMode(35, INPUT_PULLUP); //interrupt to get data when DTRDY signal on IS3DH INT PIN goes HIGH
  
  attachInterrupt(35, accelReady, RISING);
  lis.read();
}

void timestamp(){
  Serial.print(hour()); //timestamp
  printDigits(minute());
  printDigits(second());
  Serial.println();
}

void printDigits(int digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

void accelReady(){
 interrupt=1;
if (mode=='a'){
  liveData.x=lis.x ;
  liveData.y=lis.y ;
  liveData.z=lis.z ;
  radio.startFastWrite(&liveData, sizeof(liveData), 1);

  if (abs(liveData.x) > abs(fiveSecData.crashx)) {
       fiveSecData.crashx = liveData.x;
  }
  if (abs(liveData.y)> abs(fiveSecData.crashy)){
       fiveSecData.crashy = liveData.y;
  }
  if ( abs(liveData.z) > abs(fiveSecData.crashz)){
       fiveSecData.crashz = liveData.z;
    }
} 
  lis.read();
}

So, essentially I think the noInterrupts or cli() is allowing an interrupt to occur which messes with the shared SPI bus.
 
Last edited:
Sorry, I did not completely trace through your code including the RF24 code base, but what you are showing, looks like a something else probably enabled interrupts and from what you said, a good candidate would be the call: radio.startWrite or what it calls...

You might play with interrupt priorities. That is interrupts have priorities. And a higher priority interrupt (lower value) can interrupt a lower priority interrupt. I also think there may be a way to set the mainline codes priority, such that when it is set to a high enough priority no lower priority interrupts will trigger... But so far I have not needed to try it yet, so hopefully others can give more concrete info.
 
Disabling interrupts for a long time is a broken design. Various stuff won't work correctly, if interrupts are disabled. Many functions will enable interrupts, when they were disabled:
https://forum.pjrc.com/threads/43795-noInterrupts()-not-working

I also think there may be a way to set the mainline codes priority, such that when it is set to a high enough priority no lower priority interrupts will trigger...

https://forum.pjrc.com/threads/45158-IntervalTimer-data-consistency?p=147408&viewfull=1#post147408
 
Disabling interrupts for a long time is a broken design. Various stuff won't work correctly, if interrupts are disabled. Many functions will enable interrupts, when they were disabled:
https://forum.pjrc.com/threads/43795-noInterrupts()-not-working



https://forum.pjrc.com/threads/45158-IntervalTimer-data-consistency?p=147408&viewfull=1#post147408

Ok,
so if I set the mainline code priority higher or both the ISRs to 192 priority, then they won't fire?
Is there a function in Teensyduino to set these interrupt priority numbers?
I noticed there's this function -
Code:
NVIC_SET_PRIORITY(IRQ_PORTA, 60)

Here's the BASEPRI register, how should I access it?
basepri-register1.png
 
Last edited:
Is there a function in Teensyduino to set these interrupt priority numbers?
I noticed there's this function -
Code:
NVIC_SET_PRIORITY(IRQ_PORTA, 60)

Yes, that's the function which configures the priority level for any interrupt.

If you want to access the BASEPRI bits, you will need to use a small chunk of inline asm with the MSR instruction.
 
Yes, that's the function which configures the priority level for any interrupt.

If you want to access the BASEPRI bits, you will need to use a small chunk of inline asm with the MSR instruction.


Thanks for the reply,
I've been away for a week so not able to test anything, I'm afraid I don't understand much arm-specific code so this may be a stupid question, but does PORTA refer to 8 interrupt pins? if so, is there a table showing which pins refer to which PORT?

Also, when you refer to MSR in asm, you mean call some assembly code in my C program with:
asm("
MSR //code
");
right?

Thanks,
Rowan
 
Status
Not open for further replies.
Back
Top