Cannot get timer 0 faster than 485 Hz

Hello,

I am trying to use timer 0 interrupts to relay some digital signals and to synthesize a basic square wave but I cannot get it to go any faster than 485hz. I am not an expert on timer/counters so its likely that I am doing something wrong.

I got the code for the timer configuration from an online calculator/generator.

here is the configuration code

cli(); // stop interrupts
TCCR0A = 0; // set entire TCCR0A register to 0
TCCR0B = 0; // same for TCCR0B
TCNT0  = 0; // initialize counter value to 0
// set compare match register for 100000 Hz increments
OCR0A = 16; // = 16000000 / (1 * 100000) - 1 (must be <256)
// turn on CTC mode
TCCR0B |= (1 << WGM01);
// Set CS02, CS01 and CS00 bits for 1 prescaler
TCCR0B |= (0 << CS02) | (0 << CS01) | (1 << CS00);
// enable timer compare interrupt
TIMSK0 |= (1 << OCIE0A);
sei(); // allow interrupts

and here is what is happening in the interrupt:

ISR(TIMER0_COMPA_vect) {
  SR.process();
}
...
    void process() {
              digitalWrite(PULSE_OUT_PIN, sw);
              this->sw = !this->sw;
    }

I am measuring the frequency with a low-end rigol DS1052E 50 mhz scope.

I was initially using timer2 but when I use timer2 or timer1 my program crashes and hangs up on the following code..

void poll_adc() {
//Hangs on the following line
  Wire.requestFrom(ADC_I2C_ADDR, 8);
  char input_c = 0;
  int idx = 0;
  char buffer[12];
  while (input_c != 0) {
    input_c = Wire.read();
    if (input_c != 0) {
      buffer[idx++] = input_c;
    } else {
      buffer[idx] = '\0';
      idx = 0;
    }
  }

  read_voltage = atof(buffer);
  add_sample(read_voltage);
}

I assume that timer1 and timer2 must be used by the I2C wire library but I have not read through that part of the datasheet yet.
Any help understanding what is going on will be greatly apprecieated.

Thanks,

Please, always post ALL the code. There are several possible problems with the snippets.

Assuming this is an ATmega328, avoid using Timer0 as that breaks millis() and delay().

Wrong register. Should be TCCR0A.

// turn on CTC mode
TCCR0B |= (1 << WGM01);

I assume that timer1 and timer2 must be used by the I2C wire library

As far as I am aware, the Wire library does not use any timer. Timer1 is usually set up for PWM by the startup processes.

Do not use bitwise OR in statements like this, if you have not already explicitly set the contents of the particular register. There are very often bits set by other applications (e.g. the boot loader) that can lead to unexpected behavior.

// enable timer compare interrupt
TIMSK0 |= (1 << OCIE0A);

Timer 0 is used for all timing and should not (can not?) be used with other frequencies. No timeouts will work properly if the T0 frequency is changed.

Thanks for the info. I understand what you are saying about timer0, however both timer1 and timer2 cause my I2C code to hang the entire process and I have a requirement to use both a timer interrupt and I2C.

Here is the remainder of the code, I am running this one a Nano with an atmel 328p

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

int input_rec;
char tty_command[32];
int state_override;

#define CNC_DIR_IN_PIN 11
#define CNC_PUL_IN_PIN 12

#define MODE_SET 2
#define MODE_RUN 1
#define MODE_JOG 3
int display_mode;

#define MODE_IN_PIN 12

//Stepper constants and variables
#define PULSE_OUT_PIN 2
#define DIR_OUT_PIN 3

//Plasma Cutter constants and variables
#define START_IN_PIN 4
#define MOVE_IN_PIN 5

int start_signal;
int move_signal;

//THC Constants and variables

#define STATE_IDLE 0
#define STATE_PIERCE 1
#define STATE_CUT 2
#define STATE_UNKNOWN -1
#define VOLTAGE_DIVIDER 50

#define DISPLAY_REFRESH_RATE 20
#define DISPLAY_I2C_ADDR 0x27

int target_voltage = 125;
long read_voltage;


class Signal {
  public:
    int mode = 0; // 0 - Relay; 1 - Move Up; 2 - Move Down; 3 - Idle
    int dir = 0;
    int sw = 0;
    int prescale_counter = 0;
    int bump_counter = 0;
    int bump_activate = 0;
    int activate = 0;
    int r_dir = 0;
    long trajectory = 0;

    void bump() {
      mode = 2;
      bump_counter = 0;
    }

    void process() {
//        digitalWrite(DIR_OUT_PIN, dir);
              digitalWrite(PULSE_OUT_PIN, sw);
              this->sw = !this->sw;
    }

    void process2() {

      if (this->prescale_counter++ > 20) {
        this->prescale_counter = 0;
        this->activate = 1;
      } else {
        this->activate = 0;
      }


      if (this->bump_activate == 1) {
        if (this->activate == 1) {
          this->bump_counter++;
          if (this->bump_counter > 60) {
            this->bump_counter = 0;
            this->bump_activate = 0;
          }
        }
      }

      switch (this->mode) {
        case 0: {
            int relay_dir = digitalRead(CNC_DIR_IN_PIN);
            digitalWrite(PULSE_OUT_PIN, digitalRead(CNC_PUL_IN_PIN));
            digitalWrite(DIR_OUT_PIN, relay_dir);


            break;
          }
        case 1: {
            if (activate == 1) {
              digitalWrite(DIR_OUT_PIN, dir);
              digitalWrite(PULSE_OUT_PIN, sw);
              this->sw = !this->sw;
              if (this->dir == 0) {
                trajectory--;
              } else {
                trajectory++;
              }
            }
            break;
          }
        case 2: {
            if (activate == 1 && bump_activate == 1) {
              digitalWrite(DIR_OUT_PIN, dir);
              digitalWrite(PULSE_OUT_PIN, sw);
              this->sw = !this->sw;
              if (this->dir == 0) {
                trajectory--;
              } else {
                trajectory++;
              }
            }
            break;
          }
        case 3:
          {
            break;
          }
      }
    }

    void bump(int dir) {
      this->bump_activate = 1;
      this->dir = dir;
      mode = 2;
      bump_counter = 0;
    }

    void set_direction(int d) {
      this->dir = d;
    }

    long get_trajectory() {
      return this->trajectory;
    }

    void set_mode(int m) {
      this->mode = m;
    }
};



//
//Management of ADC
//
#define MAX_SAMPLES 25

double voltage_samples[MAX_SAMPLES];
int sample_index = 0;
int sample_count = 0;

#define ADC_I2C_ADDR 8

void add_sample(double s) {
  if (sample_index >= MAX_SAMPLES) {
    sample_index = 0;
  }
  if (sample_count >= MAX_SAMPLES) {
    sample_count = MAX_SAMPLES;
  }
  voltage_samples[sample_index++] = s;
  sample_count++;

}

Signal SR;

void init_timer00() {
cli(); // stop interrupts
TCCR0A = 0; // set entire TCCR0A register to 0
TCCR0B = 0; // same for TCCR0B
TCNT0  = 0; // initialize counter value to 0
// set compare match register for 1000000 Hz increments
OCR0A = 1; // = 16000000 / (1 * 1000000) - 1 (must be <256)
// turn on CTC mode
TCCR0B |= (1 << WGM01);
// Set CS02, CS01 and CS00 bits for 1 prescaler
TCCR0B |= (0 << CS02) | (0 << CS01) | (1 << CS00);
// enable timer compare interrupt
TIMSK0 |= (1 << OCIE0A);
sei(); // allow interrupts

}

void init_timer11( ){
  // TIMER 1 for interrupt frequency 100000 Hz:
// TIMER 1 for interrupt frequency 1000000 Hz:
cli(); // stop interrupts
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TCNT1  = 0; // initialize counter value to 0
// set compare match register for 1000000 Hz increments
OCR1A = 15; // = 16000000 / (1 * 1000000) - 1 (must be <65536)
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS12, CS11 and CS10 bits for 1 prescaler
TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei(); // allow interrupts

}
void init_timer1() {
  cli();
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  // set compare match register for 1hz increments
  OCR1A = 1;// = (16*10^6) / (1*1024) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS12 and CS10 bits for 1024 prescaler
  TCCR1B |= (1 << CS12) | (1 << CS10);
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);

  sei();
}

void init_timer() {
  cli();          // disable all interrupts

  TCCR2A = 0;// set entire TCCR2A register to 0
  TCCR2B = 0;// same for TCCR2B
  TCNT2  = 0;//initialize counter value to 0
  // set compare match register for 8khz increments
  OCR2A = 2;// = (16*10^6) / (8000*8) - 1 (must be <256)
  // turn on CTC mode
  TCCR2A |= (1 << WGM21);
  // Set CS21 bit for 8 prescaler
  TCCR2B |= (1 << CS21);
  // enable timer compare interrupt
  TIMSK2 |= (1 << OCIE2A);

  sei();           // enable
}

int atw = 0;

ISR(TIMER2_COMPA_vect) {
//  SR.process();
              digitalWrite(PULSE_OUT_PIN, atw);
              atw = !atw;
}


double average_voltage() {
  long acc = 0;
  int i = 0;
  for (i = 0; i < sample_count; i++) {
    acc += voltage_samples[i];
  }
  return acc / sample_count;
}

void poll_adc() {

  Wire.requestFrom(ADC_I2C_ADDR, 8, false );
  char input_c = 0;
  int idx = 0;
  char buffer[12];
  while (input_c != 0) {
    input_c = Wire.read();
    if (input_c != 0) {
      buffer[idx++] = input_c;
    } else {
      buffer[idx] = '\0';
      idx = 0;
    }
  }

  read_voltage = atof(buffer);
  add_sample(read_voltage);
}



//
//Management of switches
//
#define SW1_IN_PIN 6
#define SW2_IN_PIN 7
#define SW3_IN_PIN 8
#define SW_UP 1
#define SW_MID 2
#define SW_DOWN 3

int switch_ps = -1;
int switch_pins[] = { SW1_IN_PIN, SW2_IN_PIN, SW3_IN_PIN };
int switches[] = { SW_UP, SW_MID, SW_DOWN };

int setup_switches() {
  pinMode(SW1_IN_PIN, INPUT_PULLUP);
  pinMode(SW2_IN_PIN, INPUT_PULLUP);
  pinMode(SW3_IN_PIN, INPUT_PULLUP);
}

int prev_switch = -1;
long switch_ts = 0;
int scan_switches() {
  int i;
  for (i = 0; i < sizeof(switch_pins) / sizeof(switch_pins[0]); i++) {
    if (digitalRead(switch_pins[i]) == LOW) {
      long ts = millis();
      if (switches[i] != prev_switch && ts - switch_ts > 50) {
        prev_switch = switches[i];
        return switches[i];
      }
    } else {
      prev_switch = -1;
    }
  }
  return -1;
}


int current_state = STATE_UNKNOWN;
//
// Management of LCD
//
LiquidCrystal_I2C lcd(DISPLAY_I2C_ADDR, 16, 2);
void init_lcd() {
  lcd.begin();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(4, 0);
}

char ui_line1[16];
char ui_line2[16];

void paint_display() {
  lcd.setCursor(0, 0);
  lcd.print(ui_line1);
  lcd.setCursor(0, 1);
  lcd.print(ui_line2);
}




void setup() {
  Serial.begin(115200);
  Serial.println("------------- TORCH_HEIGHT_CONTROLLER v3 [THC]----------");
  Serial.println("[THC] Comms Initialized.");
  Wire.begin();
  Serial.println("[THC] I2C Bus Initialized.");
  setup_switches();

  pinMode(CNC_DIR_IN_PIN, INPUT);
  pinMode(CNC_PUL_IN_PIN, INPUT);

  pinMode(PULSE_OUT_PIN, OUTPUT);
  pinMode(DIR_OUT_PIN, OUTPUT);
  pinMode(START_IN_PIN, INPUT);
  pinMode(MOVE_IN_PIN, INPUT);
  pinMode(MODE_IN_PIN, INPUT);

  init_lcd();
  init_timer();

  Serial.println("[THC] Setup complete.");

}


void log_state(int st) {
  Serial.print("STATE CHANGED TO: ");
  if (st == STATE_IDLE) {
    Serial.println("IDLE");
  }
  if (st == STATE_PIERCE) {
    Serial.println("PIERCE");
  }
  if (st == STATE_CUT) {
    Serial.println("CUT");
  }
  if (st == STATE_UNKNOWN) {
    Serial.println("UNKNOWN");
  }
}

int current_state_pv = -1;
void get_status() {
  start_signal = digitalRead(START_IN_PIN);
  move_signal = digitalRead(MOVE_IN_PIN);

  //  Serial.println(start_signal);
  //  Serial.println(move_signal);

  if (state_override != 1) {
    if (start_signal == 1 && move_signal == 1) {
      current_state = STATE_CUT;
      display_mode = MODE_RUN;
    }
    else if (start_signal == 1 && move_signal == 0) {
      display_mode = MODE_RUN;
      current_state = STATE_PIERCE;
    }
    else if (start_signal == 0 and move_signal == 0) {
      current_state = STATE_IDLE;
    } else {
      current_state = STATE_UNKNOWN;
    }
  }
  if (current_state != current_state_pv) {
    current_state_pv = current_state;
    log_state(current_state);
  }
  poll_adc();
}


void set_mode() {

  int sw_in = scan_switches();
  if (sw_in != switch_ps) {
    switch (sw_in) {
      case SW_UP:
        target_voltage++;
        break;
      case SW_DOWN:
        target_voltage--;
        break;
      default:
        Serial.println("Unrecognized input");
    }
  }
  sprintf(ui_line1, "TARGET: %d", target_voltage);
  sprintf(ui_line2, "STAT: %d", current_state);
  paint_display();

}

void log_info() {
  Serial.print(target_voltage);
  Serial.print(", ");
  Serial.print(read_voltage);
  Serial.print(", ");
  Serial.print(current_state);
  Serial.print(", ");
  Serial.print(SR.get_trajectory());
  Serial.println();
}

void track_torch() {
  double d = average_voltage();

  Serial.print(target_voltage);
  Serial.print(" = ");
  Serial.println(d);
  int delta = target_voltage - d;
  if (delta != 0) {
    int dir = (delta > 0);
    SR.bump(dir);
  }
}

void run_mode() {



  if (current_state == STATE_CUT) {
    Serial.println("RUN MODE - STATE CUT");
    track_torch();
    log_info();
  }



  sprintf(ui_line1, "TARGET: %d", target_voltage);
  sprintf(ui_line2, "V %d S %d", average_voltage(), current_state);
  paint_display();

}

int prev_entry = 0;
void jog_mode() {

  int sw_in = scan_switches();
  if (sw_in != switch_ps) {
    switch (sw_in) {
      case SW_UP:
        SR.bump(1);
        break;
      case SW_DOWN:
        SR.bump(0);
        break;
      default:
        Serial.println("Unrecognized input");
    }
  }
  sprintf(ui_line1, "CW=UP CCW=DWN");
  sprintf(ui_line2, "POS: %d", prev_entry);

  paint_display();
}

int display_mode_pv = 0;
int get_mode() {
  if (digitalRead(MODE_IN_PIN) == HIGH) {
    display_mode = MODE_SET;
  } else {
    display_mode = MODE_JOG;
  }
  display_mode = MODE_RUN;
  return display_mode;
}



void cnc_relay_z() {
  digitalWrite(PULSE_OUT_PIN, digitalRead(CNC_PUL_IN_PIN));
  digitalWrite(DIR_OUT_PIN, digitalRead(CNC_DIR_IN_PIN));
}



int debug_tty = 0;
void process_serial_event() {

  if ( strcmp( tty_command, "" ) == 0 ) {
    return;
  }

  char * command = strtok(tty_command, "=");
  char * argument = strtok(0, "=");
  char buff[64];
  sprintf(buff, "");

  if (strcmp(command, "$so") == 0) {
    state_override = atoi(argument);
    sprintf(buff, "[SO]: %d", state_override);
  }
  if (strcmp(command, "$set-state") == 0) {
    current_state = atoi(argument);
    sprintf(buff, "[CURRENT STATE]: %d", current_state);
  }
  if (strcmp(command, "$get-state") == 0) {
    sprintf(buff, "[CURRENT STATE]: %d", current_state);
  }
  if (strcmp(command, "$set-target") == 0) {
    target_voltage = atoi(argument);
    sprintf(buff, "[TARGET VOLTAGE]: %d", target_voltage);
  }
  if (strcmp( command, "$get-target") == 0) {
    Serial.println(target_voltage);
    sprintf(buff, "[TARGET VOLTAGE]: %d", target_voltage);
  }
  Serial.println(buff);


  strcpy(tty_command, "");
}


long hb_pv = 0;

void loop() {


  if (input_rec == 1) {
    input_rec = 0;
    process_serial_event();
  }
  //
  //  long hb = millis();
  //  if(hb - hb_pv > 1000) {
  //    Serial.println(hb);
  //    hb_pv = hb;
  //  }
  get_status();
  switch (current_state) {
    case STATE_CUT:
      //        SR.disable();
      display_mode = MODE_RUN;
      run_mode();
      break;
    default:
      SR.set_mode(0);
      display_mode = get_mode();

      if (display_mode == MODE_JOG) {
        jog_mode();

      }
      else if (display_mode == MODE_SET) {
        set_mode();
      }
  }
}

int cmd_i = 0;
char command[128];

void serialEvent() {
  char input;

  while (Serial.available()) {
    input = (char)Serial.read();
    if (input != '\n') {
      command[cmd_i] = input;
      cmd_i++;
      command[cmd_i] = '\0';
    } else {
      sprintf(tty_command, "%s", command);
      Serial.print("TTY COMMAND: ");
      Serial.println(tty_command);
      cmd_i = 0;
      sprintf(command, "");
      input_rec = 1;
    }
  }

  //  strcpy(tty_command, command);
  // tty_command = command;
  //    Serial.println(3);


}


Instead of messing with the timer yourself, you could use the tone() command...

Using digitalWrite() in an ISR is likely to be slow (not as slow as 500Hz, but...) Better to use the timer's ability to toggle an output pin.

I2C code does not involve the timers. Your code is crashing for some other reason.

Do not use bitwise OR in statements like this, if you have not already explicitly set the contents of the particular register.

The code posted did in fact carefully set all the registers to 0 before it started ORing in new config...

This won't work properly if you modify the T0 frequency. Same for (I2C...) timeouts.

For 1Hz interrupts no timer is required.
For toggling an output pin regularly no interrupt is required.

As I already pointed out, you are initializing the timers incorrectly. ALL of them.

Nothing will work as you intend until you fix that, so spend some more time studying the timer sections of the data sheet.

@username_arduino_forum
All variables used with millis() must be unsigned long type (not long).

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