Hi,
I've been assigned a task to dim LEDs using arduino with some conditions;
I've to dim 3 leds using arduino timers
Can't use PWM mode or analogue write function i.e have to generate my own variable duty cycle using timers and its registers.
Should avoid hard coded numbers to allow scalability in code. (use formulas or equations)
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;
}
}
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.
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.
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.
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]);
}
}
}