diff --git a/f103-ledmatrix.pro b/f103-ledmatrix.pro index 05ff5f4..3a64fc6 100644 --- a/f103-ledmatrix.pro +++ b/f103-ledmatrix.pro @@ -91,7 +91,9 @@ HEADERS += \ lib/cmsis/DSP_Lib/Include/core_cmFunc.h \ lib/cmsis/DSP_Lib/Include/core_cmInstr.h \ lib/cmsis/DSP_Lib/Include/core_cmSimd.h \ - project/mode_audio.h + project/mode_audio.h \ + project/font.h \ + project/scrolltext.h SOURCES += \ lib/cmsis/core_cm3.c \ @@ -422,7 +424,8 @@ SOURCES += \ lib/cmsis/DSP_Lib/Source/TransformFunctions/arm_rfft_init_q31.c \ lib/cmsis/DSP_Lib/Source/TransformFunctions/arm_rfft_q15.c \ lib/cmsis/DSP_Lib/Source/TransformFunctions/arm_rfft_q31.c \ - project/mode_audio.c + project/mode_audio.c \ + project/scrolltext.c DISTFILES += \ style.astylerc \ diff --git a/project/font.h b/project/font.h new file mode 100644 index 0000000..ebe223a --- /dev/null +++ b/project/font.h @@ -0,0 +1,108 @@ +#ifndef FONT_H +#define FONT_H + +#define FONT_MIN ' ' +#define FONT_MAX '~' + +#define FONT_WIDTH 6 + +const unsigned char font[96][6] = { + {0x00,0x00,0x00,0x00,0x00,0x00}, // + {0x5c,0x00,0x00,0x00,0x00,0x00}, // ! + {0x06,0x00,0x06,0x00,0x00,0x00}, // " + {0x28,0x7c,0x28,0x7c,0x28,0x00}, // # + {0x5c,0x54,0xfe,0x54,0x74,0x00}, // $ + {0x44,0x20,0x10,0x08,0x44,0x00}, // % + {0x28,0x54,0x54,0x20,0x50,0x00}, // & + {0x06,0x00,0x00,0x00,0x00,0x00}, // ' + {0x38,0x44,0x00,0x00,0x00,0x00}, // ( + {0x44,0x38,0x00,0x00,0x00,0x00}, // ) + {0x02,0x07,0x02,0x00,0x00,0x00}, // * + {0x10,0x10,0x7c,0x10,0x10,0x00}, // + + {0xc0,0x00,0x00,0x00,0x00,0x00}, // , + {0x10,0x10,0x10,0x10,0x10,0x00}, // - + {0x40,0x00,0x00,0x00,0x00,0x00}, // . + {0x60,0x10,0x0c,0x00,0x00,0x00}, // / + {0x7c,0x64,0x54,0x4c,0x7c,0x00}, // 0 + {0x48,0x7c,0x40,0x00,0x00,0x00}, // 1 + {0x64,0x54,0x54,0x54,0x48,0x00}, // 2 + {0x44,0x54,0x54,0x54,0x6c,0x00}, // 3 + {0x3c,0x20,0x70,0x20,0x20,0x00}, // 4 + {0x5c,0x54,0x54,0x54,0x24,0x00}, // 5 + {0x7c,0x54,0x54,0x54,0x74,0x00}, // 6 + {0x04,0x04,0x64,0x14,0x0c,0x00}, // 7 + {0x7c,0x54,0x54,0x54,0x7c,0x00}, // 8 + {0x5c,0x54,0x54,0x54,0x7c,0x00}, // 9 + {0x44,0x00,0x00,0x00,0x00,0x00}, // : + {0xc4,0x00,0x00,0x00,0x00,0x00}, // ; + {0x10,0x28,0x44,0x00,0x00,0x00}, // < + {0x28,0x28,0x28,0x28,0x28,0x00}, // = + {0x44,0x28,0x10,0x00,0x00,0x00}, // > + {0x08,0x04,0x54,0x08,0x00,0x00}, // ? + {0x7c,0x44,0x54,0x54,0x5c,0x00}, // @ + {0x7c,0x24,0x24,0x24,0x7c,0x00}, // A + {0x7c,0x54,0x54,0x54,0x6c,0x00}, // B + {0x7c,0x44,0x44,0x44,0x44,0x00}, // C + {0x7c,0x44,0x44,0x44,0x38,0x00}, // D + {0x7c,0x54,0x54,0x54,0x44,0x00}, // E + {0x7c,0x14,0x14,0x14,0x04,0x00}, // F + {0x7c,0x44,0x44,0x54,0x74,0x00}, // G + {0x7c,0x10,0x10,0x10,0x7c,0x00}, // H + {0x44,0x44,0x7c,0x44,0x44,0x00}, // I + {0x60,0x40,0x40,0x44,0x7c,0x00}, // J + {0x7c,0x10,0x10,0x28,0x44,0x00}, // K + {0x7c,0x40,0x40,0x40,0x40,0x00}, // L + {0x7c,0x08,0x10,0x08,0x7c,0x00}, // M + {0x7c,0x08,0x10,0x20,0x7c,0x00}, // N + {0x38,0x44,0x44,0x44,0x38,0x00}, // O + {0x7c,0x14,0x14,0x14,0x08,0x00}, // P + {0x3c,0x24,0x64,0x24,0x3c,0x00}, // Q + {0x7c,0x14,0x14,0x14,0x68,0x00}, // R + {0x5c,0x54,0x54,0x54,0x74,0x00}, // S + {0x04,0x04,0x7c,0x04,0x04,0x00}, // T + {0x7c,0x40,0x40,0x40,0x7c,0x00}, // U + {0x0c,0x30,0x40,0x30,0x0c,0x00}, // V + {0x3c,0x40,0x30,0x40,0x3c,0x00}, // W + {0x44,0x28,0x10,0x28,0x44,0x00}, // X + {0x0c,0x10,0x60,0x10,0x0c,0x00}, // Y + {0x44,0x64,0x54,0x4c,0x44,0x00}, // Z + {0x7c,0x44,0x00,0x00,0x00,0x00}, // [ + {0x0c,0x10,0x60,0x00,0x00,0x00}, // "\" + {0x44,0x7c,0x00,0x00,0x00,0x00}, // ] + {0x00,0x01,0x00,0x01,0x00,0x00}, // ^ + {0x40,0x40,0x40,0x40,0x40,0x40}, // _ + {0x00,0x01,0x00,0x00,0x00,0x00}, // ` + {0x7c,0x24,0x24,0x24,0x7c,0x00}, // a + {0x7c,0x54,0x54,0x54,0x6c,0x00}, // b + {0x7c,0x44,0x44,0x44,0x44,0x00}, // c + {0x7c,0x44,0x44,0x44,0x38,0x00}, // d + {0x7c,0x54,0x54,0x54,0x44,0x00}, // e + {0x7c,0x14,0x14,0x14,0x04,0x00}, // f + {0x7c,0x44,0x44,0x54,0x74,0x00}, // g + {0x7c,0x10,0x10,0x10,0x7c,0x00}, // h + {0x44,0x44,0x7c,0x44,0x44,0x00}, // i + {0x60,0x40,0x40,0x44,0x7c,0x00}, // j + {0x7c,0x10,0x10,0x28,0x44,0x00}, // k + {0x7c,0x40,0x40,0x40,0x40,0x00}, // l + {0x7c,0x08,0x10,0x08,0x7c,0x00}, // m + {0x7c,0x08,0x10,0x20,0x7c,0x00}, // n + {0x38,0x44,0x44,0x44,0x38,0x00}, // o + {0x7c,0x14,0x14,0x14,0x08,0x00}, // p + {0x3c,0x24,0x64,0x24,0x3c,0x00}, // q + {0x7c,0x14,0x14,0x14,0x68,0x00}, // r + {0x5c,0x54,0x54,0x54,0x74,0x00}, // s + {0x04,0x04,0x7c,0x04,0x04,0x00}, // t + {0x7c,0x40,0x40,0x40,0x7c,0x00}, // u + {0x0c,0x30,0x40,0x30,0x0c,0x00}, // v + {0x3c,0x40,0x30,0x40,0x3c,0x00}, // w + {0x44,0x28,0x10,0x28,0x44,0x00}, // x + {0x0c,0x10,0x60,0x10,0x0c,0x00}, // y + {0x44,0x64,0x54,0x4c,0x44,0x00}, // z + {0x10,0x7c,0x44,0x00,0x00,0x00}, // { + {0x6c,0x00,0x00,0x00,0x00,0x00}, // | + {0x44,0x7c,0x10,0x00,0x00,0x00}, // } + {0x02,0x01,0x02,0x01,0x00,0x00}, // ~ + {0x00,0x00,0x00,0x00,0x00,0x00} +}; + +#endif // FONT_H diff --git a/project/main.c b/project/main.c index 54d142a..8c3d754 100644 --- a/project/main.c +++ b/project/main.c @@ -18,18 +18,84 @@ #include "arm_math.h" #include "mode_audio.h" +#include "scrolltext.h" + +/** Functional mode */ +typedef enum { + MODE_AUDIO, + MODE_LIFE, + MODE_SNAKE, + MODE_END, +} GameMode; static void poll_subsystems(void); +static void gamepad_rx(ComIface *iface); +static void boot_animation(void); +static void switch_mode(void *unused); // circle b/w modes +static void activate_mode(void); // activate currently selected mode static task_pid_t capture_task_id; -static void boot_animation(void) +static GameMode app_mode; + + +static void switch_mode(void *unused) { - // Boot animation (for FFT) - for(int i = 0; i < 16; i++) { - dmtx_set(dmtx, i, 0, 1); + (void)unused; + + if (++app_mode == MODE_END) { + app_mode = 0; + } + + activate_mode(); +} + + +#define SCROLL_STEP 20 +static void activate_mode(void) +{ + // --- Audio FFT mode --- + + if (app_mode == MODE_AUDIO) { + info("MODE: Audio"); + + scrolltext("Audio FFT", SCROLL_STEP); + + audio_mode_active = true; + enable_periodic_task(capture_task_id, true); + } else { + audio_mode_active = false; + enable_periodic_task(capture_task_id, false); + } + + // --- Game Of Life --- + + if (app_mode == MODE_LIFE) { + info("MODE: Life"); + + scrolltext("Game of Life", SCROLL_STEP); + + dmtx_clear(dmtx); + dmtx_set(dmtx, 5, 5, 1); dmtx_show(dmtx); - delay_ms(25); + // + } else { + // + } + + // --- Snake Minigame --- + + if (app_mode == MODE_SNAKE) { + info("MODE: Snake"); + + scrolltext("Snake", SCROLL_STEP); + + dmtx_clear(dmtx); + dmtx_set(dmtx, 13, 13, 1); + dmtx_show(dmtx); + // + } else { + // } } @@ -42,11 +108,10 @@ int main(void) banner_info("(c) Ondrej Hruska, 2016"); boot_animation(); + gamepad_iface->rx_callback = gamepad_rx; - //debug_iface->rx_callback = rx_char; capture_task_id = add_periodic_task(capture_audio, NULL, 10, false); - ms_time_t last; while (1) { if (ms_loop_elapsed(&last, 500)) { @@ -80,18 +145,31 @@ static void poll_subsystems(void) } -//static void rx_char(ComIface *iface) -//{ -// uint8_t ch; -// while(com_rx(iface, &ch)) { -// if (ch == 'p') { -// info("PRINT_NEXT"); -// print_next_fft = true; -// } -// } -//} +static void boot_animation(void) +{ + // Boot animation (for FFT) + for(int i = 0; i < 16; i++) { + dmtx_set(dmtx, i, 0, 1); + dmtx_show(dmtx); + delay_ms(25); + } +} +static void gamepad_rx(ComIface *iface) +{ + uint8_t ch; + while(com_rx(iface, &ch)) { +// com_tx(debug_iface, ch); +// com_tx(debug_iface, '\n'); + + /* SELECT */ + if (ch == 'I') { + tq_post(switch_mode, NULL); + } + } +} + //void dlnk_rx(SBMP_Datagram *dg) //{ diff --git a/project/mode_audio.c b/project/mode_audio.c index aa45382..94b11a8 100644 --- a/project/mode_audio.c +++ b/project/mode_audio.c @@ -4,6 +4,8 @@ #include "dotmatrix.h" +bool audio_mode_active = true; + static volatile bool capture_pending = false; static volatile bool print_next_fft = false; @@ -59,7 +61,12 @@ void DMA1_Channel1_IRQHandler(void) TIM_Cmd(TIM3, DISABLE); ADC_DMACmd(ADC1, DISABLE); - tq_post(audio_capture_done, NULL); + if (audio_mode_active) { + tq_post(audio_capture_done, NULL); + } else { + // unset 'pending' + capture_pending = false; + } } @@ -143,6 +150,7 @@ void capture_audio(void *unused) { (void)unused; if (capture_pending) return; + if (! audio_mode_active) return; capture_pending = true; diff --git a/project/mode_audio.h b/project/mode_audio.h index 06de92b..6c979a7 100644 --- a/project/mode_audio.h +++ b/project/mode_audio.h @@ -3,6 +3,8 @@ #include "main.h" +extern bool audio_mode_active; + void capture_audio(void *unused); #endif // MODE_AUDIO_H diff --git a/project/scrolltext.c b/project/scrolltext.c new file mode 100644 index 0000000..103f757 --- /dev/null +++ b/project/scrolltext.c @@ -0,0 +1,59 @@ +#include "scrolltext.h" + +#include "font.h" +#include "com/debug.h" + +static void printtext(const char *text, int x, int y) +{ + dmtx_clear(dmtx); + dmtx_show(dmtx); + + int totalX = 0; + + for (int textX = 0; textX < (int)strlen(text); textX++) { + uint8_t ch = (uint8_t)text[textX]; + if (ch < FONT_MIN) ch = '?'; + if (ch > FONT_MAX) ch = '?'; + ch -= ' '; // normalize for font table + + if (ch == 0) { // space + totalX += 3; + continue; + } + + // one letter + uint8_t blanks = 0; + + // skip empty space on right + for (int charX = FONT_WIDTH-1; charX >= 0; charX--) { + uint8_t col = font[ch][charX]; + if (col == 0x00) { + blanks++; + } else { + break; + } + } + + for (int charX = 0; charX < FONT_WIDTH - blanks; charX++) { + uint8_t col = font[ch][charX]; + for (int charY = 0; charY < 8; charY++) { + dmtx_set(dmtx, x + totalX, y + 8 - charY, (col >> charY) & 1); + } + totalX++; + } + + totalX++; // gap + } + dmtx_show(dmtx); +} + +void scrolltext(const char *text, ms_time_t step) +{ + (void)step; + + for (int i = 0; i < (int)strlen(text)*FONT_WIDTH + 15; i++) { + if (i > 0) delay_ms(step); + + printtext(text, 15-i, 4); + } +} diff --git a/project/scrolltext.h b/project/scrolltext.h new file mode 100644 index 0000000..06ab3a4 --- /dev/null +++ b/project/scrolltext.h @@ -0,0 +1,10 @@ +#ifndef SCROLLTEXT_H +#define SCROLLTEXT_H + +#include "main.h" +#include "dotmatrix.h" +#include "utils/timebase.h" + +void scrolltext(const char *text, ms_time_t step); + +#endif // SCROLLTEXT_H