#include #include #include "pico/stdlib.h" #include "hardware/gpio.h" #include #define DISP_INTERVAL 3 /* DOUT */ #define DISP1_CLK 17 #define DISP1_DIO 16 #define DISP2_CLK 19 #define DISP2_DIO 18 #define LED_PIN PICO_DEFAULT_LED_PIN #define BUTTON_P1 12 #define BUTTON_P2 8 /* DIN */ #define ROT_S0 2 #define ROT_S1 3 #define ROT_KEY 6 #define BUZZ_DURATION 2000 #define BUZZ_FREQ 220 /* GP7 is PWM B[3] */ #define SPK_PWM_CHAN PWM_CHAN_B #define SPK_PWM_SLICE 3 #define get_absolute_time_ms() us_to_ms(get_absolute_time()) /* TM1637 display driver */ static void tm_clk_start(uint8_t disp) { uint8_t clk = DISP1_CLK, dio = DISP1_DIO; if (disp == 2) clk = DISP2_CLK, dio = DISP2_DIO; gpio_put(clk, 1); gpio_put(dio, 1); sleep_us(DISP_INTERVAL); gpio_put(dio, 0); sleep_us(DISP_INTERVAL); } static void tm_clk_stop(uint8_t disp) { uint8_t clk = DISP1_CLK, dio = DISP1_DIO; if (disp == 2) clk = DISP2_CLK, dio = DISP2_DIO; gpio_put(clk, 0); sleep_us(DISP_INTERVAL); gpio_put(dio, 0); sleep_us(DISP_INTERVAL); gpio_put(clk, 1); sleep_us(DISP_INTERVAL); gpio_put(dio, 1); sleep_us(DISP_INTERVAL); } static uint8_t tm_wait_ack(uint8_t disp) { uint8_t clk = DISP1_CLK, dio = DISP1_DIO; uint8_t ack; if (disp == 2) clk = DISP2_CLK, dio = DISP2_DIO; gpio_put(clk, 0); sleep_us(2 * DISP_INTERVAL); gpio_put(clk, 1); sleep_us(DISP_INTERVAL); gpio_put(clk, 0); return 0; } void tm_write(uint8_t disp, const uint8_t *b, uint8_t sz, int nostop) { uint8_t clk = DISP1_CLK, dio = DISP1_DIO; int i, c; if (disp == 2) clk = DISP2_CLK, dio = DISP2_DIO; gpio_put(dio, 0); sleep_us(DISP_INTERVAL); gpio_put(clk, 0); sleep_us(DISP_INTERVAL); for (c = 0; c < sz; c++) { uint8_t sliding_buf = b[c]; for (i = 0; i < 8; i++) { gpio_put(clk, 0); gpio_put(dio, sliding_buf & 0x01); //gpio_put(dio, (sliding_buf & 0x80) >> 7); sleep_us(DISP_INTERVAL); sliding_buf >>= 1; //sliding_buf = (sliding_buf & 0x7F) << 1; gpio_put(clk, 1); sleep_us(DISP_INTERVAL); } tm_wait_ack(disp); } } #define CMD_DATA 0x40 #define CMD_DATA_WRITE 0x00 #define CMD_DATA_READ 0x02 #define CMD_DATA_FLAG_AAUTO 0x04 #define CMD_DATA_FLAG_TEST 0x08 #define CMD_ADDRESS 0xC0 #define CMD_DISPLAY 0x80 #define CMD_DISPLAY_SWITCH_ON 0x08 #define CMD_DISPLAY_PULSE_MASK 0x07 const unsigned char numchar[10] = { /* 0 = 11111100 */ 0x3F, /* 1 = 01100000 */ 0x06, /* 2 = 11011010 */ 0x5B, /* 3 = 11110010 */ 0x4F, /* 4 = 01100110 */ 0x66, /* 5 = 10110110 */ 0x6D, /* 6 = 10111110 */ 0x7D, /* 7 = 11100000 */ 0x07, /* 8 = 11111110 */ 0x7F, /* 9 = 11110110 */ 0x6F }; const unsigned char CHAR_NONE = 0x00; const unsigned char CHAR_A = 0x77; const unsigned char CHAR_B = 0x7C; const unsigned char CHAR_C = 0x39; const unsigned char CHAR_D = 0x5E; const unsigned char CHAR_E = 0x79; const unsigned char CHAR_F = 0x71; const unsigned char CHAR_G = numchar[9]; const unsigned char CHAR_H = 0x76; const unsigned char CHAR_I = numchar[1]; const unsigned char CHAR_L = 0x38; const unsigned char CHAR_N = 0x54; const unsigned char CHAR_O = 0x5C; const unsigned char CHAR_P = 0x73; const unsigned char CHAR_R = 0x50; const unsigned char CHAR_S = numchar[5]; const unsigned char CHAR_T = numchar[7]; const unsigned char CHAR_U = 0x3E; const unsigned char CHAR_Y = 0x6E; const unsigned char CHAR_Z = numchar[2]; const unsigned char txt_sel[4] = {CHAR_S, CHAR_E, CHAR_L, CHAR_NONE }; const unsigned char txt_cus[4] = {CHAR_C, CHAR_U, CHAR_S, CHAR_T }; const unsigned char txt_good[4] = {CHAR_G, CHAR_O, CHAR_O, CHAR_D }; const unsigned char txt_day[4] = {CHAR_NONE, CHAR_D, CHAR_A, CHAR_Y }; const unsigned char txt_done[4] = {CHAR_D, CHAR_O, CHAR_N, CHAR_E }; const unsigned char txt_pause0[4] = {CHAR_P, CHAR_A, CHAR_U, CHAR_S }; const unsigned char txt_pause1[4] = {CHAR_E, CHAR_D, CHAR_NONE, CHAR_NONE }; const unsigned char CHAR_DOTS = 0x80; const unsigned char CHAR_MINUS = 0x40; static void display_set_brightness(int disp, uint8_t brightness) { uint8_t cmd; tm_clk_start(disp); cmd = CMD_DISPLAY | CMD_DISPLAY_SWITCH_ON | (brightness & CMD_DISPLAY_PULSE_MASK); tm_write(disp, &cmd, 1, 0); tm_clk_stop(disp); } static void display_show(uint8_t disp, const unsigned char *c, int colon) { int i; unsigned char cmd = CMD_DATA; unsigned char payload[6]; tm_clk_start(disp); tm_write(disp, &cmd, 1, 1); tm_clk_stop(disp); sleep_ms(1); memcpy(payload + 1, c, 4); payload[0] = CMD_ADDRESS; if (colon == 1) { payload[2] |= CHAR_DOTS; } if (colon == 2) { payload[3] |= CHAR_DOTS; } tm_clk_start(disp); tm_write(disp, payload, 5, 1); tm_clk_stop(disp); sleep_ms(1); } static void display_clear(uint8_t disp) { const unsigned char zero[4] = {0, 0, 0, 0}; display_show(disp, zero, 0); } void num_to_digits(uint16_t n, uint8_t *out, int lead_zero) { memset(out, 0, 2); if (n > 99) return; if (n < 10) { if (lead_zero) out[0] = numchar[0]; out[1] = numchar[n]; } else { out[0] = numchar[n / 10]; out[1] = numchar[n % 10]; } } static void display_time(uint8_t disp, uint32_t t_sec, uint32_t t_usec, int col) { uint32_t minutes = 0; uint32_t cent = 0; unsigned char out[4]; int colon; memset(out, 0, 4); if (t_usec < 500000) colon = 1; if (t_sec > 59) { minutes = t_sec / 60; t_sec -= minutes * 60; num_to_digits(minutes, out, 0); num_to_digits(t_sec, out + 2, 1); display_show(disp, out, col); } else { cent = t_usec / 10000; num_to_digits(t_sec, out, 0); num_to_digits(cent, out + 2, 1); out[3] = CHAR_NONE; display_show(disp, out, col); } } /* States */ #define STATE_WALLCLOCK 0 /* Press B1 or B2: GAME_SET, long press KEY: clock adjust */ #define STATE_GAME_SET 1 /* Setup menu, confirm = GAME_READY */ #define STATE_GAME_CUSTOM 2 /* Setup custom time, confirm = GAME_READY */ #define STATE_GAME_READY 3 /* Press B1 or B2: GAME_PLAY P2/P1 */ #define STATE_GAME_PLAY_P1 4 /* Press B1: GAME_PLAY_P2, press B1+B2: GAME_PAUSE */ #define STATE_GAME_PLAY_P2 5 /* Bress B2: GAME_PLAY_P1, press B1+B2: GAME_PAUSE */ #define STATE_GAME_PAUSE 6 /* Press B1: GAME_PLAY_P2, B2: GAME_PLAY_P1, KEY: GAME_SET */ #define STATE_GAME_TIMEOUT 7 /* Press any key: GAME_SET */ #define STATE_CLOCK_ADJUST 8 /* */ static uint8_t State = STATE_WALLCLOCK; struct play_mode { uint32_t minutes; uint32_t increment_seconds; }; static struct play_mode Modes[] = { { 10, 0 }, { 15, 10}, { 30, 0 }, { 45, 45}, { 60, 0 }, { 5, 5 }, { 3, 2 }, { 2, 1 }, { 0, 0 } //Custom }; #define N_MODES 9 static int current_mode = 0; static void prev_mode(void) { if (current_mode == 0) current_mode = N_MODES - 1; else current_mode--; } static void next_mode(void) { if (current_mode == N_MODES - 1) current_mode = 0; else current_mode++; } struct game { int black_player; uint32_t p1_time; uint32_t p2_time; uint32_t move_count; }; static struct game Game = { 0, 0, 0, 0 }; static void game_start(void) { Game.p1_time = Modes[current_mode].minutes * 60000; Game.p2_time = Modes[current_mode].minutes * 60000; Game.move_count = 0; } #define BUTTON_DEBOUNCE_TIME 5 #define BUTTON_LONG_PRESS_TIME 80 uint32_t rotary_action(void) { uint32_t action = 0; if (gpio_get(ROT_S0)) action |= 1; if (gpio_get(ROT_S1)) action |= 2; return action; } #define ROT_UP 1 #define ROT_DOWN 2 int rot_updown(void) { static int rot_up = 0; static int rot_down = 0; static uint32_t rotary_pos = 0; uint32_t rotary_now; rotary_now = rotary_action(); if (rotary_now != rotary_pos) { if ((rotary_pos == 0 && rotary_now == 3) || (rotary_pos == 3 && rotary_now == 2) || (rotary_pos == 2 && rotary_now == 0) || (rotary_pos == 1 && rotary_now == 3)) { rot_down++; rot_up = 0; } if ((rotary_pos == 0 && rotary_now == 1) || (rotary_pos == 3 && rotary_now == 1) || (rotary_pos == 2 && rotary_now == 3) || (rotary_pos == 1 && rotary_now == 0)) { rot_up++; rot_down = 0; } rotary_pos = rotary_now; } if (rot_up > 1) { rot_up = 0; return ROT_UP; } if (rot_down > 1) { rot_down = 0; return ROT_DOWN; } return 0; } void poll_buttons(void) { static uint32_t b1_press_counter = 0; static uint32_t b2_press_counter = 0; static uint32_t key_press_counter = 0; int rot_ud = 0; uint8_t oldstate; uint8_t black_player = 0; static uint32_t last_update = 0; if (gpio_get(ROT_KEY) == 0) { printf("K ON\n"); key_press_counter++; } else { key_press_counter = 0; } if (gpio_get(BUTTON_P1) == 0) b1_press_counter++; else b1_press_counter = 0; if (gpio_get(BUTTON_P2) == 0) b2_press_counter++; else b2_press_counter = 0; oldstate = State; if (b1_press_counter >= BUTTON_DEBOUNCE_TIME) printf("b1: pressed\n"); if (b2_press_counter >= BUTTON_DEBOUNCE_TIME) printf("b2: pressed\n"); switch(State) { case STATE_WALLCLOCK: if ((b1_press_counter == BUTTON_DEBOUNCE_TIME) || (b2_press_counter == BUTTON_DEBOUNCE_TIME) || (key_press_counter == BUTTON_DEBOUNCE_TIME)) { State = STATE_GAME_SET; } break; case STATE_GAME_SET: rot_ud = rot_updown(); if (rot_ud == ROT_UP) { next_mode(); } if (rot_ud == ROT_DOWN) { prev_mode(); } // display_current_mode // if (key_press_counter == BUTTON_LONG_PRESS_TIME) { if (current_mode == N_MODES - 1) { } game_start(); State = STATE_GAME_READY; } break; case STATE_GAME_READY: if (b1_press_counter > 0) { /* P1 is black */ State = STATE_GAME_PLAY_P2; last_update = get_absolute_time_ms(); } else if (b2_press_counter > 0) { /* P2 is black */ State = STATE_GAME_PLAY_P1; last_update = get_absolute_time_ms(); } break; case STATE_GAME_PLAY_P1: { uint32_t now = get_absolute_time_ms(); int diff = now - last_update; if (diff >= Game.p1_time) { Game.p1_time = 0; State = STATE_GAME_TIMEOUT; break; } Game.p1_time -= diff; last_update = now; if (b1_press_counter > 0) { Game.p1_time += Modes[current_mode].increment_seconds * 1000; State = STATE_GAME_PLAY_P2; break; } if (key_press_counter > BUTTON_DEBOUNCE_TIME) { b2_press_counter = 0; key_press_counter = 0; State = STATE_GAME_PAUSE; } } break; case STATE_GAME_PLAY_P2: { uint32_t now = get_absolute_time_ms(); int diff = now - last_update; if (diff >= Game.p2_time) { Game.p2_time = 0; State = STATE_GAME_TIMEOUT; break; } Game.p2_time -= diff; last_update = now; if (b2_press_counter > 0) { Game.p2_time += Modes[current_mode].increment_seconds * 1000; State = STATE_GAME_PLAY_P1; } if (key_press_counter > BUTTON_DEBOUNCE_TIME) { State = STATE_GAME_PAUSE; } } break; case STATE_GAME_PAUSE: if (b2_press_counter == BUTTON_DEBOUNCE_TIME) { State = STATE_GAME_PLAY_P1; } else if (b1_press_counter == BUTTON_DEBOUNCE_TIME) { State = STATE_GAME_PLAY_P2; } break; case STATE_GAME_TIMEOUT: if (key_press_counter == BUTTON_LONG_PRESS_TIME) { State = STATE_WALLCLOCK; } break; } if (oldstate != State) { b1_press_counter = 0; b2_press_counter = 0; key_press_counter = 0; printf("State: %d\n", State); } } void system_boot(void) { int i; set_sys_clock_48mhz(); stdio_init_all(); /* LED and Douts */ gpio_init(LED_PIN); gpio_set_dir(LED_PIN, GPIO_OUT); gpio_put(LED_PIN,1); sleep_ms(1000); gpio_put(LED_PIN, 0); printf("Setting LEDs and digital outputs...\n"); gpio_init(DISP1_CLK); gpio_init(DISP1_DIO); gpio_init(DISP2_CLK); gpio_init(DISP2_DIO); gpio_set_dir(DISP1_CLK, GPIO_OUT); gpio_set_dir(DISP1_DIO, GPIO_OUT); gpio_set_dir(DISP2_CLK, GPIO_OUT); gpio_set_dir(DISP2_DIO, GPIO_OUT); gpio_set_pulls(DISP1_CLK, 1, 0); gpio_set_pulls(DISP1_DIO, 1, 0); gpio_set_pulls(DISP2_CLK, 1, 0); gpio_set_pulls(DISP2_DIO, 1, 0); gpio_put(DISP1_CLK, 1); gpio_put(DISP1_DIO, 1); gpio_put(DISP2_CLK, 1); gpio_put(DISP2_DIO, 1); printf("Done.\n"); /* BTN Dins */ printf("Setting buttons and digital inputs...\n"); gpio_init(BUTTON_P1); gpio_init(BUTTON_P2); gpio_init(ROT_KEY); gpio_init(ROT_S1); gpio_init(ROT_S0); gpio_set_dir(BUTTON_P1, GPIO_IN); gpio_set_dir(BUTTON_P2, GPIO_IN); gpio_set_dir(ROT_KEY, GPIO_IN); gpio_set_dir(ROT_S0, GPIO_IN); gpio_set_dir(ROT_S1, GPIO_IN); gpio_pull_down(ROT_KEY); gpio_pull_up(BUTTON_P1); gpio_pull_up(BUTTON_P2); printf("Wating up...\n"); sleep_ms(2000); printf("Done.\n"); } int main() { uint val = 1; uint8_t btn; uint16_t pot = 0; static uint16_t oldpot = 0; int i; system_boot(); printf("Loop started.\n"); display_set_brightness(1, 7); display_set_brightness(2, 7); display_clear(1); display_clear(2); while (1) { poll_buttons(); switch (State) { case STATE_WALLCLOCK: { display_set_brightness(1, 7); display_set_brightness(2, 7); display_show(2, txt_good, 0); display_show(1, txt_day, 0); } break; case STATE_GAME_SET: { struct play_mode *md = &Modes[current_mode]; display_show(2, txt_sel, 0); if (current_mode == N_MODES - 1) { display_show(1, txt_cus, 0); } else { uint8_t dn[4]; num_to_digits(md->minutes, dn, 0); num_to_digits(md->increment_seconds, dn + 2, 0); display_show(1, dn, 1); } } break; case STATE_GAME_CUSTOM: { struct play_mode *md = &Modes[N_MODES - 1]; uint8_t dn[4]; display_show(2, txt_cus, 0); num_to_digits(md->minutes, dn, 0); num_to_digits(md->increment_seconds, dn + 2, 0); display_show(1, dn, 1); } break; case STATE_GAME_READY: { char txt_2[4] = { CHAR_P, CHAR_U, CHAR_S, CHAR_H }; char txt_1[4] = { CHAR_B, CHAR_L, CHAR_A, CHAR_C }; display_show(2, txt_2, 0); display_show(1, txt_1, 0); } break; case STATE_GAME_PLAY_P1: { int col = 0; if ((Game.p1_time % 1000) < 500) col = 1; display_time(1, Game.p1_time / 1000, (Game.p1_time % 1000) * 1000, col); display_time(2, Game.p2_time / 1000, (Game.p2_time % 1000) * 1000, 1); display_set_brightness(1, 7); display_set_brightness(2, 1); } break; case STATE_GAME_PLAY_P2: { int col = 0; if ((Game.p2_time % 1000) < 500) col = 1; display_time(1, Game.p1_time / 1000, (Game.p1_time % 1000) * 1000, 1); display_time(2, Game.p2_time / 1000, (Game.p2_time % 1000) * 1000, col); display_set_brightness(1, 1); display_set_brightness(2, 7); } break; case STATE_GAME_PAUSE: { display_show(2, txt_pause0, 0); display_show(1, txt_pause1, 0); } break; case STATE_GAME_TIMEOUT: { uint32_t now = get_absolute_time_ms(); if ((now % 1000) > 500) { if (Game.p1_time == 0) { display_clear(1); } if (Game.p2_time == 0) { display_clear(2); } } else { if (Game.p1_time == 0) { display_time(1,0,0,1); } if (Game.p2_time == 0) { display_time(2,0,0,1); } } } break; default: display_show(2, txt_good, 0); display_show(1, txt_done, 0); } sleep_ms(10); } return 0; }