a strange problem, help needed!

Hi everyone, I'm writing a very simple program to test how fast the arduino uno work by usuing the timer inturrept, my first code is as follow:

#include <MsTimer2.h>

unsigned long c = 0;
void setup(void){
  MsTimer2::set(1000, intProc);     //trigger intProc every 1 second
  MsTimer2::start();
  Serial.begin(9600);
}

void loop(void){
    while(true) c++;
}

void intProc(void){
   Serial.println(c);    //send the contents of c via serial port
}

But the serial monitor shows the value of the global variable c is always 0, I don't know why.
Then, I modified the code, the intProc just set a global boolean variable to indicates the main loop to output the value of c:

#include <MsTimer2.h>

boolean p = false;
unsigned long c = 0;
void setup(void){
  MsTimer2::set(1000,intProc);
  MsTimer2::start();
  Serial.begin(9600);
}

void loop(void){ 
  while(true)  { 
    c++;
    if(p) {
      Serial.println(c);
      p = false;
    }
  }
}

void intProc(void) {
  p = true;
}

But this time the serial monitor show nothing, Serial.println was not executed at all.
The most strangest thing happens, when I move the delcaration of variable c inside the loop() function, the program works! I get the value of c once a second in the serial monitor, just one line modified:

#include <MsTimer2.h>

boolean p = false;
void setup(void){
  MsTimer2::set(1000,intProc);
  MsTimer2::start();
  Serial.begin(9600);
}

void loop(void){ 
  unsigned long c = 0;

  while(true)  { 
    c++;
    if(p) {
      Serial.println(c);
      p = false;
    }
  }
}

void intProc(void) {
  p = true;
}

After that I add a one line to reset c to zero after Serial.println is executed, but the program does not work a gain, the serial monitor get nothing at all:

#include <MsTimer2.h>

boolean p = false;
void setup(void){
  MsTimer2::set(1000,intProc);
  MsTimer2::start();
  Serial.begin(9600);
}

void loop(void){ 
  unsigned long c = 0;

  while(true)  { 
    c++;
    if(p) {
      Serial.println(c);
      p = false;
     c = 0;
    }
  }
}

void intProc(void) {
  p = true;
}

Strange and interesting problem, I can't fine the reason, can anybody give me an explaination to me? Thank you very much.

Well, for one thing, variables used in an interrupt service routine should be declared volatile.

Thank you for your reply, I delcared variable c as violate according to your advice, the following program works but the result is not as expected(variable c was not set to zero after every intProc was executed , please see my code below:

#include <MsTimer2.h>

#include <MsTimer2.h>
static volatile unsigned long c = 0;
void setup(void){
  MsTimer2::set(1000, intProc);     //trigger intProc every 1 second
  MsTimer2::start();
  Serial.begin(9600);
}

void loop(void){
    while(true) c++;
}

void intProc(void){
   Serial.println(c);    //send the contents of c via serial port
   c = 0;                 //set c to zero
}

the serial monitor shows:

716302
1433071
2149804
716728
1433501
2150229
769809
1486580
2158521
778102
1494872
2158524
2875253
3591987
4308664
4976569
5693116

still very strange to me XD

still very strange to me

What is very strange to me is that you are using Serial.print() in an interrupt service routine. Interrupts are disabled in the ISR, and Serial.print() needs interrupts enabled to function properly.

What's also strange is that you have not defined just what you expect to see happen. Add a small delay() after incrementing c. See if that makes any difference.

Finally, why is your global variable also static?

ba5ham:
Hi everyone, I'm writing a very simple program to test how fast the arduino uno work ...

It works at the clock speed. Why do you need to test that?

Don't do serial printing in an interrupt service routine.

Read the TLDR part.

ba5ham:
variable c was not set to zero after every intProc was executed

How do you know that?

Hi PaulS, there is no static in my actual program, I posted a wrong version, just: violate unsigned long c=0;
OK, I understand now that I should not user Serial.print in any interrupt service routine, I will try later. Thank you for you help.

Hi PaulS, there is no static in my actual program, I posted a wrong version, just: violate unsigned long c=0;

Hey, I'm having some problem with my code. Let me show you some other code, and maybe you can help me figure out what is wrong with my code. Now, is that really likely to happen?

Hi Nick Gammon, thanks again.

My program reset variable c to zero after each intProc() was called every one second, so I think the value in the serial monitor should be the incremental of c every seconds, something close to 716302 every time.

I'm a newbee of arduino, so I have not any experience on MCU programing. I'm trying to control a AD9851 DDS chip as a sweep frequency generator, I found it works much slower than I expected, to sweep a 1mhz range by 1hz incremental, it takes about 9 minutes, just 1800hz a second, so I want to find how fast the arduino works, by the way, learn how to handal interrupts :).

I modified the program according your advice, It works now. As you saied in in your post, I made two mistakes:

  1. call Serial.Print in interrupt service routine
    2.. variable used in the interrupt service routine does not declared as volatile

The corrent code:

#include <MsTimer2.h>

volatile boolean p = false;
void setup(void){
  MsTimer2::set(1000,intProc);
  MsTimer2::start();
  Serial.begin(9600);
}

void loop(void){ 
  unsigned long c = 0;
  while(true)  { 
    c++;
    if(p) {
      Serial.println(c);
      p = false;
      c = 0;
    }
  }
}

void intProc(void) {
  p = true;
}

Now, it works as I expected, hihi. Thank all of you!

I made another test, declare c as volatile unsigned char global variable, reset c to zero in intProc, this time the output is not correct again.

#include <MsTimer2.h>

volatile boolean p = false;
volatile unsigned long c = 0;
void setup(void){
  MsTimer2::set(1000,intProc);
  MsTimer2::start();
  Serial.begin(9600);
}

void loop(void){ 
  while(true)  { 
    c++;
    if(p) {
      Serial.println(c);
      p = false;
    }
  }
}

void intProc(void) {
  p = true;
  c = 0;
}

The output seems to be random value, what's wrong this time?

BTW, this test shows that c is increased about 630754 times once a second, the MCU of arduino uno running at 16mhz clock, the code in the loop costs so many clock cycles?

    c++;
    if(p) {
      Serial.println(c);

How many clock cycles does it take to increment c? What happens if the interrupt happens while the increment is occurring?

Any non-atomic operation, like increment on a multi-byte variable, needs to be done while interrupts are disabled.

I tried another method and found that the digitalWritre() functiuons costed most of the running time. Ecch calling of digitalWrite() function will spend 4.8us, that means 78 CPU clock cycles at 16mhz. See my code below:

    unsigned long t, t1, t2;

    t1=micros();
    //call  digitalWrite() 10 times
    digitalWrite(CLK, HIGH);
    digitalWrite(CLK, LOW);
    digitalWrite(CLK, HIGH);
    digitalWrite(CLK, LOW);
    digitalWrite(CLK, HIGH);
    digitalWrite(CLK, LOW);
    digitalWrite(CLK, HIGH);
    digitalWrite(CLK, LOW);
    digitalWrite(CLK, HIGH);
    digitalWrite(CLK, LOW);
    t2=micros();
    t= t2 - t1;
    Serial.println(t);

Why digitalWrite() funnction is so slow?

Why digitalWrite() funnction is so slow?

It's just lazy. Why don't you go look at the source code and find out? There have been plenty of discussions on why digitalWrite() "takes so long" and there are plenty of alternatives - some portable, some not.

Hi, PaulS, thank you very much for your advice, I will try to read the source code to find how digitalWrite() work. I got my arduino uno toy several days, just a beginner, so much questions to ask and so much thinga to learn.

ba5ham:
Why digitalWrite() funnction is so slow?

It's slow because it takes a variable as a pin number. Then it has to work out which hardware pin corresponds to the supplied pin. This involves a couple of table lookups (there is a hardware register, and a bit position within that register).

There is a digitalWriteFast function (Google for that library) that works somewhat faster if you pass a constant pin number.

As for your problem with updating variables:

Inside that is a discussion about protecting atomic operations.

From Reply #10

void loop(void){ 
  while(true)  { 
    c++;
    if(p) {
      Serial.println(c);
      p = false;
    }
  }
}

void intProc(void) {
  p = true;
  c = 0;
}

How can it make sense to Zero the value you want to print before you print it?
The correct place for c=0; is after Serial.println(c);

Measuring the repeat frequency like this is pretty useless. You don't actually want to run an empty loop.

Why not write a function that simulates what you actually want to do and repeat it for 1000 times. Record the value of micros() before and after and you will get a practical indication of what is possible.

...R

I agree with Robin2. All this fooling around doesn't prove much, especially as the compiler optimizes stuff. Make up a test of what you really want to do and time it.

The core information is that the processor runs at 16 MHz. That's 62.5 nS per clock cycle. Most hardware instructions take one or two clock cycles (some take 3 or 4). How fast it "works" depends on what instructions you are executing and how many of them there are.

Hi everyone, I tried to access the I/O port register DDRx and PORTx by sbi() and cbi() functions instead of digitalWrite(), the program runs almost 10 times faster now. Thank you very much for your help.