#include #include #include #ifdef ENABLE_OLED # include "i2c_master.h" #endif // Port A0 - A3 -> LED Data // Port A5 -> Select Output // Port A4, A6 -> I2C Display // Port A7 -> Enable Output // Port B2 -> Button Input (Pulled-Up) // Button Press Times #define BTN_LONG_PRESS_MS 1000 #define BTN_DEBOUNCE_MS 50 // Size of each strip #define LED0_COUNT 12 #define LED1_COUNT 12 #define LED2_COUNT 12 #define LED3_COUNT 15 // ms per frame #define LED0_MS 500 #define LED1_MS 500 #define LED2_MS 500 #define LED3_MS 400 // ms offset #define LED0_MS_OFFSET 0 #define LED1_MS_OFFSET 0 #define LED2_MS_OFFSET 0 #define LED3_MS_OFFSET 0 #define LED0_PORT PORTA #define LED1_PORT PORTA #define LED2_PORT PORTA #define LED3_PORT PORTA #define LED0_BIT 0 #define LED1_BIT 1 #define LED2_BIT 2 #define LED3_BIT 3 // NeoPixel Defines #define T1H 900 // Width of a 1 bit in ns #define T1L 600 // Width of a 1 bit in ns #define T0H 400 // Width of a 0 bit in ns #define T0L 900 // Width of a 0 bit in ns #define RES 6000 #define RES_HALF RES / 2 #define NS_PER_SEC (1000000000L) #define CYCLES_PER_SEC (F_CPU) #define NS_PER_CYCLE ( NS_PER_SEC / CYCLES_PER_SEC ) #define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE ) // ms counter uint64_t ms = 0; // Led update times uint64_t led_ms_update[4] = {LED0_MS_OFFSET, LED1_MS_OFFSET, LED2_MS_OFFSET, LED3_MS_OFFSET}; #ifdef ENABLE_OLED // I2C Display Address uint8_t display_address = 0x78; // Draw only changed digits uint8_t old_time[7] = {0, 0, 0, 0, 0, 0, 0}; uint64_t old_time_ms = 0; #endif // ms Counter Func ISR(TIM1_CAPT_vect) { ms += 10; } // NeoPixel Funcs static inline void send_bit_led0(uint8_t bitVal); static inline void send_bit_led1(uint8_t bitVal); static inline void send_bit_led2(uint8_t bitVal); static inline void send_bit_led3(uint8_t bitVal); static inline void send_byte(uint8_t byte, uint8_t led); static inline void send_pixel(uint8_t r, uint8_t g, uint8_t b, uint8_t led); void show_leds(); // Hardware Init void init(); // Function to switch states void state_switch(uint8_t is_onoff); #ifdef ENABLE_OLED // I2C Display Funcs void init_display(); void send_command(uint8_t cmd); void set_display_coords(uint8_t x_start, uint8_t x_end, uint8_t y_start, uint8_t y_end); void show_time(uint64_t ms); void draw_dots(uint8_t pos); void draw_digit(uint8_t pos, uint8_t digit); #endif // Call function to show a frame and prepare idx for next frame void sun_and_moon(uint8_t count, uint8_t* idx, uint8_t colour, uint8_t led); void clock_no_bg(uint8_t count, uint8_t* idx, uint8_t colour, uint8_t led); void loading(uint8_t count, uint8_t* idx, uint8_t colour, uint8_t led); void off(uint8_t count, uint8_t* idx, uint8_t colour, uint8_t led); void (*func_list[4])(uint8_t, uint8_t*, uint8_t, uint8_t) = { sun_and_moon, clock_no_bg, loading, off }; void (*send_ptr[4])(uint8_t) = { send_bit_led0, send_bit_led1, send_bit_led2, send_bit_led3 }; enum states { STATE_SUN_MOON = 0, STATE_CLOCK_NO1, STATE_CLOCK_NO2, STATE_LOADING, STATE_PC }; uint8_t led0_idx = 0; uint8_t led1_idx = 0; uint8_t led2_idx = 0; uint8_t led3_idx = 0; uint8_t colour = 0; uint8_t func_idx = 0; uint8_t backup_func_idx = sizeof(func_list) / sizeof(func_list[0]) - 1; uint8_t btn_old_state = 0; uint8_t colour_state = STATE_SUN_MOON; uint8_t current_state; uint64_t btn_down = -1; int main() { uint8_t show; init(); // Main loop while(1) { show = 0; if (ms >= led_ms_update[0]) { led_ms_update[0] += LED0_MS; func_list[func_idx](LED0_COUNT, &led0_idx, colour, 0); show = 1; } if (ms >= led_ms_update[1]) { led_ms_update[1] += LED1_MS; func_list[func_idx](LED1_COUNT, &led1_idx, colour, 1); show = 1; } if (ms >= led_ms_update[2]) { led_ms_update[2] += LED2_MS; func_list[func_idx](LED2_COUNT, &led2_idx, colour, 2); show = 1; } if (ms >= led_ms_update[3]) { led_ms_update[3] += LED3_MS; func_list[func_idx](LED3_COUNT, &led3_idx, colour, 3); show = 1; } if (show) { show_leds(); } #ifdef ENABLE_OLED if (ms - 1000 >= old_time_ms) { show_time(ms); old_time_ms = ms; } #endif // Button Press current_state = PINB & 0x04; if (btn_old_state && !current_state) { btn_down = ms; } // Button Unpress if (!btn_old_state && current_state) { if (ms - BTN_DEBOUNCE_MS >= btn_down) { btn_down = -1; state_switch(0); } } btn_old_state = current_state; // Long Press Time Has Passed if (ms - BTN_LONG_PRESS_MS >= btn_down) { btn_down = -1; state_switch(1); } } } inline void send_bit_led0(uint8_t value) { if (value) { asm volatile ( "sbi %[port], %[bit] \n\t" ".rept %[onCycles] \n\t" "nop \n\t" ".endr \n\t" "cbi %[port], %[bit] \n\t" ".rept %[offCycles] \n\t" "nop \n\t" ".endr \n\t" :: [port] "I" (_SFR_IO_ADDR(LED0_PORT)), [bit] "I" (LED0_BIT), [onCycles] "I" (NS_TO_CYCLES(T1H) - 2), [offCycles] "I" (NS_TO_CYCLES(T1L) - 2) ); } else { asm volatile ( "sbi %[port], %[bit] \n\t" ".rept %[onCycles] \n\t" "nop \n\t" ".endr \n\t" "cbi %[port], %[bit] \n\t" ".rept %[offCycles] \n\t" "nop \n\t" ".endr \n\t" :: [port] "I" (_SFR_IO_ADDR(LED0_PORT)), [bit] "I" (LED0_BIT), [onCycles] "I" (NS_TO_CYCLES(T0H) - 2), [offCycles] "I" (NS_TO_CYCLES(T0L) - 2) ); } } inline void send_bit_led1(uint8_t value) { if (value) { asm volatile ( "sbi %[port], %[bit] \n\t" ".rept %[onCycles] \n\t" "nop \n\t" ".endr \n\t" "cbi %[port], %[bit] \n\t" ".rept %[offCycles] \n\t" "nop \n\t" ".endr \n\t" :: [port] "I" (_SFR_IO_ADDR(LED1_PORT)), [bit] "I" (LED1_BIT), [onCycles] "I" (NS_TO_CYCLES(T1H) - 2), [offCycles] "I" (NS_TO_CYCLES(T1L) - 2) ); } else { asm volatile ( "sbi %[port], %[bit] \n\t" ".rept %[onCycles] \n\t" "nop \n\t" ".endr \n\t" "cbi %[port], %[bit] \n\t" ".rept %[offCycles] \n\t" "nop \n\t" ".endr \n\t" :: [port] "I" (_SFR_IO_ADDR(LED1_PORT)), [bit] "I" (LED1_BIT), [onCycles] "I" (NS_TO_CYCLES(T0H) - 2), [offCycles] "I" (NS_TO_CYCLES(T0L) - 2) ); } } inline void send_bit_led2(uint8_t value) { if (value) { asm volatile ( "sbi %[port], %[bit] \n\t" ".rept %[onCycles] \n\t" "nop \n\t" ".endr \n\t" "cbi %[port], %[bit] \n\t" ".rept %[offCycles] \n\t" "nop \n\t" ".endr \n\t" :: [port] "I" (_SFR_IO_ADDR(LED2_PORT)), [bit] "I" (LED2_BIT), [onCycles] "I" (NS_TO_CYCLES(T1H) - 2), [offCycles] "I" (NS_TO_CYCLES(T1L) - 2) ); } else { asm volatile ( "sbi %[port], %[bit] \n\t" ".rept %[onCycles] \n\t" "nop \n\t" ".endr \n\t" "cbi %[port], %[bit] \n\t" ".rept %[offCycles] \n\t" "nop \n\t" ".endr \n\t" :: [port] "I" (_SFR_IO_ADDR(LED2_PORT)), [bit] "I" (LED2_BIT), [onCycles] "I" (NS_TO_CYCLES(T0H) - 2), [offCycles] "I" (NS_TO_CYCLES(T0L) - 2) ); } } inline void send_bit_led3(uint8_t value) { if (value) { asm volatile ( "sbi %[port], %[bit] \n\t" ".rept %[onCycles] \n\t" "nop \n\t" ".endr \n\t" "cbi %[port], %[bit] \n\t" ".rept %[offCycles] \n\t" "nop \n\t" ".endr \n\t" :: [port] "I" (_SFR_IO_ADDR(LED3_PORT)), [bit] "I" (LED3_BIT), [onCycles] "I" (NS_TO_CYCLES(T1H) - 2), [offCycles] "I" (NS_TO_CYCLES(T1L) - 2) ); } else { asm volatile ( "sbi %[port], %[bit] \n\t" ".rept %[onCycles] \n\t" "nop \n\t" ".endr \n\t" "cbi %[port], %[bit] \n\t" ".rept %[offCycles] \n\t" "nop \n\t" ".endr \n\t" :: [port] "I" (_SFR_IO_ADDR(LED3_PORT)), [bit] "I" (LED3_BIT), [onCycles] "I" (NS_TO_CYCLES(T0H) - 2), [offCycles] "I" (NS_TO_CYCLES(T0L) - 2) ); } } inline void send_byte(uint8_t byte, uint8_t led) { uint8_t i; for (i = 0; i < 8; i++) { send_ptr[led](byte & 0xF0); byte <<= 1; } } inline void send_pixel(uint8_t r, uint8_t g, uint8_t b, uint8_t led) { cli(); send_byte(g, led); send_byte(r, led); send_byte(b, led); sei(); } void show_leds() { asm volatile ( ".rept %[resCycles] \n\t" "nop \n\t" ".endr \n\t" ".rept %[resCycles] \n\t" "nop \n\t" ".endr \n\t" :: [resCycles] "I" (NS_TO_CYCLES(RES_HALF) - 2) ); } void init() { DDRA = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 5) | (1 << 7); PORTB = (1 << 2); #ifdef ENABLE_OLED init_display(); #endif // TIM1 init - CTC - prescaler 8 - 10ms interrupt TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); ICR1 = (F_CPU / 8 / 100) - 1; TIMSK1 = (1 << ICIE1); sei(); } void state_switch(uint8_t is_onoff) { uint8_t tmp_func_idx; if (is_onoff == 0) { // If we're off don't change anything if (func_idx == sizeof(func_list) / sizeof(func_list[0]) - 1) { return; } switch (colour_state) { case STATE_SUN_MOON: func_idx = 1; colour_state = STATE_CLOCK_NO1; colour = 0; break; case STATE_CLOCK_NO1: colour_state = STATE_CLOCK_NO2; colour = 1; break; case STATE_CLOCK_NO2: func_idx = 2; colour_state = STATE_LOADING; colour = 0; break; case STATE_LOADING: func_idx = 0; PORTA ^= 0x20; colour_state = STATE_PC; break; case STATE_PC: PORTA ^= 0x20; colour_state = STATE_SUN_MOON; break; } } else { tmp_func_idx = backup_func_idx; backup_func_idx = func_idx; func_idx = tmp_func_idx; } led0_idx = 0; led1_idx = 0; led2_idx = 0; led3_idx = 0; } void sun_and_moon(uint8_t count, uint8_t* idx, uint8_t colour, uint8_t led) { uint8_t i; uint8_t colour1[3] = {0xFF, 0x80, 0x00}; uint8_t colour2[3] = {0x00, 0x80, 0xFF}; uint8_t min; uint8_t max; if (*idx <= count / 2) { min = *idx; max = *idx + count / 2; for (i = 0; i < min; ++i) { send_pixel(colour1[0], colour1[1], colour1[2], led); } for (i = min; i < max; ++i) { send_pixel(colour2[0], colour2[1], colour2[2], led); } for (i = max; i < count; ++i) { send_pixel(colour1[0], colour1[1], colour1[2], led); } } else { min = *idx - count / 2; max = *idx; for (i = 0; i < min; ++i) { send_pixel(colour2[0], colour2[1], colour2[2], led); } for (i = min; i < max; ++i) { send_pixel(colour1[0], colour1[1], colour1[2], led); } for (i = max; i < count; ++i) { send_pixel(colour2[0], colour2[1], colour2[2], led); } } (*idx)++; (*idx) %= count; } void clock_no_bg(uint8_t count, uint8_t* idx, uint8_t colour, uint8_t led) { uint8_t i; uint8_t colour1[3] = {0xFF, 0x80, 0x00}; uint8_t colour2[3] = {0x00, 0x80, 0xFF}; uint8_t black[3] = {0x00, 0x00, 0x00}; uint8_t* main_colour; if (0 == colour) { main_colour = colour1; } else { main_colour = colour2; } for (i = 0; i < count; ++i) { if (i == *idx) { send_pixel(main_colour[0], main_colour[1], main_colour[2], led); } else { send_pixel(black[0], black[1], black[2], led); } } (*idx)++; (*idx) %= count; } void loading(uint8_t count, uint8_t* idx, uint8_t colour, uint8_t led) { uint8_t i; uint8_t colour1[3] = {0xFF, 0x80, 0x00}; uint8_t colour2[3] = {0x00, 0x80, 0xFF}; if (*idx < count) { for (i = 0; i < count; ++i) { if (i <= *idx) { send_pixel(colour1[0], colour1[1], colour1[2], led); } else { send_pixel(colour2[0], colour2[1], colour2[2], led); } } } else { for (i = 0; i < count; ++i) { if (i <= *idx - count) { send_pixel(colour2[0], colour2[1], colour2[2], led); } else { send_pixel(colour1[0], colour1[1], colour1[2], led); } } } (*idx)++; (*idx) %= count * 2; } void off(uint8_t count, uint8_t* idx, uint8_t colour, uint8_t led) { uint8_t black[3] = {0x00, 0x00, 0x00}; for (uint8_t i = 0; i < count; ++i) { send_pixel(black[0], black[1], black[2], led); } } #ifdef ENABLE_OLED void init_display() { uint16_t i = 0; uint8_t cmdInit[] = { 0xAE, 0xD5, 0x80, 0xA8, 0x1F, 0xD3, 0x00, 0x40, 0x8D, 0x14, 0x20, 0x00, 0xA1, 0xC8, 0xDA, 0x02, 0x81, 0x8F, 0xD9, 0xF1, 0xDB, 0x40, 0xA4, 0xA6, 0x2E, 0xAF }; i2c_init(); for (i = 0; i < sizeof(cmdInit); ++i) { send_command(cmdInit[i]); } set_display_coords(0x00, 0x7F, 0x00, 0x03); for(i = 0; i < 128*4; ++i) { i2c_write(0x00); } i2c_stop(); } void send_command(uint8_t cmd) { i2c_start(display_address); i2c_write(0x00); i2c_write(cmd); i2c_stop(); } void set_display_coords(uint8_t x_start, uint8_t x_end, uint8_t y_start, uint8_t y_end) { send_command(0x21); send_command(x_start); send_command(x_end); send_command(0x22); send_command(y_start); send_command(y_end); i2c_start(display_address); i2c_write(0x40); } void show_time(uint64_t ms) { uint8_t new_time[7] = {0, 0, 0, 0, 0, 0, 0}; uint8_t i; ms /= 1000; new_time[6] = ms % 10; ms /= 10; new_time[5] = ms % 6; ms /= 6; new_time[4] = ms % 10; ms /= 10; new_time[3] = ms % 6; ms /= 6; new_time[2] = ms % 10; ms /= 10; new_time[1] = ms % 10; ms /= 10; new_time[0] = ms % 10; for (i = 0; i < 7; ++i) { if (old_time[i] != new_time[i]) { if (((2 == i) || (4 == i)) && (0 == old_time[i])) { draw_dots(i); } draw_digit(i, new_time[i]); } old_time[i] = new_time[i]; } } void draw_dots(uint8_t pos) { // Between hours and minutes if (2 == pos) { set_display_coords(50, 53, 1, 2); } // Between minutes and seconds if (4 == pos) { set_display_coords(90, 93, 1, 2); } i2c_write(0x0F); i2c_write(0x0F); i2c_write(0x0F); i2c_write(0x0F); i2c_write(0xF0); i2c_write(0xF0); i2c_write(0xF0); i2c_write(0xF0); i2c_stop(); } void draw_digit(uint8_t pos, uint8_t digit) { uint8_t i, j; uint8_t x_start; uint8_t x_end; uint8_t row[12]; x_start = pos * 16; if (pos > 2) { x_start += 8; } if (pos > 4) { x_start += 8; } x_end = x_start + 15; set_display_coords(x_start + 2, x_end - 2, 0, 3); row[0] = 0xF0; row[1] = 0x00; row[2] = 0x00; row[3] = 0x03; row[4] = 0x00; row[5] = 0x00; row[6] = 0xC0; row[7] = 0xC0; row[8] = 0xC0; row[9] = 0x0F; row[10] = 0x03; row[11] = 0x0F; if (digit > 1) { row[1] = 0xF0; } if (digit > 2) { row[2] = 0xF0; } for (i = 3; i < 8; ++i) { if (digit > i) { row[i] = 0xFF; } } if (0 == digit) { row[2] = 0xF0; row[3] = 0xFF; row[5] = 0xFF; row[6] = 0xFF; row[8] = 0xFF; } for (i = 0; i < 12 * 4; ++i) { i2c_write(row[i / 4]); } i2c_stop(); } #endif