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>
|
|
|
|
|
2018-04-04 19:37:26 +02:00
|
|
|
#include "timer.h"
|
2018-03-27 01:59:25 +02:00
|
|
|
#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
|
2018-03-28 18:49:24 +02:00
|
|
|
#define HUE_STEP 8 // Bis 3 sollte gehen
|
2018-03-27 01:59:25 +02:00
|
|
|
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);
|
2018-03-28 18:49:24 +02:00
|
|
|
void nextColor(hsv_t* hsvList, rgb_t* rgbList, hsv_t start, hsv_t end);
|
2018-03-27 01:59:25 +02:00
|
|
|
|
|
|
|
// 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-28 18:49:24 +02:00
|
|
|
uint32_t volatile cooldown = 3000000; // * 50 ns = 150 ms disabled interrupt
|
|
|
|
uint8_t volatile triggered = 0;
|
2018-03-27 01:59:25 +02:00
|
|
|
//~ uint32_t volatile delay = 0;
|
|
|
|
|
|
|
|
// On External Interrupt:
|
|
|
|
ISR(INT1_vect) {
|
2018-04-04 19:37:26 +02:00
|
|
|
uint32_t now = timer_now32(); // time + TCNT1;
|
2018-03-28 18:49:24 +02:00
|
|
|
//~ triggered = (triggered + 1) % 4;
|
|
|
|
if(now > cooldown) {
|
|
|
|
triggered = true;
|
|
|
|
//~ EIMSK &= 0xfd; // Disable INT1
|
|
|
|
//~ EICRA &= 0xf3; //
|
|
|
|
PORTC &= 0xef; // Turn LED off while INT1 disabled (PORTC3 = PIN26)
|
|
|
|
PORTC ^= 0x02; // toggle LED on INT1 rising edge (PORTC1 = PIN24)
|
|
|
|
//~ uint64_t time2 = TCNT2;
|
|
|
|
//~ uint64_t time1 = TCNT1;
|
|
|
|
//~ uint64_t time0 = TCNT0;
|
|
|
|
//~ T_half = time2 | (time1 << 9) | (time0 << 26);
|
|
|
|
T_half = now;
|
|
|
|
//~ delay = T_half / 16; // Delay for the next possible external interrupt
|
|
|
|
cooldown = T_half / 2;
|
|
|
|
//~ TCNT2 = 0; // Reset Timer/Counter2
|
|
|
|
//~ TCNT1 = 0; // Reset Timer/Counter1
|
|
|
|
TCNT0 = 0; // Reset Timer/Counter0
|
|
|
|
time = 0;
|
2018-04-04 19:37:26 +02:00
|
|
|
oldTime = 0;
|
|
|
|
timer_reset();
|
2018-03-28 18:49:24 +02:00
|
|
|
}
|
|
|
|
//~ triggered = 0;
|
|
|
|
min = HUE_MID;
|
|
|
|
max = HUE_MID;
|
2018-03-27 01:59:25 +02:00
|
|
|
//~ status = (status == 1 ? -1 : 1);
|
|
|
|
}
|
|
|
|
|
2018-03-27 22:01:57 +02:00
|
|
|
//~ // On Timer Overflow Interrupt:
|
2018-04-04 19:37:26 +02:00
|
|
|
//~ ISR(TIMER1_OVF_vect) {
|
|
|
|
//~ // time += 0x00010000;
|
|
|
|
//~ timer_overflow();
|
|
|
|
//~ }
|
2018-03-27 01:59:25 +02:00
|
|
|
|
|
|
|
int main(void) {
|
|
|
|
|
|
|
|
EIMSK |= 0x02; // External Interrupt Mask Register
|
|
|
|
// Enable INT1 (PIN5) for interrupt
|
|
|
|
EICRA |= 0x0c; // External Interrupt Control Register
|
2018-03-28 18:49:24 +02:00
|
|
|
// INT1 on falling edge
|
2018-03-27 01:59:25 +02:00
|
|
|
|
|
|
|
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-04-04 19:37:26 +02:00
|
|
|
timer_init();
|
|
|
|
timer_reset();
|
2018-03-27 22:01:57 +02:00
|
|
|
|
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
|
2018-03-28 18:49:24 +02:00
|
|
|
int8_t sign = -1;
|
2018-03-27 01:59:25 +02:00
|
|
|
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-04-04 19:37:26 +02:00
|
|
|
uint32_t now = timer_now32(); // time + TCNT1;
|
2018-03-27 01:59:25 +02:00
|
|
|
//~ if(now > delay) {
|
|
|
|
//~ PORTC |= 0x08; // PORTC3 = HIGH
|
|
|
|
//~ }
|
|
|
|
//~ if(now > delay * 2) {
|
|
|
|
//~ PORTC &= 0xf7; // PORTC3 = LOW
|
|
|
|
//~ }
|
|
|
|
|
|
|
|
if(now > cooldown) {
|
2018-03-28 18:49:24 +02:00
|
|
|
//~ EIMSK |= 0x02; // Enable INT1
|
|
|
|
PORTC |= 0x10; // Turn on LED while INT1 enabled (PORTC3 = PIN26)
|
2018-03-27 01:59:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t T_step = T_half / colorSteps;
|
|
|
|
|
|
|
|
// Check if it is time for next color
|
2018-03-28 18:49:24 +02:00
|
|
|
if(now > oldTime) { // + T_step || (now < T_step && oldTime == 0)) {
|
2018-03-27 01:59:25 +02:00
|
|
|
oldTime += T_step;
|
2018-03-28 18:49:24 +02:00
|
|
|
PORTC ^= 0x04; // toggle LED when writing new color (PORTC2 = PIN25)
|
2018-03-27 01:59:25 +02:00
|
|
|
// Assign color
|
2018-03-27 22:01:57 +02:00
|
|
|
//~ EIMSK &= 0xfd; //~ cli();
|
2018-03-28 18:49:24 +02:00
|
|
|
nextColor( colorsHSV, colorsRGB, init_hsv_t(min, HUE_SAT, HUE_VAL), init_hsv_t(max, HUE_SAT, HUE_VAL));
|
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;
|
2018-03-28 18:49:24 +02:00
|
|
|
|
|
|
|
//~ 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);
|
2018-03-27 01:59:25 +02:00
|
|
|
// 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;
|
|
|
|
//~ }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-28 18:49:24 +02:00
|
|
|
void inline nextColor(hsv_t* hsvList, rgb_t* rgbList, hsv_t start, hsv_t end) {
|
|
|
|
interpolate(start, // from color
|
|
|
|
end, // to color
|
|
|
|
LEDS,
|
|
|
|
hsvList
|
|
|
|
);
|
|
|
|
hsv2rgbList(hsvList, rgbList, LEDS);
|
|
|
|
}
|
|
|
|
|
2018-03-27 01:59:25 +02:00
|
|
|
/***********************************************************************
|
|
|
|
*
|
|
|
|
* 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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|