233 lines
6.4 KiB
C++
233 lines
6.4 KiB
C++
// Features
|
|
|
|
// #define FEAT_UNDERVOLTSLEEP
|
|
// Activates VLM Feature of Tiny10 (Voltage Level Monitoring) and sets it to the lowest value Vcc=2.5V
|
|
// In 3V supply scenarios (2 AG13/LR44 button cells in serial) this makes no sense because Vcc almost immediately drops below 2.5V
|
|
|
|
#define FEAT_STANDBY
|
|
// Activates PB1 and PB2. PB2 is the standby button, pull it to ground to activate deep sleep or wake up. Its pullup resistor is enabled.
|
|
// PB1 is set to high when awake and low when asleep. Can be used to disable external hardware during sleep mode
|
|
|
|
// Color values
|
|
#define R_STATIC 110 //80
|
|
#define G_HIGH 55 //40
|
|
#define B_STATIC 0 // 0
|
|
#define G_BURN 46 //34
|
|
#define G_FLICKER 43 //31
|
|
#define G_FLUTTER 35 //49 //25
|
|
|
|
// Timings
|
|
#define T_ON 5
|
|
#define T_BURN 4
|
|
#define T_FLICKER 3
|
|
#define T_FLUTTER 2
|
|
|
|
#ifndef F_CPU
|
|
#define F_CPU 8000000UL
|
|
#endif
|
|
|
|
#include <avr/io.h>
|
|
#include <avr/sleep.h>
|
|
#include <avr/interrupt.h>
|
|
|
|
extern "C" {
|
|
#include "ws2812_config.h"
|
|
#include "light_ws2812.h"
|
|
}
|
|
|
|
struct cRGB led[1];
|
|
|
|
volatile uint8_t timertick = 0;
|
|
|
|
void delayms(uint16_t ms);
|
|
|
|
|
|
#ifdef FEAT_STANDBY
|
|
void buttonrelease() {
|
|
while ((PINB & (1<<PB2)) == 0) { // Wait 50ms per check until PB2 goes up again (pulled low by pressed button)
|
|
delayms(50);
|
|
}
|
|
delayms(50); // Wait additional 50ms to skip released button bouncing
|
|
}
|
|
|
|
void checksleep() {
|
|
if ((EIMSK & (1<<INT0)) == 0) {
|
|
uint8_t tempr = led[0].r;
|
|
uint8_t tempg = led[0].g;
|
|
uint8_t tempb = led[0].b;
|
|
|
|
led[0].r = 0; led[0].g = 0; led[0].b = 0;
|
|
ws2812_setleds(led,1);
|
|
PORTB &= ~(1<<DDB1); // Deactivate LED Power supply
|
|
|
|
buttonrelease(); // Wait for button release
|
|
EIMSK |= (1<<INT0); // Reenable INT0 for wakeup
|
|
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Prepare sleep mode
|
|
sleep_enable();
|
|
sleep_mode(); // Go down until PB2 gets low (by pressed button
|
|
sleep_disable();
|
|
PORTB |= (1<<DDB1); // Activate LED Power supply
|
|
buttonrelease(); // Wait for button release
|
|
|
|
led[0].r = tempr; led[0].g = tempg; led[0].b = tempb;
|
|
ws2812_setleds(led,1); // Restore previous colors
|
|
EIMSK |= (1<<INT0); // Reenable INT0 for next shutdown
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void fire(uint8_t g_low, uint8_t g_high, uint8_t gdelay) {
|
|
for (led[0].g = g_high; led[0].g > g_low; led[0].g--) {
|
|
ws2812_setleds(led,1);
|
|
delayms(gdelay);
|
|
}
|
|
for (led[0].g = g_low; led[0].g < g_high; led[0].g++) {
|
|
ws2812_setleds(led,1);
|
|
delayms(gdelay);
|
|
}
|
|
#ifdef FEAT_STANDBY
|
|
checksleep();
|
|
#endif
|
|
}
|
|
|
|
void on(uint8_t f) {
|
|
led[0].g = G_HIGH;
|
|
ws2812_setleds(led,1);
|
|
for (uint8_t t = f<<4; t > 0; t--) { // f*16
|
|
delayms(62);
|
|
#ifdef FEAT_STANDBY
|
|
checksleep();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void burn(uint8_t f) {
|
|
for(uint8_t var = f<<4; var > 0; var--) { // f*16
|
|
fire(G_BURN, G_HIGH, T_BURN);
|
|
}
|
|
}
|
|
|
|
void flicker(uint8_t f) {
|
|
fire(G_BURN,G_HIGH,T_BURN);
|
|
for(uint8_t var = f<<4; var > 0; var--) { // f*16
|
|
fire(G_FLICKER,G_HIGH,T_FLICKER);
|
|
}
|
|
fire(G_BURN,G_HIGH,T_BURN);
|
|
fire(G_BURN,G_HIGH,T_BURN);
|
|
fire(G_BURN,G_HIGH,T_BURN);
|
|
}
|
|
|
|
void flutter(uint8_t f) {
|
|
fire(G_BURN,G_HIGH,T_BURN);
|
|
fire(G_FLICKER,G_HIGH,T_FLICKER);
|
|
for(uint8_t var = f<<4; var > 0; var--) { // f*16
|
|
fire(G_FLUTTER,G_HIGH,T_FLUTTER);
|
|
}
|
|
fire(G_FLICKER,G_HIGH,T_FLICKER);
|
|
fire(G_BURN,G_HIGH,T_BURN);
|
|
fire(G_BURN,G_HIGH,T_BURN);
|
|
}
|
|
|
|
void delayms(uint16_t ms) {
|
|
timertick = 0;
|
|
cli();
|
|
PRR &= ~(1<<PRTIM0); // Power up Timer 0
|
|
TCNT0 = 0; // Reset Timer to Zero
|
|
sei();
|
|
TIMSK0 = 2; // Set OCIE0A Flag, Compare Match Interrupt for OCR0A
|
|
if ( ms < 525 ) { // At Prescaler /64 and CPU = 8MHz, 1ms = 125 ticks. TIMER0 can only handle 524 of those
|
|
OCR0A = (ms<<7) - ms - ms - ms; // ms * 125
|
|
} else { // TIMER0 can only count 16bit, 2^16 / 125 per ms = 524,28ms max
|
|
OCR0A = 65535;
|
|
}
|
|
// TCCR0A = 0; // Ports OC0A and OC0B disconnected, Waveform Generation Mode: CTC OCR0A
|
|
TCCR0B = 11; // CS0 = 011 = Prescaler clk/1024), Waveform Generation Mode: CTC OCR0A
|
|
|
|
while (timertick == 0) {
|
|
set_sleep_mode(SLEEP_MODE_IDLE); // While we wait for the timer to finish its delay, put the CPU in idle mode
|
|
sleep_enable();
|
|
sleep_mode(); // Start idle mode. Timer interrupt will wake up the CPU again
|
|
sleep_disable();
|
|
} // CPU could wake up due to Timer interrupt or INT0 low level. If latter, make sure to go to sleep again.
|
|
|
|
TCCR0B = 0; // Timer0 off
|
|
TIMSK0 = 0; // Disable Timer Interrupts
|
|
PRR |= (1<<PRTIM0); // Power down Timer0
|
|
}
|
|
|
|
ISR(TIM0_COMPA_vect) {
|
|
timertick = 1;
|
|
}
|
|
|
|
#ifdef FEAT_UNDERVOLTSLEEP
|
|
ISR(VLM_vect) { // Battery below 2.5V, power down for safety
|
|
cli(); // Disable interrupts so that nothing can wake up deep sleep
|
|
VLMCSR = 0; // Disable VLM entirely, interrupts will be ignored anyway
|
|
|
|
struct cRGB leds[1];
|
|
leds[0].r=0; leds[0].g=0; leds[0].b=0;
|
|
ws2812_setleds(leds,1); // Turn LEDs off
|
|
|
|
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Prepare eternal sleep
|
|
sleep_enable();
|
|
sleep_mode(); // Go down until someone changes the battery and resets the Tiny10
|
|
}
|
|
#endif
|
|
|
|
#ifdef FEAT_STANDBY
|
|
ISR(INT0_vect) {
|
|
EIMSK &= ~(1<<INT0); // Disable INT0
|
|
}
|
|
#endif
|
|
|
|
int __attribute__((OS_main))
|
|
main (void) {
|
|
CCP = 0xD8; // configuration change protection, write signature
|
|
CLKPSR = 0; // set cpu clock prescaler =1 (8Mhz)
|
|
|
|
ADCSRA &= ~(1<<ADEN); // Disable ADC
|
|
PRR |= (1<<PRADC); // Power down ADC entirely
|
|
PRR |= (1<<PRTIM0); // Power down Timer0 for now, we will enable it when needed
|
|
|
|
#ifdef FEAT_UNDERVOLTSLEEP
|
|
VLMCSR |= (1<<VLMIE) | (1<<VLM1) | (1<<VLM0); // Enable VLM Interrupt, set VLM Level to 2.5V
|
|
#endif
|
|
|
|
// Pin Setup:
|
|
// PB0 - LED Data (out) Reset (PB3)
|
|
// GND VCC
|
|
// PB1 - LED Power (out) PB2 (INT0) - Sleep Button (in)
|
|
// PB1 and PB2 are only used if FEAT_STANDBY is defined
|
|
|
|
#ifdef FEAT_STANDBY
|
|
DDRB |= (1<<DDB1); // Set PB1 to output leave everything else at default (input)
|
|
PUEB |= (1<<PUEB2); // Activate internal Pullup resistor for PB2
|
|
|
|
PORTB |= (1<<DDB1); // Activate LED Power supply
|
|
|
|
EICRA = 0; // Set External Interrupt INT0 to low level interrupt request generation
|
|
EIMSK |= (1<<INT0); // Enable INT0
|
|
sei(); // Make sure interrupts are coming through
|
|
#endif
|
|
|
|
led[0].r=R_STATIC; led[0].g=G_HIGH; led[0].b=B_STATIC;
|
|
ws2812_setleds(led,1);
|
|
delayms(100);
|
|
|
|
while (1)
|
|
{
|
|
on(12);
|
|
burn(3);
|
|
on(10);
|
|
burn(3);
|
|
on(5);
|
|
flicker(3);
|
|
burn(2);
|
|
on(12);
|
|
flutter(1);
|
|
burn(4);
|
|
on(15);
|
|
burn(3);
|
|
}
|
|
return(0);
|
|
}
|