/* * _AudioMux_controller.xc * * Created on: 15. aug. 2018 * Author: teig */ #define INCLUDES #ifdef INCLUDES #include #include // tile #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 #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 "_texts_and_constants.h" #include "button_press.h" #include "pwm_softblinker.h" #include "maths_fix.h" // Needs stdint.h #include "mics_in_headset_out.h" #include "_Beep_dsp_handling.h" #include "headset_out.h" #include "port_tile0_task.h" #include "logger.h" #include "_Beep_BRRR_01.h" #endif #if (WARNINGS == 1) #if (MIC_ARRAY_NUM_MICS == 8) #warning MIC_ARRAY_NUM_MICS = 8 #elif (MIC_ARRAY_NUM_MICS == 4) #warning MIC_ARRAY_NUM_MICS = 4 #else #error #endif #endif // Observe that I have no control of the ports during xTIMEcomposer downloading // I have observed a 700-800 ms low on signal pins before my code starts #if (IS_MYTARGET == IS_MYTARGET_MIC_ARRAY) // A PDM microphone requires a clock and a data pin. For eight PDM microphones a single clock // can be shared between all microphones and the data can be sampled on a single 8 bit port. // On an xCORE the pins are controlled by ports. The application therefore declares one 1-bit port // and one 8-bit port: // MIC-ARRAY-1V3-MOD.xc on tile[0]: in port p_pdm_clk = XS1_PORT_1E; // PORT_PDM_CLK on tile[0]: in buffered port:32 p_pdm_mics = XS1_PORT_8B; // The 8 bit wide port connected to the PDM microphones // 7 used: MIC_0_DATA 8B0 to MIC_6_DATA 8B6 (8B7 not connected) // Also: "Count of microphones(channels) must be set to a multiple of 4" // PORT_PDM_DATA Inputs four chunks of 8 bits into a 32 bits value // To generate the PDM clock a 24.576MHz master clock is divided by 8 using a clock block. // These two hardware resources are declared with: // on tile[0]: in port p_mclk = XS1_PORT_1F; // Resolved here (no .xn needed) on tile[0]: clock pdmclk = XS1_CLKBLK_1; // Resolved here (clocks never in .xn) In "xs1b_user.h" system on tile[0]: out port p_scope_gray = XS1_PORT_1J; // Mic array expansion header J5, pin 12 on tile[0]: in buffered port:4 p_4_buttons = XS1_PORT_4A; // BUTTONS_NUM_CLIENTS // on tile[0]: port p_display_scl = XS1_PORT_1H; // Mic array expansion header J5, pin 3 on tile[0]: port p_display_sda = XS1_PORT_1G; // Mic array expansion header J5, pin 1 on tile[0]: out port p_display_notReset = XS1_PORT_1A; // Mic array expansion header J5, pin 5 Adafruit 326 v2.1 does not NOT have on-board reset logic on tile[0]: out port p_scope_orange = XS1_PORT_1D; // Mic array expansion header J5, pin 7 on tile[0]: out port p_scope_violet = XS1_PORT_1I; // Mic array expansion header J5, pin 9 on tile[0]: out port p_leds_00_07 = XS1_PORT_8C; // BIT0 is LED_0 on tile[0]: out buffered port:1 p_led_08 = XS1_PORT_1K; on tile[0]: out port p_led_09 = XS1_PORT_1L; on tile[0]: out port p_led_10 = XS1_PORT_1M; on tile[0]: out port p_led_11 = XS1_PORT_1N; on tile[0]: out port p_led_12 = XS1_PORT_1O; #if (P_SCOPE_VIOLET_CONTROLLED_FROM_TILE == 0) #define PORT_VIOLET_TILE0 p_scope_violet // mics_in_headset_out_task_a #define PORT_VIOLET_TILE1 p_led_10 // logger_task #elif (P_SCOPE_VIOLET_CONTROLLED_FROM_TILE == 1) #define PORT_VIOLET_TILE1 p_scope_violet // logger_task #define PORT_VIOLET_TILE0 p_led_10 // mics_in_headset_out_task_a #else #error #endif // If we need to collect them: // out port leds_08_12[NUM_LEDS_08_12] = {XS1_PORT_1K, XS1_PORT_1L, XS1_PORT_1M, XS1_PORT_1N, XS1_PORT_1O}; #define I2S_BOARD_NUM_CS43L21 1 // HW PORTS #define I2C_BOARD_NUM_CLIENTS 1 // SW interfaces 4F0=DAC_RST_N Audio CS43L21 // 4F1=ETH_RST_N Ethernet LAN8710A // MIC-ARRAY-1V3-MOD.xc on tile[1]: port p_shared_notReset = XS1_PORT_4F; // PORT_SHARED_RESET. BIT reset when low, high operation: // DAC_RST_N=BIT0 (Audio CS43L21), ETH_RST_N=BIT1 (Ethernet LAN8710A) on tile[1]: out buffered port:32 p_i2s_dac_data[I2S_BOARD_NUM_CS43L21] = {XS1_PORT_1P}; // PORT_I2S_DAC0 I2S_DAC_DATA is SDIN P32 on CS43L21 on tile[1]: in port p_mclk_in1 = XS1_PORT_1O; on tile[1]: out buffered port:32 p_i2s_bclk = XS1_PORT_1M; // PORT_I2S_BCLK on tile[1]: out buffered port:32 p_i2s_lrclk = XS1_PORT_1N; // PORT_I2S_LRCLK on tile[1]: port p_i2c = XS1_PORT_4E; // SCL=BIT0, SDA=BIT1 on tile[1]: clock mclk = XS1_CLKBLK_3; on tile[1]: clock bclk = XS1_CLKBLK_4; #endif // ===================================================================== // = PARAMETER INTO LIBRARIES: DO A "Clean Project" after modification = // = SPEED_THROUGHPUT_TAG, MAX_WAIT_TIME_TO_SEND_DATA_MS = // ===================================================================== // #define I2C_BOARD_SPEED_K_BITS_PER_SECOND 100 // See box above // From the AN00219 descriptiom PDF: // The data must also be double-word aligned. For example: // int data[8][THIRD_STAGE_COEFS_PER_STAGE*DECIMATION_FACTOR]; // Note that on the xCORE-200 all global ARRAYS are guaranteed to be double-word aligned. // With this as a global array the stack is smaller, code same, data larger. // See AN00219, I tested it both as task local and global, and both worked // For s STRUCT (like mic_array_frame_time_domain) double-word alignment is assured with a long long first // mic_data_t mics_data [NUM_DATA_X] [NUM_DATA_Y]; // FIR memory int main() { interface pin_if_1 if_pin [BUTTONS_NUM_CLIENTS]; interface button_if_1 if_button [BUTTONS_NUM_CLIENTS]; interface pwm_if if_pwm; interface softblinker_if if_softblinker; i2c_general_commands_if if_i2c_general_commands [I2C_GENERAL_NUM_CLIENTS]; i2c_master_if if_i2c [I2C_MASTER_ONE_CLIENT]; port_commands_if if_port_commands; streaming chan sch_ab; chan ch_ba; // bidirectional chan ch_headset; chan ch_bx; // streaming not necessary chan ch_yx; // bidirectional streaming chan sch_yb; // bidirectional chan ch_logger; // bidirectional #if (TEST_HEADSET_BUFFER_TASK!=0) // TODO remove TEST_HEADSET_BUFFER_TASK #if (WARNINGS==1) #warning Why this, was it not fixed in v0632? #endif chan ch_headset_buffer; #endif i2s_callback_if if_i2s_board; // AN00219 headset out i2c_master_if if_i2c_board [I2C_BOARD_NUM_CLIENTS]; // AN00219 headset out par { // Most on TILE[1] HAS TO DO WITH HEADSET OUT on tile[1]: { configure_clock_src (mclk, p_mclk_in1); start_clock (mclk); // Observe that even if this handles "frames" this is NOT the "frame-based" master, // which is called i2s_frame_master i2s_master ( // == i2s_master0 if_i2s_board, p_i2s_dac_data, I2S_BOARD_NUM_CS43L21, null, 0, p_i2s_bclk, p_i2s_lrclk, bclk, mclk); } on tile[1]: [[distribute]] i2c_master_single_port (if_i2c_board, 1, p_i2c, I2C_BOARD_SPEED_K_BITS_PER_SECOND, 0, 1, 0); // single-port since both SDA and SDL are on p_i2c XS1_PORT_4E // bit_time = (XS1_TIMER_MHZ * 1000) / kbits_per_second = 100 * 1000 / 100 = 1000 tick * 10ns = 10us = 100 kHz on tile[1]: [[distribute]] i2s_task (if_i2s_board, if_i2c_board[0], p_shared_notReset, ch_headset); // Here on TILE[1] start DSP handling on tile[1]: buffer_task_x (ch_bx, ch_yx); on tile[1]: dsp_task_y (ch_yx, ch_logger, sch_yb); #if (TEST_HEADSET_BUFFER_TASK==2) on tile[1]: logger_task (ch_logger, if_i2c_general_commands[I2C_GENERAL_IOF_ICLIENT_1], if_port_commands); on tile[1]: headset_buffer_task (ch_headset_buffer, ch_headset); #elif (TEST_HEADSET_BUFFER_TASK==3) on tile[1]: { [[combine]] par { headset_buffer_task (ch_headset_buffer, ch_headset); logger_task (ch_logger, if_i2c_general_commands[I2C_GENERAL_IOF_ICLIENT_1], if_port_commands); } } #else on tile[1]: logger_task (ch_logger, if_i2c_general_commands[I2C_GENERAL_IOF_ICLIENT_1], if_port_commands); #endif // TILE[0] all sorts: on tile[0]:{ // Begin a 3.072MHz PDM clock used for clocking the microphone data into the xCORE: // configure_clock_src_divide (pdmclk, p_mclk, MASTER_PLL_24576KHZ_TO_PDM_CLOCK_DIVIDER); // XS1_CLKBLK_1 configure_port_clock_output (p_pdm_clk, pdmclk); configure_in_port (p_pdm_mics, pdmclk); start_clock (pdmclk); streaming chan sch_pdm_to_dec [NUM_4MIC_DECIMATORS]; // (sch_pdm_to_dec) streaming chan sch_ds_ptr_output [NUM_4MIC_DECIMATORS]; // (c_ds_output) chan contains pointers to mics_data and control information // Relies oo shared memory, so both's ends taks must on the same tile as this task // mic_array_pdm_rx() // samples up to 8 microphones and filters the mics_data to provide up to eight 384 kHz mics_data streams, split in two streams of four channels. // The gain is corrected so that a maximum signal on the PDM microphone corresponds to a maximum signal on the PCM signal. // PDM microphones typically have an initialization delay in the order of about 28ms. They also typically have a DC offset. // Both of these will be specified in the datasheet. par { #if (MIC_ARRAY_NUM_MICS == 8) mic_array_pdm_rx (p_pdm_mics, sch_pdm_to_dec[0], sch_pdm_to_dec[1]); // in pdm.xc, calls pdm_rx_asm in pdm_rx.xc which never returns mic_array_decimate_to_pcm_4ch (sch_pdm_to_dec[0], sch_ds_ptr_output[0], MIC_ARRAY_NO_INTERNAL_CHANS); // asm in decimate_to_pcm_4ch.S mic_array_decimate_to_pcm_4ch (sch_pdm_to_dec[1], sch_ds_ptr_output[1], MIC_ARRAY_NO_INTERNAL_CHANS); // asm in decimate_to_pcm_4ch.S #elif (MIC_ARRAY_NUM_MICS == 4) mic_array_pdm_rx (p_pdm_mics, sch_pdm_to_dec[0], null); // in pdm.xc, calls pdm_rx_asm in pdm_rx.xc which never returns mic_array_decimate_to_pcm_4ch (sch_pdm_to_dec[0], sch_ds_ptr_output[0], MIC_ARRAY_NO_INTERNAL_CHANS); // asm in decimate_to_pcm_4ch.S #else #error #endif #if (TEST_HEADSET_BUFFER_TASK==0) mics_in_headset_out_task_a ( // Much of lores_DAS_fixed in AN00219 sch_ds_ptr_output, // chan contains ptr to shared mics_data, must be on the same tile as mic_array_decimate_to_pcm_4ch mics_data, // double-word aligned p_scope_gray, PORT_VIOLET_TILE0, ch_headset, sch_ab, ch_ba); #else mics_in_headset_out_task_a ( // Much of lores_DAS_fixed in AN00219 sch_ds_ptr_output, // chan contains ptr to shared mics_data, must be on the same tile as mic_array_decimate_to_pcm_4ch mics_data, // double-word aligned p_scope_gray, PORT_VIOLET_TILE0, ch_headset_buffer, sch_ab, ch_ba); #endif } } par { on tile[0]: Handle_Beep_BRRRR_task_b ( if_button, p_leds_00_07, if_softblinker, if_i2c_general_commands[I2C_GENERAL_IOF_ICLIENT_0], p_display_notReset, p_scope_orange, sch_ab, ch_ba, ch_bx, sch_yb); // Having this here, and not in combined part below, avoids "line 2303" compiler crash: on tile[0].core[0]: I2C_Client_Task (if_i2c_general_commands, if_i2c); } on tile[0]: { // Having these share a core avoids "line 183" compiler crash and runs: [[combine]] par { #if (TEST_HEADSET_BUFFER_TASK==1) headset_buffer_task (ch_headset_buffer, ch_headset); #endif softblinker_task (if_pwm, if_softblinker); pwm_for_LED_task (if_pwm, p_led_08); i2c_master ( // Synchronous==distributable, but here placed as combinable if_i2c, I2C_MASTER_ONE_CLIENT, p_display_scl, p_display_sda, I2C_EXTERNAL_SPEED_K_BITS_PER_SECOND); // OBS "Clean Project" to change port_tile0_task (if_port_commands, PORT_VIOLET_TILE1); } } on tile[0]: { // [[TODO]] Consider mabs_button_and_led_server in lib_mic_array_board_support [[combine]] par { Buttons_demux_task (p_4_buttons, if_pin); Button_task (IOF_BUTTON_LEFT, if_pin[IOF_BUTTON_LEFT], if_button[IOF_BUTTON_LEFT]); // BUTTON_A Button_task (IOF_BUTTON_CENTER, if_pin[IOF_BUTTON_CENTER], if_button[IOF_BUTTON_CENTER]); // BUTTON_B Button_task (IOF_BUTTON_RIGHT, if_pin[IOF_BUTTON_RIGHT], if_button[IOF_BUTTON_RIGHT]); // BUTTON_C Button_task (IOF_BUTTON_FAR_RIGHT, if_pin[IOF_BUTTON_FAR_RIGHT], if_button[IOF_BUTTON_FAR_RIGHT]); // BUTTON_D // port_tile0_task (if_port_commands, PORT_VIOLET_TILE1); } } } return 0; }