Control LED via PWM - linear fading based on duty cycle?

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I know this seems to be one of the simplest projects that people dive into when playing around with AVR's - however I'm not sure if it is just me or not, but at duty cycle's above around 20% the LED looks like it is full-on to me. from 0% to 20% I get the nice fading look to the LED, but beyond that no real difference.

I'm using a real-time timer (contained in the structure time) to change the duty cycle of the PWM (by changing OCR1A) but it seems to go full brightness at 20% duty cycle - irregardless of frequency.

I was using an ATmega128 on an STK501 and using OC1A pin output of the timer to control the LED fading. The ATmega128 is sourcing the current and a current limiting 220 Ohm resistor is in place. Any ideas?

Below is my main.c which controls the PWM.. any glaring issues that might be causing this non-linearity on brightness vs pwm duty cycle?

/******************************************************************************
Project Name:	led_pwm
File Name:		main.c
Version:		1.0
Description:	Control an LED brightness with PWM
Author:			Paul C. Murdock
Change Log:		27-Dec-2006 / File Created
******************************************************************************/
#include "main.h"

#pragma interrupt_handler timer0_handler:iv_TIMER0_COMP 

time_struct time;

void timer0_handler()
{
	if (0 == --time.t_count)
	{
		if (60 == ++time.sec)
		{
			if (60 == ++time.min)
			{
				if (24 == ++time.hour)
					time.hour = 0x00;
				
				time.min = 0x00;
			}
			
			time.sec = 0x00;
			
		}
		
		// reset t_count
		time.t_count = 1000;
	}
	
}

/* This putchar works with the M128 UART0. It will work with other devices
 * if their UART registers use the same names.  If the UART IO locations
 * are different from the M2560, then you will also have to change the
 * #include header file statement.
 *
 * For device with multiple UARTs and if you want to use a different UART, 
 * change the IO register names as appropriate.
 */
int putchar(char c)
{
    /*if (_textmode && c == '\n')
        putchar('\r');*/
    /* Wait for empty transmit buffer */
    while (!(UCSR1A & (1<<UDRE1)));                     
    /* Putting data into buffer , sends the data */
    UDR1 = c;  
    return c;
}


void init_device()
{
	CLI();
	
	// Setup Ports
	DDRE = 0xFF;
	PORTE = 0x00;
	DDRB = 0xFF;
	PORTB = 0x00;
	DDRD = 0xFF;
	
	// Enable Timer0/Counter - 8bit
	// Choose waveform type - CTC Mode
	TCCR0 |= (1<<WGM01);
	// Choose OC0 behavior (PIN PB4)
	TCCR0 |= (1<<COM00);
	// Timer/Counter Control Register - Turn ON timer
	TCCR0 |= (1<<CS01)|(1<<CS00);
	// Timer/Counter Interrupt Mask - overflow enable for Timer/Counter0
	TIMSK |= (1<<OCIE0);
	// Setup timer to Compare/Math OCR0 at 72 for 1ms interrupt
	OCR0 = 0xE7;	
	
	// Enable Timer/Counter1 - 8-bit PWM
	// Choose waveform type - 8bit PWM Phase Correct
	TCCR1A |= (1<<WGM10);
	// Choose Timer Clock - determines PWM frequency
	TCCR1B |= (1<<CS12);  
	// Choose OC1A pin functionality (PB5) (determines duty cycle)
	TCCR1A |= (1<<COM1A1); // Clear OC1A on compare match
	// Choose default of 50% duty cycle to start out with
	OCR1A = 0x00;
	
	// Enable debugging USART1
	// Disable while setting baud rates,etc
	UCSR1B = 0x00;
	UCSR1A = 0x00;
	// Set Frame Format - 8-N-1
	UCSR1C |= (1<<UCSZ11)|(1<<UCSZ10);
	// Set baud rate = 3 -> 115200 @ 7.3728 MHz
	UBRR1H = 0x00;
	UBRR1L = 0x03;
	// Enable Transmitter
	UCSR1B |= (1<<TXEN1);
	
	// Clear System Real-Time Clock
	time.sec = 0x00;
	time.min = 0x00;
	time.hour = 0x00;
	time.t_count = 1000; // 1000 ms per second
			
	SEI();

}


void main(void)
{
	char temp_sec;
	char temp_msec;
	int temp;
	
	// temp time to look for
	temp_sec = 0;
	temp_msec = 50;
	temp = 0;
	
	init_device();

	while (1)
	{
		if (time.sec == temp_sec) {
			printf("%02d:%02d:%02d\r", time.hour, time.min, time.sec);
			if (60 == ++temp_sec)
				temp_sec = 0;
			PORTE ^= (1<<PE0);

		}

		if (!(time.t_count % temp_msec) && (temp != time.t_count)) {
			temp = time.t_count;
			
			OCR1A++;
			
		}		
		
	

	}
}

[/code]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pmurdock wrote:
I know this seems to be one of the simplest projects that people dive into when playing around with AVR's - however I'm not sure if it is just me or not, but at duty cycle's above around 20% the LED looks like it is full-on to me. from 0% to 20% I get the nice fading look to the LED, but beyond that no real difference.

brightness at 20% duty cycle - irregardless of frequency.

Yes LEDs need very small pulses, especially the hi efficiency types (modern leds)
Use more resolution and lets say a frequency of 100Hz. Use a 16bit timer.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I use an 8 byte table with 8 bit pwm... gives 8 'log brightness' levels
pwmdat[8]={0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,0xff};

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Human pmurdock,

Your observation is correct.
Your imaging sensor has a somewhat logaritmic response to light.
For us, the Borg, a change from 80% PWM to full drive is seen as a 25% increase of intensity.
For your inferior imaging-sensor, the difference is hardly detectable.
Human Bob uses a table to correct for that inferiority.

Your hearing device has a simular design-flaw. 100 Watt electric power to a loudspeaker will not give a 100 times stronger sound-impression than a 1 Watt amplifier.

The human species is not efficient.

When you are assimilated (and you will !!), your imaging-sensor, sound-sensor and processor will be replaced with our superior designs. Amongst other improvements, of course.

Resistance is futile

The Borg

A GIF is worth a thousend words   She is called Rosa, lives at Mint17.3 https://www.linuxmint.com/

Dragon broken ? http://aplomb.nl/TechStuff/Dragon/Dragon.html for how-to-fix tips

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To get a reasonable fade you need a minimum of 12 bit PWM, plus a nonlinear lookup table to map an 8-bit brightness value to the 12 bit PWM value.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yep - ye olde human eyeball is not linear. That helps us to have such an enormous dynamic range - from the flicker of a distant candle to full sunlight.

http://www.icaen.uiowa.edu/~aip/... is a good read...

8bit of PWM isn't too bad - but the steps will be observable especially at the low end where the human eye is pretty good at detecting small changes.

george.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Think cameras. A full stop reduction is half the previous brightness; a half stop is (1/root-2) of the previous brightness. So for an 8 bit PWM, equal half-stop steps would be:
255, 180, 128, 90, 64, 45, 32, 23, 16, 12, 8, 6, 4, 3, 2, 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks guys for all the input! It does make sense and the logarithmic response is indeed what I was needing to know.

cheers!
Paul