Browse Source

First working prototype

Daniele Lacamera 1 year ago
commit
6cb124d7da
4 changed files with 696 additions and 0 deletions
  1. 3 0
      .gitmodules
  2. 20 0
      CMakeLists.txt
  3. 672 0
      main.c
  4. 1 0
      pico-sdk

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "pico-sdk"]
+	path = pico-sdk
+	url = git@github.com:raspberrypi/pico-sdk.git

+ 20 - 0
CMakeLists.txt

@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.13)
+
+# initialize pico-sdk from submodule
+# note: this must happen before project()
+include(pico-sdk/pico_sdk_init.cmake)
+
+project(chessclock)
+
+# initialize the Raspberry Pi Pico SDK
+pico_sdk_init()
+
+
+add_executable(main main.c)
+
+target_link_libraries(main pico_stdlib)
+
+pico_enable_stdio_usb(main 1)
+pico_enable_stdio_uart(main 0)
+
+pico_add_extra_outputs(main)

+ 672 - 0
main.c

@@ -0,0 +1,672 @@
+#include <stdio.h>
+#include <stdint.h>
+#include "pico/stdlib.h"
+#include "hardware/gpio.h"
+#include <string.h>
+
+
+#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;
+}

+ 1 - 0
pico-sdk

@@ -0,0 +1 @@
+Subproject commit 426e46126b5a1efaea4544cdb71ab81b61983034