/* * mics_in_headset_out.g * * Created on: 22. apr. 2021 * Author: teig * * Needed include files, see bottom of this file */ #ifndef MICS_IN_HEADSET_OUT_H_ #define MICS_IN_HEADSET_OUT_H_ // ============= // MICS_IN PART // ============= #ifndef MIC_ARRAY_WORD_LENGTH_SHORT #error MIC_ARRAY_WORD_LENGTH_SHORT not defined #else // in mic_array_frame.h in lib_mic_array // // From "mic_array_conf.h" in lib_mic_array user guide PDF // MIC_ARRAY_WORD_LENGTH_SHORT // If this define is set to non-zero then this configures the output word length to be a 16 bit // short otherwise its left as 32 bit word length output. All internal processing will be done at // 32 bits, only during the write to frame memory will the truncation happen. // This is done in mic_array_init_time_domain_frame and mic_array_get_next_time_domain_frame // #if (MIC_ARRAY_WORD_LENGTH_SHORT==0) // THIS typedef int32_t mic_sample_t; // Data in 16 first LSB bits, .. #define MIC_ARRAY_WORD_DB(arg) int32_dB(arg) #define MIC_ARRAY_WORD_MIN INT_MIN #define MIC_ARRAY_WORD_MAX INT_MAX #elif (MIC_ARRAY_WORD_LENGTH_SHORT==1) typedef int16_t mic_sample_t; // ..here these 16 bits are all that's present #define MIC_ARRAY_WORD_DB(arg) int16_dB(arg) #define MIC_ARRAY_WORD_MIN SHRT_MIN #define MIC_ARRAY_WORD_MAX SHRT_MAX #endif #endif typedef int mic_data_t; // Must be int since mic_array_decimator_config_t has int as field 2 typedef unsigned mics_direction_t; // [NUM_CIRCULAR_MICS] = [0..(NUM_CIRCULAR_MICS-1)] typedef struct { bool use_beamforming; // USE_BEAMFORMING == 1 mics_direction_t mics_direction; } directive_mics_t; #if (USE_BEAMFORMING == 1) // Always associate with MIC_ARRAY_NUM_MICS==8 typedef unsigned delay_mics_num_samples_t; #define MAX_DELAY_SAMPLE_CNT 16 #define NUM_PHYSICAL_MICS (MIC_ARRAY_NUM_MICS - 1) // 7 #define NUM_CIRCULAR_MICS (NUM_PHYSICAL_MICS - 1) // 6 // Based on the spreadsheet mic_array_das_beamformer_calcs.xls, // which can be found in the root directory of this app #define DELAY_MICS_HEAD_DEFAULT 0 #define DELAY_SAMPLE_COUNT_OF_0 43 #define DELAY_SAMPLE_COUNT_ONE_METER_THIRTY_DEGREES { 0, 3, 8, 11, 8, 3 } // NUM_CIRCULAR_MICS #elif (USE_BEAMFORMING == 0) // Always associate with MIC_ARRAY_NUM_MICS==4 #define NUM_PHYSICAL_MICS MIC_ARRAY_NUM_MICS #else #error USE_BEAMFORMING error #endif // #define START_CIRCULAR_MICS 3 // Assume this is the meaning in AN00219 in formula in set_dir #ifndef MIC_ARRAY_NUM_MICS #error mic_array_conf.h not seen #endif #define NUM_4MICS_DECIMATOR_CHANS 4 // (DECIMATOR_CH_COUNT) Simply because mic_array_decimate_to_pcm_4ch takes 4 mics #define NUM_4MIC_DECIMATORS (MIC_ARRAY_NUM_MICS / NUM_4MICS_DECIMATOR_CHANS) // (DECIMATOR_COUNT) 8 channels requires 2 decimators: // // 2/1 when MIC_ARRAY_NUM_MICS == 8/4 #define FRAME_BUFFER_COUNT 2 // AN00220/AN00219: The minimum of 2 will suffice for these examples (when DECIMATOR_NO_FRAME_OVERLAP) #define NUM_DATA_X (NUM_4MIC_DECIMATORS * NUM_4MICS_DECIMATOR_CHANS) // 8/4 when MIC_ARRAY_NUM_MICS == 8/4 #define NUM_DATA_Y (THIRD_STAGE_COEFS_PER_STAGE * DECIMATION_FACTOR) // 192 = 32*6 // ================ // HEADSET_OUT PART // ================ typedef sfix16_t headset_gain_t; // signed 32 bits even if never below 0, sine we're multiplying with mic_data_t #define _HEADSET_GAIN_UNITY_BITPOS FIX16_UNITY_BITPOS #define _HEADSET_GAIN_UNITY (1 << _HEADSET_GAIN_UNITY_BITPOS) typedef enum do_headset_gain_e { do_headset_gain_zero, do_headset_gain_default, do_headset_gain_max, do_headset_gain_increase, do_headset_gain_decrease } do_headset_gain_e; // Observe that AN00219 and mic_array_conf.h MIC_ARRAY_FIXED_GAIN work with shifting // bits (ie. div/mul by 2,4,8,16 etc), not a dB scale as I have chosen: // // https://en.wikipedia.org/wiki/Half-power_point // https://en.wikipedia.org/wiki/Decibel and https://www.rapidtables.com/electric/decibel.html // // Amplitude ratio 20*log10(Vin/Vout) // #define NUM_AMPLITUDE_STEPS 11 // 22 #define _IOF_HEADSET_GAIN_DB_OFF 0 #define _IOF_HEADSET_GAIN_DB_UNITY 4 // 11 #define _IOF_HEADSET_GAIN_DB_MAX (NUM_AMPLITUDE_STEPS-1) // #define SINE_480HZ_DIV_FACTOR 1000 // -60dB. To protect the ears and the headset #define SINE_480HZ_SHL_BITS_FACTOR 10 // About -60dB. To protect the ears and the headset // #define AMPLITUDE_FIX16_UNITY_FACTORS_TABLE { \ 0, /* OFF */ \ /* _HEADSET_GAIN_UNITY / 1000, -80 dB */ \ /* _HEADSET_GAIN_UNITY * 0.00316, -50 dB */ \ /* _HEADSET_GAIN_UNITY / 100, -40 dB */ \ /* _HEADSET_GAIN_UNITY * 0.032, -30 dB */ \ /* _HEADSET_GAIN_UNITY / 10, -20 dB */ \ _HEADSET_GAIN_UNITY * 0.316, /* -10 dB */ \ _HEADSET_GAIN_UNITY * 0.501, /* -6 dB (HALF 0.5 is -6.0206 dB) */ \ _HEADSET_GAIN_UNITY * 0.708, /* -3 dB (1/Ã2 is -3.0103 dB) */ \ /* _HEADSET_GAIN_UNITY * 0.794, -2 dB */ \ /* _HEADSET_GAIN_UNITY * 0.891, -1 dB */ \ _HEADSET_GAIN_UNITY * 1, /* 0 dB [_IOF_HEADSET_GAIN_DB_UNITY] */ \ /* _HEADSET_GAIN_UNITY * 1.122, 1 dB */ \ /* _HEADSET_GAIN_UNITY * 1.259, 2 dB */ \ _HEADSET_GAIN_UNITY * 1.413, /* 3 dB (Ã2 is 3.0103 dB ) */ \ _HEADSET_GAIN_UNITY * 1.995, /* 6 dB (DOUBLE 2 is 6.0206 dB) */ \ _HEADSET_GAIN_UNITY * 3.162, /* 10 dB */ \ _HEADSET_GAIN_UNITY * 10, /* 20 dB */ \ _HEADSET_GAIN_UNITY * 31.623, /* 30 dB */ \ _HEADSET_GAIN_UNITY * 100, /* 40 dB */ \ /* _HEADSET_GAIN_UNITY * 316.228, 50 dB */ \ /* _HEADSET_GAIN_UNITY * 1000 60 dB */ \ }; #define IOF_HEADSET_GAIN_DB_DEFAULT _IOF_HEADSET_GAIN_DB_UNITY #define IOF_HEADSET_GAIN_DB_MAX _IOF_HEADSET_GAIN_DB_MAX #define IOF_HEADSET_GAIN_DB_OFF _IOF_HEADSET_GAIN_DB_OFF // #define HEADSET_GAIN_DEFAULT _HEADSET_GAIN_UNITY #define HEADSET_GAIN_BITPOS_DEFAULT _HEADSET_GAIN_UNITY_BITPOS // #define HEADSET_GAIN_IS_OFF_VALUE_DEFAULT false; #define AMPLITUDE_DB_TABLE \ { \ -99, /* OFF */ \ -10, \ -6, \ -3, \ 0, /* [_IOF_HEADSET_GAIN_DB_UNITY] */ \ 3, \ 6, \ 10, \ 20, \ 30, \ 40, \ }; typedef struct { unsigned iof_gain; bool headset_gain_is_off; headset_gain_t gain; mic_data_t amplitude_fix16_unity_factors_table [NUM_AMPLITUDE_STEPS]; signed amplitude_db_table [NUM_AMPLITUDE_STEPS]; } headset_gain_context_t; void set_headset_gain ( const do_headset_gain_e do_headset_gain, headset_gain_context_t &ctx); #define I2C_ADDRESS_OF_DAC 0x4A // CS43L21, see page 5 of mic_array board manual #define CS41L21_DAC_ETH_RESET 0x03 // 4F0=DAC_RST_N Audio CS43L21 // 4F1=ETH_RST_N Ethernet LAN8710A // typedef enum i2c_reg_addr_dac_headphones_cs43l21_e { CS41L21_DAC_REG_01_CHIP_ID_AND_REVISION = 0x01, // USED CS41L21_DAC_REG_02_POWER_CONTROL_1 = 0x02, // USED CS41L21_DAC_REG_03_SPEED_CONTROL = 0x03, // USED CS41L21_DAC_REG_04_INTERFACE_CONTROL = 0x04, CS41L21_DAC_REG_08_DAC_OUTPUT_CONTROL = 0x08, CS41L21_DAC_REG_09_DAC_CONTROL = 0x09, CS41L21_DAC_REG_10_MIXER_VOLUME_CONTROL_PCMA = 0x10, // USED CS41L21_DAC_REG_11_MIXER_VOLUME_CONTROL_PCMB = 0x11, CS41L21_DAC_REG_12_BEEP_FREQUENCY_AND_TIMING_CONFIGURATION = 0x12, CS41L21_DAC_REG_13_BEEP_OFF_TIME_AND_VOLUME = 0x13, CS41L21_DAC_REG_14_BEEP_CONFIGURATION_AND_TONE_CONFIGURATION = 0x14, CS41L21_DAC_REG_15_TONE_CONTROL = 0x15, CS41L21_DAC_REG_16_VOLUME_CONTROL_AOUTA = 0x16, CS41L21_DAC_REG_17_VOLUME_CONTROL_AOUTB = 0x17, CS41L21_DAC_REG_18_PCM_CHANNEL_MIXER = 0x18, CS41L21_DAC_REG_19_LIMITER_THRESHOLD_SZC_DISABLE = 0x19, CS41L21_DAC_REG_1A_LIMITER_RELEASE_RATE_REGISTER = 0x1A, CS41L21_DAC_REG_1B_LIMITER_ATTACK_RATE_REGISTER = 0x1B, CS41L21_DAC_REG_20_STATUS = 0x20, CS41L21_DAC_REG_21_CHARGE_PUMP_FREQUENCY = 0x21 } i2c_reg_addr_dac_headphones_cs43l21_e; #define I2C_ADDRESS_OF_PLL 0x4E // CS2100-CP, see page 5 of mic_array board manual // typedef enum i2c_reg_addr_pll_cs2100cp_e { CS2100CP_PLL_REG_01_DEVICE_ID_AND_REVISION = 0x01, CS2100CP_PLL_REG_02_DEVICE_CONTROL = 0x02, CS2100CP_PLL_REG_03_DEVICE_CONFIGURATION_1 = 0x03, CS2100CP_PLL_REG_05_GLOBAL_CONFIGURATION = 0x05, CS2100CP_PLL_REG_06_RATIO = 0x06, CS2100CP_PLL_REG_07_RATIO = 0x07, CS2100CP_PLL_REG_08_RATIO = 0x08, CS2100CP_PLL_REG_09_RATIO = 0x09, CS2100CP_PLL_REG_16_FUNCTION_CONFIGURATION_1 = 0x16, CS2100CP_PLL_REG_17_FUNCTION_CONFIGURATION_2 = 0x17, CS2100CP_PLL_REG_1E_FUNCTION_CONFIGURATION_3 = 0x1E } i2c_reg_addr_pll_cs2100cp_e; [[distributable]] void i2s_task ( server i2s_callback_if i2s, // From i2s_master0 client i2c_master_if i2c, port p_rst_shared, chanend c_audio); // ============= // COMMON IN-OUT // ============= typedef struct mic_result_t { // TODO: needed? unsigned num_samples; mic_sample_t sample_if_one_only; // Only valid if num_samples==1 int output_to_dac; // Test, did not help time32_t used_ticks; // debug time32_t max_used_ticks; // debug } mic_result_t; typedef struct menu_result_t { bool headset_gain_updated; bool headset_gain_is_off; headset_gain_t headset_gain; bool directive_mics_valid; directive_mics_t directive_mics; bool clear_debug_vars; } menu_result_t; #define MAX_WAIT_TIME_TO_SEND_DATA_MS 40 // Dependent on SPEED_THROUGHPUT_TAG, I2C_EXTERNAL_SPEED_K_BITS_PER_SECOND // Ver 0414 5Jan2022 (on button push and writing to display): // 33 -> [264]@ 48 kHz gives num_lost_overflowed 1 or 2 // 34 -> [272]@ 48 kHz gives num_lost_overflowed always zero // 40 -> [320]@ 48 kHz good margin #define SAMPLES_BUF_DIM ((SAMPLING_FREQUENCY_HZ * MAX_WAIT_TIME_TO_SEND_DATA_MS) / (1000 * USE_EVERY_N_SAMPLE)) typedef struct { mic_sample_t arr[SAMPLES_BUF_DIM]; } sch_ab_struct_with_array_t; #define SEND_VALUE_NEEDED_TICKS (6 * XS1_TIMER_MHZ) // Was 6 us for KnockCome. TODO: Not tested for "implementation F" (lkie 0413) // 6 us means that calcs should get done in 20-6=14 us: // SAMPLING_FREQUENCY_HZ 48000 -> f = (1/48000) = 20.833 us // TESTED to be between 160 ns and 1.6 us, version 0235 #define SPEED_THROUGHPUT_TAG // Not used in code, only made to search for. Depends on: // I2C_EXTERNAL_SPEED_K_BITS_PER_SECOND, SAMPLES_BUF_DIM // IMPLEMENTATION F // typedef unsigned num_mic_samples_t; typedef unsigned chan_mutual_packet_dim_e; // Using defines, since preprocessor will not resolve the tests below if not: // // Modify in, should this list be changed: // get_chan_mutual_packet_dim // send_variant_protocol_then_shift_down // receive_variant_protocol // #define NUM_000 0 #define NUM_001 1 #define NUM_002 2 #define NUM_004 4 #define NUM_008 8 #define NUM_016 16 #define NUM_032 32 #define NUM_064 64 #define NUM_NORM 128 // Max sent over channel when requested, phase II (N times first) (*) #define NUM_BUF SAMPLES_BUF_DIM // Max buffered // (*) Tested with 70, 92, 128 see version 0407, 0498, 0409. All work #if (NUM_NORM >= NUM_BUF) #error Buffer must be the largest #endif #if ((SAMPLES_BUF_DIM - NUM_NORM) > (4 * NUM_NORM)) // 320-128=192 < 4*128=512 ok now #error Wrong strategy? Use ring buffer instead? #endif #define MAX_SIZEOF_NONBLOCKING_STREAMING_CHAN (8) // 2 words * 4 bytes typedef struct { chan_mutual_packet_dim_e chan_mutual_packet_dim; mic_sample_t mic_sample; } sch_ab_one_sample_t; // Observe MAX_SIZEOF_NONBLOCKING_STREAMING_CHAN, xassert done typedef struct { mic_sample_t samples [NUM_001]; } mic_samples_001_t; typedef struct { mic_sample_t samples [NUM_002]; } mic_samples_002_t; typedef struct { mic_sample_t samples [NUM_004]; } mic_samples_004_t; typedef struct { mic_sample_t samples [NUM_008]; } mic_samples_008_t; typedef struct { mic_sample_t samples [NUM_016]; } mic_samples_016_t; typedef struct { mic_sample_t samples [NUM_032]; } mic_samples_032_t; typedef struct { mic_sample_t samples [NUM_064]; } mic_samples_064_t; typedef struct { mic_sample_t samples [NUM_NORM]; } mic_samples_norm_t; typedef struct { mic_sample_t samples [NUM_BUF]; } mic_samples_buf_t; typedef struct { unsigned num_samples; // The number of valid samples unsigned num_lost_overflowed; } ch_ab_nums_s; typedef struct { ch_ab_nums_s nums; mic_samples_001_t mic_samples; } ch_ab_sample_001_t; typedef struct { ch_ab_nums_s nums; mic_samples_002_t mic_samples; } ch_ab_sample_002_t; typedef struct { ch_ab_nums_s nums; mic_samples_004_t mic_samples; } ch_ab_sample_004_t; typedef struct { ch_ab_nums_s nums; mic_samples_008_t mic_samples; } ch_ab_sample_008_t; typedef struct { ch_ab_nums_s nums; mic_samples_016_t mic_samples; } ch_ab_sample_016_t; typedef struct { ch_ab_nums_s nums; mic_samples_032_t mic_samples; } ch_ab_sample_032_t; typedef struct { ch_ab_nums_s nums; mic_samples_064_t mic_samples; } ch_ab_sample_064_t; typedef struct { ch_ab_nums_s nums; mic_samples_norm_t mic_samples; } ch_ab_sample_norm_t; typedef struct { ch_ab_nums_s nums; mic_samples_buf_t mic_samples; } ch_ab_sample_buf_t; typedef struct { // sizeof these individually: 001: 12, 004: 24, 128: 520 union { // With full extra round, no DAC output ch_ab_sample_001_t rec_001; // ch_ab_sample_002_t rec_002; // ch_ab_sample_004_t rec_004; // 1.3 us ch_ab_sample_008_t rec_008; // 1.8 us ch_ab_sample_016_t rec_016; ch_ab_sample_032_t rec_032; ch_ab_sample_064_t rec_064; ch_ab_sample_norm_t rec_norm; // "NORM/norm" because some of these would be sent first, then one of the smaller ch_ab_sample_buf_t rec_buf; } samples; } ch_ab_all_samples_t; typedef struct { chan_mutual_packet_dim_e chan_mutual_packet_dim; menu_result_t menu_result; } ch_ba_t; typedef unsigned ch_ba_init_synch_t; // ==== ONLY FOR THE DAC OUT ==== // #define MASTER_PLL_24576KHZ_TO_PDM_CLOCK_DIVIDER (4) #define MASTER_CLOCK_FREQUENCY_HZ 24576000 #define PDM_CLOCK_FREQUENCY_HZ (MASTER_CLOCK_FREQUENCY_HZ / (2 * MASTER_PLL_24576KHZ_TO_PDM_CLOCK_DIVIDER)) #define DAC_OUTPUT_SAMPLE_FREQUENCY_HZ (PDM_CLOCK_FREQUENCY_HZ / (32 * DECIMATION_FACTOR)) #define MCLK_BCLK_RATIO (DECIMATION_FACTOR * MASTER_PLL_24576KHZ_TO_PDM_CLOCK_DIVIDER) // ####_####_##### from these formulas: // mclk_bclk_ratio = (MASTER_CLOCK_FREQUENCY_HZ/DAC_OUTPUT_SAMPLE_FREQUENCY_HZ)/64 // Reducing formula above: // D DECIMATION_FACTOR // A (4) MASTER_PLL_24576KHZ_TO_PDM_CLOCK_DIVIDER // B 24576000 MASTER_CLOCK_FREQUENCY_HZ // C (B / (2 * A)) (MASTER_CLOCK_FREQUENCY_HZ / (2 * MASTER_PLL_24576KHZ_TO_PDM_CLOCK_DIVIDER)) // E (C / (32 * D)) (PDM_CLOCK_FREQUENCY_HZ / (32 * DECIMATION_FACTOR)) // // MCLK_BCLK_RATIO = (B/E)/64 = D * A = // DECIMATION_FACTOR * MASTER_PLL_24576KHZ_TO_PDM_CLOCK_DIVIDER void send_variant_protocol_then_shift_down ( streaming chanend sch_out, const chan_mutual_packet_dim_e chan_mutual_packet_dim, ch_ab_all_samples_t &data_ch_ab_all_samples); void receive_variant_protocol ( streaming chanend sch_in, const chan_mutual_packet_dim_e chan_mutual_packet_dim, ch_ab_all_samples_t &data_ch_ab_all_samples); void mics_in_headset_out_task_a ( streaming chanend ds_output_is_ptr_chan [NUM_4MIC_DECIMATORS], // [2/1] when MIC_ARRAY_NUM_MICS == 8/4 mic_data_t mics_data [NUM_DATA_X] [NUM_DATA_Y], // [8/4] [192] when MIC_ARRAY_NUM_MICS == 8/4. Double-word aligned out port p_scope_gray, // (*) out port p_scope_violet, chanend ch_headset_out, streaming chanend sch_ab, // sch_ab_sample_001_t, ... chanend ch_ba); // ch_ba_t, ch_ba_init_synch_t // (*) Having this as a param instead of being a global, as in the AN00220 example, yields the same // amount of all resources, on the byte, but the XTA timing analysis is _a_little_ worse. Strange. See files // "2021 04 23 09.40 build data global to example.txt" // xta: /Users/teig/workspace/lib_mic_array/src/decimate_to_pcm_4ch.S:30: warning: route(4) Pass with 4 unknowns, Num Paths: 1, Slack: 994.0 ns, Required: 2.6 us, Worst: 1.6 us, Min Core Frequency: 309 MHz // "2021 04 23 09.40 build data param to example.txt" ######## ####### // xta: /Users/teig/workspace/lib_mic_array/src/decimate_to_pcm_4ch.S:30: warning: route(4) Pass with 4 unknowns, Num Paths: 1, Slack: 984.0 ns, Required: 2.6 us, Worst: 1.6 us, Min Core Frequency: 311 MHz #else #error NESTED .h FILE. BUT IT NEEDS: /* TODO: make this list for all header files lib_mic_array included in makefile will resolve local "mic_array_conf.h" to be seen #include #include #include "mic_array.h" #include "_globals.h" #include "maths_fix16.h" */ #endif