AVR vets, what is different between these two lines of code?

tl;dr What is the difference between these two lines of code?

GIMSK |= (1 << PCIE); (PCIE is defined as 5)

GIMSK |= 0x00100000;


These two lines of code, which I understand to be functionally identical, behave differently on an ATiny85. I don’t THINK the rest of the program will be needed, but I’ve included it at the end of the post just in case.

This one works (interrupts are enabled and function)

GIMSK |= (1 << PCIE);

This one does not work (interrupts are not enabled and do not function)

GIMSK |= 0x00100000;

According to iotn85.h from ATTinyCore:

GIMSK, used in both, is defined as:

#define GIMSK _SFR_IO8(0x3B)

PCIE, which identifies the bit in GIMSK that needs to be modified, is defined as:

#define PCIE 5

It must be something obvious, what am I overlooking? I’ve been using this method to modify registers for years, I even tested it with PCMSK on this project without issue.

For those who are curious, I’m using the PC interrupt for a level switch in the reservoir to prevent the pump from running dry. This way, the pump will be shut off NOW when the water gets low, and even with a power outage the interrupt will trigger on a reboot and keep the pump from starting.

//lower moisture readings correlate to an increase in soil moisture
//a dry sensor in open air reads ~520, a sensor submerged in water reads ~204
#include <avr/io.h>
#include <avr/interrupt.h>

#define INTERRUPT_PIN PCINT0  // This is PB1 per the schematic
#define INT_PIN PB0           // Interrupt pin of choice: PB1 (same as PCINT1) - Pin 6
#define PCINT_VECTOR PCINT0_vect      // This step is not necessary - it's a naming thing for clarit


// reference mouisture reading used as a dry soil threshold
const int dry = 350;

//reference mousture reading used as a wet soil threshold
const int wet = 276;

unsigned long waterTimeLast = millis();
unsigned long waterTimeNow;

// hours * minutes/hour * seconds/minute * milliseconds/second
const int waterInterval = 6 * 60 * 60 * 1000;

const int pumpPin = 4;
const int soilSensor = A1;
const int ledPin = 1;


// used to account for millis() rollover
int rollOverFlag = 0;

int moisture = 0;

const int levelSwitchOutPin = 3; //provides a current sink to pull the levelSwitchInt pin LOW when the reservior is at LLL
const int levelSwitchIntPin = 0; //pc interrupt capable pin

void lights(int num);
void water();

void setup() {
  pinMode(pumpPin, OUTPUT);
  pinMode(soilSensor, INPUT);
  pinMode(ledPin, OUTPUT);
  
  digitalWrite(pumpPin, LOW);
  digitalWrite(ledPin, LOW);

//get the reservior level interrupt set up now in case the board resets while the level is low
pinMode(levelSwitchOutPin, OUTPUT);

//provides a current sink to pull the levelSwitchInt pin LOW when the reservior is at LLL
digitalWrite(levelSwitchOutPin, LOW);


//attachInterrupt(digitalPinToInterrupt(levelSwitchIntPin), level_ISR, CHANGE);

  cli();                            // Disable interrupts during setup
  PCMSK |= (1 << INTERRUPT_PIN);    // Enable interrupt handler (ISR) for our chosen interrupt pin
  GIMSK |= (1 << PCIE);             // Enable PCINT interrupt in the general interrupt mask
  //GIMSK |= 0x00100000;
  pinMode(INT_PIN, INPUT_PULLUP);   // Set our interrupt pin as input with a pullup to keep it stable
  sei();                            //last line of setup - enable interrupts after setup

  moisture = analogRead(soilSensor);
  water();
}

void loop() {
  waterTimeNow = millis();

  //this will handle millis() rolling over by resetting waterTimeLast and watering now with rollOverFlag
  if (waterTimeNow < waterTimeLast) {
    waterTimeLast = waterTimeNow;
    waterTimeNow = millis();
    rollOverFlag = 1;
  }
  
  if ((waterTimeNow - waterTimeLast >= waterInterval) || (rollOverFlag == 1)) {

    moisture = analogRead(soilSensor);

    // if soil is above dry threshhold 

    if (moisture > dry) {
      water();
    }  
      
    waterTimeLast = waterTimeNow;
    
    rollOverFlag = 0;
  }
}

void lights(int num) {
 for (int i = 0; i <= num; i++) {
  digitalWrite(ledPin, HIGH);
  delay (250);
  digitalWrite(ledPin, LOW) ;
  delay (250);
 }
}

// 1) water for two seconds 
// 2) wait for 30 seconds for water to absorb into the soil
// 3) take mouisture reading 
// 4) repeat until soil is below wet threshold
void water () {
      while (moisture >= wet) {
        digitalWrite(pumpPin, HIGH);
        delay(2000);
        digitalWrite(pumpPin, LOW);
        delay (30000);
        moisture = analogRead(soilSensor);
      } 
    }  

//this interrupt is triggired when the water level in the reservior is at LLL
ISR(PCINT_VECTOR)
{
  //turn the pump off to prevent running dry
  digitalWrite(pumpPin, LOW);

  //turn on the onboard LED as a visual indicator that the board is held in the interrupt state
  digitalWrite(ledPin, HIGH);

  //hold here until reservior is refilled and the level switch goes high again
  while (digitalRead(levelSwitchIntPin)==0);

  //turn the LED off before we exit
  digitalWrite(ledPin, LOW);
}

While I’ve never programmed specifically for the atmel variety of MCUs, I’m fairly certain that you meant to define the binary literal as “0b”, and not as hexidecimal “0x”. Other than that, the only other reason I can think of would be the statement being cast to a signed int instead of unsigned.

Is there any reason to not use the bit-shift method? AFAIK the C compiler should optimize it out anyway.

3 Likes

Ah! Good call with the binary vs hex number. I’ve been in ARM land for a while and must’ve gotten accustomed to using hex numbers without realizing it.

I’ll test that out in the morning!

No reason not to bit shift, it arguably even makes the code more readable. The way I taught myself makes “figuring out” bit shifting or built in #defines vs how I read the datasheet feels like an extra step. With more practice i intend to get better about that.

Tested this morning, that was all it was.

The other register, PCMSK, was being written with 0x00000001, which was giving me a false positive. Thanks!

2 Likes

Oh, yeah I understand the whole “direct translation” from the datasheet thing. I’m also used to the some of the oddities with embedded ARM processors, and have a compulsive habbit of casting any number to uint32_t.

1 Like

Glad I could help. It seems that the hardest bugs to solve are from single character typos.

1 Like