/** * MIT License * * Copyright (c) 2022 Vincent Mistler * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "i2s.h" i2s_config_t i2s_get_default_config(void) { i2s_config_t i2s_config = { .sample_freq = 44100, .channel_count = 2, .data_pin = 26, .clock_pin_base = 27, .pio = pio0, .sm = 0, .dma_channel = 0, .dma_buf = NULL, .dma_trans_count = 0, }; return i2s_config; } void i2s_init(i2s_config_t *i2s_config) { uint8_t func=GPIO_FUNC_PIO0; // TODO: GPIO_FUNC_PIO0 for pio0 or GPIO_FUNC_PIO1 for pio1 gpio_set_function(i2s_config->data_pin, GPIO_FUNC_PIO0); gpio_set_function(i2s_config->clock_pin_base, GPIO_FUNC_PIO0); gpio_set_function(i2s_config->clock_pin_base+1, GPIO_FUNC_PIO0); i2s_config->sm = pio_claim_unused_sm(i2s_config->pio, true); uint offset = pio_add_program(i2s_config->pio, &audio_i2s_program); audio_i2s_program_init(i2s_config->pio, i2s_config->sm , offset, i2s_config->data_pin , i2s_config->clock_pin_base); // Set PIO clock uint32_t system_clock_frequency = clock_get_hz(clk_sys); uint32_t divider = system_clock_frequency * 4 / i2s_config->sample_freq; // avoid arithmetic overflow pio_sm_set_clkdiv_int_frac(i2s_config->pio, i2s_config->sm , divider >> 8u, divider & 0xffu); pio_sm_set_enabled(i2s_config->pio, i2s_config->sm, false); // Allocate memory for the DMA buffer i2s_config->dma_buf=malloc(i2s_config->dma_trans_count*sizeof(uint32_t)); // Direct Memory Access setup i2s_config->dma_channel = dma_claim_unused_channel(true); dma_channel_config dma_config = dma_channel_get_default_config(i2s_config->dma_channel); channel_config_set_read_increment(&dma_config, true); channel_config_set_write_increment(&dma_config, false); channel_config_set_dreq(&dma_config, pio_get_dreq(i2s_config->pio, i2s_config->sm, true)); channel_config_set_transfer_data_size(&dma_config, DMA_SIZE_32); dma_channel_configure(i2s_config->dma_channel, &dma_config, &(i2s_config->pio->txf[i2s_config->sm]), // Destination pointer i2s_config->dma_buf, // Source pointer i2s_config->dma_trans_count, // Number of 32 bits words to transfer false // Start immediately ); pio_sm_set_enabled(i2s_config->pio, i2s_config->sm , true); } void i2s_write(const i2s_config_t *i2s_config,const int16_t *samples,const size_t len) { for(size_t i=0;ipio, i2s_config->sm, (uint32_t)samples[i]); } } // Write samples to DMA buffer and initiate DMA transfer (non blocking) void i2s_dma_write(i2s_config_t *i2s_config,const int16_t *samples) { // Wait the completion of the previous DMA transfer dma_channel_wait_for_finish_blocking(i2s_config->dma_channel); // Copy samples into the DMA buffer memcpy(i2s_config->dma_buf,samples,i2s_config->dma_trans_count*sizeof(int32_t)); // Initiate the DMA transfer dma_channel_transfer_from_buffer_now(i2s_config->dma_channel, i2s_config->dma_buf, i2s_config->dma_trans_count); }