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();
}
}