mca-pendel/sketches/pendulum/pendulum.c
2018-04-04 19:37:26 +02:00

268 lines
7.1 KiB
C

#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 "timer.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 // 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 = timer_now32(); // 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;
timer_reset();
}
//~ triggered = 0;
min = HUE_MID;
max = HUE_MID;
//~ status = (status == 1 ? -1 : 1);
}
//~ // On Timer Overflow Interrupt:
//~ ISR(TIMER1_OVF_vect) {
//~ // time += 0x00010000;
//~ timer_overflow();
//~ }
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_init();
timer_reset();
// 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 = timer_now32(); // 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();
}
}
}