LEDs dimming without using PWM

Hi,
I've been assigned a task to dim LEDs using arduino with some conditions;

  1. I've to dim 3 leds using arduino timers
  2. Can't use PWM mode or analogue write function i.e have to generate my own variable duty cycle using timers and its registers.
  3. Should avoid hard coded numbers to allow scalability in code. (use formulas or equations)
  4. The 3 leds should dim 120 degree out of phase from one another

I'm attaching the progress that I've made, but it is not giving the precise results. Can't think of another way with these limitations. If someone can help instantly, I'd be obliged.

Thanking in anticipation.

#define UPPERLIMIT 65000 // maximum timer count
#define LOWER_LIMIT 100  // minimum timer count
#define TIMER_CHANGE 50  // increment value to create a variable duty cycle
#define LED_NUM 3        // no. of leds


const int led1 = 10;
const int led2 = 11;
const int led3 = 12;

// allocating different start timer values to 3 leds to create a phase difference of 120 degrees in dimming 
uint16_t led1CompareValue = UPPERLIMIT/3; 
uint16_t led2CompareValue = led1CompareValue * 2 ;
uint16_t led3CompareValue = UPPERLIMIT;


uint16_t ocrCompareValue = led3CompareValue; // initializing compare value for ocr register
uint16_t led_num = 1;

volatile uint8_t overFlowFlag = 0;


volatile uint8_t compareMatchFlag = 0;

uint8_t flag1 = 0;
uint8_t flag2 = 0;
uint8_t flag3 = 0;

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

  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);

 //initialize timer
 TCCR1A = 0;
 TCCR1B = 0;
   
 TCCR1B |= (1 << CS10);    // prescalar 1
 TIMSK1 |= (1 << TOIE1)| (1<<OCIE1A) ;   

 OCR1A = led1CompareValue; // setting register value
 led_num++;
  
}

ISR(TIMER1_OVF_vect)
{
 overFlowFlag = 1;
 //led_num = 1;
}

ISR(TIMER1_COMPA_vect)
{
  compareMatchFlag = 1;
  OCR1A = ocrCompareValue; 
  //led_num ++ ;
}



void loop()
{
    //Serial.println (OCR1A);
    if (overFlowFlag == 1) {
      overFlowFlag = 0;


      //starting with all leds on and then dimming them with different intervals
      digitalWrite(led1, HIGH);
      digitalWrite(led2, HIGH);
      digitalWrite(led3, HIGH);
    }

    
    if (compareMatchFlag == 1) {
      compareMatchFlag = 0;
      
      set_compare_value(); 
      set_led();
      led_num++;
      
      if(led_num > LED_NUM)
        {
        led_num = 1;
        }
  
   
   }
 }

// this function updates the compare value in register
void set_compare_value()
{
  switch (led_num)
  {
    case 1: 
    {
      //Serial.println ("case 1");
      ocrCompareValue = led1CompareValue;
     
    }
      break;
    case 2: 
    {
      //Serial.println ("Case 2");
      ocrCompareValue = led2CompareValue;
    
    }
      break;

     case 3: 
    {
      //Serial.println ("Case 3");
      ocrCompareValue = led3CompareValue;
    
    }
      break;
  }
  
}

// updates value stored in ledCompareValue to generate duty cycle
void set_led()
{
  switch (led_num)
  {
    case 1: 
    {
      // Serial.println ("Case 4");
      
         digitalWrite(led1, LOW);
      
      if (led1CompareValue <= LOWER_LIMIT) 
        flag1 = true;
      
      if (led1CompareValue >= UPPERLIMIT)
        flag1 = false;
      
      
      if(flag1)
        led1CompareValue += TIMER_CHANGE;
      
      else 
          led1CompareValue -= TIMER_CHANGE;
    }
    break;
  
      
    case 2: 
    {  
       //Serial.println ("Case 5");
     
       digitalWrite(led2, LOW);
      
      if (led2CompareValue <= LOWER_LIMIT) 
        flag2 = true;
      
      if (led2CompareValue >= UPPERLIMIT)
        flag2 = false;
      
      
      if(flag2)
        led2CompareValue += TIMER_CHANGE;
      
      else 
          led2CompareValue -= TIMER_CHANGE;
    
    }
      break;

 case 3: 
    {
      // Serial.println ("Case 6");
      
         digitalWrite(led3, LOW);
      
      if (led3CompareValue <= LOWER_LIMIT) 
        flag3 = true;
      
      if (led3CompareValue >= UPPERLIMIT)
        flag3 = false;
      
      
      if(flag3)
        led3CompareValue += TIMER_CHANGE;
      
      else 
          led3CompareValue -= TIMER_CHANGE;
    }
    break;
  }
   
}

I can't help: that timer and register stuff is way above me.

But it might help your cause if you elaborate on this:

zaar:
it is not giving the precise results.

meltDown:
But it might help your cause if you elaborate on this:

There are jerks in two leds while one led dims normally. The other two however show irregular behaviour. Also the phase difference between the leds is not according to the requirements, i.e they are not 120 degrees out of phase.

You might get more help if you post your code.
In code tags.

AWOL:
You might get more help if you post your code.
In code tags.

I've added the code in code tags. Kindly help me in completing my task.

I reckon if you have a program that uses Atmega registers you are greatly reducing the number of people who will be able to help.

For example although I have written programs using registers I find it very difficult to figure out another person's code when it uses registers. That's mainly because I have no need to remember the purpose and usage of the registers. I would have to look up everything in the Atmega datasheet and I'm just too lazy to do that.

The way I debug my own code when I use registers is by developing it in very small chunks and testing at each stage.

...R

zaar:
See if you can help me now

That sounds very cheeky when directed to someone who is offering to help for free.

...R

I think that part of the problem is that you are setting 'ocrCompareValue' in sequence, even after the ledXCompareValue's are no longer in ascending order. You can't just do the LEDs in order... You have to find the lowest compare value greater than the last one. You also have to deal with the possibility that two LEDs will have the same compare value as the intervals pass each other on the way up and down the brightness scale.

johnwasser:
I think that part of the problem is that you are setting 'ocrCompareValue' in sequence, even after the ledXCompareValue's are no longer in ascending order. You can't just do the LEDs in order... You have to find the lowest compare value greater than the last one. You also have to deal with the possibility that two LEDs will have the same compare value as the intervals pass each other on the way up and down the brightness scale.

You're right, I get it. Thank you
Is there any other way to generate varying duty cycle using timers in non-pwm modes to get the pwm effect? If you've any knowledge about it, kindly guide me. Or give me a simple idea to proceed with, I'll be highly obliged if someone can help me with that.

have to generate my own variable duty cycle

That is the very definition of PWM

PWM - Wikipedia

I suspect you're trying to avoid 'hardware' PWM...
There are many examples of generating 'software' PWM

lastchancename:
That is the very definition of PWM

PWM - Wikipedia

I suspect you're trying to avoid 'hardware' PWM...
There are many examples of generating 'software' PWM

You're right. You can say I'm trying to generate PWM without using software PWM modes. If you know of any examples, kindly link it here. Thanks

There is only hardware -or- software PWM.
Google ‘Arduino software PWM’ should get you moving.

zaar:
Is there any other way to generate varying duty cycle using timers in non-pwm modes to get the pwm effect?

I think you are close. You turn on the LEDs at timer overflow (just like PWM) and turn each one off at the corresponding OCR interrupt. With PWM you would be using a separate OCR register for each LED. Since you want to control all three LEDs with a single OCR register you have to figure out which LEDs the current OCR interrupt is for and set the OCR for the next LED. Of course the 'next' LED could be the earliest one if this interrupt was for the latest one.
I wrote this and it compiles without error but I have not tested it in any way. It might do what you want.

const uint16_t MaxBrightness = 65000; // maximum timer count
const uint16_t MinBrightness = 100;  // minimum timer count
const uint16_t BrightnessRange = MaxBrightness - MinBrightness;


const uint32_t TotalSteps = BrightnessRange * 2; // Ramp up and down


#define LED_NUM 3        // no. of leds


const byte LEDPins[LED_NUM] = {10, 11, 12};
volatile uint16_t Brightness[LED_NUM] = {MinBrightness, MinBrightness, MinBrightness};


// Start them 120-degrees (1/3rd cycle) out of phase.
uint32_t CurrentStep[LED_NUM] = {0, TotalSteps / 3, (TotalSteps * 2) / 3};


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


  for (int i = 0; i < LED_NUM; i++)
    pinMode(LEDPins[i], OUTPUT);


  //initialize timer
  TCCR1A = 0;
  TCCR1B = 0;


  TCCR1B |= (1 << CS10);    // prescalar 1
  TIMSK1 |= (1 << TOIE1) | (1 << OCIE1A) ;
}


// Turn on all LEDs at timer overflow
ISR(TIMER1_OVF_vect)
{
  for (int i = 0; i < LED_NUM; i++)
    digitalWrite(LEDPins[i], HIGH);
}


// Turn off past LEDs at Compare
ISR(TIMER1_COMPA_vect)
{
  uint16_t time = TCNT1;
  uint16_t nextOCR1A = MaxBrightness + 1;
  uint16_t lowestBrightness = MaxBrightness + 1;
  for (int i = 0; i < LED_NUM; i++)
  {
    if (Brightness[i] < lowestBrightness)
      lowestBrightness = Brightness[i];


    if (time >= Brightness[i]) // The time to turn off has passed
    {
      digitalWrite(LEDPins[i], LOW);
    }
    else  // This LED turns off in the future
    {
      if (Brightness[i] < nextOCR1A)
      {
        nextOCR1A = Brightness[i];  // Find the earliest future time.
      }
    }
  }


  // If no brightnesses are in the future, use the lowest value
  if (nextOCR1A ==  (MaxBrightness + 1))
    nextOCR1A = lowestBrightness;


  OCR1A = nextOCR1A;
}


uint16_t BrightnessFromStep(uint32_t stepNumber)
{
  // Assume we are in the upward slope
  uint16_t brightness = stepNumber + MinBrightness;


  if (stepNumber > BrightnessRange)
  {
    // We are in the downward slope
    brightness = (TotalSteps - stepNumber) + MinBrightness;
  }


  return brightness;
}


void loop()
{
  uint32_t currentMillis = millis();
  static uint32_t lastUpdateMillis = 0;
  const uint32_t updateIntervalMillis = 100;


  if (currentMillis - lastUpdateMillis >= updateIntervalMillis)
  {
    // For each LED...
    for (int i = 0; i < LED_NUM; i++)
    {
      // Update the position in the cycle
      CurrentStep[i] = (CurrentStep[i] + 50) % TotalSteps;


      // Update the brightness
      Brightness[i] = BrightnessFromStep(CurrentStep[i]);
    }
  }
}