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 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;
}

Welcome cristopher21, looking at your schematic and code I see oooooooooops they are missing. Please post a schematic, not a frizzy thing and your code. Post links to technical information on all hardware devices. Explain how you measured the duty cycle, frequency and voltage. Another minor detail what arduino are you using?

What does 65 for high mean?

What does 35 for 0 volts mean?

Ask this question in the exact same words Siri, Alexa, or google
or the computer of spaceship enterprise
The answer will always be

int ledPin = 9;     
int button = 10; 
void setup() {
  // put your setup code here, to run once:
  pinMode(button, INPUT);
  pinMode(ledPin, OUTPUT); 
}

void loop() {
  // put your main code here, to run repeatedly:
  if (digitalRead(button)){
    analogWrite(ledPin, 65);
  } else {
    analogWrite(ledPin, 35);
  }
}

??

We need some more information to help you out here.

Use 'digitalRead()' to read the pin.

If you mean 65% and 35% use 166 and 89 (255 * .65 and 255 * .35).

Yeah, you are completely right, just made something up last night!

Sorry, I fixed the post.

Please look at the post again, it was badly edited.

Please, look at the post again, it was badly edited.

as a general advise:

to proceed in a thread post a

new posting

re-editing code with more than just typos can cause making the answers senseless given to the original version.

I know almost nothing about the timer/counter-modes.
If you don't need anything else than this 50 Hz-pwm signal
you could just setup a software pwm like this

the 50 Hz are created through counting up and check the counter to match the number that relates to the desired frequency.
The software-pwm needs to be tweaked to the execution speed of loop
hence the strange values.


boolean MicroSecsAreOver (unsigned long &periodStartTime, unsigned long TimePeriod) {
  unsigned long currentMicros  = micros();
  if ( currentMicros - periodStartTime >= TimePeriod )
  {
    periodStartTime = currentMicros; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}

unsigned long MyTestTimer = 0;                   // variables MUST be of type unsigned long
unsigned long pwmCount = 0;

const byte PWM_Pin = 4;

void setup() {
  pinMode(PWM_Pin, OUTPUT);
}


void loop() {

  if ( MicroSecsAreOver(MyTestTimer, 4) ) {
    pwmCount++;
  }

  if (pwmCount == 2672) {
    digitalWrite(PWM_Pin, HIGH );
    pwmCount = 0;
  }
  
  if (pwmCount == 1737) {
    digitalWrite(PWM_Pin, LOW );
  }
}

or using the same basic principle engaging a timer-interrupt.
This code includes some additional more general features
a non-blocking timing function
compact to write serial debug-output
including sending sourcecode-info to the serial monitor using compiler-macros

The timer-interrupt-frequency is chosen pretty high to give you a high resolution for the dutycycle
and it demonstrates how to change the dutycacle on runtime

It does calculate the setup of the internal registers for a high range of frequencies
with which the timer-interrupt is fired

// this demo-code belongs to the public domain
// this code demonstrates how to blink the  onboard-LED
// of an Arduino-Uno (connected to IO-pin 13)
// in a nonblocking way using timing based on function millis()
// and creating a pwm-signal "in the backround"
// through setting up a timer-interrupt through configuring timer2

// A T T E N T I O N !    R E M A R K
// some other libraries that make use of timer2 may conflict with this


// start of macros dbg and dbgi
#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope

#define dbgi(myFixedText, variableName,timeInterval) \
  do { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  } while (false);
// usage: dbgi("2:my fixed text",myVariable,1000);
// myVariable can be any variable or expression that is defined in scope
// third parameter is the time in milliseconds that must pass by until the next time a 
// Serial.print is executed
// end of macros dbg and dbgi

void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__));
  Serial.print( F("  compiled ") );
  Serial.print(F(__DATE__));
  Serial.print( F(" ") );
  Serial.println(F(__TIME__));  
}


boolean TimePeriodIsOver (unsigned long &periodStartTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();  
  if ( currentMillis - periodStartTime >= TimePeriod )
  {
    periodStartTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  } 
  else return false;            // not expired
}

unsigned long MyTestTimer =  0;                   // variables MUST be of type unsigned long
const byte    OnBoard_LED = 13;


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);
  
  if ( TimePeriodIsOver(MyBlinkTimer,BlinkPeriod) ) {
    digitalWrite(IO_Pin,!digitalRead(IO_Pin) ); 
  }
}


unsigned long pwmBaseFrequency = 100000;

volatile long pwmCounter     = 0;
volatile long periodCount    = 2000;
         long dutyPercentage = 65;

volatile long DutyCount      = periodCount * dutyPercentage / 100;


void setupTimerInterrupt(unsigned long ISR_call_frequency) {
  const byte Prescaler___8 = (1 << CS21);
  const byte Prescaler__32 = (1 << CS21) + (1 << CS20);
  const byte Prescaler__64 = (1 << CS22);
  const byte Prescaler_128 = (1 << CS22) + (1 << CS20);
  const byte Prescaler_256 = (1 << CS22) + (1 << CS21);
  const byte Prescaler1024 = (1 << CS22) + (1 << CS21) + (1 << CS20);

  const unsigned long CPU_Clock = 16000000;
  const byte toggleFactor = 1;

  unsigned long OCR2A_value;

  cli();//stop interrupts

  TCCR2A = 0;// set entire TCCR2A register to 0
  TCCR2B = 0;// same for TCCR2B
  TCNT2  = 0;//initialize counter value to 0

  TCCR2A |= (1 << WGM21); // turn on CTC mode
  TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt

  // the prescaler must be setup to a value that the calculation
  // of the value for OCR2A is below 256
  TCCR2B = Prescaler___8;
  OCR2A_value = (CPU_Clock / ( 8 * ISR_call_frequency * toggleFactor) )  - 1;
  dbg("setup: timer done", OCR2A_value);

  if (OCR2A_value > 256) {  // if value too big
    TCCR2B = Prescaler__32; // set higher prescaler
    OCR2A_value = (CPU_Clock / ( 32 * ISR_call_frequency * toggleFactor) )  - 1;
    dbg("setup: prescaler 32", OCR2A_value);
  }

  if (OCR2A_value > 256) { // if value too big
    TCCR2B = Prescaler__64;// set higher prescaler
    OCR2A_value = (CPU_Clock / ( 64 * ISR_call_frequency * toggleFactor) )  - 1;
    dbg("setup: prescaler 64", OCR2A_value);
  }

  if (OCR2A_value > 256) { // if value too big
    TCCR2B = Prescaler_128;// set higher prescaler
    OCR2A_value = (CPU_Clock / ( 128 * ISR_call_frequency * toggleFactor) )  - 1;
    dbg("setup: prescaler 128", OCR2A_value);
  }

  if (OCR2A_value > 256) {  // if value too big
    TCCR2B = Prescaler_256; // set higher prescaler
    OCR2A_value = (CPU_Clock / ( 256 * ISR_call_frequency * toggleFactor) )  - 1;
    dbg("setup: prescaler 256", OCR2A_value);
  }

  if (OCR2A_value > 256) {   // if value too big
    TCCR2B = Prescaler1024;  // set higher prescaler
    OCR2A_value = (CPU_Clock / ( 1024 * ISR_call_frequency * toggleFactor) )  - 1;
    dbg("setup: prescaler 1024", OCR2A_value);
  }

  OCR2A = OCR2A_value; // finally set the value of OCR2A

  sei();//allow interrupts
  dbg("setup: timer done", OCR2A_value);
}

unsigned long pwmCount = 0;
const byte PWM_Pin     = 4;


void setup() {
  Serial.begin(115200);
  Serial.println( F("Setup-Start") );
  PrintFileNameDateTime();
  pinMode(PWM_Pin, OUTPUT);
  
  setupTimerInterrupt(pwmBaseFrequency);
}


ISR(TIMER2_COMPA_vect) {

  pwmCount++;

  if (pwmCount >= periodCount) {
    digitalWrite(PWM_Pin, HIGH );
    pwmCount = 0;
  }

  if (pwmCount >= DutyCount) {
    digitalWrite(PWM_Pin, LOW );
  }
}


void loop() {
  BlinkHeartBeatLED(OnBoard_LED,500);

  if ( TimePeriodIsOver(MyTestTimer,2000) ) {
    
    if (dutyPercentage == 65) {
      dutyPercentage = 35;
    }
    else {
      dutyPercentage = 65;
    }
    DutyCount   = periodCount * dutyPercentage / 100;  
  }    
}

best regards Stefan

Your input capture code is too mystical for me to try to deduce the nature of the input. What, exactly, qualifies as a '1' and as a '0' in the signal being captured?

You could vary based on a simple logic input with:

/*
 * Sketch:  sketch_jan04d
 * Target:  Mega2560
 *
 */

const uint16_t uDuty65 = 26000u;    //#     DC = N/40E3
const uint16_t uDuty35 = 14000u;    //#     DC = N/40E3

const uint8_t pinOut = 11;          //output pin
const uint8_t pinIn = 2;            //logic input

void setup() 
{
    pinMode( pinIn, INPUT_PULLUP );
    //WGM14, /8
    //pin sets on BOT, clears on match
    TCCR1A = _BV(COM1A1) | _BV(WGM11);
    TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11);
    ICR1 = 39999;       //set period
    OCR1A = uDuty65;     //set high duty cycle
    pinMode( pinOut, OUTPUT );

}//setup

void loop() 
{
    //set duty cycle based on state of input pin
    OCR1A = (digitalRead( pinIn ) == HIGH) ? uDuty65:uDuty35;

}//loop

@Blackfin wow very compact code

Your code is for Arduino Mega 2560
What has to be changed to make it work on an Arduino Uno?

On my Mega 2560 I had to tweak the value of

  ICR1 = 39780;       //set period

to obtain 50,00 Hz

best regards Stefan

Hi,
Can you tell us what your project is?
What do you need 50hz 65V PWM signal for?

Tom.... :grinning: :+1: :coffee: :australia:

I understand it this way:

best regards Stefan

Since the Uno also has a 16-bit Timer1 I think you'd only really need to adjust the output pin to match the 328's OC1A output: PB1 or pin 9 on the board.

Interesting. At 16MHz and a prescaler of /8, T1 should be running at 2MHz or with a period of 500nS. 20mS/500nS = 40000 counts; 0-39999 would give those 40K counts.

39781 (39780+1) ticks of a 500nS clock would give a period of 19.8905mS or 50.275Hz. If you really needed to adjust the TOP to that value to get a 50Hz output dead-nuts on, you might have an out-of-spec crystal or your measuring equipment is out of calibration.

Is there a place on the PCB of an arduino Mega 2560 where I could probe the clock-signal?

Hm what would you recommend as a first test to check if my DSO is out of calibration?

best regards Stefan

On the 2560 board, near the pins labeled "POWER" are four components arranged in a row. The two nearest the MCU are the crystal and a parallel load resistor. You can measure the side of the resistor nearest the MCU. Note that the addition of a scope load may cause the crystal frequency to change slightly.

I should qualify/correct my "out of spec" comment. If the period was 63nS (for a frequency of 15.873MHz), the crystal system is 126,984Hz of the 16MHz ideal giving an error of 0.79%, well within the specification for most crystals. It might be possible to adjust the parallel (R1) and series (R2) resistors to get it closer to the ideal but less than 1% error seems pretty good and, in the real world -- e.g. playing with servos, for example -- would have no practical negative effect.

That is not how you clear the Input Capture Flag.

From the datasheet:

ICFn can be cleared by writing a logic one to its bit location.

  TIFR4 = (1 << ICF4);  //clear ICF4 flag after a change in capture edge, 
                          //per ATmega2560 datasheet, section 
                          //17.6.3 - using the Input Capture Unit

What pin is your scope connected to?!?
Pin 9 (OC2B/PH6) should be 61Hz, 25% duty cycle.
Pin 11 (OC1A/PB5) won't do anything because OCR1A is used for TOP in WGM 4
Pin 13 (OC1C/PB7) should be 50 Hz (16 MHz / 256 / 625 / 2)

If you want Pin 13 to be variable duty cycle, use a Fast PWM mode like 9, 11, or 15 and not the CTC mode (4). In the CTC mode, OC1C will toggle once each timer period and will always be 50% duty cycle.