Dashboard Led dimming Using ATtiny13

So my old Renault 16 has a dimming switch to dim the Dashboard backlight. I am changing over from incandescent bulbs to LED's also because i wanted to change the color from Green to Blue.
Bulbs get their power through a low resistance potmeter, and share common Ground.
Now of course i want the LED's to be dimmable as well.
Now there are several ways of doing this. Using a pot to control the voltage of a PNP transistor was my first attempt, but getting the voltage division proved a bit to fiddly.
Then i tried using a LM317 regulator to provide a variable voltage, which showed promise, but then i thought to switch to PWM dimming, using an ATtiny13a
I use a PNP - transistor (BC557B) to open the power to the LED's a 10 K resistor gets GND from a PC817 Opto-coupler which is driven by the ATtiny.
Works fine, but i found that the resolution makes quite a big step between fully-off and just on. I know it also has something to do with the type of LED, but these are the blue ones i have for now and i still have plenty of them.

#define PWM_PIN 0
#define POT_PIN A3

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

void loop() {
  uint16_t val = analogRead(POT_PIN);
  val = map(val, 0, 1023, 0, 254); // never fully off
  analogWrite(PWM_PIN, val);
}

Simple enough.
Now i found this project, and i can manage 8 bit PWM like that, but increasing the resolution caused it to flicker due to slow speed.
this is the implementation of PWM on the ATtiny in the microcore, and i decided to combine these 2 things and came to this

#define POT_PIN A3
#define RESO (0xff >> 6)

volatile uint8_t i = 0;
volatile uint8_t pwmValH = 0;
volatile uint8_t pwmValL = 0;

void setup() {
  noInterrupts();
  DDRB |= (1 << PB0);

  TCCR0B = _BV(CS01);
  TIMSK0 |= (1 << TOIE0);
  interrupts();
}

void loop() {
  uint16_t val = (analogRead(POT_PIN));
  
  delay(1);
  pwmWrite(val);
  bright++;
}

void pwmWrite(uint16_t duty) {
  noInterrupts();
  pwmValH = (duty >> 8) & RESO;
  pwmValL = duty & 0xff;
  interrupts();
}


ISR(TIM0_OVF_vect) {
  i++;
  i &= RESO;
  if (i == pwmValH) {
    switch (pwmValL) {
      case 0:
        TCCR0A &= ~_BV(COM0A1); // stop pwm
        PORTB &= ~(1 << PB0); // set pin LOW
        break;
      case 255:
        TCCR0A &= ~_BV(COM0A1); // stop pwm
        PORTB |= (1 << PB0);  // set pin HIGH
        break;
      default:
        OCR0A = pwmValL;
        TCCR0A |= _BV(WGM00) | _BV(COM0A1); // start PWM
    }
  }
  else {
    TCCR0A &= ~_BV(COM0A1); // stop pwm
    if (i < pwmValH) {
      PORTB |= (1 << PB0); // set pin HIGH
    }
    else {
      PORTB &= ~(1 << PB0); // set pin LOW
    }
  }
}

which does provide me with a small enough step for minimum brightness, but it seems that on the crossover between the steps there is some inaccuracy. No i wonder if anybody knows how that could be helped, or has any other bright ideas.

Why not just read a pot and scale it to PWM values? If You can use Vcc for powering the LEDs things would be easier.
Please post links to the datasheets of the different LEDs. What is the forward voltage of the different LEDs?
In the old days red had some 2.5 volt, green roughmy the same but what about blue?

Why using an opto coupler? All is on the same GND. Opto isolation is 0,0.

BC557(B) is max 100mA. Better to use a Mosfet.

Use PWM at least 100Hz or more. With PWM you can drive from 0 to 99%.

Remove the delay.

Your PWM is more complex then needed.

#define outputPin x

uint8_t maxValue = 127;// 50% duty cycle
uint8_t pwmCount;

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

void loop(){
	if(pwmCount > maxValue) && digitalRead(outputPin)){
		digitalWrite(outputPin, low);
	}
	else if(!digitalRead(outputPin)){
		digitalWrite(outputPin, high);
	}
	pwmCount++;
}

About 3,2V and 20mA and more.

I cannot decipher this at all.but I'm gonna take a WAG that you may be referring to the non-linearity of the LED's fading .

If that is the case long ago @Grumpy_Mike posted this code to produce a linear fade for LED's:

/*
  Change brightness of LED linearly to Human eye
  32 step brightness using 8 bit PWM of Arduino
  brightness step 24 should be twice bright than step 12 to your eye.
  
  reply #11 by Grumpy Mike: http://forum.arduino.cc/index.php?topic=147818.0
*/


#include <avr/pgmspace.h>
#define CIELPWM(a) (pgm_read_word_near(CIEL8 + a)) // CIE Lightness lookup table function

/*
  5 bit CIE Lightness to 8 bit PWM conversion
  L* = 116(Y/Yn)^1/3 - 16 , Y/Yn > 0.008856
  L* = 903.3(Y/Yn), Y/Yn <= 0.008856
*/

const uint8_t CIEL8[] PROGMEM = {
  0,    1,    2,    3,    4,    5,    7,    9,    12,
  15,    18,    22,    27,    32,    38,    44,    51,    58,
  67,    76,    86,    96,    108,    120,    134,    148,    163,
  180,    197,    216,    235,    255
};

int brightness = 0;             // initial brightness of LED
int fadeAmount = 1;
int led1Pin = 9;

unsigned long startTime = 0;    // timing variables
const long interval = 100;


void setup()  {  
  pinMode(led1Pin, OUTPUT);     // declare pin 9 to be an output:
}

void loop()  {
  unsigned long currentTime = millis();
  if (currentTime - startTime >= interval)
  {
    startTime += interval;                         // increment timing sequence
    analogWrite(led1Pin, CIELPWM(brightness));     // set the brightness of pin 9:, 0-31, 5 bit steps of brightness
    brightness += fadeAmount;                      // change the brightness for next time through the loop:
    if (brightness == 0 || brightness == 31)       // reverse the direction of the fading at the ends of the fade:
    {
      fadeAmount = -fadeAmount ;
    }
  }
}

Using a logarithmic potentiometer is another solution.

Isn't that2 what i did in the first sketch ?

It might be but i don't think that is a real option. They are turned on with the lights of the car coming on.

I bought These a few years back for bulk, it says 1.8v forward voltage, but a real datasheet is a bit much to ask for that sort of price.

Sorry my mistake, i didn't post a schematic. (edit sorry i had a GND line wrong & forgot to connect RST to VCC and i had the LED connected wrong pff man how many mistakes one can make)

Agreed there, but my local shop does not have a P-channel mosfet, or a PNP power transistor that would suit this project. For automotive use, mosfets can be a bit fragile, particularly if you do welding on the car afterwards. I have ordered some TIP125's which will work quite nicely, buit for now i will have to do with what i have and that is about 100pcs of BC557, i can put a few in parallel if needed, or wait for the TIP125.

my PWM is combination of swPWM & hwPWM, but i like your simple solution and i will try it out. I didn't think of that one myself and it looks like a good idea !!

It seemed OK during daylight, but last night i saw some dropouts when turning the pot. It only occurred to me when walking out the door to go to work, that i could hook up the oscilloscope to see what is actually going. I used Fast hw PWM before and then there were clear dropouts.

Yes a lookup table ! i was considering to do this after i had at least the target resolution to what i (think) i need, which is 10-bit to switch between 'fully off' and 'just a little bit on'

I don't see how that improves my output resolution.

I want at least 10-bit PWM, how i convert from a pot to an output is not so much an issue, but more that the resolution doesn't provide me with a low enough dimming level (I think, i mean it's probably OK as it is, but i want that 10-bit or more)

A schematic would tell. The "jungle" of words was obviously hiding that.

Let the entire build be powered on that way. The L7805 will power some LEDs without problem. You can likely feed the LEDs from the microcontroller without OPTOs, without transistors. What do You say?

Why? The human eye will not notice that. Eventual none linearity could be seen but 10 bit PWM does not fix that.

I have something similar on my jeep. I needed to vary the led string to control the lights for driving at night...

I believe I used an IRLZ24 power mosfet. It's on an attiny85 and controls the mosfet via a resistor around 150 ohms on the gate. It's still working... I ran the prototype for a few months with no resistor...

Infineon_IRLZ24N_DataSheet_v01_01_EN-1228535.pdf

The tiny85 ran off a 5V regulator and the strip was at 12V and the mosfet completed the ground to turn on the strip... it only drew three amps max ... I thought it needed a heat sink but it ran quite cool.

It's still in the vehicle, but the software and hardware files were lost or I'd post the schematic.

A couple of years ago one of the engineering sites were talking about how the automotive industry has been changed by the advent of low cost power mosfets...

They stated some number like 8B devices for that year ... that's a lot of devices ...

Good luck

Fun project.

:smiley_cat:

How many is some?

L7805 in TO220 package has a thermal resistance to ambient 50°C/W. 9V drops (14V for full battery) and 10 * 20mA = 200mA = 0,2A. 0,2A * 9V = 1,8W * 50°C/W = 90°C + ambient temperature 25°C or more gives 115°C. Why the power from the leds taking from a LDO? Why not from the 12V system?

Look to the array. It is close near to a logarithmic scale. 1, 2, 4, 8, 16, 32, 64, .... Lowest resistor value go slow higher, the wider on te scale the faster.

Has a Hfe 4. With 20mA from the µC that is 80mA current for the leds. Not much.

I miss 100n behind the L7805 and between VCC and GND near the ATtiny.

Using automotive mosfet?

It is about that final step. say PWM 0 is off, then PWM 1 at 8-bit is just too bright, whereas at 10-bit is is a lot closer to what i want.
I am pretty sure i have explained this in at least 2 ways already,

that first sketch ?


#define PWM_PIN 0
#define POT_PIN A3

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

void loop() {
  uint16_t val = analogRead(POT_PIN);
  val = map(val, 0, 1023, 0, 254); // never fully off
  analogWrite(PWM_PIN, val);
}

i was actually planning to use TO-92 package, but i guess a to-229 can yes.

well no. If it was to much power for a BC557, then for sure it is to much for a GPIO pin. I need about 20mA to fully light the LED, so transistors are a must. and i only need 1 Opto-coupler so i don't really see any advantage.
I have the parts either way, but what is the advantage ?

I did mention that the bulbs share common GND on the dash PCB with some of the gauges ? and that a P-channel is hard to come by..?

It is, and they are all shielded and installed after all the welding is done. This is a 43 year old Renault... but power-transistor or mosfet, P-channel is preferred.

Thinking about it it could go for an N-channel and separate GND everywhere, the bulbs are not going in the fitting anymore anyway. I don't really want to add wires to the unit that will need more connectors, but with convincing arguments i could be persuaded. Still this is all about the hardware, and with the LED's i have i want either 10-bit PWM or do it with a LM317, which means i only need to get the perfect pot-meter / variable resistance..

yes but it's still based on an 8-bit output, i want it to start at 0.25 (or a quarter of 1)

I always put 1uF, i don't put more than 10uF or it will break. Any component can do with an extra 100nF ceramic between GND & VCC, but it is not in the ATtiny datasheet as far as i know.

Is N-Mosfet to drive to ground. That didn't work in TS schematic. TS must seen how the dashboard is made. Switched to high or switched to low.

IRLZ24 @3A Rdson 0.06Ω = 0,18V * 3A = 0,54W. Thermal 62°C/W. 0,54W * 62°C/W = 33°C.

Use of a 555 timer would be simple enough.

Your eyes not see the difference. Do you use a 10 turn pot? Your fingers can't turn a pot 300° (mechanical travel) in 1023 steps or 0,29°/step.

It is a good practice to put 100n close as possible to any IC. For ATmega 2. One between VCC and GND and 1 between AVCC and AGND. You have never too much disconnection.

Yeah i thought of that as well, but aren't the extreme duty cycles are hard to achieve ?
I figured it would be fiddly since you do need some minimum resistance between the capacitor and pin 7 and pin 7 and Vcc at all time, but how much and with what pot ? i mean the capacitor size is pretty much the smaller the higher the frequency. The main advantage of doing it digitally would be that it can be done with any size pot-meter pretty much, and i do have some, 1K, 2K 50K 100K 220K 250K etc, but for my ideal LM317 circuit i would want a 5K pot.

Sorry but your brain is missing the point here.
the difference between

analogWrite(pin, 0);

and

analogWrite(pin, 1);

is too big.

like on the example schematic for a W5500 ?
We are getting spiritual here, we want more of nothing !

Well the thing is that tried something simple like this

#define PWM_PIN 0
#define POT_PIN A3

void setup() {
  pinMode(PWM_PIN, OUTPUT);
  //DDRB |= (1 << PB0);
}

void loop() {
  static uint16_t i = 0;
  uint16_t val = analogRead(POT_PIN);
  digitalWrite(PWM_PIN, i < val);
  i++;
  i = i % 1023;  // val is maximum 1023, for the pin to stay HIGH, i always has to be below that.
}

which is even steps, but waaayyy to slow.
Now doing a modulo is really slow of course, but when i changed that to '&' (which doesn't have a fully HIGH state, minor detail)it is still waaay to slow, so it must be reading the ADC that's so slow, hence the whole interrupt based system, where you can write to the pin in the background, and read from the ADC as you please.
The delay is to reduce the amount of reads, it does nothing else. but writing to the pin and reading from the ADC both in the main loop doesn't work as far as i can tell.

Mine uses a photo transistor to detect ambient light and 'automagically' adjusted the lights...

So I needed an adc and a simple software way to control it..

I have a bunch of the tiny85s and use them for many things, even where a different way would be lower cost up front ... but I usually only make one or two...

:smiley_cat:

I thought it would need one... I'm assuming it's not getting too hot because the pwm rate is pretty slow for dim.

Thanks for the computations....

:smiley_cat:


Works more then 10 years without problems.

Other possibility.

1 Like

Hehe, well that is a way, of course the R16 TX had the dashboard dimming function already, which was quite something fancy for European cars in those days, and it used a Pot, so i'll stick with that.

Really like that !! partly analog.

But continuing on the path i was on, i figured maybe the inconsistencies were due to switching the PWM on and off all the time, so i tried this

#define POT_PIN A3
#define RESOLUTION 10  // bit
#define RESO (0xff >> (16 - RESOLUTION))

volatile uint8_t i = 0;
volatile uint8_t pwmValH = 0;
volatile uint8_t pwmValL = 0;

void setup() {
  noInterrupts();
  DDRB |= (1 << PB0);
  OCR0A = 255;
  TCCR0B = _BV(CS01);  // PWM frequency = (F_CPU/256) / 8
  TIMSK0 |= (1 << TOIE0);
  TCCR0A |= _BV(WGM00) | _BV(WGM01) | _BV(COM0A1);
  interrupts();
}

void loop() {
  uint16_t val = analogRead(POT_PIN) << (RESOLUTION - 10);
  delay(10);
  pwmWrite(val);
}

void pwmWrite(uint16_t duty) {
  noInterrupts();
  pwmValH = (duty >> 8) & RESO;
  pwmValL = duty & 0xff;
  interrupts();
}


ISR(TIM0_OVF_vect) {
  if (i == pwmValH) {
    OCR0A = pwmValL;
  }
  else if (i < pwmValH) {
    OCR0A = 255;
    PORTB |= (1 << PB0);
  }
  else {
    OCR0A = 0;
    PORTB &= ~(1 << PB0); // set pin LOW
  }
  i++;
  i &= RESO;
}

And that does seem to work, still setting the PIN to it's state manually on the extremes but leaving the PWM running.
tested it with a blink between the max and max -1, and got that stable with 12-bit. Tried 16 bit, but that was to slow and it started flickering. Tried to increase the PWM frequency, but i think that the ISR became a bit to slow,
Anyway now i can create a table in progmem to convert my 10-bit input to a scaled 12-bit output.
The more i think about it the more i think the whole thing is serious overkill. Anyway, many thanx for the help from all. Some interesting ideas and solutions that are all greatly appreciated.

And in that last part i was wrong as i manage to illustrate to myself with this code

#define POT_PIN A3
#define RESOLUTION 16  // bit
#define RESO (0xff >> (16 - RESOLUTION))
#define MIN_BRIGHT 0xffff

volatile uint8_t i = 0;
volatile uint8_t pwmValH = 0;
volatile uint8_t pwmValL = 0;

void setup() {
  noInterrupts();
  DDRB |= (1 << PB0);
  OCR0A = 255;
  //TCCR0B = _BV(CS01);  // PWM frequency = (F_CPU/256) / 8
  TCCR0B = _BV(CS00);  // PWM frequency = (F_CPU/256) / 1
  TIMSK0 |= (1 << TOIE0);
  TCCR0A |= _BV(WGM00) | _BV(WGM01) | _BV(COM0A1);
  interrupts();
}

void loop() {
  static uint16_t val = MIN_BRIGHT;
  delay(500);
  pwmWrite(val);
  val--;
  if (val < MIN_BRIGHT - 10) val = MIN_BRIGHT;
}

void pwmWrite(uint16_t duty) {
  noInterrupts();
  pwmValH = (duty >> 8) & RESO;
  pwmValL = duty & 0xff;
  interrupts();
}


ISR(TIM0_OVF_vect) {
  if (i == pwmValH) {
    OCR0A = pwmValL;
  }
  else if (i < pwmValH) {
    OCR0A = 255;
    PORTB |= (1 << PB0);
  }
  else {
    OCR0A = 0;
    PORTB &= ~(1 << PB0); // set pin LOW
  }
  i++;
  i &= RESO;
}

which does show an increased brightness from 'fully-off'
So 16-bit it is !!