Code only works with Serial.print()

Hi everybody,

I have a strange issue: This code only works, if the "Serial.print(".");" statement is present in the powerOn/Off animation function.
Any ideas why?
Thanks for your help!

EDIT: "Does not work" means the "shiftOutOwn" function does not shift out any data (measured with oscilloscope). With the print(), it does.

EDIT2: Solution found: Making the variables volatile

#define SR_DAT A4
#define SR_CLK A5
#define C1 7 //Changed here
#define C2 6 //Changed here

uint8_t CHAR1 = 0x00;
uint8_t CHAR2 = 0x00;

int displaystate = 0;

void setup() 
{
  setupTimer2();
  pinMode(SR_DAT, OUTPUT);
  pinMode(SR_CLK, OUTPUT);
  pinMode(C1, OUTPUT);
  pinMode(C2, OUTPUT);
}

void loop() 
{
  powerOnAnimation();
  delay(1000);
  powerOffAnimation();
  delay(1000);
}

void updateDisplay()
{
  if(displaystate)
  {
    digitalWrite(C1, LOW);
    shiftOutOwn(CHAR2);
    digitalWrite(SR_CLK, HIGH);
    digitalWrite(SR_CLK, LOW);
    digitalWrite(C2, HIGH);
  }
  else
  {
    digitalWrite(C2, LOW);
    shiftOutOwn(CHAR1);
    digitalWrite(SR_CLK, HIGH);
    digitalWrite(SR_CLK, LOW);
    digitalWrite(C1, HIGH);
  }
}

void shiftOutOwn(uint8_t dat)
{
  for(int i = 0; i < 8; i++)
  {
    digitalWrite(SR_DAT, ( dat & (1<<7-i) ));
    digitalWrite(SR_CLK, HIGH);
    digitalWrite(SR_CLK, LOW);
  }
}

#define ANIMATION_DELAY 100
void powerOnAnimation()
{
  CHAR1 = 0;
  CHAR2 = 0;
  delay(ANIMATION_DELAY);
  
  CHAR1 = 0x60;
  Serial.print(".");

  delay(ANIMATION_DELAY);
  CHAR1 = 0xF0;
  Serial.print(".");

  delay(ANIMATION_DELAY);
  CHAR2 = 0x90;
  Serial.print(".");

  delay(ANIMATION_DELAY);
  CHAR2 = 0x9A;
  Serial.print(".");
}

void powerOffAnimation()
{
  CHAR2 &= 0xF5;
  Serial.print(".");
  
  delay(ANIMATION_DELAY);
  CHAR2 &= 0x6F;
  Serial.print(".");
  
  delay(ANIMATION_DELAY);
  CHAR1 &= 0x6F;
  Serial.print(".");
  
  delay(ANIMATION_DELAY);
  CHAR1 &= 0x9F;
  Serial.print(".");
}

void setupTimer2() 
{
  noInterrupts();
  // Clear registers
  TCCR2A = 0;
  TCCR2B = 0;
  TCNT2 = 0;

  // 100.16 Hz (16000000/((155+1)*1024))
  OCR2A = 155;
  // CTC
  TCCR2A |= (1 << WGM21);
  // Prescaler 1024
  TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);
  // Output Compare Match A Interrupt Enable
  TIMSK2 |= (1 << OCIE2A);
  interrupts();
}

ISR(TIMER2_COMPA_vect)
{  
  displaystate ^= 1;
  updateDisplay();
}

Please clarify what's means "code works" and otherwise and what the difference from one to another

Sure, sorry for that!
This code controlls a shift-register as well as two mosfets (C1&C2) which are attached to a 7-segment display.
Without the print() statements, nothing lights up, with print(), the 7 segment shows the animation.
The reason for that is, that the shift_out function does not shift out any data without the print()

You are using Serial-print without Serial.begin(baudrate);
not sure if this makes a difference.

executing the line Serial.print()
needs some additional time
So have you tested what your code does if you increase the other delays() that you are using?

Yes, I have testet that. Also, instead of Serial.print() I can use interrupts(), then it also works, but not under any other case..

explain in more detail
you have tested longer delays. What is the result if you use longer delays?

where did you use interrupts()?

could it be that you are disabling interrupts somewhere?

and what do you mean by writing
but not under any other case.. what are the other cases?

I have tested multiple delays, for example 10ms, 100ms, 1000ms
There is no change, as long as the "Serial.print(".");" is not present in the code, no data is shifted out

Instead of "Serial.print(".");" I can also use "interrupts();", then it works
But it really needs to be after every time I cange the value of "CHAR1", like this:

void powerOnAnimation()
{
  CHAR1 = 0;
  CHAR2 = 0;
  delay(ANIMATION_DELAY);
  
  CHAR1 = 0x60;
  interrupts();

  delay(ANIMATION_DELAY);
  CHAR1 = 0xF0;
  interrupts();

  delay(ANIMATION_DELAY);
  CHAR2 = 0x90;
  interrupts();

  delay(ANIMATION_DELAY);
  CHAR2 = 0x9A;
  interrupts();
}

the function delay() is blocking.

I'm not sure if

  • as long as the delay() is executing

even the timer-interrupt is blocked

Have you ever worked with non-blocking timing?

did you do check if your timer-interrupt is really called at a frequency of 100 Hz and not faster?

Yes, the interrupt is called for sure. Checked that with the oscilloscope as well. And no, delay() does not block the timer interrupt.
I have found a sloution, to make the CHAR1 and CHAR2 variables " volatile"
Not sure why it only works then, but this solves the case for me

Where are a shift operations in the code?

They run at 100Hz in the timer isr (to be specific in the update_display function

Any variables, used in interrupts handler MUST BE volatile.

This topic is excellent example of why it's good to read textbook :slight_smile: )

Not sure why it only works then, but this solves the case for me

The compiler always tries to optimize code so as to make it run faster. While it will assign a space in memory to hold a variable, it's entirely possible that it will be able to make the code run faster by putting the variable into a CPU register, and never actually updating the memory copy. In this case, since the setup() and loop() functions change the value but never use it, the compiler might even optimize the variable away entirely.

Meanwhile, your interrupt code uses those variables. That code probably ends up grabbing the copy from memory that may, or may not have been updated by the main program.

The volatile keyword is there to solve this problem. It warns the compiler that the contents of this variable are subject to change without warning, so do not do any optimization involving this variable. The general rule is that any variable that appears in interrupt, AND non interrupt code, MUST be declared volatile. The same volatile keyword must also be used for any 'variable' that may be changed by external hardware. If you dig way down into the header files, you'll find that the hardware registers like TCCR2A and TCNT2 are declared as volatile.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.