2018-03-27 01:59:25 +02:00
|
|
|
#define F_CPU 20000000UL
|
|
|
|
#include <util/delay.h>
|
|
|
|
#include <avr/interrupt.h>
|
|
|
|
#include <avr/io.h>
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
|
|
|
#include "color_hsv.h"
|
|
|
|
#define HUE_MAX 610
|
|
|
|
#define HUE_MIN 350
|
|
|
|
#define HUE_MID ((HUE_MAX + HUE_MIN) / 2)
|
|
|
|
#define HUE_DIF (HUE_MAX - HUE_MIN)
|
|
|
|
#define HUE_SAT 100 // 0 = white, 100 = color
|
|
|
|
#define HUE_VAL 20 // 0 = black, 100 = color
|
|
|
|
#define HUE_STEP 8 // 8
|
|
|
|
const uint32_t colorSteps = HUE_DIF / HUE_STEP;
|
|
|
|
//~ const uint32_t colorSteps = 5;
|
|
|
|
|
|
|
|
#define LEDS 30
|
|
|
|
|
|
|
|
// Prototypes:
|
|
|
|
void writeZero(void);
|
|
|
|
void writeOne(void);
|
|
|
|
void writeRGB(uint8_t red, uint8_t green, uint8_t blue);
|
|
|
|
|
|
|
|
// Interrupt variables:
|
|
|
|
int8_t volatile status = 1;
|
|
|
|
uint16_t volatile min = HUE_MID;
|
|
|
|
uint16_t volatile max = HUE_MID;
|
|
|
|
uint32_t volatile time = 0;
|
|
|
|
uint32_t volatile T_half = 0;
|
|
|
|
uint32_t volatile oldTime = 0;
|
2018-03-27 22:01:57 +02:00
|
|
|
uint32_t volatile cooldown = 14000000;
|
2018-03-27 01:59:25 +02:00
|
|
|
//~ uint32_t volatile delay = 0;
|
|
|
|
|
|
|
|
// On External Interrupt:
|
|
|
|
ISR(INT1_vect) {
|
|
|
|
EIMSK &= 0xfd; // Disable INT1
|
2018-03-27 22:01:57 +02:00
|
|
|
PORTC &= 0xef; // Turn LED off while INT1 disabled
|
|
|
|
//~ uint64_t time2 = TCNT2;
|
|
|
|
//~ uint64_t time1 = TCNT1;
|
|
|
|
//~ uint64_t time0 = TCNT0;
|
|
|
|
//~ T_half = time2 | (time1 << 9) | (time0 << 26);
|
2018-03-27 01:59:25 +02:00
|
|
|
T_half = time + TCNT1;
|
2018-03-27 22:01:57 +02:00
|
|
|
//~ delay = T_half / 16; //Delay for the next possible external interrupt
|
|
|
|
//~ cooldown = T_half / 2;
|
|
|
|
TCNT2 = 0; // Reset Timer/Counter2
|
2018-03-27 01:59:25 +02:00
|
|
|
TCNT1 = 0; // Reset Timer/Counter1
|
2018-03-27 22:01:57 +02:00
|
|
|
TCNT0 = 0; // Reset Timer/Counter0
|
2018-03-27 01:59:25 +02:00
|
|
|
time = 0;
|
|
|
|
oldTime = 0;
|
|
|
|
PORTC ^= 0x02;
|
|
|
|
//~ min = HUE_MID;
|
|
|
|
//~ max = HUE_MID;
|
|
|
|
//~ status = (status == 1 ? -1 : 1);
|
|
|
|
}
|
|
|
|
|
2018-03-27 22:01:57 +02:00
|
|
|
//~ // On Timer Overflow Interrupt:
|
2018-03-27 01:59:25 +02:00
|
|
|
ISR(TIMER1_OVF_vect) {
|
|
|
|
time += 0x00010000;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(void) {
|
|
|
|
|
|
|
|
EIMSK |= 0x02; // External Interrupt Mask Register
|
|
|
|
// Enable INT1 (PIN5) for interrupt
|
|
|
|
EICRA |= 0x0c; // External Interrupt Control Register
|
|
|
|
// INT1 on rising edge
|
|
|
|
|
|
|
|
TIMSK1 |= 0x01; // Timer/Counter1 Interrupt Mask Register
|
|
|
|
// Enable overflow interrupt
|
|
|
|
TCCR1B |= 0x01; // Timer/Counter1 Control Register B
|
|
|
|
// Prescale Factor 1
|
|
|
|
|
2018-03-27 22:01:57 +02:00
|
|
|
|
2018-03-27 01:59:25 +02:00
|
|
|
SREG |= 0x80; // Status Register
|
|
|
|
// Enable global interrupts
|
|
|
|
|
|
|
|
DDRC = 0x3f; // Digital Direction PORTC[5:0] = output
|
|
|
|
PORTC = 0x00;
|
|
|
|
|
2018-03-27 22:01:57 +02:00
|
|
|
// Timer concatenation
|
|
|
|
// Timer 2 (8 Bit, 50 ns)
|
|
|
|
//~ DDRB |= 0x08; // Digital Direction PORTB3 = output (PIN17 OC2A)
|
|
|
|
//~ PORTB |= 0x08; // POTRB3 = HIGH
|
|
|
|
//~ TCNT2 = 0x00; // Timer/Counter2 value register clear
|
|
|
|
//~ OCR2A = 0xff; // Timer/Counter2 output compare register A
|
|
|
|
//~ TIMSK2 |= 0x02; // Timer/Counter2 Interrupt Mask Register
|
|
|
|
//~ // Enable compare A match interrupt
|
|
|
|
//~ TCCR2A |= 0x40; // Timer/Counter2 Control Register A
|
|
|
|
//~ // Toggle OC2A on compare match
|
|
|
|
//~ TCCR2B |= 0x01; // Timer/Counter2 Control Register B
|
|
|
|
//~ // Prescale Factor 1
|
|
|
|
//~ // Timer 1 (16 Bit, 25.55 µs)
|
|
|
|
//~ DDRB |= 0x02; // Digital Direction PORTB1 = output (PIN15 OC1A)
|
|
|
|
//~ PORTB |= 0x02; // POTRB1 = HIGH
|
|
|
|
//~ TCNT1 = 0x0000; // Timer/Counter1 value register clear
|
|
|
|
//~ OCR1A = 0xffff; // Timer/Counter1 output compare register A
|
|
|
|
//~ TIMSK1 |= 0x02; // Timer/Counter1 Interrupt Mask Register
|
|
|
|
//~ // Enable compare register A match interrupt
|
|
|
|
//~ TCCR1A |= 0x40; // Timer/Counter1 Control Register A
|
|
|
|
//~ // Toggle OC1A on compare match
|
|
|
|
//~ TCCR1B |= 0x07; // Timer/Counter1 Control Register B
|
|
|
|
//~ // External Clock Source on T1 (PIN11) on rising edge
|
|
|
|
//~ // Timer 0 (8 Bit, 3,34891515 s)
|
|
|
|
//~ TCNT0 = 0x00; // Timer/Counter0 value register clear
|
|
|
|
//~ TCCR0B |= 0x07; // Timer/Counter0 Control Register B
|
|
|
|
//~ // External clock source on T0 (PIN6, PD4) on rising edge
|
|
|
|
|
2018-03-27 01:59:25 +02:00
|
|
|
// Init color
|
|
|
|
hsv_t colorsHSV[LEDS];
|
|
|
|
rgb_t colorsRGB[LEDS];
|
|
|
|
interpolate(
|
|
|
|
init_hsv_t(min, HUE_SAT, HUE_VAL), // from color
|
2018-03-27 22:01:57 +02:00
|
|
|
init_hsv_t(max, HUE_SAT, HUE_VAL), // to color
|
2018-03-27 01:59:25 +02:00
|
|
|
LEDS,
|
|
|
|
colorsHSV
|
|
|
|
);
|
|
|
|
hsv2rgbList(colorsHSV, colorsRGB, LEDS);
|
|
|
|
// Assign color
|
|
|
|
for(int i = 0; i < LEDS; i++) {
|
|
|
|
writeRGB( colorsRGB[i].r, colorsRGB[i].g, colorsRGB[i].b);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next color
|
|
|
|
int8_t sign = 1;
|
|
|
|
min += sign * HUE_STEP;
|
|
|
|
max -= sign * HUE_STEP;
|
|
|
|
interpolate(
|
2018-03-27 22:01:57 +02:00
|
|
|
init_hsv_t(min, HUE_SAT, HUE_VAL), // from color
|
|
|
|
init_hsv_t(max, HUE_SAT, HUE_VAL), // to color
|
2018-03-27 01:59:25 +02:00
|
|
|
LEDS,
|
|
|
|
colorsHSV
|
|
|
|
);
|
|
|
|
hsv2rgbList(colorsHSV, colorsRGB, LEDS);
|
|
|
|
|
|
|
|
while(1) {
|
2018-03-27 22:01:57 +02:00
|
|
|
//~ uint64_t time2 = TCNT2;
|
|
|
|
//~ uint64_t time1 = TCNT1;
|
|
|
|
//~ uint64_t time0 = TCNT0;
|
|
|
|
//~ uint64_t now = time2 | (time1 << 9) | (time0 << 26);
|
2018-03-27 01:59:25 +02:00
|
|
|
uint32_t now = time + TCNT1;
|
|
|
|
//~ if(now > delay) {
|
|
|
|
//~ PORTC |= 0x08; // PORTC3 = HIGH
|
|
|
|
//~ }
|
|
|
|
//~ if(now > delay * 2) {
|
|
|
|
//~ PORTC &= 0xf7; // PORTC3 = LOW
|
|
|
|
//~ }
|
|
|
|
|
|
|
|
if(now > cooldown) {
|
2018-03-27 22:01:57 +02:00
|
|
|
EIMSK |= 0x02; // Enable INT1
|
|
|
|
PORTC |= 0x10; // Turn on LED while INT1 enabled
|
2018-03-27 01:59:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t T_step = T_half / colorSteps;
|
|
|
|
|
|
|
|
// Check if it is time for next color
|
|
|
|
if(now > oldTime + T_step) {
|
|
|
|
oldTime += T_step;
|
|
|
|
PORTC ^= 0x04;
|
|
|
|
// Assign color
|
2018-03-27 22:01:57 +02:00
|
|
|
//~ EIMSK &= 0xfd; //~ cli();
|
2018-03-27 01:59:25 +02:00
|
|
|
for(int i = 0; i < LEDS; i++) {
|
|
|
|
writeRGB( colorsRGB[i].r, colorsRGB[i].g, colorsRGB[i].b);
|
|
|
|
}
|
2018-03-27 22:01:57 +02:00
|
|
|
//~ EIMSK |= 0x02; //~ sei();
|
2018-03-27 01:59:25 +02:00
|
|
|
//~ _delay_us(51);
|
|
|
|
|
|
|
|
// Next color
|
|
|
|
min += sign * HUE_STEP;
|
|
|
|
max -= sign * HUE_STEP;
|
|
|
|
interpolate(
|
|
|
|
init_hsv_t(min, HUE_SAT, HUE_VAL), // from color
|
|
|
|
init_hsv_t(max, HUE_SAT, HUE_VAL), // to color
|
|
|
|
LEDS,
|
|
|
|
colorsHSV
|
|
|
|
);
|
|
|
|
hsv2rgbList(colorsHSV, colorsRGB, LEDS);
|
|
|
|
// Check if reached amplitude
|
|
|
|
if(sign == 1 && (min >= HUE_MAX || max <= HUE_MIN)) {
|
|
|
|
sign = -1;
|
|
|
|
} else if(sign == -1 && (min <= HUE_MIN || max >= HUE_MAX)) {
|
|
|
|
sign = 1;
|
|
|
|
}
|
|
|
|
//~ if(status == 1 && sign == 1 && (min >= HUE_MAX || max <= HUE_MIN)) {
|
|
|
|
//~ sign = -1;
|
|
|
|
//~ } else if(status == 1 && sign == -1 && (min <= HUE_MIN || max >= HUE_MAX)) {
|
|
|
|
//~ sign = 1;
|
|
|
|
//~ } else if(status == -1 && sign == 1 && (min <= HUE_MIN || max >= HUE_MAX)) {
|
|
|
|
//~ sign = -1;
|
|
|
|
//~ } else if(status == -1 && sign == -1 && (min >= HUE_MAX || max <= HUE_MIN)) {
|
|
|
|
//~ sign = 1;
|
|
|
|
//~ }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
*
|
|
|
|
* TIMING
|
|
|
|
*
|
|
|
|
************************************************************************
|
|
|
|
* f=20MHz -> T=0,05 µs = 50 ns
|
|
|
|
*
|
|
|
|
* 0,05 µs * 6 == 0.3 µs ~ 0.4 µs (+- 150ns) == [0.25, 0.55] µs
|
|
|
|
* 0,05 µs * 14 == 0.7 µs ~ 0.8 µs (+- 150ns) == [0.65, 0.95] µs
|
|
|
|
* 0,05 µs * 15 == 0.75 µs ~ 0.85 µs (+- 150ns) == [0.70, 1.00] µs
|
|
|
|
* 0,05 µs * 8 == 0.35 µs ~ 0.45 µs (+- 150ns) == [0.30, 0.60] µs
|
|
|
|
* 51 µs > 50 µs
|
|
|
|
************************************************************************/
|
|
|
|
// AVR-GCC optimizes multiple "nop"s on every optimization level
|
|
|
|
// So we use timing from:
|
|
|
|
// https://github.com/cpldcpu/light_ws2812/blob/master/light_ws2812_AVR/Light_WS2812/light_ws2812.c
|
|
|
|
#define w_nop1 __asm__("nop \n\t")
|
|
|
|
#define w_nop2 __asm__("rjmp .+0 \n\t")
|
|
|
|
#define w_nop4 w_nop2; w_nop2
|
|
|
|
#define w_nop8 w_nop4; w_nop4
|
|
|
|
#define w_nop16 w_nop8; w_nop8 // Not used
|
|
|
|
|
|
|
|
#define wait6 w_nop2; w_nop4
|
|
|
|
#define wait8 w_nop8
|
|
|
|
#define wait14 wait8; wait6
|
|
|
|
#define wait15 wait14; w_nop1
|
|
|
|
|
|
|
|
// WS2812B protocol
|
|
|
|
inline void writeZero() {
|
|
|
|
PORTC |= 0x01; // PORTC0 = 1, PORTC[5:1] = invariant
|
|
|
|
wait6;
|
|
|
|
PORTC &= 0xfe; // PORTC0 = 0, PORTC[5:1] = invariant
|
|
|
|
wait15;
|
|
|
|
}
|
|
|
|
|
|
|
|
// WS2812B protocol
|
|
|
|
inline void writeOne() {
|
|
|
|
PORTC |= 0x01; // PORTC0 = 1, PORTC[5:1] = invariant
|
|
|
|
wait14;
|
|
|
|
PORTC &= 0xfe; // PORTC0 = 0, PORTC[5:1] = invariant
|
|
|
|
wait8;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void writeRGB(uint8_t red, uint8_t green, uint8_t blue) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for( i = 128; i > 0; i >>= 1 ) {
|
|
|
|
if( green & i ){
|
|
|
|
writeOne();
|
|
|
|
} else {
|
|
|
|
writeZero();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for( i = 128; i > 0; i >>= 1 ) {
|
|
|
|
if( red & i ){
|
|
|
|
writeOne();
|
|
|
|
} else {
|
|
|
|
writeZero();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for( i = 128; i > 0; i >>= 1 ) {
|
|
|
|
if( blue & i ){
|
|
|
|
writeOne();
|
|
|
|
} else {
|
|
|
|
writeZero();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|