attiny10-lantern/attiny10-lantern.cpp

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