/* * _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 #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" #include "mic_array.h" #include "maths_fix16.h" // Needs stdint.h #include "F_Pattern_KnockCome.h" #include "mics_in_headset_out.h" #include "_Beep_BRRR_01.h" #define DO_DEBUG_PRINT 1 // 0 has consequences if (USE_XSCOPE == 1) #define DO_WARNINGS 1 #include "_print_macros_xc.h" #endif typedef enum display_screen_name_t { SCREEN_INIT, SCREEN_MIC, // SCREEN_MICS_CONFIG, SCREEN_HEADSET_GAIN_DB, SCREEN_ABOUT, // LAST SEEN SCREEN_DARK // LAST (NOT SEEN) } display_screen_name_t; typedef enum {is_on, is_off} display_state_e; #define DISPLAY_LOG_PACKETS_NUM 3 // 2 or larger 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; unsigned num_lost_overflowed; unsigned num_samples_arr [DISPLAY_LOG_PACKETS_NUM]; unsigned chan_mutual_packet_dim_arr[DISPLAY_LOG_PACKETS_NUM]; } 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 mic_result_t mic_result; unsigned KnockCome_cnt; // debug unsigned KnockCome_OverflowCntDiff_Max; // debug uint64_t KnockCome_OverflowCntDiff_Sum; // debug } 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, // No & no modification headset_gain_context_t headset_gain_context, // No & no modification 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_INIT: { debug_print("%s\n", "Display_screen"); setTextSize(2); display_context.sprintf_numchars = sprintf (display_context.display_ts1_chars, "Beep-BRRR"); display_context.cnt++; do_display_print = true; } break; case SCREEN_MIC: { // NOT USED } break; case SCREEN_HEADSET_GAIN_DB: { setTextSize(1); // See ...Beep_BRRR v0240 display KnockCome measurements.jpg display photos: SPEED_THROUGHPUT_TAG const unsigned KnockCome_OverflowCntDiff_Mean = (unsigned) (buttons_context.KnockCome_OverflowCntDiff_Sum / (uint64_t) buttons_context.KnockCome_cnt); if (headset_gain_context.headset_gain_is_off) { display_context.sprintf_numchars = sprintf (display_context.display_ts1_chars, "AV: -- dB\nOvfl: %u\n#: %u %u %u\nD: %u %u %u", display_context.num_lost_overflowed, display_context.num_samples_arr[0], display_context.num_samples_arr[1], display_context.num_samples_arr[2], display_context.chan_mutual_packet_dim_arr[0], display_context.chan_mutual_packet_dim_arr[1], display_context.chan_mutual_packet_dim_arr[2] ); } else { display_context.sprintf_numchars = sprintf (display_context.display_ts1_chars, "UT: %d dB %s\nOvfl: %u\n#: %u %u %u\nD: %u %u %u", headset_gain_context.amplitude_db_table[headset_gain_context.iof_gain], (headset_gain_context.iof_gain==IOF_HEADSET_GAIN_DB_MAX) ? "MAX" : "", display_context.num_lost_overflowed, display_context.num_samples_arr[0], display_context.num_samples_arr[1], display_context.num_samples_arr[2], display_context.chan_mutual_packet_dim_arr[0], display_context.chan_mutual_packet_dim_arr[1], display_context.chan_mutual_packet_dim_arr[2] ); } do_display_print = true; } break; case SCREEN_ABOUT: { 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 do_display_print = true; } break; default: {} break; } } else { // is_off do_display_print = true; // To make it dark } 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_debug (display_context_t &display_context) { for (unsigned ix=0; ix time_ticks; // Immediately; while(1) { [[ordered]] // Ok, but not strictly necessary select { case sch_ab :> data_sch_ab_one_sample : { data_ch_ba.menu_result = menu_result; if (data_sch_ab_one_sample.chan_mutual_packet_dim == NUM_000) { data_ch_ba.chan_mutual_packet_dim = NUM_000; // Ready for next = handshake ch_ba <: data_ch_ba; } else { DEBUG_1PORT_HIGH (p_scope_orange); data_ch_ba.chan_mutual_packet_dim = data_sch_ab_one_sample.chan_mutual_packet_dim; // For round trip // === ATOMIC TRANSACTION (BEGIN) === // SEND ACK: ch_ba <: data_ch_ba; // RECEIVE DATA // WAS SENT BY send_variant_protocol_then_shift_down receive_variant_protocol (sch_ab, data_ch_ba.chan_mutual_packet_dim, data_ch_ab_all_samples); for (unsigned ix=0; ix<(DISPLAY_LOG_PACKETS_NUM-1); ix++) { display_context.num_samples_arr [ix] = display_context.num_samples_arr [ix+1]; display_context.chan_mutual_packet_dim_arr[ix] = display_context.chan_mutual_packet_dim_arr[ix+1]; } display_context.num_samples_arr [DISPLAY_LOG_PACKETS_NUM-1] = data_ch_ab_all_samples.samples.rec_buf.nums.num_samples; display_context.chan_mutual_packet_dim_arr[DISPLAY_LOG_PACKETS_NUM-1] = data_ch_ba.chan_mutual_packet_dim; display_context.num_lost_overflowed = data_ch_ab_all_samples.samples.rec_buf.nums.num_lost_overflowed; // === ATOMIC TRANSACTION (END) === DEBUG_1PORT_LOW (p_scope_orange); } menu_result.headset_gain_updated = false; } break; case tmr when timerafter (time_ticks) :> void : { time_ticks += (XS1_TIMER_HZ/NUM_TIMEOUTS_PER_SECOND); buttons_context.button.valid = false; if (display_context.state == is_off) { // No code, don't exercise the display and I2C } else 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, headset_gain_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)); #if (USE_XSCOPE == 1) // Would have destroyd all timing #elif (USE_XSCOPE == 0) debug_print("A Handle_Beep_BRRRR_task_b [%u]=%u\n", iof_button, button_action); #endif 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; // switch (iof_button) { case IOF_BUTTON_LEFT: { set_headset_gain (do_headset_gain_zero, headset_gain_context); } break; case IOF_BUTTON_CENTER: { set_headset_gain (do_headset_gain_decrease, headset_gain_context); } break; case IOF_BUTTON_RIGHT: { set_headset_gain (do_headset_gain_increase, headset_gain_context); } break; case IOF_BUTTON_FAR_RIGHT: { set_headset_gain (do_headset_gain_default, headset_gain_context); } break; // default: fault by xC runtime } display_context.display_screen_name = SCREEN_HEADSET_GAIN_DB; // menu_result.clear_debug_vars = (iof_button == IOF_BUTTON_LEFT); // Return value too // if (menu_result.clear_debug_vars) { buttons_context_init (buttons_context); display_context_init_debug (display_context); } else {} menu_result.headset_gain = headset_gain_context.gain; menu_result.headset_gain_is_off = headset_gain_context.headset_gain_is_off; menu_result.headset_gain_updated = true; // SPEED_THROUGHPUT_TAG this is the one that seems to have a 100 ms delay Display_screen (display_context, buttons_context, headset_gain_context, if_i2c_internal_commands); data_ch_ba.menu_result = menu_result; // ch_ba <: data_ch_ba; } } break; } } };