/* * _beep_brrr_01.xc * * Created on: 2. apr. 2021 * Author: teig */ #define INCLUDES #ifdef INCLUDES #include #include // slice #include // delay_milliseconds(200), XS1_TIMER_HZ etc #include // uint8_t #include // printf #include // memcpy #include // REFERENCE_PARAM(my_app_ports_t, my_app_ports) -> my_app_ports_t &my_app_ports #include // not etc. #include #include "_version.h" // First this.. #include "_globals.h" // ..then this #include "param.h" #include "i2c_client_task.h" #include "display_ssd1306.h" #include "core_graphics_adafruit_gfx.h" #include "core_graphics_font5x8.h" #include "_texts_and_constants.h" #include "button_press.h" #include "pwm_softblinker.h" #include "maths.h" #define DO_DEBUG_PRINT 1 #define DO_WARNINGS 1 #include "_print_macros_xc.h" #endif #include "mic_array.h" #include "AN00220_app_phase_aligned_example.h" typedef enum display_screen_name_t { SCREEN_TEST, SCREEN_ABOUT_AND_MIC, // LAST SEEN SCREEN_MIC, SCREEN_DARK // LAST } display_screen_name_t; typedef enum {is_on, is_off} display_state_e; typedef struct { display_state_e state; display_screen_name_t display_screen_name; display_screen_name_t display_screen_name_when_into_dark; char display_ts1_chars [SSD1306_TS1_DISPLAY_VISIBLE_CHAR_LEN]; // 84 chars for display needs 85 char buffer (with NUL) when sprintf is use (use SSD1306_TS1_DISPLAY_ALL_CHAR_LEN for full flexibility) int sprintf_numchars; unsigned screen_timeouts_since_last_button_countdown; // From NUM_TIMEOUTS_BEFORE_SCREEN_DARK to zero for SCREEN_DARK unsigned cnt; } display_context_t; typedef struct { bool now; unsigned timeouts_after_last_button_cnt; unsigned volume_step_factor; // increasing for every repeat taken, so that going to VOLUME_MIN_DB does not feel "endless" } repeat_t; typedef enum {was_none, was_button, was_timeout} last_action_e; typedef struct { bool valid; unsigned index; // [0..BUTTONS_NUM_CLIENTS] } button_t; typedef struct { button_t button; bool pressed_ever; bool button_action_taken; button_action_t button_action [BUTTONS_NUM_CLIENTS]; repeat_t repeat; last_action_e last_action; // AMUX=005 bool ignore_left_button_release_no_wake_from_dark; // AMUX=006 new. Since I started with LEFT_BUTTON take on released bool button_far_right_first_action; mic_result_t mic_result; } buttons_context_t; // For set_one_percent_ms and set_sofblink_percentages #define SOFTBLINK_RESTARTED_ONE_PERCENT_MS 1 // 1 ms goes to 100 in 0.1 seconds -> 5 blinks per second #define SOFTBLINK_RESTARTED_UNIT_MAX_PERCENTAGE 100 #define SOFTBLINK_RESTARTED_UNIT_MIN_PERCENTAGE 0 // #define SOFTBLINK_DARK_DISPLAY_ONE_PERCENT_MS 30 // 30 ms goes to 100 in 3.0 seconds #define SOFTBLINK_DARK_DISPLAY_MAX_PERCENTAGE 40 #define SOFTBLINK_DARK_DISPLAY_MIN_PERCENTAGE 10 // #define SOFTBLINK_LIT_DISPLAY_ONE_PERCENT_MS 10 // 10 ms goes to 100 in 1.0 seconds #define SOFTBLINK_LIT_DISPLAY_MAX_PERCENTAGE 80 #define SOFTBLINK_LIT_DISPLAY_MIN_PERCENTAGE 10 #define NUM_TIMEOUTS_PER_SECOND 2 #define NUM_TIMEOUTS_BEFORE_REPEAT (1.5 * NUM_TIMEOUTS_PER_SECOND) // 1,5 seconds // BUTTON_ACTION_PRESSED_FOR_LONG_TIMEOUT_MS must be so long that it does not interfere here // From makefile #if (DISPLAY_FAST_DARK==1) #if (WARNINGS==1) #warning Not standard display to dark timeout #endif #define NUM_TIMEOUTS_BEFORE_SCREEN_DARK (10 * NUM_TIMEOUTS_PER_SECOND) // 10 seconds, for screen_timeouts_since_last_button #elif (DISPLAY_FAST_DARK==0) #define NUM_TIMEOUTS_BEFORE_SCREEN_DARK ((5*60) * NUM_TIMEOUTS_PER_SECOND) // 5 minutes, for screen_timeouts_since_last_button #endif void do_display_params_zero (display_context_t &display_context) { Clear_All_Pixels_In_Buffer(); for (int index_of_char = 0; index_of_char < NUM_ELEMENTS(display_context.display_ts1_chars); index_of_char++) { display_context.display_ts1_chars [index_of_char] = ' '; } setTextColor(WHITE); setCursor(0,0); setTextSize(1); // SSD1306_TS1_LINE_CHAR_NUM gives 21 chars per line (but crlf takes two, if used) } char button_to_char (const button_t button) { char char_ret; if (button.valid) { char_ret = 'A' + button.index; } else { char_ret = '?'; } return char_ret; } // MUST NOT MODIFY ANY STATE VALUES! // 0104 kills real-time (see scope picture) bool // i2c_ok Display_screen ( display_context_t &display_context, buttons_context_t &buttons_context, client i2c_internal_commands_if if_i2c_internal_commands) { bool i2c_ok = true; bool do_display_print = false; do_display_params_zero (display_context); if (display_context.state == is_on) { switch (display_context.display_screen_name) { case SCREEN_TEST: { debug_print("%s\n", "Display_screen"); setTextSize(2); char button_str[2] = "\0"; // all become 0 button_str[0] = button_to_char (buttons_context.button); display_context.sprintf_numchars = sprintf (display_context.display_ts1_chars, "Beep-BRRR\n%s %u", button_str, display_context.cnt); display_context.cnt++; do_display_print = true; } break; case SCREEN_ABOUT_AND_MIC: { if (buttons_context.button_far_right_first_action) { debug_print("%s\n", "Display_screen"); buttons_context.button_far_right_first_action = false; const char char_OE_str[] = CHAR_OE_STR; // ¯ const char char_right_arrow_str[] = CHAR_RIGHT_ARROW_STR; setTextSize(1); display_context.sprintf_numchars = sprintf (display_context.display_ts1_chars, "MicArray board XMOS\nXC KODE %s\nV:%s xT:%s\n%syvind Teig", __DATE__, BEEP_BRRR_VERSION_STR, XTIMECOMPOSER_VERSION_STR, char_OE_str); // MicArray XMOS XA // XC CODE JUN 18 2020 // V:0.1.75 xT:14.4.1 // ¯yvind Teig } else { setTextSize(1); display_context.sprintf_numchars = sprintf (display_context.display_ts1_chars, "%s", "---"); } do_display_print = true; } break; case SCREEN_MIC: { if (not buttons_context.button_far_right_first_action) { debug_print( "CNT: %u MIN: %d (%d) MAX: %d (%d)\n", buttons_context.mic_result.cnt, buttons_context.mic_result.min, MIC_ARRAY_WORD_DB(buttons_context.mic_result.min), buttons_context.mic_result.max, MIC_ARRAY_WORD_DB(buttons_context.mic_result.max)); setTextSize(1); display_context.sprintf_numchars = sprintf (display_context.display_ts1_chars, "MIN %d\n%d dB\nMAX %d\n%d dB", buttons_context.mic_result.min, MIC_ARRAY_WORD_DB(buttons_context.mic_result.min), buttons_context.mic_result.max, MIC_ARRAY_WORD_DB(buttons_context.mic_result.max)); do_display_print = true; } else {} // No do_display_print } break; default: {} break; } } else { // is_off : no code } if (do_display_print) { display_print (display_context.display_ts1_chars, display_context.sprintf_numchars); // num chars not including NUL i2c_ok = writeToDisplay_i2c_all_buffer (if_i2c_internal_commands); // debug_print ("%s\n", i2c_ok ? "ok2" : "err2"); } else {} return i2c_ok; } void display_context_init (display_context_t &display_context) { display_context.display_screen_name = SCREEN_TEST; display_context.state = is_on; display_context.screen_timeouts_since_last_button_countdown = NUM_TIMEOUTS_BEFORE_SCREEN_DARK; display_context.cnt = 0; } void buttons_context_init (buttons_context_t &buttons_context) { buttons_context.button.valid = false; buttons_context.button_far_right_first_action = true; } // [[combinable]] void Handle_Beep_BRRRR_task ( server button_if_1 if_button [BUTTONS_NUM_CLIENTS], out port leds_00_07, client softblinker_if if_softblinker, client i2c_internal_commands_if if_i2c_internal_commands, client i2c_general_commands_if if_i2c_general_commands, out port p_display_notReset, // #if (MY_CONFIG == CONFIG_USE_INTERFACE) server mic_result_if if_mic_result) { #elif (MY_CONFIG == CONFIG_USE_CHAN) chanend c_mic_result) { #endif debug_print("%s\n", "Handle_Beep_BRRRR_task started"); unsigned one_percent_ms = SOFTBLINK_DARK_DISPLAY_ONE_PERCENT_MS; unsigned cnt = 0; timer tmr; time32_t time_ticks; bool do_Adafruit_SSD1306_i2c_begin = true; display_context_t display_context; buttons_context_t buttons_context; // STARTUP if_softblinker.set_one_percent_ms (one_percent_ms); if_softblinker.set_sofblink_percentages (SOFTBLINK_RESTARTED_UNIT_MAX_PERCENTAGE, SOFTBLINK_RESTARTED_UNIT_MIN_PERCENTAGE); buttons_context_init (buttons_context); display_context_init (display_context); Adafruit_GFX_constructor (SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT); tmr :> time_ticks; // Immeddiately; while(1) { select { case tmr when timerafter (time_ticks) :> void : { time_ticks += (XS1_TIMER_HZ/NUM_TIMEOUTS_PER_SECOND); buttons_context.button.valid = false; if (do_Adafruit_SSD1306_i2c_begin) { do_Adafruit_SSD1306_i2c_begin = false; Adafruit_SSD1306_i2c_begin (if_i2c_internal_commands, p_display_notReset); Display_screen (display_context, buttons_context, if_i2c_internal_commands); } if (display_context.screen_timeouts_since_last_button_countdown > 0) { display_context.screen_timeouts_since_last_button_countdown--; } else { // == 0 display_context.state = is_off;; Display_screen (display_context, buttons_context, if_i2c_internal_commands); } } break; // If the button is "stable", react when the I/O pin changes value case if_button[unsigned iof_button].button (const button_action_t button_action) : { // // 0 BUTTON_ACTION_VOID, // 1 BUTTON_ACTION_PRESSED, // 2 BUTTON_ACTION_PRESSED_FOR_LONG, // BUTTON_ACTION_PRESSED_FOR_LONG_TIMEOUT_MS // 3 BUTTON_ACTION_RELEASED // Not after BUTTON_ACTION_PRESSED_FOR_LONG one_percent_ms = (SOFTBLINK_RESTARTED_ONE_PERCENT_MS * (iof_button+1)); debug_print("A Handle_Beep_BRRRR_task [%u]=%u\n", iof_button, button_action); if_softblinker.set_one_percent_ms (one_percent_ms); cnt++; if (button_action == BUTTON_ACTION_PRESSED) { display_context.state = is_on; display_context.screen_timeouts_since_last_button_countdown = NUM_TIMEOUTS_BEFORE_SCREEN_DARK; buttons_context.button.valid = true; buttons_context.button.index = iof_button; if (iof_button == IOF_BUTTON_FAR_RIGHT) { display_context.display_screen_name = SCREEN_ABOUT_AND_MIC; } else { display_context.display_screen_name = SCREEN_TEST; } Display_screen (display_context, buttons_context, if_i2c_internal_commands); } } break; #if (MY_CONFIG == CONFIG_USE_INTERFACE) case if_mic_result.send_mic_data (const mic_result_t mic_result) : { buttons_context.mic_result = mic_result; // BREAKS TIMING, probably because interface returns at the end of the case, and this // needs to wait unitil Display_screen and by it writeToDisplay_i2c_all_buffer #elif (MY_CONFIG == CONFIG_USE_CHAN) case c_mic_result :> buttons_context.mic_result : { // TIMING of example_task not disturbed because the channel input "returns" immediately #endif display_context.state = is_on; display_context.screen_timeouts_since_last_button_countdown = NUM_TIMEOUTS_BEFORE_SCREEN_DARK; display_context.display_screen_name = SCREEN_MIC; Display_screen (display_context, buttons_context, if_i2c_internal_commands); } break; } } };