#define F_CPU 20000000UL #include #include #include #include #include #include #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 // Bis 3 sollte gehen 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); void nextColor(hsv_t* hsvList, rgb_t* rgbList, hsv_t start, hsv_t end); // 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; uint32_t volatile cooldown = 3000000; // * 50 ns = 150 ms disabled interrupt uint8_t volatile triggered = 0; //~ uint32_t volatile delay = 0; // On External Interrupt: ISR(INT1_vect) { uint32_t now = time + TCNT1; //~ 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; oldTime = 0; } //~ triggered = 0; min = HUE_MID; max = HUE_MID; //~ status = (status == 1 ? -1 : 1); } //~ // On Timer Overflow Interrupt: 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 falling edge TIMSK1 |= 0x01; // Timer/Counter1 Interrupt Mask Register // Enable overflow interrupt TCCR1B |= 0x01; // Timer/Counter1 Control Register B // Prescale Factor 1 SREG |= 0x80; // Status Register // Enable global interrupts DDRC = 0x3f; // Digital Direction PORTC[5:0] = output PORTC = 0x00; // 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 // Init color hsv_t colorsHSV[LEDS]; rgb_t colorsRGB[LEDS]; 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); // 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( 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); while(1) { //~ uint64_t time2 = TCNT2; //~ uint64_t time1 = TCNT1; //~ uint64_t time0 = TCNT0; //~ uint64_t now = time2 | (time1 << 9) | (time0 << 26); uint32_t now = time + TCNT1; //~ if(now > delay) { //~ PORTC |= 0x08; // PORTC3 = HIGH //~ } //~ if(now > delay * 2) { //~ PORTC &= 0xf7; // PORTC3 = LOW //~ } if(now > cooldown) { //~ EIMSK |= 0x02; // Enable INT1 PORTC |= 0x10; // Turn on LED while INT1 enabled (PORTC3 = PIN26) } uint32_t T_step = T_half / colorSteps; // Check if it is time for next color if(now > oldTime) { // + T_step || (now < T_step && oldTime == 0)) { oldTime += T_step; PORTC ^= 0x04; // toggle LED when writing new color (PORTC2 = PIN25) // Assign color //~ EIMSK &= 0xfd; //~ cli(); nextColor( colorsHSV, colorsRGB, init_hsv_t(min, HUE_SAT, HUE_VAL), init_hsv_t(max, HUE_SAT, HUE_VAL)); for(int i = 0; i < LEDS; i++) { writeRGB( colorsRGB[i].r, colorsRGB[i].g, colorsRGB[i].b); } //~ EIMSK |= 0x02; //~ sei(); //~ _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; //~ } } } } 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); } /*********************************************************************** * * 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(); } } }