I cannot modify my duty cycle, I want to have it set to 65 for high or 5 V and 35 for low or 0 volts. I have my frequency in approximate 50 hz and the period in 20 ms, but I cannot vary the frequency. Does anyone notice something? I would appreciate very much if you help me, I am a little new on the subject. I am measuring the signal with a Tektronix TDS 2012C oscilloscope.
#define NOP __asm__("nop\n\t")
volatile byte captureIndex = 0; //to keep track if we are capturing falling or rising edge
volatile unsigned short captureRefValue = 0;
volatile unsigned short captureLowValue = 0; //amount of time the signal stayed low
volatile unsigned short captureHighValue = 0; //amount of time the signal stayed high
volatile bool captureTimeout = false; //flag to indicate that the capture timer has overflowed at least once
volatile unsigned short captureTimesOverflowed = 0; //number of times the capture timer has overflowed since last event
unsigned short rawDutyRef=0;
unsigned short rawDutyLow=0;
unsigned short rawDutyHigh=0;
float dutyPercent=0;
bool rawDutyAvailable = false;
enum captureStatus {captureOK, capturePending, captureTimeouted};
// Defines a structure to hold values related to measuring pulses with a timer, just to keep it tidy
struct PulseMeasurement {
unsigned short rawDutyRef=0;
unsigned short rawDutyLow=0;
unsigned short rawDutyHigh=0;
volatile byte captureIndex = 0; //to keep track if we are capturing falling or rising edge
volatile unsigned short captureRefValue = 0;
volatile unsigned short captureLowValue = 0; //amount of time the signal stayed low
volatile unsigned short captureHighValue = 0; //amount of time the signal stayed high
volatile bool captureTimeout = false; //flag to indicate that the capture timer has overflowed at least once
volatile unsigned short captureTimesOverflowed = 0; //number of times the capture timer has overflowed since last event
float dutyCycle=0; //Duty cycle expressed as fraction (from 0.00 to 1.00)
float semiPeriodHigh; //Time the pulse stayed high
float semiPeriodLow; //Time the pulse stayed low)
float period; //Period of the wave
};
struct PulseMeasurement reedSwPulse;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("Alive");
timer1_init();
timer2_init();
timer4_init();
}
void loop() {
// put your main code here, to run repeatedly:
byte s;
startCapture();
do {
s = getCapture();
} while(s == capturePending);
if (s == captureOK) {
printCaptureDetails();
}
else if (s == captureTimeouted) {
Serial.println("Overflowed");
}
delay(100);
}
void printCaptureDetails()
{
Serial.print("New:\t");
Serial.print(rawDutyHigh,DEC);
Serial.print("\t");
Serial.print(rawDutyLow,DEC);
Serial.print("\t");
Serial.print(100.0*dutyPercent);
Serial.print("%\t");
Serial.print(0.004*rawDutyHigh);
Serial.print("ms\t");
Serial.print(0.004*rawDutyLow);
Serial.println("ms");
}
// --- timer1_init() ---
//Function: sets up a 50Hz output from timer 1 on
//Arduino Mega pins 11 and 13
//CTC mode OCRnA
// WGM1 3:0 = 0100
//Toggle OC1C and OC1A on compare match
// COM1C 1:0 = 01
// COM1A 1:0 = 01
//OC1B normal operation
// COM1B 1:0 = 00
//Prescaler set to 256 for 16MHz system clock, divided will equal 62500Hz
// CS1 2:0 = 100
//OCR1A is set to 0x04E1, decimal 1249.
// Since we count from zero, that divides the prescaled clock as follows:
// 62500Hz/1250 = 50Hz
//Additional reference :
//https://forum.arduino.cc/t/timer-in-ctc-mode-to-generate-1mhz-pulse/119495
//We will need to reconfigure this to give 50Hz output at ~33% duty cycle
void timer1_init()
{
DDRB |= (1 << PINB7) | (1 << PINB5); //set relevant pins as outputs; Arduino pins 13 and 11 (OC1C and OC1A)
//Set TCCRn registers to 0, looks like this is necessary to do first
TCCR1A = 0;
TCCR1B = 0;
//Set count to 0
TCNT1 = 0;
// initialize compare value for 50 Hz.
//This is the max value the timer is allowed to reach before
//rolling over to 0
OCR1A = 0x0270; //we are counting from 0, inclusive
OCR1B = 12;
// set up timer with prescaler = 1024 and CTC mode
TCCR1A |= (0 << COM1A1) | (1 << COM1A0) | //set output toggle for COM1A on OC1A
(0 << COM1B1) | (0 << COM1B0) | //set normal mode for COM1B on OC1B
(0 << COM1C1) | (1 << COM1C0) | //set normal mode for COM1C on OC1C
(0 << WGM11) | (0 << WGM10); //set CTC mode
TCCR1B |= (0 << WGM13) | (1 << WGM12) | //set CTC mode, prescaler to 256
(1 << CS12) | (0 << CS11) | (0 << CS10);
}
// --- timer2_init()
// Funtion: generate PWM pulses for self testing
// This is an 8 bit timer
void timer2_init()
{
//Set TCCR2 registers to 0, looks like this is necessary to do first
TCCR2A = 0;
TCCR2B = 0;
//Set count to 0
TCNT2 = 0;
//Initialize compare value for 25% duty cycle
OCR2B = 63;
// set up timer with prescaler = 1024 and fast PWM mode
// This will give an output frequency of ~61.035Hz, but it's what we've got...
// also set up OC2B to toggle non-inverting mode (ATmega 2560 datasheet, table 20-6)
TCCR2A |= (1 << COM2B1) | (0 << COM2B0) | (1 << WGM21) | (1 << WGM20);
TCCR2B |= (0 << FOC2A) | (0 << FOC2B) | (0 << WGM22) | (1 << CS22) | (1 << CS21) | (1 << CS20);
DDRH |= (1 << PINH6); //set relevant pins as outputs; Arduino pin 9 (OC2B pin)
}
// --- timer4_init() ---
// Function: sets up a capture timer for measuring the duty cycle
// We will use pin PINL0 / ICP4 / Arduino pin 49
void timer4_init()
{
//Set TCCRn registers to 0 , looks like this is necessary to do first
TCCR4A = 0;
TCCR4B = 0;
//Set count to 0
TCNT4 = 0;
//Set input capture register to 0, to initialize
ICR4 = 0;
//Set Capture on rising edge
//Set prescaler to 64. At 16MHz clock, we can measure events up to 262.144ms in length (~3.81Hz).
TCCR4B |= (1 <<ICES1) | (0 << CS42) | (1 << CS41) | (1 << CS40);
//Enable timer 4 capture interrupt and timer 4 overflow interrupt
TIMSK4 |= (1 << ICIE4) | (1 << TOIE4);
sei(); //enable global interrupts
}
void startCapture() //start or restart capture
{
TIMSK4 &= ~((1 << ICIE4) | (1 << TOIE4)); //disable capture and overflow interrupts
captureTimeout = false;
captureTimesOverflowed = 0;
captureIndex = 0;
TCCR4B |= (1<<ICES1); //configure to capture on rising edge
TIFR4 &= ~(1 << ICF4); //clear ICF4 input capture flag 4
TCNT4 = 0;
TIMSK4 |= (1 << ICIE4) | (1 << TOIE4); //enable capture and overflow interrupts
}
void stopCapture()
{
TIMSK4 &= ~((1 << ICIE4) | (1 << TOIE4)); //disable capture and overflow interrupts
}
//returns capture status
byte getCapture()
{
if(captureTimeout == true) {
return captureTimeouted;
}
else {
if(captureIndex > 2) {
//rawDutyRef = captureRefValue;
rawDutyHigh = captureHighValue - captureRefValue;
rawDutyLow = captureLowValue - captureHighValue;
dutyPercent = (float)rawDutyHigh / (float)(rawDutyHigh+rawDutyLow);
rawDutyAvailable = true;
return captureOK;
}
else {
return capturePending;
}
}
}
ISR (TIMER4_CAPT_vect)
{
// case 0: record the timestamp of the rising edge. this is our reference. switch to falling edge
// case 1: record the timestamp of the falling edge; switch to rising edge
// case 2: record the timestamp of the rising edge
// when captueIndex > 2 an external function reads the values from case 0 to case 2
// the times are calculated: rawDutyLow = case1-case0 ; rawDutyHigh = case2-case1;
switch(captureIndex) {
case 0:
captureRefValue = ICR4;
TCCR4B &= ~(1<<ICES1); //configure to capture on falling edge
break;
case 1:
captureHighValue = ICR4;
TCCR4B |= (1<<ICES1); //configure to capture on rising edge
break;
case 2:
captureLowValue = ICR4;
break;
case 255:
captureTimeout=true; //to signal that we have old data. Case # can be reduced faster timeout needed
captureIndex = 3; //don't let this switch() run again
break;
}
TIFR4 &= ~(1 << ICF4); //clear ICF4 flag after a change in capture edge,
//per ATmega2560 datasheet, section
//17.6.3 - using the Input Capture Unit
captureIndex++; //when this reaches >2, it's time to read the captures on the main loop
}
ISR (TIMER4_OVF_vect) //To handle a capture timer overflow
{
//This overflow can happen if there is no input or the input frequency is too low
captureTimesOverflowed++;
captureTimeout = true;
}
