Attiny85 output

Hello,

I would like to light a model car with several LED's in a program loop with the Attiny. It's something really simple like headlights on - wait a second - blinkers on - wait a second - blue and red strobes on the roof on - wait 5 seconds - blinkers off - wait a minute - everything off.

The only difficulty is that I need several LED alight at the same time, which is too much for the Attiny. Which output driver would you recommend? I need 4, maybe 5 output channels that can run at the same time and drive 2-3 led each. I drive my LED with roughly 6mA to 10mA.

I checked the ULN2801A, but that guy seems to be a little bit overkill, also a little bit too big (mm size) for what I have in mind. If a similar bit exists with less channels and legs and millimeters, that would be great.

Is there a Attiny-serial-communication-understanding output driver chip?

Alternatively, if there is a pre-soldered board with sufficient small outline size for playing with the Attiny and that has ~20mA output per pin instead, I would take it, too.

About me: I do have some experience with the Attiny85, but it's like 10 years ago that I made my last project with it. Programming, electronic, no problem, but recent developments in this special niche - no clue :slight_smile:

Maybe logic level MOSFET such as SI2300. They are for much larger current, but they are quite small in size.

Thanks for your answer, flashko... I don't feel that individual transistors would be the solution. I'd need 5 of them plus birdseed around them... and my space is quite constrained. It is a solution of last resort for sure, thanks for the suggestion!

I just read the datasheet you posted, indeed, that could be one possible solution. That's one good mosfet!

No, not at all. Each pin can safely provide 20 mA, but the ATtiny package is limited to 200 mA total, absolute maximum.

15 LEDs at 10 mA each is reasonable.

These can be very small if you use surface mounted components. You can do this on the underside of strip board. This picture shows the mounting of a FET, other "bird seed" like resistors can be mounted either between tracks, or between cuts in the track made by a sharp scalpel or knife.

You will also need a pair of tweezers.

1 Like

A hand-operated new/sharp 4mm drill bit works best for me.
Put in the hole, and slowly turn.
Leo..

This is a flasher driver I made for model airplanes. It is based on an Attiny and a ULN2003.
I also wanted it small, so decided to bite the bullet and use SMD components. This is hand soldered.

2 Likes

Thanks a lot for your answers,
I think between the hand SMD proposal and the fact that the Attiny can drive 20mA on each pin (instead of 20mA in total like I thought) I think this topic is solved.

Thanks a lot everyone!

hmeijdam,

Could you share your platine layout? I think if I decide for the ULM2803A, this is the good layout

Here you go with the gerber file. in this version I added the led series resistors on the PCB.

4ch driver.zip (9.5 KB)

The RC filter is 20 Ohm for R and 470uF for C.

1 Like

Did you know you can get surface mounted ULM2803A and still solder them to normal strip board if you cut the tracks between the holes in strip board. Like this:-

hmeijdam,
How did you program the smd attiny? For the normal it's easy, you insert it in the breadboard and connect cables as needed, but where do you insert the attiny before soldering it? Or did you program it directly on the target ?

I use a programming clip connected to a usbasp adapter

in this case directly on the target, before the capacitor is soldered, as the capacitor gets a little in the way of the clip.

1 Like

I've just seen that there is no ESD protection on your layout. Did you ever face difficulties because of that?

I am thinking about adding a zener but not sure if I should.

Never experienced such a problem.

The ports have a built in diode to VCC as some form of ESD protection.

1 Like

That's the issue: when the VCC is being "filled" with excess voltage, someone needs to consume it in order to protect the chip.

I guess your C has transient behavior that will do at least a part of that... I will start with exactly your layout then :slight_smile:

Thanks for your time man!

Yes that someone is the power supply.

It is nothing to do with C.

Correct the C, as part of the R/C filter, will absorb spikes. Though intended to smoothen undervoltage because of the flashes from the power diodes. In my plane i drive them without a resistor in very short pulses. That way the diodes last about 2 flying seasons.

1 Like

Final feedback!

I used your free Pin2 as weak output, too. Thanks to your super-compact design I was able to hide the electronic in this model.

Here you go. Thank you!

Ork Police

#include <TinySoftPwm.h>

/*
Uses 5 PWM channels to control 5 lights:
2x red, 2x blue and 1x white
They shall approximate an 80's US police car with soft on/off cycles like light
bulbs with turning mirrors. For that, each channel is controlled by a state 
machine and has a start delay. For the moment, I declared just one LED_OFF_TIME, 
one LED_ON_TIME and one LED_CYCLE_TIME time for all of them.
If individual time table shall be used, the state machine would have to be 
copied and pasted (220 bytes)

The out-Pin(s) are declared in "librarie/TinySoftPwm/TinySoftPwm.h".
As it is bad style to edit a common library for one specific project, I undeclare 
the unused pin here and not
in the .h file, despite the examples recommending to do it there.

I use "micros()" instead of a timer ISR to cycle the PWM's because for lighting 
stuff, we don't need a super-exact pwm.

*/  

/* not using number 5 here because that's the reset pin */
#undef TINY_SOFT_PWM_USES_PIN5

/* pin numbers */
#define LED_RED_1 0 
#define LED_BLUE_1 1
#define LED_WHITE 2
#define LED_RED_2  3
#define LED_BLUE_2 4

// complete cycle time for soft blink
#define LED_OFF_TIME 33
#define LED_ON_TIME 2
#define LED_CYCLE_TIME 10

// complete cycle times for triple flash
#define LED_FLASH_TIME 1
#define LED_FLASH_BETWEEN_TIME 7
#define LED_FLASH_OFF_TIME 50

// brightness at the end of each step
#define LED_BRIGHT_OFF 0
#define LED_BRIGHT_LOW 30
#define LED_BRIGHT_HIGH 180
#define LED_BRIGHT_FLASH 255

// Start delay for each individual LED
#define LED_RED_1_DELAY 0
#define LED_BLUE_1_DELAY 25
#define LED_WHITE_DELAY 18
#define LED_RED_2_DELAY 17
#define LED_BLUE_2_DELAY 40

// If you want the LED's to be slightly out of tact
#define LED_RED_1_SLOW 1
#define LED_BLUE_1_SLOW 1
#define LED_WHITE_SLOW 2
#define LED_RED_2_SLOW 0
#define LED_BLUE_2_SLOW 0

/* Defining the states for the LEDs*/
#define LED_PRESTART 0
#define LED_OFF 1
#define LED_START_GOING_ON 2
#define LED_GOING_ON 3
#define LED_ON 4
#define LED_START_GOING_OUT 5
#define LED_GOING_OUT 6
#define LED_TRIPLE_1 7
#define LED_TRIPLE_2 8
#define LED_TRIPLE_3 9
#define LED_TRIPLE_OFF_1 10
#define LED_TRIPLE_OFF_2 11

void going_out(uint32_t timer, int8_t led, uint32_t cycle_time)
{
  uint8_t pwm = 0;
  pwm = int8_t(LED_BRIGHT_HIGH - ((timer * (LED_BRIGHT_HIGH - LED_BRIGHT_LOW)) / cycle_time) );
  TinySoftPwm_analogWrite(led, pwm);
}

void going_on(uint32_t timer, int8_t led, uint32_t cycle_time)
{
  uint8_t pwm = 0;
  pwm = int8_t( ((timer * (LED_BRIGHT_HIGH - LED_BRIGHT_LOW)) / cycle_time) + LED_BRIGHT_LOW);
  TinySoftPwm_analogWrite(led, pwm);
}

void deactivate_pin(int8_t led)
{
    pinMode(led, INPUT);      // Set port as INPUT to save energy
    digitalWrite (led, LOW);  //
}

void reactivate_pin(int8_t led)
{
  pinMode(led, OUTPUT);
}

int8_t triple_flash_state(int8_t state, uint32_t* timer, int8_t led, uint8_t individual_time, uint32_t start_delay)
{
  switch(state)
  {
    case LED_OFF:
    {
      /* Wait until the off-time is over */
      if (*timer > LED_FLASH_OFF_TIME) 
      { // time is over, full on and go to next state
        state = LED_TRIPLE_1;
        *timer = 0;
        TinySoftPwm_analogWrite(led, LED_BRIGHT_FLASH);
      }
      break;
    }
    case LED_TRIPLE_1:
    {
      /* Wait state to continue running the LED full power*/
      if (*timer > LED_FLASH_TIME + individual_time) 
      { // flash time over, turn off and go to next state
        *timer = 0;
        TinySoftPwm_analogWrite(led, LED_BRIGHT_OFF);
        state = LED_TRIPLE_OFF_1;
      }
      break;
    }
    case LED_TRIPLE_OFF_1:
    {
      /* Wait until the off-time is over */
      if (*timer > LED_FLASH_BETWEEN_TIME) 
      { // time is over, full on and go to next state
        state = LED_TRIPLE_2;
        *timer = 0;
        TinySoftPwm_analogWrite(led, LED_BRIGHT_FLASH);
      }
      break;
    }

    case LED_TRIPLE_2:
    {
      /* Wait state to continue running the LED full power*/
      if (*timer > LED_FLASH_TIME + individual_time) 
      { // flash time over, turn off and go to next state
        *timer = 0;
        TinySoftPwm_analogWrite(led, LED_BRIGHT_OFF);
        state = LED_TRIPLE_OFF_2;
      }
      break;
    }
    case LED_TRIPLE_OFF_2:
    {
      /* Wait until the off-time is over */
      if (*timer > LED_FLASH_BETWEEN_TIME) 
      { // time is over, full on and go to next state
        state = LED_TRIPLE_3;
        *timer = 0;
        TinySoftPwm_analogWrite(led, LED_BRIGHT_FLASH);
      }
      break;
    }

    case LED_TRIPLE_3:
    {
      /* Wait state to continue running the LED full power*/
      if (*timer > LED_FLASH_TIME + individual_time) 
      { // flash time over, turn off and go to next state
        *timer = 0;
        TinySoftPwm_analogWrite(led, LED_BRIGHT_OFF);
        state = LED_OFF;
      }
      break;
    }

    case LED_PRESTART:
    {
      /* Wait until prestart delay is over, then switch to "going on"*/
      if (*timer >= start_delay) 
      {
        state = LED_TRIPLE_1;
        *timer = 0;
        TinySoftPwm_analogWrite(led, LED_BRIGHT_OFF);
      }
      break;
    }      
  } // End of Blue 1 State Machine
  return (state);
}


/******************************************************************************/
/*LED soft blink state machine: This State Machine let's an LED ramp up it's 
brightness, keeps the top for a while, and ramps it down again.
 
Parameters: 

int8_t state goes in and returns. It has to be persisted somewhere else. The 
state normally should start with 0, which is the LED prestart wait state. 

uint32_t *timer: comes as pointer. It counts timer steps that a given part of 
the code is already being used, and measures those against the state times given 
in the #define section.

int8_t led is the led-out-pin number that shall be used. Note that for this code, 
the out-pin has to be declared as soft-pwm pin, by importing TinySoftPWM.h and 
undefining all pins you don't want to be taken over by it, for example if you 
want to save your reset pin:
#undef TINY_SOFT_PWM_USES_PIN5

uint8_t individual_time is a (small) time delay you can add per LED. It is 
added to the full-on-time of that specific LED, to achieve that it is slightly
out-of-sync with the rest. Like that you can emulate rotating mirror lamps,
those were never fully in sync. If you prefer to have all LEDs fully in Sync, 
give it a 0 here.

uint32_t start_delay:
If you start this state machine with state 0, the start_delay is the number
of timer steps that it stays in the off-state before really launching the LED 
blink cycle.
*/
/***********************************************************/
int8_t soft_blink_state(int8_t state, uint32_t* timer, int8_t led, uint8_t individual_time, uint32_t start_delay)
{
  switch(state)
  {
    case LED_OFF:
    {
      /* Wait until the off-time is over */
      if (*timer > LED_OFF_TIME) 
      {
        //reactivate_pin(led);
        state = LED_START_GOING_ON;
      }          
      break;
    }
    case LED_START_GOING_ON:
    { // sets the start values for the LED brightness ramp up
      state = LED_GOING_ON;    
      *timer = 0;
      TinySoftPwm_analogWrite(led, LED_BRIGHT_LOW);
      break;
    }
    case LED_GOING_ON:
    {
      if (*timer < LED_CYCLE_TIME) 
      {
        going_on(*timer, led, LED_CYCLE_TIME);
      }
      else
      { // time is over, full on and go to next state
        state = LED_ON;
        *timer = 0;
        TinySoftPwm_analogWrite(led, LED_BRIGHT_FLASH);
      }
      break;
    }
    case LED_ON:
    {
      /* Wait state to continue running the LED full power*/
      if (*timer > LED_ON_TIME + individual_time) 
      {
        state = LED_START_GOING_OUT;
      }
      break;
    }
    case LED_START_GOING_OUT:
    {
      state = LED_GOING_OUT;
      *timer = 0;
      TinySoftPwm_analogWrite(led, LED_BRIGHT_HIGH);
      break;
    }
    case LED_GOING_OUT:
    {
      going_out(*timer, led, LED_CYCLE_TIME);
      if (*timer >= LED_CYCLE_TIME) 
      { // End of cycle, turn off and next state
        state = LED_OFF;
        *timer = 0;
        TinySoftPwm_analogWrite(led, LED_BRIGHT_OFF);
        //deactivate_pin(led);
      }
      break;
    }
    case LED_PRESTART:
    {
      /* Wait until prestart delay is over, then switch to "going on"*/
      if (*timer >= start_delay) 
      {
        state = LED_GOING_ON;
        *timer = 0;
        TinySoftPwm_analogWrite(led, LED_BRIGHT_OFF);
      }
      break;
    }      
  } // End of Blue 1 State Machine
  return (state);
}

/***********************************************************/
/*LED Red 1 handler*/
/***********************************************************/
void red_1_handler()
{
  static int8_t red_1_state = LED_PRESTART;
  static uint32_t red_1_timer = 0;
  red_1_timer++;
  red_1_state = soft_blink_state(red_1_state, &red_1_timer, LED_RED_1, LED_RED_1_SLOW, LED_RED_1_DELAY);

} // End of Red 1


/***********************************************************/
/*LED Red 2 handler*/
/***********************************************************/
void red_2_handler()
{
  static int8_t red_2_state = LED_PRESTART;
  static uint32_t red_2_timer = 0;
  red_2_timer++;
  red_2_state = soft_blink_state(red_2_state, &red_2_timer, LED_RED_2, LED_RED_2_SLOW, LED_RED_2_DELAY);
} // End of Red 2 

/***********************************************************/
/*LED Blue 1 handler*/
/***********************************************************/
void blue_1_handler()
{
  static int8_t blue_1_state = LED_PRESTART;
  static uint32_t blue_1_timer = 0;
  blue_1_timer++;
  blue_1_state = soft_blink_state(blue_1_state, &blue_1_timer, LED_BLUE_1, LED_BLUE_1_SLOW, LED_BLUE_1_DELAY);
}

/***********************************************************/
/*LED Blue 2 handler*/
/***********************************************************/
void blue_2_handler()
{
  static int8_t blue_2_state = LED_PRESTART;
  static uint32_t blue_2_timer = 0;
  blue_2_timer++;
  blue_2_state = soft_blink_state(blue_2_state, &blue_2_timer, LED_BLUE_2, LED_BLUE_2_SLOW, LED_BLUE_2_DELAY);
} // End of Blue 2 

/***********************************************************/
/*LED white handler*/
/***********************************************************/
void white_handler()
{
  static int8_t white_state = LED_PRESTART;
  static uint32_t white_timer = 0;
  white_timer++;
  white_state = triple_flash_state(white_state, &white_timer, LED_WHITE, LED_WHITE_SLOW, LED_WHITE_DELAY);
}



/* setup is called exactly once. Here I just need to set up the softPWM. */
void setup()
{ 
  // See page 37 in the datasheet: Disabling unused peripherals
  // disable ADC
  ADCSRA &= ~(1<<ADEN); 
  // Shut down clock to ADC pg38
  PRR |= 1 << PRADC;
  // disable analogue comparator pg109
  ACSR |= 1 << ACD; 
  // Disable digital input buffers for analogue inputs pg121
  DIDR0 |= 1 << AIN1D;
  DIDR0 |= 1 << AIN0D;
  // disable watchdog timer  pg45
  WDTCR &= ~(1<<WDE); 
  // Shut down clock to Timer 0, pg38
  PRR |= 1 << PRTIM0; 

  // Enable fast internal 16  MHz PLL clock
  //PLLCSR |= 1 << PLLE;
  // Set Clock internal division to 1
  //CLKPR |= 1 << CLKPCE;
  //CLKPR = 0;
  
  TinySoftPwm_begin(128, 0); /* 128 x TinySoftPwm_process() calls before overlap (Frequency tuning), 0 = PWM init for all declared pins */
}
/* central loop */
void loop()
{
  static uint32_t StartUs=micros();
  static uint32_t StartMs=millis();

  /***********************************************************/
  /* Call TinySoftPwm_process() with a period of 7 us       */
  /* The PWM frequency = 128 x 7 ~ 0.89 ms -> F ~ 1,1kHz      */
  /* 128 is the first argument passed to TinySoftPwm_begin() */
  /***********************************************************/
  if((micros() - StartUs) >= 7)
  {
    /* We arrive here every 7 microseconds */
    StartUs=micros();
    TinySoftPwm_process(); /* This function shall be called periodically (like here, based on micros(), or in a timer ISR) */
  }
  
  /*************************************************************/
  /* Increment state machine with a period of 1 ms */
  /*************************************************************/
  if((millis()-StartMs) >= 5)
  {
    /* We arrive here every millisecond */
    StartMs=millis();
    red_1_handler();
    red_2_handler();
    blue_1_handler();
    blue_2_handler();
    white_handler();
    
  }


}



Cool, that works nicely that way. in the meantime I have updated my design to be 100% SMD. Now the capacitor of the RC filter is no longer interfering with my program clip and the led series resistors are integrated on the board


4ch driver V2.zip (8.9 KB)