Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 8 of 8

Thread: attachInterrupt issue - function seems to be called twice

  1. #1

    Question attachInterrupt issue - function seems to be called twice

    I've been playing around with some analog devices ADCs (SPI parts). One that I'm fairly familiar with is the AD7794. However, I've never used it in continuous read mode. (Teensy 3.0, Teensyduino 1.18)

    In continuous read, the part uses its DOUT to indicate conversion complete and ready to read by pulling it low. You then clock in 24 bits of 0 to read the 24 bit data register, and DOUT is pulled high as soon as the last bit is clocked out. I'm just using the SPI.h library for this.

    The problem I'm having is that the interrupt routine runs twice, resulting in valid data the first time, and 0xFFFFFF the second, since it's just reading the DOUT line pulled high. I'm new to the world of Arduino/Teensy, so I'm not sure if I'm misunderstanding how attachInterrupt works, and how it masks the interrupt its processing. Anyway, here is what I see on the scope: DOUT is pulled low, three bytes of 0 are clocked in, all good, but then it clocks in another 3 bytes of 0's for no apparent reason (DOUT is high). I've tried putting a delay inside the interrupt function in case it was catching the last 0 on the data line, but it makes no difference.

    What am I missing?

    Click image for larger version. 

Name:	DS1Z_QucikPrint1.png 
Views:	440 
Size:	52.3 KB 
ID:	1642

    Code:
    #include <SPI.h>
    
    volatile long adcValue;  // AD7794 continuous read value 
    
    // attachInterrupt function for AD7794 continuous read
    
    void readADC() {
       adcValue = SPI.transfer(0x00) << 16 | SPI.transfer(0x00) << 8 | SPI.transfer(0x00); // clock in 24 0's to read AD7794 data register
    }
    
    void setup() {
      
      Serial.begin(9600);
    
      // setup _CS_ AD7794
      pinMode(6, OUTPUT); digitalWrite(6, HIGH);
      
      // setup interrupt pin (wired to pin 12 - was originally attaching interrupt to pin 12, but moved in case of SPI library conflict - but no difference)
      pinMode(9, INPUT);
    
      SPI.begin();
      delay(3000);
      
      digitalWrite(6, LOW); // select AD7794
      
      for (int i = 0; i < 8; i++) { // reset AD7794
        SPI.transfer(0xFF);
      }
      delay(1); // settle time only 10uS actually needed
      
    // *** this is just set and readback code for the configuration registers on the AD7794 while I'm testing the part ***
    
      Serial.println(); Serial.println("...............<<<<>>>>................."); Serial.println();
      
    // **** STATUS REGISTER ****
    
      SPI.transfer(0b01000000); // set to read the Status register - 8 bits
      long response = SPI.transfer(0x80);
      Serial.print("Status Register: "); Serial.println(response);
      delay(1);
      
    // ***** CONFIGURATION REGISTER ****
    
      SPI.transfer(0b01010000); // set to read the register - 16 bits
      response = SPI.transfer(0x80) << 8 | SPI.transfer(0x80);
      Serial.print("Configuration Register: "); Serial.println(response);
      
      SPI.transfer(0b00010000); // set to write the register - 16 bits
      SPI.transfer(0b00010000); SPI.transfer(0b00100000);
      delay(1);
      
      SPI.transfer(0b01010000); // set to read back the configuration - 16 bits
      response = SPI.transfer(0x80) << 8 | SPI.transfer(0x80);
      Serial.print("Configuration Register: "); Serial.println(response);
      delay(1);
      
    // ***** MODE REGISTER ****  
    
      SPI.transfer(0b01001000);  // set to read the mode register - 16 bits
      response = SPI.transfer(0x80) << 8 | SPI.transfer(0x80);
      Serial.print("Mode Register: "); Serial.println(response);
      delay(1);
      
    // Internal Calibration: zero scale
      SPI.transfer(0b00001000); //select mode register for write
      SPI.transfer(0b10000000); SPI.transfer(0b00001000); //write configuration bits to calibrate
      delay(1); //wait a moment
      SPI.transfer(0b01001000); //select mode register for read
      response = SPI.transfer(0x80) << 8 | SPI.transfer(0x80); //read new configuration
      Serial.print("Mode Register: "); Serial.println(response);
      delay(1);
      
    // Internal Calibration: full scale
      SPI.transfer(0b00001000); //select mode register for write
      SPI.transfer(0b10100000); SPI.transfer(0b00001000); //write configuration bits
      delay(1); //wait a moment
      SPI.transfer(0b01001000); //select mode register for read
      response = SPI.transfer(0x80) << 8 | SPI.transfer(0x80); //read new configuration
      Serial.print("Mode Register: "); Serial.println(response);
      delay(1);
      
    // Return to continuous conversion mode
      SPI.transfer(0b00001000); //select mode register for write
      SPI.transfer(0b00000000); SPI.transfer(0b00001000); //write configuration bits
      delay(1); //wait a moment
      SPI.transfer(0b01001000); //select mode register for read
      response = SPI.transfer(0x80) << 8 | SPI.transfer(0x80); //read new configuration
      Serial.print("Mode Register: "); Serial.println(response);
      delay(500);
    
    
    // enable interrupt on pin 6 and initiate continuous read
      
      attachInterrupt(9, readADC, LOW);
      SPI.transfer(0x5C);
      
    }
    
    
    void loop() {
    
    }
    Last edited by adeptrc; 03-16-2014 at 08:06 PM. Reason: Clarify platform

  2. #2
    Senior Member xxxajk's Avatar
    Join Date
    Nov 2013
    Location
    Buffalo, NY USA
    Posts
    545
    Depending on the interrupt setting, you will get an interrupt on just the change of the state.
    Since it is getting hit more than one time, you are interrupting more than once.
    One thing also to remember is that even if you just write, data is always read, even if it is unused.
    One thing you could do is set a volatile variable to indicate when you are actually expecting a valid value, and ignore the bogus event if you are not expecting it.
    Another way to do this is that once your transfer is completed, you need to clear the interrupt, because each transition is actually causing an interrupt.
    And yet another way is to disable the IRQ while reading, and once done, re-enable it.


    The last suggestion is easiest. In the ISR, detach the ISR, do your read, then attach it back.
    Last edited by xxxajk; 03-16-2014 at 08:23 PM.

  3. #3
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,450
    Try interrupting on the falling edge instead of on the LOW level:
    Code:
      attachInterrupt(9, readADC, FALLING);
    Pete

  4. #4
    I thought the interrupt was automatically masked by attachInterrupt . . . it consistently only triggers one extra reading per DOUT going low outside the function. I've used both LOW and FALLING as triggers.

    Is my assumption that the interrupt gets masked until the function is complete for attachInterrupt on the Teensy 3.0 incorrect?

    Thanks . . .

  5. #5
    Senior Member xxxajk's Avatar
    Join Date
    Nov 2013
    Location
    Buffalo, NY USA
    Posts
    545
    Yes, IRQ is masked, but the IRQ still happens while you are in the ISR.... once the ISR exits, it re-hits because it hit low *during the data transfer*.

    Again...in the ISR, detach, read, attach.

    One other helpful tip...
    Outside of the ISR be sure to cli(), copy the value to a different variable, sei() to avoid the possibility of a race condition, where the value changes as you read it.

  6. #6
    Quote Originally Posted by xxxajk View Post
    Yes, IRQ is masked, but the IRQ still happens while you are in the ISR.... once the ISR exits, it re-hits because it hit low *during the data transfer*.

    Again...in the ISR, detach, read, attach.

    One other helpful tip...
    Outside of the ISR be sure to cli(), copy the value to a different variable, sei() to avoid the possibility of a race condition, where the value changes as you read it.
    Ok, got it . . . I had assumed attachInterrupt disabled the IRQ for that pin while processing the ISR automatically . . . I was confused by the fact I was only getting a single extra read (one of the reasons I tested FALLING was to see if I got multiple repeats - but perhaps the uC is configured to only allow one IRQ per ISR in progress to be queued?

  7. #7
    Senior Member xxxajk's Avatar
    Join Date
    Nov 2013
    Location
    Buffalo, NY USA
    Posts
    545
    Quote Originally Posted by adeptrc View Post
    Ok, got it . . . I had assumed attachInterrupt disabled the IRQ for that pin while processing the ISR automatically . . . I was confused by the fact I was only getting a single extra read (one of the reasons I tested FALLING was to see if I got multiple repeats - but perhaps the uC is configured to only allow one IRQ per ISR in progress to be queued?
    It isn't actually queued, but that is a good analogy.
    What happens is that the 'ISR happened' bit is cleared BEFORE your routine is called, so that other ISR may be caught while you are doing something, so that those IRQ are not lost.
    By detaching the actual ISR doing the work will ignore the pending IRQ, and clear it if it is pending when you re-attach it... Or it should if those routines are properly written.
    Try my suggestion, and post if this fixed the problem or if it did not.

  8. #8
    Sorry, the "Ok, got it" was the all clear, it works fine. I'll have to crack the NVIC section of the manual to understand the details better. The detach/attach process seems to add a reasonable amount of overhead - not relevant to me in this case - it's around 0.5uS - but there must be a more efficient way to disable and enable interrupt detection on a pin entering and exiting an ISR.

    Thanks.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •