//! //! @brief This function ensures that no underflow/underflow will never occur //! by adjusting the SSC/ABDAC frequencies. void usb_stream_resync(void) { if (!usb_stream_context->synchronized) return; if( !cpu_is_timeout(&usb_resync_timer) ) return; if( twi_is_busy() ) return; // time-out occur. Let's check frequency deviation. int nb_full_buffers = usb_stream_fifo_get_used_room(); // Frequency control if( nb_full_buffers>USB_STREAM_BUFFER_NUMBER/2 ) { // Need to increase the frequency if( nb_full_buffers >= usb_stream_resync_last_room ) { usb_stream_resync_freq_ofst += usb_stream_resync_step; usb_stream_resync_ppm_ofst += USB_STREAM_RESYNC_PPM_STEPS; usb_stream_resync_last_room = nb_full_buffers; cs2200_freq_clk_adjust((uint16_t)_32_BITS_RATIO(usb_stream_resync_freq_ofst, CS2200_FREF)); cpu_set_timeout( cpu_ms_2_cy(TIMER_USB_RESYNC_CORRECTION, FCPU_HZ), &usb_resync_timer ); } } else if (nb_full_buffers<USB_STREAM_BUFFER_NUMBER/2 ) { // Need to slow down the frequency if( nb_full_buffers <= usb_stream_resync_last_room ) { usb_stream_resync_freq_ofst -= usb_stream_resync_step; usb_stream_resync_ppm_ofst -= USB_STREAM_RESYNC_PPM_STEPS; usb_stream_resync_last_room = nb_full_buffers; cs2200_freq_clk_adjust((uint16_t)_32_BITS_RATIO(usb_stream_resync_freq_ofst, CS2200_FREF)); cpu_set_timeout( cpu_ms_2_cy(TIMER_USB_RESYNC_CORRECTION, FCPU_HZ), &usb_resync_timer ); } } }
bool cs2200_setup(uint32_t out_freq, uint32_t fosc) { int device_id; int nb_tries = CS2200_NB_TRIES; do { device_id = (CS2200_READ_DEVICE_ID() & CS2200_DEVICE_ID_REG_MASK) >> CS2200_DEVICE_ID_REG_OFFSET; // Make sure the chip is functional. } while ((device_id != CS2200_EXPECTED_DEVICE_ID) && --nb_tries); // If number of tries is over, return an error. if (!nb_tries) return false; // Freeze chip during the programming sequence CS2200_WRITE_GLOBAL_CFG(1<<3); CS2200_WRITE_DEVICE_CTRL(0x00); // AUX_OUT output driver enabled. CLK_OUT output driver enabled. CS2200_WRITE_DEVICE_CFG_1( 0 << 5 // Left-shift R-value by 0 (x 1). | 0 << 1 // RefClk: is the source of the AUX_OUT signal ); CS2200_WRITE_32_BITS_RATIO(_32_BITS_RATIO(out_freq, fosc)); // Program a default clock. CS2200_WRITE_FUNCT_CFG_1( 0x00 << 6 // Push-Pull, Active High (output `high' for unlocked condition, `low' for locked condition). | 0x02 << 3 // Reference Clock Input Divider: / 1 [8 MHz to 18.75 MHz] ); CS2200_WRITE_FUNCT_CFG_2( 0x00 << 4 // Clock outputs are driven `low' when PLL is unlocked. ); // Unleash chip CS2200_WRITE_GLOBAL_CFG( 0x00 << 3 // Freeze | 0x01 << 0 // EnDevCfg2 ); cs2200_enter_test_mode(); return true; }
/*! \brief Main function. Execution starts here. * * \retval 42 Fatal error. */ int main(void) { uint32_t iter=0; uint32_t cs2200_out_freq=11289600; static bool b_sweep_up=true; static uint32_t freq_step=0; // USART options. static usart_serial_options_t USART_SERIAL_OPTIONS = { .baudrate = USART_SERIAL_EXAMPLE_BAUDRATE, .charlength = USART_SERIAL_CHAR_LENGTH, .paritytype = USART_SERIAL_PARITY, .stopbits = USART_SERIAL_STOP_BIT }; // Initialize the TWI using the internal RCOSC init_twi(AVR32_PM_RCOSC_FREQUENCY); // Initialize the CS2200 and produce a default frequency. cs2200_setup(11289600, FOSC0); sysclk_init(); // Initialize the board. // The board-specific conf_board.h file contains the configuration of the board // initialization. board_init(); // Initialize the TWI init_twi(sysclk_get_pba_hz()); // Initialize Serial Interface using Stdio Library stdio_serial_init(USART_SERIAL_EXAMPLE,&USART_SERIAL_OPTIONS); // Initialize the HMatrix. init_hmatrix(); print_dbg("\r\nCS2200 Example\r\n"); // Generate a 12.288 MHz frequency out of the CS2200. print_dbg("Output 12.288 MHz\r\n"); cs2200_freq_clk_out(_32_BITS_RATIO(12288000, FOSC0)); cpu_delay_ms( 10000, sysclk_get_cpu_hz()); // Generate a 11.2896 MHz frequency out of the CS2200. print_dbg("Output 11.2896 MHz\r\n"); cs2200_freq_clk_out(_32_BITS_RATIO(cs2200_out_freq, FOSC0)); cpu_delay_ms( 10000, sysclk_get_cpu_hz()); print_dbg("Sweep from 11.2896 MHz steps of 100 PPM\r\n"); freq_step = PPM(cs2200_out_freq, 100); while(1) { uint32_t ratio; if(b_sweep_up) { if( iter<=10 ) { print_dbg("Add 100 PPM\r\n"); iter++; cs2200_out_freq += freq_step; ratio = _32_BITS_RATIO(cs2200_out_freq, FOSC0); cs2200_freq_clk_adjust((uint16_t)ratio); cpu_delay_ms( 1000, sysclk_get_cpu_hz()); while( twi_is_busy() ); } else b_sweep_up=false; } if(!b_sweep_up) { if( iter>0 ) { print_dbg("Sub 100 PPM\r\n"); iter--; cs2200_out_freq -= freq_step; ratio = _32_BITS_RATIO(cs2200_out_freq, FOSC0); cs2200_freq_clk_adjust((uint16_t)ratio); cpu_delay_ms( 1000, sysclk_get_cpu_hz()); while( twi_is_busy() ); } else b_sweep_up=true; } } }
void aic23b_dac_start(uint32_t sample_rate_hz, uint8_t num_channels, uint8_t bits_per_sample, bool swap_channels, void (*callback)(uint32_t arg), uint32_t callback_opt, uint32_t pba_hz) { #if AIC23B_CTRL_INTERFACE == AIC23B_CTRL_INTERFACE_SPI static const spi_options_t AIC23B_SPI_OPTIONS = { .reg = AIC23B_SPI_NPCS, .baudrate = AIC23B_SPI_MASTER_SPEED, .bits = AIC23B_CTRL_SIZE, .spck_delay = 0, .trans_delay = 0, .stay_act = 0, .spi_mode = 3, .modfdis = 1 }; spi_setupChipReg(AIC23B_SPI, &AIC23B_SPI_OPTIONS, pba_hz); #endif aic23b_dac_stop(); gpio_enable_module(AIC23B_SSC_DAC_GPIO_MAP, sizeof(AIC23B_SSC_DAC_GPIO_MAP) / sizeof(AIC23B_SSC_DAC_GPIO_MAP[0])); aic23b_pdc_t pdc; pdc.data = AIC23B_DEFAULT(AIC23B_PDC); pdc.off = 0; pdc.clk = 0; pdc.osc = 0; pdc.out = 0; pdc.dac = 0; pdc.adc = 1; pdc.mic = 1; pdc.line = 1; aic23b_set_power_down_state(pdc); aic23b_dac_setup(sample_rate_hz, num_channels, bits_per_sample, swap_channels, callback, callback_opt, pba_hz); aic23b_aapc_t aapc; aapc.data = AIC23B_DEFAULT(AIC23B_AAPC); aapc.ste = 0; aapc.dac = 1; aapc.byp = 0; aapc.micm = 1; aapc.micb = 0; aic23b_set_analog_audio_path(aapc); aic23b_dapc_t dapc; dapc.data = AIC23B_DEFAULT(AIC23B_DAPC); dapc.dacm = 0; dapc.deemp = AIC23B_DAPC_DEEMP_NONE; dapc.adchp = 1; aic23b_set_digital_audio_path(dapc); // set an acceptable start volume aic23b_set_headphone_volume(AIC23B_LEFT_CHANNEL | AIC23B_RIGHT_CHANNEL, -30, true); aic23b_activate_dig_audio(true); INTC_register_interrupt(&aic23b_ssc_tx_pdca_int_handler, AIC23B_SSC_TX_PDCA_IRQ, AIC23B_SSC_TX_PDCA_INT_LEVEL); } void aic23b_dac_setup(uint32_t sample_rate_hz, uint8_t num_channels, uint8_t bits_per_sample, bool swap_channels, void (*callback)(uint32_t arg), uint32_t callback_opt, uint32_t pba_hz) { uint32_t master_clock = AIC23B_MCLK_HZ; // default configuration // Change the CPU frequency // //Disable_global_interrupt(); // Switch to OSC0 during OSC1 transition //pm_switch_to_osc0(&AVR32_PM, FOSC0, OSC0_STARTUP); // Switch to PLL0 as the master clock //pm_switch_to_clock(&AVR32_PM, AVR32_PM_MCCTRL_MCSEL_PLL0); if (sample_rate_hz < (8000 + 8021) / 2) { // 8000 Hz } else if (sample_rate_hz < (8021 + 32000) / 2) { // 8021 Hz } else if (sample_rate_hz < (32000 + 44100) / 2) { // 32000 Hz master_clock = usb_stream_resync_frequency = 8192000; cs2200_freq_clk_out(_32_BITS_RATIO(usb_stream_resync_frequency, FOSC0)); pba_hz = FCPU_HZ = FHSB_HZ = FPBA_HZ = FPBB_HZ = FMCK_HZ(8192000); } else if (sample_rate_hz < (44100 + 48000) / 2) { // 44100 Hz master_clock = usb_stream_resync_frequency = 11289600; cs2200_freq_clk_out(_32_BITS_RATIO(usb_stream_resync_frequency, FOSC0)); pba_hz = FCPU_HZ = FHSB_HZ = FPBA_HZ = FPBB_HZ = FMCK_HZ(11289600); } else if (sample_rate_hz < (48000 + 88200) / 2) { // 48000 Hz master_clock = usb_stream_resync_frequency = 12288000; cs2200_freq_clk_out(_32_BITS_RATIO(usb_stream_resync_frequency, FOSC0)); pba_hz = FCPU_HZ = FHSB_HZ = FPBA_HZ = FPBB_HZ = FMCK_HZ(12288000); } else if (sample_rate_hz < (88200 + 96000) / 2) { // 88200 Hz } else { // 96000 Hz } //Enable_global_interrupt(); #if defined(AIC23B_DAC_USE_RX_CLOCK) && AIC23B_DAC_USE_RX_CLOCK == true #if defined(AIC23B_DAC_RX_CLOCK_SET_CALLBACK) AIC23B_DAC_RX_CLOCK_SET_CALLBACK(2 * sample_rate_hz * ((bits_per_sample <= 16) ? 16 : (bits_per_sample <= 20) ? 20 : (bits_per_sample <= 24) ? 24 : 32)); #endif ssc_i2s_init(AIC23B_SSC, sample_rate_hz, bits_per_sample, (bits_per_sample <= 16) ? 16 : (bits_per_sample <= 20) ? 20 : (bits_per_sample <= 24) ? 24 : 32, SSC_I2S_MODE_STEREO_OUT_EXT_CLK, pba_hz); #else ssc_i2s_init(AIC23B_SSC, sample_rate_hz, bits_per_sample, (bits_per_sample <= 16) ? 16 : (bits_per_sample <= 20) ? 20 : (bits_per_sample <= 24) ? 24 : 32, SSC_I2S_MODE_STEREO_OUT, pba_hz); #endif pdca_channel_options_t aic23b_ssc_pdca_options = { .addr = NULL, .size = 0, .r_addr = NULL, .r_size = 0, .pid = AIC23B_SSC_TX_PDCA_PID, .transfer_size = (bits_per_sample <= 8) ? PDCA_TRANSFER_SIZE_BYTE : (bits_per_sample <= 16) ? PDCA_TRANSFER_SIZE_HALF_WORD : PDCA_TRANSFER_SIZE_WORD }; pdca_init_channel(AIC23B_SSC_TX_PDCA_CHANNEL, &aic23b_ssc_pdca_options); pdca_enable(AIC23B_SSC_TX_PDCA_CHANNEL); #if !defined(AIC23B_DAC_USE_RX_CLOCK) || AIC23B_DAC_USE_RX_CLOCK == false || \ !defined(AIC23B_DAC_RX_CLOCK_SET_CALLBACK) // Set DAC frequency aic23b_configure_freq(master_clock, sample_rate_hz); #endif aic23b_daif_t daif; daif.data = AIC23B_DEFAULT(AIC23B_DAIF); daif.ms = AIC23B_DAIF_MS_SLAVE; daif.lrswap = swap_channels; daif.lrp = 0; daif.iwl = (bits_per_sample <= 16) ? AIC23B_DAIF_IWL_16 : (bits_per_sample <= 20) ? AIC23B_DAIF_IWL_20 : (bits_per_sample <= 24) ? AIC23B_DAIF_IWL_24 : AIC23B_DAIF_IWL_32; daif.fmt = AIC23B_DAIF_FMT_I2S; aic23b_write_reg(AIC23B_DAIF, daif.data); aic23b_output_params.num_channels = num_channels; aic23b_output_params.callback = callback; aic23b_output_params.callback_opt = callback_opt; } #endif bool aic23b_dac_output(void *sample_buffer, size_t sample_length) { bool global_interrupt_enabled; if (!(pdca_get_transfer_status(AIC23B_SSC_TX_PDCA_CHANNEL) & PDCA_TRANSFER_COUNTER_RELOAD_IS_ZERO)) return false; if (sample_length) { if (aic23b_output_params.num_channels == 1) { int16_t *s16_sample_buffer = sample_buffer; int i; for (i = sample_length - 1; i >= 0; i--) { s16_sample_buffer[2 * i + 1] = s16_sample_buffer[2 * i] = s16_sample_buffer[i]; } } // The PDCA is not able to synchronize its start of transfer with the SSC // start of period, so this has to be done by polling the TF pin. // Not doing so may result in channels being swapped randomly. if ((global_interrupt_enabled = Is_global_interrupt_enabled())) Disable_global_interrupt(); if (pdca_get_transfer_status(AIC23B_SSC_TX_PDCA_CHANNEL) & PDCA_TRANSFER_COMPLETE) { while (gpio_get_pin_value(AIC23B_SSC_TX_FRAME_SYNC_PIN)); while (!gpio_get_pin_value(AIC23B_SSC_TX_FRAME_SYNC_PIN)); } pdca_reload_channel(AIC23B_SSC_TX_PDCA_CHANNEL, sample_buffer, sample_length * 2); pdca_get_reload_size(AIC23B_SSC_TX_PDCA_CHANNEL); if (global_interrupt_enabled) Enable_global_interrupt(); if (aic23b_output_params.callback_opt & AUDIO_DAC_OUT_OF_SAMPLE_CB) pdca_enable_interrupt_transfer_complete(AIC23B_SSC_TX_PDCA_CHANNEL); if (aic23b_output_params.callback_opt & AUDIO_DAC_RELOAD_CB) pdca_enable_interrupt_reload_counter_zero(AIC23B_SSC_TX_PDCA_CHANNEL); } return true; }
void aic23b_codec_start(uint32_t sample_rate_hz, uint8_t num_channels, uint8_t bits_per_sample, bool swap_channels, void (*callback)(uint32_t arg), uint32_t callback_opt, uint32_t pba_hz) { #if AIC23B_CTRL_INTERFACE == AIC23B_CTRL_INTERFACE_SPI static const spi_options_t AIC23B_SPI_OPTIONS = { .reg = AIC23B_SPI_NPCS, .baudrate = AIC23B_SPI_MASTER_SPEED, .bits = AIC23B_CTRL_SIZE, .spck_delay = 0, .trans_delay = 0, .stay_act = 0, .spi_mode = 3, .modfdis = 1 }; spi_setupChipReg(AIC23B_SPI, &AIC23B_SPI_OPTIONS, pba_hz); #endif aic23b_codec_stop(); gpio_enable_module(AIC23B_SSC_CODEC_GPIO_MAP, sizeof(AIC23B_SSC_CODEC_GPIO_MAP) / sizeof(AIC23B_SSC_CODEC_GPIO_MAP[0])); aic23b_pdc_t pdc; pdc.data = AIC23B_DEFAULT(AIC23B_PDC); pdc.off = 0; pdc.clk = 0; pdc.osc = 0; pdc.out = 0; pdc.dac = 0; pdc.adc = 0; #if (AIC23B_INPUT==AIC23B_INPUT_LINE) pdc.mic = 1; pdc.line = 0; #elif (AIC23B_INPUT==AIC23B_INPUT_MIC) pdc.mic = 0; pdc.line = 1; #else #error No Input defined in file 'conf_tlv320aic23b.h' #endif aic23b_set_power_down_state(pdc); aic23b_codec_setup(sample_rate_hz, num_channels, bits_per_sample, swap_channels, callback, callback_opt, pba_hz); aic23b_aapc_t aapc; aapc.data = AIC23B_DEFAULT(AIC23B_AAPC); #if (AIC23B_INPUT==AIC23B_INPUT_LINE) aapc.ste = 0; aapc.dac = 1; aapc.byp = 0; aapc.insel = 0; aapc.micm = 0; aapc.micb = 1; #elif (AIC23B_INPUT==AIC23B_INPUT_MIC) aapc.ste = 0; aapc.dac = 1; aapc.sta = 4; aapc.byp = 0; aapc.insel = 1; aapc.micm = 0; aapc.micb = 1; #else #error No Input defined in file 'conf_tlv320aic23b.h' #endif aic23b_set_analog_audio_path(aapc); aic23b_dapc_t dapc; dapc.data = AIC23B_DEFAULT(AIC23B_DAPC); dapc.dacm = 0; dapc.deemp = AIC23B_DAPC_DEEMP_NONE; dapc.adchp = 0; aic23b_set_digital_audio_path(dapc); aic23b_llicvc_t llivc; llivc.data = AIC23B_DEFAULT(AIC23B_LLICVC); llivc.liv = 20; llivc.lim = 0; llivc.lrs = 1; aic23b_write_reg(AIC23B_LLICVC, llivc.data); aic23b_rlicvc_t rlivc; rlivc.data = AIC23B_DEFAULT(AIC23B_RLICVC); rlivc.riv = 20; rlivc.rim = 0; rlivc.rls = 1; aic23b_write_reg(AIC23B_RLICVC, rlivc.data); INTC_register_interrupt(&aic23b_ssc_rx_pdca_int_handler, AIC23B_SSC_RX_PDCA_IRQ, AIC23B_SSC_RX_PDCA_INT_LEVEL); // set an acceptable start volume aic23b_set_headphone_volume(AIC23B_LEFT_CHANNEL | AIC23B_RIGHT_CHANNEL, -30, true); aic23b_activate_dig_audio(true); INTC_register_interrupt(&aic23b_ssc_tx_pdca_int_handler, AIC23B_SSC_TX_PDCA_IRQ, AIC23B_SSC_TX_PDCA_INT_LEVEL); } void aic23b_codec_setup(uint32_t sample_rate_hz, uint8_t num_channels, uint8_t bits_per_sample, bool swap_channels, void (*callback)(uint32_t opt), uint32_t callback_opt, uint32_t pba_hz) { uint32_t master_clock = AIC23B_MCLK_HZ; // default configuration // Change the CPU frequency // //Disable_global_interrupt(); // Switch to OSC0 during OSC1 transition //pm_switch_to_osc0(&AVR32_PM, FOSC0, OSC0_STARTUP); // Switch to PLL0 as the master clock //pm_switch_to_clock(&AVR32_PM, AVR32_PM_MCCTRL_MCSEL_PLL0); if (sample_rate_hz < (8000 + 8021) / 2) { // 8000 Hz } else if (sample_rate_hz < (8021 + 32000) / 2) { // 8021 Hz } else if (sample_rate_hz < (32000 + 44100) / 2) { // 32000 Hz master_clock = usb_stream_resync_frequency = 8192000; cs2200_freq_clk_out(_32_BITS_RATIO(usb_stream_resync_frequency, FOSC0)); pba_hz = FCPU_HZ = FHSB_HZ = FPBA_HZ = FPBB_HZ = FMCK_HZ(8192000); } else if (sample_rate_hz < (44100 + 48000) / 2) { // 44100 Hz master_clock = usb_stream_resync_frequency = 11289600; cs2200_freq_clk_out(_32_BITS_RATIO(usb_stream_resync_frequency, FOSC0)); pba_hz = FCPU_HZ = FHSB_HZ = FPBA_HZ = FPBB_HZ = FMCK_HZ(11289600); } else if (sample_rate_hz < (48000 + 88200) / 2) { // 48000 Hz master_clock = usb_stream_resync_frequency = 12288000; cs2200_freq_clk_out(_32_BITS_RATIO(usb_stream_resync_frequency, FOSC0)); pba_hz = FCPU_HZ = FHSB_HZ = FPBA_HZ = FPBB_HZ = FMCK_HZ(12288000); } else if (sample_rate_hz < (88200 + 96000) / 2) { // 88200 Hz } else { // 96000 Hz } //Enable_global_interrupt(); ssc_i2s_init(AIC23B_SSC, sample_rate_hz, bits_per_sample, (bits_per_sample <= 16) ? 16 : (bits_per_sample <= 20) ? 20 : (bits_per_sample <= 24) ? 24 : 32, SSC_I2S_MODE_STEREO_OUT_STEREO_IN, pba_hz); pdca_channel_options_t aic23b_ssc_pdca_options_rx = { .addr = NULL, .size = 0, .r_addr = NULL, .r_size = 0, .pid = AIC23B_SSC_RX_PDCA_PID, .transfer_size = (bits_per_sample <= 8) ? PDCA_TRANSFER_SIZE_BYTE : (bits_per_sample <= 16) ? PDCA_TRANSFER_SIZE_HALF_WORD : PDCA_TRANSFER_SIZE_WORD }; pdca_init_channel(AIC23B_SSC_RX_PDCA_CHANNEL, &aic23b_ssc_pdca_options_rx); pdca_enable(AIC23B_SSC_RX_PDCA_CHANNEL); pdca_channel_options_t aic23b_ssc_pdca_options_tx = { .addr = NULL, .size = 0, .r_addr = NULL, .r_size = 0, .pid = AIC23B_SSC_TX_PDCA_PID, .transfer_size = (bits_per_sample <= 8) ? PDCA_TRANSFER_SIZE_BYTE : (bits_per_sample <= 16) ? PDCA_TRANSFER_SIZE_HALF_WORD : PDCA_TRANSFER_SIZE_WORD }; pdca_init_channel(AIC23B_SSC_TX_PDCA_CHANNEL, &aic23b_ssc_pdca_options_tx); pdca_enable(AIC23B_SSC_TX_PDCA_CHANNEL); // Set codec frequency aic23b_configure_freq(master_clock, sample_rate_hz); aic23b_daif_t daif; daif.data = AIC23B_DEFAULT(AIC23B_DAIF); daif.ms = AIC23B_DAIF_MS_SLAVE; daif.lrswap = swap_channels; daif.lrp = 0; daif.iwl = (bits_per_sample <= 16) ? AIC23B_DAIF_IWL_16 : (bits_per_sample <= 20) ? AIC23B_DAIF_IWL_20 : (bits_per_sample <= 24) ? AIC23B_DAIF_IWL_24 : AIC23B_DAIF_IWL_32; daif.fmt = AIC23B_DAIF_FMT_I2S; aic23b_write_reg(AIC23B_DAIF, daif.data); aic23b_output_params.num_channels = num_channels; aic23b_output_params.callback = callback; aic23b_output_params.callback_opt = callback_opt; } void aic23b_codec_flush(void) { pdca_disable_interrupt_transfer_complete(AIC23B_SSC_RX_PDCA_CHANNEL); while (!(pdca_get_transfer_status(AIC23B_SSC_RX_PDCA_CHANNEL) & PDCA_TRANSFER_COMPLETE)); pdca_disable_interrupt_transfer_complete(AIC23B_SSC_TX_PDCA_CHANNEL); while (!(pdca_get_transfer_status(AIC23B_SSC_TX_PDCA_CHANNEL) & PDCA_TRANSFER_COMPLETE)); } void aic23b_codec_stop(void) { aic23b_codec_flush(); aic23b_reset(); aic23b_pdc_t pdc; pdc.data = AIC23B_DEFAULT(AIC23B_PDC); pdc.off = 1; pdc.clk = 1; pdc.osc = 1; pdc.out = 1; pdc.dac = 1; pdc.adc = 1; pdc.mic = 1; pdc.line = 1; aic23b_set_power_down_state(pdc); pdca_disable(AIC23B_SSC_RX_PDCA_CHANNEL); pdca_disable(AIC23B_SSC_TX_PDCA_CHANNEL); ssc_i2s_reset(AIC23B_SSC); gpio_enable_gpio(AIC23B_SSC_CODEC_GPIO_MAP, sizeof(AIC23B_SSC_CODEC_GPIO_MAP) / sizeof(AIC23B_SSC_CODEC_GPIO_MAP[0])); aic23b_output_params.num_channels = 0; aic23b_output_params.callback = NULL; aic23b_output_params.callback_opt = 0; }
//! //! @brief This function takes the stream coming from the selected USB pipe and sends //! it to the DAC driver. Moreover, it ensures that both input and output stream //! keep synchronized by adding or deleting samples. //! //! @param side USB_STREAM_HOST for USB host, USB_STREAM_DEVICE for device. //! @param pipe_in Number of the addressed pipe/endpoint //! @param pFifoCount (return parameter) NULL or pointer to the number of used buffers at this time //! //! @return status: (USB_STREAM_STATUS_OK, USB_STREAM_STATUS_NOT_SYNCHRONIZED, //! USB_STREAM_STATUS_SPEED_UP, USB_STREAM_STATUS_SLOW_DOWN, USB_STREAM_STATUS_BUFFER_OVERFLOW) //! int usb_stream_input(usb_stream_side_t side, uint8_t pipe_in, uint32_t* pFifoCount) { uint16_t fifo_used_cnt; uint16_t byte_count=0; uint32_t i; UnionPtr pswap; UnionPtr buffer; // We comes here since we have received something. Let's increase the internal // activity counter. usb_stream_cnt++; fifo_used_cnt=usb_stream_fifo_get_used_room(); if (pFifoCount) *pFifoCount = fifo_used_cnt; // usb_stream_fifo_get_free_room() if( USB_STREAM_BUFFER_NUMBER-fifo_used_cnt==0 ) { // Fatal error: even with the synchro mechanism acting, we are in a case in which the // buffers are full. usb_stream_context->synchronized = false; usb_stream_context->status = USB_STREAM_ERROR_NOT_SYNCHRONIZED; return usb_stream_context->status; } pswap.s8ptr = buffer.s8ptr = usb_stream_fifo_get_buffer(usb_stream_context->wr_id); #if USB_HOST_FEATURE == true if( side==USB_STREAM_HOST ) { byte_count=Host_byte_count(pipe_in); } #endif #if USB_DEVICE_FEATURE == true if( side==USB_STREAM_DEVICE ) { byte_count=Usb_byte_count(pipe_in); } #endif if( byte_count==0 ) { if( cpu_is_timeout(&broken_stream_timer) ) { usb_stream_context->status = USB_STREAM_ERROR_BROKEN_STREAM; } else { usb_stream_context->status = USB_STREAM_ERROR_NO_DATA; } return usb_stream_context->status; } else { // reset time out detection cpu_set_timeout(cpu_ms_2_cy(BROKEN_STREAM_TIMER, FCPU_HZ), &broken_stream_timer); } #if USB_HOST_FEATURE == true if( side==USB_STREAM_HOST ) { Host_reset_pipe_fifo_access(pipe_in); host_read_p_rxpacket(pipe_in, (void*)buffer.s8ptr, byte_count, NULL); } #endif #if USB_DEVICE_FEATURE == true if( side==USB_STREAM_DEVICE ) { Usb_reset_endpoint_fifo_access(pipe_in); usb_read_ep_rxpacket(pipe_in, (void*)buffer.s8ptr, byte_count, NULL); } #endif usb_stream_context->status = USB_STREAM_ERROR_NONE; if( byte_count > USB_STREAM_REAL_BUFFER_SIZE ) { byte_count = USB_STREAM_REAL_BUFFER_SIZE; usb_stream_context->status = USB_STREAM_ERROR_OVERFLOW; } // Swap samples since they are coming from the USB world. if( usb_stream_context->bits_per_sample==16 ) for( i=0 ; i<byte_count/(16/8) ; i++ ) pswap.s16ptr[i] = swap16(pswap.s16ptr[i]); else if( usb_stream_context->bits_per_sample==32 ) for( i=0 ; i<byte_count/(32/8) ; i++ ) pswap.s32ptr[i] = swap32(pswap.s32ptr[i]); //for( i=0 ; i<byte_count/2 ; i++ ) // printf("0x%04hx ", pswap[i]); //printf("\r\n"); usb_stream_fifo_push(byte_count); fifo_used_cnt++; if( !usb_stream_context->synchronized ) { usb_stream_context->status = USB_STREAM_ERROR_NOT_SYNCHRONIZED; if( fifo_used_cnt>=(USB_STREAM_BUFFER_NUMBER/2) ) { // We have enough buffers to start the playback. void* buffer; uint16_t size; // CS2200 cs2200_freq_clk_out(_32_BITS_RATIO(usb_stream_resync_frequency, CS2200_FREF)); usb_stream_resync_step = PPM(usb_stream_resync_frequency, USB_STREAM_RESYNC_PPM_STEPS); usb_stream_resync_freq_ofst = usb_stream_resync_frequency; usb_stream_resync_ppm_ofst = 0; usb_stream_resync_last_room = fifo_used_cnt; #define TIMER_USB_RESYNC_CORRECTION 320 cpu_set_timeout( cpu_ms_2_cy(TIMER_USB_RESYNC_CORRECTION, FCPU_HZ), &usb_resync_timer ); usb_stream_context->synchronized=true; usb_stream_fifo_get(&buffer, &size); audio_mixer_dacs_output_direct(buffer, size/(usb_stream_context->channel_count*usb_stream_context->bits_per_sample/8)); // Fill also the reload stage of the PDCA. usb_stream_fifo_pull(); usb_stream_fifo_get(&buffer, &size); audio_mixer_dacs_output_direct(buffer, size/(usb_stream_context->channel_count*usb_stream_context->bits_per_sample/8)); } } return usb_stream_context->status; }
void ms3_dac_setup(U32 sample_rate_hz, U8 num_channels, U8 bits_per_sample, bool swap_channels, void (*callback)(U32 arg), U32 callback_opt, U32 pba_hz) { //configure clock if (sample_rate_hz < (8000 + 8021) / 2) { usb_stream_resync_frequency = 4096000; cs2200_freq_clk_out(_32_BITS_RATIO(usb_stream_resync_frequency, CS2200_FREF)); } else if (sample_rate_hz < (8021 + 22050) / 2) { usb_stream_resync_frequency = 4106752; cs2200_freq_clk_out(_32_BITS_RATIO(usb_stream_resync_frequency, CS2200_FREF)); } else if (sample_rate_hz < (22050 + 32000) / 2) { usb_stream_resync_frequency = 11289600; cs2200_freq_clk_out(_32_BITS_RATIO(usb_stream_resync_frequency, CS2200_FREF)); } else if (sample_rate_hz < (32000 + 44100) / 2) { usb_stream_resync_frequency = 16384000; cs2200_freq_clk_out(_32_BITS_RATIO(usb_stream_resync_frequency, CS2200_FREF)); } else if (sample_rate_hz < (44100 + 48000) / 2) { usb_stream_resync_frequency = 22579200; cs2200_freq_clk_out(_32_BITS_RATIO(usb_stream_resync_frequency, CS2200_FREF)); } else if (sample_rate_hz < (48000 + 88200) / 2) { usb_stream_resync_frequency = 24576000; cs2200_freq_clk_out(_32_BITS_RATIO(usb_stream_resync_frequency, CS2200_FREF)); } //configure ssc to use clock on TX_CLOCK pin AVR32_SSC.tcmr = (0 << AVR32_SSC_TCMR_CKO_OFFSET) | (1 << AVR32_SSC_TCMR_STTDLY_OFFSET) | (2 << AVR32_SSC_TCMR_CKS_OFFSET) | (7 << AVR32_SSC_TCMR_START_OFFSET) | (0x1f << AVR32_SSC_TCMR_PERIOD_OFFSET); AVR32_SSC.tfmr = (0xf << AVR32_SSC_TFMR_DATLEN_OFFSET) | (1 << AVR32_SSC_TFMR_MSBF_OFFSET) | (1 << AVR32_SSC_TFMR_FSOS_OFFSET) | (1 << AVR32_SSC_TFMR_FSLENHI_OFFSET) | (0xf << AVR32_SSC_TFMR_FSLEN_OFFSET); AVR32_SSC.cr = AVR32_SSC_CR_TXEN_MASK; //configure DMA pdca_channel_options_t ms3_ssc_pdca_options = { .addr = NULL, .size = 0, .r_addr = NULL, .r_size = 0, .pid = AVR32_PDCA_PID_SSC_TX, .transfer_size = (bits_per_sample <= 8)?PDCA_TRANSFER_SIZE_BYTE: (bits_per_sample <= 16)?PDCA_TRANSFER_SIZE_HALF_WORD: PDCA_TRANSFER_SIZE_WORD }; pdca_init_channel(MS3_SSC_TX_PDCA_CHANNEL, &ms3_ssc_pdca_options); pdca_enable(MS3_SSC_TX_PDCA_CHANNEL); //configure audio parameters ms3_output_params.num_channels = num_channels; ms3_output_params.callback = callback; ms3_output_params.callback_opt = callback_opt; } bool ms3_dac_output(void *sample_buffer, size_t sample_length) { bool global_interrupt_enabled; if (!(pdca_get_transfer_status(MS3_SSC_TX_PDCA_CHANNEL) & PDCA_TRANSFER_COUNTER_RELOAD_IS_ZERO)) return false; if (sample_length) { if (ms3_output_params.num_channels == 1) { S16 *s16_sample_buffer = sample_buffer; int i; for (i = sample_length - 1; i >= 0; i--) { s16_sample_buffer[2 * i + 1] = s16_sample_buffer[2 * i] = s16_sample_buffer[i]; } } // The PDCA is not able to synchronize its start of transfer with the SSC // start of period, so this has to be done by polling the TF pin. // Not doing so may result in channels being swapped randomly. if ((global_interrupt_enabled = Is_global_interrupt_enabled())) Disable_global_interrupt(); if (pdca_get_transfer_status(MS3_SSC_TX_PDCA_CHANNEL) & PDCA_TRANSFER_COMPLETE) { while (gpio_get_pin_value(MS3_SSC_TX_FRAME_SYNC_PIN)); while (!gpio_get_pin_value(MS3_SSC_TX_FRAME_SYNC_PIN)); } pdca_reload_channel(MS3_SSC_TX_PDCA_CHANNEL, sample_buffer, sample_length * 2); pdca_get_reload_size(MS3_SSC_TX_PDCA_CHANNEL); if (global_interrupt_enabled) Enable_global_interrupt(); if (ms3_output_params.callback_opt & AUDIO_DAC_OUT_OF_SAMPLE_CB) pdca_enable_interrupt_transfer_complete(MS3_SSC_TX_PDCA_CHANNEL); if (ms3_output_params.callback_opt & AUDIO_DAC_RELOAD_CB) pdca_enable_interrupt_reload_counter_zero(MS3_SSC_TX_PDCA_CHANNEL); } return true; }