Need help adjusting code for Duty-Cycle of Strobe Light

Hello!

I am working with the code below (the full code at the bottom of the post). It strobes a light on and off at a set frequency, which is displayed on a screen in hz (flashes per second)

The length of time the led is on per flash cycle is known as the "duty-cycle". In the code it is referred to as the 'pulse-length'.

So if it is set to 1hz, that is 1 flashes per second. A 50% duty-cycle means it will flash on for .5 seconds, and off for .5 seconds. A 10% duty cycle means it would flash on for .1 seconds, then turn off for the remaining .9 seconds.

Right now, to help achieve a true 'strobe' effect, the code is setup to not let the duty-cycle go over 10%. For the purpose of my project, I need the duty-cycle to always be 50%.

In addition, it looks like the current duty cycle is based on a set length in microseconds, and doesn't change along with the hz adjustment. So for example, if it is set to 1hz, and the duty-cycle is set to 64mu...

If I change it to 10hz, the duty-cycle will still be 64mu, not a % of the cycle duration.

byte len=5;   //pulselength in units of 64 microseconds
//set frequency in Hz, pulse length in units of 64mus
void setfreq(int f, int len){

  //calculate what to put into the timing registers
  long unsigned int ticks=(160000000+f/2)/f; // f_clock/0.1f
  unsigned int ps=1;
  if (ticks>    0xFFFF)ps=   8;
  if (ticks>  8*0xFFFF)ps=  64;
  if (ticks> 64*0xFFFF)ps= 256;
  if (ticks>256*0xFFFF)ps=1024;
  unsigned int val_ICR1=ticks/ps-1;
  unsigned int val_OCR1B=len*(1024/ps)-1;
  unsigned int val_TCCR1B=B00011001;
  if (ps==   8) val_TCCR1B=B00011010;
  if (ps==  64) val_TCCR1B=B00011011;
  if (ps== 256) val_TCCR1B=B00011100;
  if (ps==1024) val_TCCR1B=B00011101;

  //set the actual values in the timing registers
  //noInterrupts();
  if (TCNT1>val_ICR1)TCNT1=0;  //do not allow timer to exceed 'top'
  ICR1=val_ICR1;
  OCR1B=val_OCR1B;
  TCCR1B=val_TCCR1B;
  //interrupts();
}

What I'd like to do:

I would like to alter this code so that the duty-cycle is always 50% of the total cycle. Any idea where a starting point is for this?

I admit, I'm a bit lost with what the author of this code is doing with the timers, and where he is getting the current 'len', so I am open and appreciative of any ideas, thank you!

// Stroboscope with 4-digit 7-segment display
// The display can be directly soldered to the Arduino Nano on pins A0-A5, D4-D9
// the frequency can be changed with two push-buttons
// the pulselength can be changed by pressing both buttons simultaneously

//for common anode display
#define DIGON  HIGH
#define DIGOFF LOW
#define SEGON  LOW
#define SEGOFF HIGH

//for common cathode display
//#define DIGON  LOW
//#define DIGOFF HIGH
//#define SEGON  HIGH
//#define SEGOFF LOW

const byte ndig=4;
const byte nseg=8;

//breadboard setup 
const byte digs[ndig] = { 9, 6, 5,A0};             // digs 1 2 3 4
const byte segs[nseg] = { 8, 4, A2, A4, A5, 7,A1, A3}; // segs ABCDEFGH
//nano setup
//const byte digs[ndig] = { 4, 7, 8,A0};             // digs 1 2 3 4
//const byte segs[nseg] = { 5, 9,A2,A4,A5, 6,A1,A3}; // segs ABCDEFGH
const byte button_up=12;
const byte button_dw=11;

//translate number into corresponding segements
const byte val2seg[12]={
  B11111100, //0
  B01100000, //1
  B11011010, //2
  B11110010, //3
  B01100110, //4
  B10110110, //5
  B10111110, //6
  B11100000, //7
  B11111110, //8
  B11110110, //9
  B00000000, //nothing
  B01001110, //mu
};

int freq=50; //frequency in units of 0.1Hz
byte len=5;   //pulselength in units of 64 microseconds

void setup() {
  
  //setup TIMER1B for fast PWM with (output on pin D10)
  TCCR1A=B00100010;
  TCCR1B=B00011000;
  setfreq(freq,len);
  pinMode(10,OUTPUT);

  // set to output and switch off all digits
  for (byte idig=0; idig<ndig; idig++){
    pinMode(digs[idig], OUTPUT);
    digitalWrite(digs[idig],DIGOFF);
  }
  // set to output and switch off all segments
  for (byte iseg=0; iseg<nseg; iseg++){
    pinMode(segs[iseg], OUTPUT);
    digitalWrite(segs[iseg],SEGOFF);
  }

  //set the pushbuttons to input with pull-up
  pinMode(button_up,INPUT_PULLUP);
  pinMode(button_dw,INPUT_PULLUP);


}

//set frequency in Hz, pulse length in units of 64mus
void setfreq(int f, int len){

  //calculate what to put into the timing registers
  long unsigned int ticks=(160000000+f/2)/f; // f_clock/0.1f
  unsigned int ps=1;
  if (ticks>    0xFFFF)ps=   8;
  if (ticks>  8*0xFFFF)ps=  64;
  if (ticks> 64*0xFFFF)ps= 256;
  if (ticks>256*0xFFFF)ps=1024;
  unsigned int val_ICR1=ticks/ps-1;
  unsigned int val_OCR1B=len*(1024/ps)-1;
  unsigned int val_TCCR1B=B00011001;
  if (ps==   8) val_TCCR1B=B00011010;
  if (ps==  64) val_TCCR1B=B00011011;
  if (ps== 256) val_TCCR1B=B00011100;
  if (ps==1024) val_TCCR1B=B00011101;

  //set the actual values in the timing registers
  //noInterrupts();
  if (TCNT1>val_ICR1)TCNT1=0;  //do not allow timer to exceed 'top'
  ICR1=val_ICR1;
  OCR1B=val_OCR1B;
  TCCR1B=val_TCCR1B;
  //interrupts();
}

//set a digit to a certain value
void setdig(byte dig, byte val, bool dot){
  //switch off all digits
  for (byte idig=0; idig<ndig; idig++){
    digitalWrite(digs[idig],DIGOFF);
  }
  //set the segments
  for (byte iseg=0; iseg<nseg; iseg++){
    if ((val2seg[val]&(1<<(nseg-1-iseg)))>0){
      digitalWrite(segs[iseg], SEGON);
    } else {
      digitalWrite(segs[iseg], SEGOFF);
    }
  }

  // set the decimal dot if needed
  if (dot){
    digitalWrite(segs[7], SEGON);
  } else{
    digitalWrite(segs[7], SEGOFF);    
  }

  //set the digit
  digitalWrite(digs[dig],DIGON);
 
}


byte dispdig=0;

byte prevbutstat=0;

unsigned long millis_butchanged=0;
unsigned long millis_valchanged=0;

void loop() {

  //check for buttons
  long unsigned millis_current=millis();
  bool change=false;
  byte butstat=(digitalRead(button_up)==LOW)*1+(digitalRead(button_dw)==LOW)*2;
  if (butstat!=prevbutstat)millis_butchanged=millis_current;

  int dt=200; int df=1;
  if (millis_current-millis_butchanged>1000)dt=100;
  if (millis_current-millis_butchanged>2000)dt=50;
  if (millis_current-millis_butchanged>3000)dt=20;
  if (millis_current-millis_butchanged>4000)dt=10;
  if (millis_current-millis_butchanged>5000)dt=5;
  if (millis_current-millis_butchanged>6000)dt=2;
  if (millis_current-millis_butchanged>7000)dt=1;
  if (millis_current-millis_butchanged>8000)df=2;
  if (millis_current-millis_butchanged>9000)df=5;
  if (millis_current-millis_butchanged>10000)df=10;
  if (butstat==3)dt=1000;
  if(millis_current-millis_valchanged>dt){
    if(butstat==1) freq=freq+df;
    if(butstat==2) freq=freq-df;
    if(butstat==3) len++;
    if(butstat>0){
        change=true; millis_valchanged=millis_current;
    }
  }
  prevbutstat=butstat;

  //keep frequency and pulselength wihin allowed domain
  freq=max(freq,3);    
  freq=min(freq,9999);
  if (len>50)len=1;
  if (len*freq>15625)len=1; //corresponds to 0.1 duty cycle
  
  if (change) setfreq(freq,len);

  //display a digit
  byte val=10; // by default nothing
  
  if (butstat==3){
    if (dispdig==0 and len>1)val=((len*64)/100)%10;
    if (dispdig==1)val=((len*64)/10)%10;
    if (dispdig==2)val=(len*64)%10;
    if (dispdig==3)val=11; //the symbol mu
  } else {
    if (dispdig==0 and freq>999)val=(freq/1000)%10;
    if (dispdig==1 and freq>99)val=(freq/100)%10;
    if (dispdig==2)val=(freq/10)%10;
    if (dispdig==3)val=freq%10;
  }
  
  setdig(dispdig,val,(dispdig==2 and butstat!=3));
  delayMicroseconds(100);
  dispdig=(dispdig+1)%ndig;
    
}

For reference, here is the project and site it is from: https://www.instructables.com/Portable-Precision-Stroboscope/

I have this built on my workbench and functioning without the 50% duty-cycle that I'm after

consider the following code. it toggles the LED on/off every period, hence 50% duty-cycle. it modifies the period based on button input

#define LED     13
#define But1    A1
#define But2    A2
#define But3    A3

byte buts [] = { But1, But2, But3 };
#define N_BUTS  sizeof(buts)

byte butSt [N_BUTS];
long butDelta [] = { -50, 50, 200 };

// -----------------------------------------------------------------------------
void setup (void)
{
    Serial.begin (9600);

    pinMode (LED, OUTPUT);

    for (unsigned n = 0; n < N_BUTS; n++)  {
        pinMode (buts [n], INPUT_PULLUP);
        butSt [n] = digitalRead (buts [n]);
    }
}

// -----------------------------------------------------------------------------
#define InitPeriod  1000
unsigned long msecPeriod    = InitPeriod;
unsigned long msecLst       = 0;
unsigned long msecDebounce  = 0;

void loop (void)
{
    unsigned long msec = millis ();

    if (msec - msecLst > msecPeriod)  {
        msecLst = msec;
        digitalWrite (LED, ! digitalRead (LED));
    }


    if (msec - msecDebounce > 50)  {
        for (unsigned n = 0; n < N_BUTS; n++)  {
            byte but = digitalRead (buts [n]);

            if (butSt [n] != but)  {
                butSt [n] = but;
                msecDebounce = msec;

                msecPeriod += butDelta [n];
                Serial.println (msecPeriod);
            }
        }
    }
}

gcjr:
consider the following code. it toggles the LED on/off every period, hence 50% duty-cycle. it modifies the period based on button input

#define LED     13

#define But1    A1
#define But2    A2
#define But3    A3

byte buts [] = { But1, But2, But3 };
#define N_BUTS  sizeof(buts)

byte butSt [N_BUTS];
long butDelta [] = { -50, 50, 200 };

// -----------------------------------------------------------------------------
void setup (void)
{
   Serial.begin (9600);

pinMode (LED, OUTPUT);

for (unsigned n = 0; n < N_BUTS; n++)  {
       pinMode (buts [n], INPUT_PULLUP);
       butSt [n] = digitalRead (buts [n]);
   }
}

// -----------------------------------------------------------------------------
#define InitPeriod  1000
unsigned long msecPeriod    = InitPeriod;
unsigned long msecLst       = 0;
unsigned long msecDebounce  = 0;

void loop (void)
{
   unsigned long msec = millis ();

if (msec - msecLst > msecPeriod)  {
       msecLst = msec;
       digitalWrite (LED, ! digitalRead (LED));
   }

if (msec - msecDebounce > 50)  {
       for (unsigned n = 0; n < N_BUTS; n++)  {
           byte but = digitalRead (buts [n]);

if (butSt [n] != but)  {
               butSt [n] = but;
               msecDebounce = msec;

msecPeriod += butDelta [n];
               Serial.println (msecPeriod);
           }
       }
   }
}

Thank you, this is very helpful, though it doesn't look like it works at higher values (over 9000).

For my function, it is flashing a light at very high speeds (12.5ms on, 12.5ms off or less).

The existing code I'm using does every thing I need it to already (display, convert the time into hz, adjust it with buttons, hold the button down for faster adjustment) with the exception of the pulse length. So I'm hesitant to scrap it and start over if at all possible.

I've been reading all morning about timers and interrupts, but I still can't see why / how the duty-cycle is being set.

Ideally, I would like the length to be a variable that is always 50% of the frequency...

I believe he is establishing the 64mu basis of the duty cycle here:

//set frequency in Hz, pulse length in units of 64mus
void setfreq(int f, int len){

  //calculate what to put into the timing registers
  long unsigned int ticks=(160000000+f/2)/f; // f_clock/0.1f
  unsigned int ps=1;
  if (ticks>    0xFFFF)ps=   8;
  if (ticks>  8*0xFFFF)ps=  64;
  if (ticks> 64*0xFFFF)ps= 256;
  if (ticks>256*0xFFFF)ps=1024;
  unsigned int val_ICR1=ticks/ps-1;
  unsigned int val_OCR1B=len*(1024/ps)-1;
  unsigned int val_TCCR1B=B00011001;
  if (ps==   8) val_TCCR1B=B00011010;
  if (ps==  64) val_TCCR1B=B00011011;
  if (ps== 256) val_TCCR1B=B00011100;
  if (ps==1024) val_TCCR1B=B00011101;

  //set the actual values in the timing registers
  //noInterrupts();
  if (TCNT1>val_ICR1)TCNT1=0;  //do not allow timer to exceed 'top'
  ICR1=val_ICR1;
  OCR1B=val_OCR1B;
  TCCR1B=val_TCCR1B;
  //interrupts();
}

It appears he is setting the prescaler there based on something, but I admit, it is beyond my current knowledge and I haven't found a resource to learn what I'm missing.

Any help is very appreciated! The project is nearly finished, just trying to adjust this duty-cycle to a constant 50%, said another way, trying to adjust the 'len' variable to be 50% of the frequency, or 'freq' variable

Just change this part:

  ICR1=val_ICR1;
  OCR1B=val_OCR1B;

to this:

  ICR1=val_ICR1; // Clock ticks per cycle
  OCR1B=val_ICR1 / 2;  // 50% duty cycle

So what is the maxium frequency in Hz the LED shall blink?
if it is lower than 50Hz (which can't be seen anymore by human eyes as blinking

It can be done easily in Software. At such low speeds it is not nescessary to use hardware-counter
except pwm for reducing the brightness of the led

So as pseudo-code

with pwm for dimming

if (TimePeriodIsOver(Offtime) {
  analogWrite(IO_Pin,brighness) // LED dimmed on
}

if (TimePeriodIsOver(ONtime) {
  analogWrite(IO_Pin,0)  // LED OFF
}

Offtime and Ontime are calculated based on the frequency
best regards Stefan