PaulStoffregen / TimerOne

TimerOne Library with optimization and expanded hardware support
http://www.pjrc.com/teensy/td_libs_TimerOne.html
467 stars 209 forks source link

External input for the timer1 to generate an interrupt when the counter reaches the maximum #38

Open rtek1000 opened 5 years ago

rtek1000 commented 5 years ago

I would like to make a frequency meter for up to 500khz (or more if possible), I would like to try via external input for the timer1 (16 bits, should give a 7 restarts). It seems that it is possible to generate an interrupt when the counter reaches the maximum (when it should restart the count).

I've been looking at the source code, but it seems like there's no such kind of setup, do you have any examples?

Thank you.

rtek1000 commented 5 years ago

Note: Atmega328

rtek1000 commented 5 years ago

I have found some codes (I need to test at 500kHz): (need a jumper from D3 to D5)

//#include <TimerOne.h>

const int led = LED_BUILTIN;  // the pin with a LED

void setup(void)
{
  pinMode(led, OUTPUT);
  //Timer1.initialize(150000);
  //Timer1.attachInterrupt(blinkLED); // blinkLED to run every 0.15 seconds

  TIMSK1 |= (1 << TOIE1);             // interrupt TIMER1 on

  TCCR1A = 0;                         //normal OC1A OC1B off
  TCCR1B = 0;                         //clear register
  TCCR1B = 0;                         //clear register
  // external clock on pin4 rising edge
  //TCCR1B |= (1<<CS12) | (1<<CS11) | (1<<CS10);
  // external clock on pin4 falling edge
  TCCR1B |= (1 << CS12) | (1 << CS11) | (0 << CS10);

  TCNT1 = 0;                          //clear register
  Serial.begin(9600);

  pinMode(5, INPUT_PULLUP);

  pinMode(3, OUTPUT);
  analogWrite(3, 128);

  TCCR2B = TCCR2B & B11111000 | B00000001; // for PWM frequency of 31372.55 Hz
}

unsigned long blinkCount = 0; // use volatile for shared variables

//void blinkLED(void)
ISR(TIMER1_OVF_vect)                              //interrupção do TIMER1 
{
  blinkCount++;  // increase when overflows

  digitalWrite(led, !digitalRead(led));
}

uint32_t millis1;
uint32_t calcMed = 0;

// The main program will print the blink count
// to the Arduino Serial Monitor
void loop(void)
{

  calcMed = (blinkCount * 65536) + TCNT1;
  blinkCount = 0;
  TCNT1 = 0;

  // 5000ms for overflow a few times
  calcMed /= 5; // 5s (5000ms) freq=1s/pulses

  Serial.print("Freq = ");

  Serial.println(calcMed);

  // 5000ms for overflow a few times (PWM at 31khz)
  while ((millis1 + 5000) > millis()) {

  }

  millis1 = millis();
}

Sources: https://www.robotshop.com/community/forum/t/arduino-101-timers-and-interrupts/13072 https://www.embarcados.com.br/timers-do-atmega328-no-arduino/ https://forum.arduino.cc/index.php?topic=169100.0 https://etechnophiles.com/change-frequency-pwm-pins-arduino-uno/

rtek1000 commented 5 years ago

D3 at 232khz!!!

//#include <TimerOne.h>

const int led = LED_BUILTIN;  // the pin with a LED

void setup(void)
{
  pinMode(led, OUTPUT);
  //Timer1.initialize(150000);
  //Timer1.attachInterrupt(blinkLED); // blinkLED to run every 0.15 seconds

  TIMSK1 |= (1 << TOIE1);             // interrupt TIMER1 on

  TCCR1A = 0;                         //normal OC1A OC1B off
  TCCR1B = 0;                         //clear register
  TCCR1B = 0;                         //clear register
  // external clock on pin4 rising edge
  //TCCR1B |= (1<<CS12) | (1<<CS11) | (1<<CS10);
  // external clock on pin4 falling edge
  TCCR1B |= (1 << CS12) | (1 << CS11) | (0 << CS10);

  TCNT1 = 0;                          //clear register
  Serial.begin(9600);

  pinMode(5, INPUT_PULLUP);

  //  pinMode(3, OUTPUT);
  //  analogWrite(3, 128);

  TCCR2B = TCCR2B & B11111000 | B00000001; // for PWM frequency of 31372.55 Hz

  pinMode(3, OUTPUT); // output pin for OCR2B, this is Arduino pin number

  // In the next line of code, we:
  // 1. Set the compare output mode to clear OC2A and OC2B on compare match.
  //    To achieve this, we set bits COM2A1 and COM2B1 to high.
  // 2. Set the waveform generation mode to fast PWM (mode 3 in datasheet).
  //    To achieve this, we set bits WGM21 and WGM20 to high.
  TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);

  // In the next line of code, we:
  // 1. Set the waveform generation mode to fast PWM mode 7 —reset counter on
  //    OCR2A value instead of the default 255. To achieve this, we set bit
  //    WGM22 to high.
  // 2. Set the prescaler divisor to 1, so that our counter will be fed with
  //    the clock's full frequency (16MHz). To achieve this, we set CS20 to
  //    high (and keep CS21 and CS22 to low by not setting them).
  TCCR2B = _BV(WGM22) | _BV(CS20);

  // OCR2A holds the top value of our counter, so it acts as a divisor to the
  // clock. When our counter reaches this, it resets. Counting starts from 0.
  // Thus 63 equals to 64 divs.
  OCR2A = 63;
  // This is the duty cycle. Think of it as the last value of the counter our
  // output will remain high for. Can't be greater than OCR2A of course. A
  // value of 0 means a duty cycle of 1/64 in this case.
  OCR2B = 0;
}

unsigned long blinkCount = 0; // use volatile for shared variables

//void blinkLED(void)
ISR(TIMER1_OVF_vect)                              //interrupção do TIMER1
{
  blinkCount++;  // increase when overflows

  digitalWrite(led, !digitalRead(led));
}

uint32_t millis1;
uint32_t calcMed = 0;

// The main program will print the blink count
// to the Arduino Serial Monitor
void loop(void)
{

  calcMed = (blinkCount * 65536) + TCNT1;
  blinkCount = 0;
  TCNT1 = 0;

  // 5000ms for overflow a few times
  //  calcMed /= 1; // 1s (5000ms) freq=1s/pulses

  Serial.print("Freq = ");

  Serial.println(calcMed);

  // 1000ms for overflow a few times (D3 at 232khz)
  while ((millis1 + 1000) > millis()) {
    _delay_us(5);
    if ( OCR2B < 63 )
      OCR2B += 5;
    else
      OCR2B = 0;
  }

  millis1 = millis();
}
rtek1000 commented 5 years ago

Note: It worked well with about 450khz (I do not have 500khz at the moment)