Nick Gammon has two timer sketches for the Atmega2560, one that calculates duty cycle and the other the frequency of a PWM signal. Has anyone combined these two sketches to provide both in the same program? The two sketches are located at: Gammon Forum : Electronics : Microprocessors : Timers and counters
What is the frequency range? Here is someone already done measurements.
The range of PWM signals is between 100 and 500 max. Does the sketch you referenced use input capture? I believe it uses regular interrupts, whereas the Nick Gammon sketches use input capture that is supposed to be more efficient. e.g. ICP1 on logical pin 83 on the atmega2560.
davidcyr2000:
The range of PWM signals is between 100 and 500 max. Does the sketch you referenced use input capture? I believe it uses regular interrupts, whereas the Nick Gammon sketches use input capture that is supposed to be more efficient. e.g. ICP1 on logical pin 83 on the atmega2560.
I think, one of the Nick Gammon's example is written for atmega2560, but another for atmega328 (UNO board). ICP1 -> PD4 is not routed on Mega board. >:(
Though it still would be possible to use input capture Timer4 or Timer5 (ICP4 & ICP5), pin 48 & 49.
I read on this forum someone pose same question and was able to successfully adapt Gammon's code to ICP4. Not sure, if it's measure duty cycle, may be only frequency.
500 Hz is quite low, you sure you can't do it with input change interrupt? 2 milliseconds period, and if arduino spend ~2 usec to jump into ISR the worst case scenario you 'd have 0.1% error, it's not much, isn't it ?
This is what I come up with, to get maximum precision out of arduino's ICP feature.
/*
Using Input Capture to measure frequency & duty cycle
on Arduino Mega2560.
Reads a PWM signal on pin 49, prints the result over Serial Monitor.
Commands to activate printout - "d".
Range 1.00 Hz <---> 86.00 kHz
Released into the public domain.
Created by Anatoly Kuzmenko, April 2018
k_anatoly@hotmail.com
*/
String in_String = "";
boolean end_input = false;
uint8_t adres_reg = 0;
uint8_t debug_osm = 0;
volatile int32_t period_hi = 0;
volatile int32_t period_lo = 0;
volatile uint8_t tmr_overf = 0;
volatile uint8_t f_capture = 0; // flag
float duty_cycl = 0.0;
float freq_cntr = 0.0;
void setup()
{
Serial.begin(115200);
in_String.reserve(200);
init_tmr4();
}
void loop()
{
int32_t tempr = 0;
char * pEnd;
if(debug_osm) {
if(f_capture) {
uint32_t summ = period_hi + period_lo;
duty_cycl = period_hi;
duty_cycl *= 100.0; // %
duty_cycl /= summ;
freq_cntr = 16000000.0 /summ;
Serial.print(F("\n\tFreq: "));
Serial.print(freq_cntr, 6);
Serial.print(F("\tduty cycle: "));
Serial.print(duty_cycl, 4);
f_capture = 0;
}
delay(500);
}
serialEvent();
if( end_input) {
char cmd = in_String[0];
in_String[0] = '+';
if( cmd == 'd' ) {
debug_osm = 1 - debug_osm;
if(debug_osm) Serial.print(F("\nDebug aktiv."));
else Serial.print(F("\nDebug de-aktiv."));
}
in_String = "";
end_input= false;
}
}
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
in_String += inChar;
if (inChar == '\n') {
end_input= true;
}
}
}
And timer4 tab:
void init_tmr4(void)
{
TCCR4A = 0;
TCCR4B = 0;
TCCR4B |= (1<< CS40); // set prescaler to 16 MHz
TCCR4B |= (1<<ICNC4); // input noise canceler on
TCCR4B |= (1<<ICES4); // input capture edge select (lo-hi)
TIMSK4 |= (1<<TOIE4); // Overflow Interrupt Enable
TIMSK4 |= (1<<ICIE4); // InCaptureInterrupt Enable
}
ISR(TIMER4_OVF_vect) {
tmr_overf++;
}
ISR(TIMER4_CAPT_vect) {
static uint16_t last_v = 0;
uint16_t curr_v = ICR4;
uint32_t accuml = 0;
accuml = curr_v + tmr_overf *65536UL;
accuml -= last_v;
last_v = curr_v;
tmr_overf = 0;
if(TCCR4B & (1<<ICES4)) { // lo-hi
TCCR4B &= ~(1<<ICES4); // input capture edge select (hi-lo) next
period_lo = accuml;
f_capture = 1;
}
else { // hi-lo
TCCR4B |= (1<<ICES4); // input capture edge select (lo-hi) next
period_hi = accuml;
}
}
Enjoy!
Freq_duty_public_1d.zip (1.82 KB)
Works like a charm! Changed to ICP1, digital pin 83, but otherwise ran it as is. Now I will have to figure out how you did it!
Thank you very much!
[BTW, both of Nick Gammon's sketches work fine on the 2560, but I simply wanted to get both frequency and duty cycle in the same sketch. My hardware is already made, so I am stuck with the ICP1 Digital pin 83 for input, and "they say" this pin is not one of the pins available for interrupts. ("Mega2560 DIGITAL PINS USABLE FOR INTERRUPTS 2, 3, 18, 19, 20, 21") I made a PCB using the atmega2560 chip, so I have access to ICP1. There is also a lot of other processing going on at the same time, so I was trying to minimize load on the system by using input capture.]