diff --git a/examples/Advanced/I2S_DAC_Output/I2S_DAC_Output.ino b/examples/Advanced/I2S_DAC_Output/I2S_DAC_Output.ino new file mode 100644 index 0000000..6c93a4b --- /dev/null +++ b/examples/Advanced/I2S_DAC_Output/I2S_DAC_Output.ino @@ -0,0 +1,47 @@ +// This example demonstrates how to capture samples from an I2S mic, +// and output them on a DAC. + +#include + +AdvancedDAC dac1(A12); +// WS, CK, SDI, SDO, MCK +AdvancedI2S i2s(PG_10, PG_11, PG_9, PB_5, PC_4); + +#define N_SAMPLES (256) +#define SAMPLE_AVERAGE(s0, s1) (((int16_t) s0 / 2) + ((int16_t) s1 / 2)) + +void setup() { + Serial.begin(9600); + while (!Serial) { + } + + // Resolution, sample rate, number of samples per channel, queue depth. + if (!dac1.begin(AN_RESOLUTION_12, 32000, N_SAMPLES, 32)) { + Serial.println("Failed to start DAC1 !"); + while (1); + } + + // I2S mode, sample rate, number of samples per channel, queue depth. + if (!i2s.begin(AN_I2S_MODE_IN, 32000, N_SAMPLES, 32)) { + Serial.println("Failed to start I2S"); + while (1); + } +} + +void loop() { + if (i2s.available() && dac1.available()) { + SampleBuffer i2sbuf = i2s.read(); + SampleBuffer dacbuf = dac1.dequeue(); + + // Write data to buffer. + for (int i=0; i> 4; + } + + // Write the buffer to DAC. + dac1.write(dacbuf); + i2sbuf.release(); + } +} diff --git a/examples/Advanced/I2S_Full_Duplex/I2S_Full_Duplex.ino b/examples/Advanced/I2S_Full_Duplex/I2S_Full_Duplex.ino new file mode 100644 index 0000000..9446da6 --- /dev/null +++ b/examples/Advanced/I2S_Full_Duplex/I2S_Full_Duplex.ino @@ -0,0 +1,32 @@ +// This example demonstrates I2S in full-duplex mode. In the main loop, samples +// are continuously captured from the I2S input, and written back to I2S output. + +#include + +// WS, CK, SDI, SDO, MCK +AdvancedI2S i2s(PG_10, PG_11, PG_9, PB_5, PC_4); + +void setup() { + Serial.begin(9600); + while (!Serial) { + + } + + // Resolution, sample rate, number of samples per channel, queue depth. + if (!i2s.begin(AN_I2S_MODE_INOUT, 32000, 512, 32)) { + Serial.println("Failed to start I2S"); + while (1); + } +} + +void loop() { + if (i2s.available()) { + SampleBuffer rxbuf = i2s.read(); + SampleBuffer txbuf = i2s.dequeue(); + for (size_t i=0; i +#include +#include +#include +#include + +USBHostMSD msd; +mbed::FATFileSystem usb("USB_DRIVE"); + +WavReader wav; +// WS, CK, SDI, SDO, MCK +AdvancedI2S i2s(PG_10, PG_11, PG_9, PB_5, PC_4); +#define N_SAMPLES (512) + +void setup() { + Serial.begin(9600); + while (!Serial) { + + } + + // Enable power for HOST USB connector. + pinMode(PA_15, OUTPUT); + digitalWrite(PA_15, HIGH); + + Serial.println("Please connect a USB stick to the USB host port..."); + while (!msd.connect()) { + delay(100); + } + + Serial.println("Mounting USB device..."); + int const rc_mount = usb.mount(&msd); + if (rc_mount) { + Serial.print("Error mounting USB device "); + Serial.println(rc_mount); + while (1); + } + + Serial.println("Opening audio file ..."); + if (!wav.begin("/USB_DRIVE/AUDIO_SAMPLE.wav", N_SAMPLES, 1, true)) { + Serial.print("Error opening audio file: "); + while (1); + } + + // Resolution, sample rate, number of samples per channel, queue depth. + if (!i2s.begin(AN_I2S_MODE_OUT, wav.sample_rate(), N_SAMPLES, 64)) { + Serial.println("Failed to start I2S"); + while (1); + } + Serial.println("Playing audio file ..."); +} + +void loop() { + if (i2s.available() && wav.available()) { + // Get a free I2S buffer for writing. + SampleBuffer i2s_buf = i2s.dequeue(); + + // Read a PCM samples buffer from the wav file. + SampleBuffer pcm_buf = wav.read(); + + // Write PCM samples to the I2S buffer. + for (size_t i = 0; i < N_SAMPLES * wav.channels(); i++) { + // Note I2S buffers are always 2 channels. + if (wav.channels() == 2) { + i2s_buf[i] = pcm_buf[i]; + } else { + i2s_buf[(i * 2) + 0] = ((int16_t) pcm_buf[i] / 2); + i2s_buf[(i * 2) + 1] = ((int16_t) pcm_buf[i] / 2); + } + } + + // Write back the I2S buffer. + i2s.write(i2s_buf); + + // Release the PCM buffer. + pcm_buf.release(); + } +} diff --git a/src/AdvancedI2S.cpp b/src/AdvancedI2S.cpp new file mode 100644 index 0000000..b0d0c98 --- /dev/null +++ b/src/AdvancedI2S.cpp @@ -0,0 +1,408 @@ +/* + This file is part of the Arduino_AdvancedAnalog library. + Copyright (c) 2024 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "Arduino.h" +#include "HALConfig.h" +#include "AdvancedI2S.h" + +struct i2s_descr_t { + I2S_HandleTypeDef i2s; + DMA_HandleTypeDef dmatx; + IRQn_Type dmatx_irqn; + DMABufferPool *dmatx_pool; + DMABuffer *dmatx_buf[2]; + DMA_HandleTypeDef dmarx; + IRQn_Type dmarx_irqn; + DMABufferPool *dmarx_pool; + DMABuffer *dmarx_buf[2]; +}; + +static i2s_descr_t i2s_descr_all[] = { + { + {SPI1}, + {DMA2_Stream1, {DMA_REQUEST_SPI1_TX}}, DMA2_Stream1_IRQn, nullptr, {nullptr, nullptr}, + {DMA2_Stream2, {DMA_REQUEST_SPI1_RX}}, DMA2_Stream2_IRQn, nullptr, {nullptr, nullptr}, + }, + { + {SPI2}, + {DMA2_Stream3, {DMA_REQUEST_SPI2_TX}}, DMA2_Stream3_IRQn, nullptr, {nullptr, nullptr}, + {DMA2_Stream4, {DMA_REQUEST_SPI2_RX}}, DMA2_Stream4_IRQn, nullptr, {nullptr, nullptr}, + }, + { + {SPI3}, + {DMA2_Stream5, {DMA_REQUEST_SPI3_TX}}, DMA2_Stream5_IRQn, nullptr, {nullptr, nullptr}, + {DMA2_Stream6, {DMA_REQUEST_SPI3_RX}}, DMA2_Stream6_IRQn, nullptr, {nullptr, nullptr}, + }, +}; + +static const PinMap PinMap_SPI_MCK[] = { + {PC_4, SPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF5_SPI1)}, + {PC_6, SPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF5_SPI2)}, + {PC_7, SPI_3, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF6_SPI3)}, + {NC, NC, 0} +}; + +extern "C" { + +void DMA2_Stream1_IRQHandler() { + HAL_DMA_IRQHandler(&i2s_descr_all[0].dmatx); +} + +void DMA2_Stream2_IRQHandler() { + HAL_DMA_IRQHandler(&i2s_descr_all[0].dmarx); +} + +void DMA2_Stream3_IRQHandler() { + HAL_DMA_IRQHandler(&i2s_descr_all[1].dmatx); +} + +void DMA2_Stream4_IRQHandler() { + HAL_DMA_IRQHandler(&i2s_descr_all[1].dmarx); +} + +void DMA2_Stream5_IRQHandler() { + HAL_DMA_IRQHandler(&i2s_descr_all[2].dmatx); +} + +void DMA2_Stream6_IRQHandler() { + HAL_DMA_IRQHandler(&i2s_descr_all[2].dmarx); +} + +} // extern C + +static uint32_t i2s_hal_mode(i2s_mode_t i2s_mode) { + if (i2s_mode == AN_I2S_MODE_OUT) { + return I2S_MODE_MASTER_TX; + } else if (i2s_mode == AN_I2S_MODE_IN) { + return I2S_MODE_MASTER_RX; + } else { + return I2S_MODE_MASTER_FULLDUPLEX; + } +} + +static i2s_descr_t *i2s_descr_get(SPI_TypeDef *i2s) { + if (i2s == SPI1) { + return &i2s_descr_all[0]; + } else if (i2s == SPI2) { + return &i2s_descr_all[1]; + } else if (i2s == SPI3) { + return &i2s_descr_all[2]; + } + return NULL; +} + +static void i2s_descr_deinit(i2s_descr_t *descr, bool dealloc_pool) { + if (descr != nullptr) { + HAL_I2S_DMAStop(&descr->i2s); + + for (size_t i=0; idmatx_buf); i++) { + if (descr->dmatx_buf[i]) { + descr->dmatx_buf[i]->release(); + descr->dmatx_buf[i] = nullptr; + } + } + + for (size_t i=0; idmarx_buf); i++) { + if (descr->dmatx_buf[i]) { + descr->dmatx_buf[i]->release(); + descr->dmatx_buf[i] = nullptr; + } + } + + if (dealloc_pool) { + if (descr->dmarx_pool) { + delete descr->dmarx_pool; + } + descr->dmarx_pool = nullptr; + + if (descr->dmatx_pool) { + delete descr->dmatx_pool; + } + descr->dmatx_pool = nullptr; + } else { + if (descr->dmarx_pool) { + descr->dmarx_pool->flush(); + } + + if (descr->dmatx_pool) { + descr->dmatx_pool->flush(); + } + } + } +} + +static int i2s_start_dma_transfer(i2s_descr_t *descr, i2s_mode_t i2s_mode) { + uint16_t *tx_buf = NULL; + uint16_t *rx_buf = NULL; + uint16_t buf_size = 0; + + if (i2s_mode & AN_I2S_MODE_IN) { + // Start I2S DMA. + descr->dmarx_buf[0] = descr->dmarx_pool->allocate(); + descr->dmarx_buf[1] = descr->dmarx_pool->allocate(); + rx_buf = (uint16_t *) descr->dmarx_buf[0]->data(); + buf_size = descr->dmarx_buf[0]->size(); + HAL_NVIC_DisableIRQ(descr->dmarx_irqn); + } + + if (i2s_mode & AN_I2S_MODE_OUT) { + descr->dmatx_buf[0] = descr->dmatx_pool->dequeue(); + descr->dmatx_buf[1] = descr->dmatx_pool->dequeue(); + tx_buf = (uint16_t *) descr->dmatx_buf[0]->data(); + buf_size = descr->dmatx_buf[0]->size(); + HAL_NVIC_DisableIRQ(descr->dmatx_irqn); + } + + // Start I2S DMA. + if (i2s_mode == AN_I2S_MODE_IN) { + if (HAL_I2S_Receive_DMA(&descr->i2s, rx_buf, buf_size) != HAL_OK) { + return 0; + } + } else if (i2s_mode == AN_I2S_MODE_OUT) { + if (HAL_I2S_Transmit_DMA(&descr->i2s, tx_buf, buf_size) != HAL_OK) { + return 0; + } + } else { + if (HAL_I2SEx_TransmitReceive_DMA(&descr->i2s, tx_buf, rx_buf, buf_size) != HAL_OK) { + return 0; + } + } + + // Re/enable DMA double buffer mode. + if (i2s_mode & AN_I2S_MODE_IN) { + hal_dma_enable_dbm(&descr->dmarx, descr->dmarx_buf[0]->data(), descr->dmarx_buf[1]->data()); + HAL_NVIC_EnableIRQ(descr->dmarx_irqn); + } + + if (i2s_mode & AN_I2S_MODE_OUT) { + hal_dma_enable_dbm(&descr->dmatx, descr->dmatx_buf[0]->data(), descr->dmatx_buf[1]->data()); + HAL_NVIC_EnableIRQ(descr->dmatx_irqn); + } + return 1; +} + +bool AdvancedI2S::available() { + if (descr != nullptr) { + if (i2s_mode == AN_I2S_MODE_IN && descr->dmarx_pool) { + return descr->dmarx_pool->readable(); + } else if (i2s_mode == AN_I2S_MODE_OUT && descr->dmatx_pool) { + return descr->dmatx_pool->writable(); + } else if (descr->dmatx_pool && descr->dmarx_pool) { + return descr->dmarx_pool->readable() && descr->dmatx_pool->writable(); + } + } + return false; +} + +DMABuffer &AdvancedI2S::read() { + static DMABuffer NULLBUF; + if (descr && descr->dmarx_pool) { + while (!descr->dmarx_pool->readable()) { + __WFI(); + } + return *descr->dmarx_pool->dequeue(); + } + return NULLBUF; +} + +DMABuffer &AdvancedI2S::dequeue() { + static DMABuffer NULLBUF; + if (descr && descr->dmatx_pool) { + while (!descr->dmatx_pool->writable()) { + __WFI(); + } + return *descr->dmatx_pool->allocate(); + } + return NULLBUF; +} + +void AdvancedI2S::write(DMABuffer &dmabuf) { + static uint32_t buf_count = 0; + + if (descr == nullptr) { + return; + } + + // Make sure any cached data is flushed. + dmabuf.flush(); + descr->dmatx_pool->enqueue(&dmabuf); + + if (descr->dmatx_buf[0] == nullptr && (++buf_count % 3) == 0) { + i2s_start_dma_transfer(descr, i2s_mode); + } +} + +int AdvancedI2S::begin(i2s_mode_t i2s_mode, uint32_t sample_rate, size_t n_samples, size_t n_buffers) { + this->i2s_mode = i2s_mode; + + // Sanity checks. + if (sample_rate < 8000 || sample_rate > 192000 || descr != nullptr) { + return 0; + } + + // Configure I2S pins. + uint32_t i2s = NC; + const PinMap *i2s_pins_map[] = { + PinMap_SPI_SSEL, PinMap_SPI_SCLK, PinMap_SPI_MISO, PinMap_SPI_MOSI, PinMap_SPI_MCK + }; + + for (size_t i=0; idmarx_pool = new DMABufferPool(n_samples, 2, n_buffers); + if (descr->dmarx_pool == nullptr) { + descr = nullptr; + return 0; + } + // Init and config DMA. + if (hal_dma_config(&descr->dmarx, descr->dmarx_irqn, DMA_PERIPH_TO_MEMORY) != 0) { + return 0; + } + __HAL_LINKDMA(&descr->i2s, hdmarx, descr->dmarx); + } + + if (i2s_mode & AN_I2S_MODE_OUT) { + // Allocate DMA buffer pool. + descr->dmatx_pool = new DMABufferPool(n_samples, 2, n_buffers); + if (descr->dmatx_pool == nullptr) { + descr = nullptr; + return 0; + } + // Init and config DMA. + if (hal_dma_config(&descr->dmatx, descr->dmatx_irqn, DMA_MEMORY_TO_PERIPH) != 0) { + return 0; + } + __HAL_LINKDMA(&descr->i2s, hdmatx, descr->dmatx); + } + + // Init and config I2S. + if (hal_i2s_config(&descr->i2s, sample_rate, i2s_hal_mode(i2s_mode), i2s_pins[4] != NC) != 0) { + return 0; + } + + if (i2s_mode == AN_I2S_MODE_IN) { + return i2s_start_dma_transfer(descr, i2s_mode); + } + + if (i2s_mode == AN_I2S_MODE_INOUT) { + // The transmit pool has to be primed with a few buffers first, before the + // DMA can be started in full-duplex mode. + for (int i=0; i<3; i++) { + SampleBuffer outbuf = dequeue(); + memset(outbuf.data(), 0, outbuf.bytes()); + write(outbuf); + } + } + return 1; +} + +int AdvancedI2S::stop() { + i2s_descr_deinit(descr, true); + descr = nullptr; + return 1; +} + +AdvancedI2S::~AdvancedI2S() { + i2s_descr_deinit(descr, true); +} + +extern "C" { + +void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *i2s) { + i2s_descr_t *descr = i2s_descr_get(i2s->Instance); + + if (descr == nullptr) { + return; + } + + // NOTE: CT bit is inverted, to get the DMA buffer that's Not currently in use. + size_t ct = ! hal_dma_get_ct(&descr->dmatx); + + // Release the DMA buffer that was just used, dequeue the next one, and update + // the next DMA memory address target. + if (descr->dmatx_pool->readable()) { + descr->dmatx_buf[ct]->release(); + descr->dmatx_buf[ct] = descr->dmatx_pool->dequeue(); + hal_dma_update_memory(&descr->dmatx, descr->dmatx_buf[ct]->data()); + } else { + i2s_descr_deinit(descr, false); + } +} + +void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *i2s) { + i2s_descr_t *descr = i2s_descr_get(i2s->Instance); + + if (descr == nullptr) { + return; + } + + // NOTE: CT bit is inverted, to get the DMA buffer that's Not currently in use. + size_t ct = ! hal_dma_get_ct(&descr->dmarx); + + // Update the buffer's timestamp. + descr->dmarx_buf[ct]->timestamp(us_ticker_read()); + + // Flush the DMA buffer that was just used, move it to the ready queue, and + // allocate a new one. + if (descr->dmarx_pool->writable()) { + // Make sure any cached data is discarded. + descr->dmarx_buf[ct]->invalidate(); + // Move current DMA buffer to ready queue. + descr->dmarx_pool->enqueue(descr->dmarx_buf[ct]); + // Allocate a new free buffer. + descr->dmarx_buf[ct] = descr->dmarx_pool->allocate(); + // Currently, all multi-channel buffers are interleaved. + if (descr->dmarx_buf[ct]->channels() > 1) { + descr->dmarx_buf[ct]->setflags(DMA_BUFFER_INTRLVD); + } + } else { + descr->dmarx_buf[ct]->setflags(DMA_BUFFER_DISCONT); + } + + // Update the next DMA target pointer. + // NOTE: If the pool was empty, the same buffer is reused. + hal_dma_update_memory(&descr->dmarx, descr->dmarx_buf[ct]->data()); +} + +void HAL_I2SEx_TxRxCpltCallback(I2S_HandleTypeDef *i2s) { + HAL_I2S_RxCpltCallback(i2s); + HAL_I2S_TxCpltCallback(i2s); +} + +} // extern C diff --git a/src/AdvancedI2S.h b/src/AdvancedI2S.h new file mode 100644 index 0000000..d269e8c --- /dev/null +++ b/src/AdvancedI2S.h @@ -0,0 +1,58 @@ +/* + This file is part of the Arduino_AdvancedAnalog library. + Copyright (c) 2023-2024 Arduino SA. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef ARDUINO_ADVANCED_I2S_H +#define ARDUINO_ADVANCED_I2S_H + +#include +#include "DMABuffer.h" +#include "AdvancedAnalog.h" + +struct i2s_descr_t; + +typedef enum { + AN_I2S_MODE_IN = (1U << 0U), + AN_I2S_MODE_OUT = (1U << 1U), + AN_I2S_MODE_INOUT = (AN_I2S_MODE_IN | AN_I2S_MODE_OUT), +} i2s_mode_t; + +class AdvancedI2S { + private: + i2s_descr_t *descr; + PinName i2s_pins[5]; + i2s_mode_t i2s_mode; + + public: + AdvancedI2S(PinName ws, PinName ck, PinName sdi, PinName sdo, PinName mck): + descr(nullptr), i2s_pins{ws, ck, sdi, sdo, mck} { + } + + AdvancedI2S() { + } + + ~AdvancedI2S(); + + bool available(); + SampleBuffer read(); + SampleBuffer dequeue(); + void write(SampleBuffer dmabuf); + int begin(i2s_mode_t i2s_mode, uint32_t sample_rate, size_t n_samples, size_t n_buffers); + int stop(); +}; + +#endif // ARDUINO_ADVANCED_I2S_H diff --git a/src/Arduino_AdvancedAnalog.h b/src/Arduino_AdvancedAnalog.h index e4bc8c4..13a3b3a 100644 --- a/src/Arduino_AdvancedAnalog.h +++ b/src/Arduino_AdvancedAnalog.h @@ -26,6 +26,7 @@ #include "AdvancedADC.h" #include "AdvancedDAC.h" +#include "AdvancedI2S.h" #include "WavReader.h" #endif /* ADVANCEDANALOGREDUX_ARDUINO_ADVANCEDANALOG_H */ diff --git a/src/HALConfig.cpp b/src/HALConfig.cpp index 493f3f9..984d9f7 100644 --- a/src/HALConfig.cpp +++ b/src/HALConfig.cpp @@ -71,6 +71,7 @@ int hal_tim_config(TIM_HandleTypeDef *tim, uint32_t t_freq) { int hal_dma_config(DMA_HandleTypeDef *dma, IRQn_Type irqn, uint32_t direction) { // Enable DMA clock __HAL_RCC_DMA1_CLK_ENABLE(); + __HAL_RCC_DMA2_CLK_ENABLE(); // DMA Init dma->Init.Mode = DMA_DOUBLE_BUFFER_M0; @@ -218,3 +219,47 @@ int hal_adc_config(ADC_HandleTypeDef *adc, uint32_t resolution, uint32_t trigger return 0; } + +int hal_i2s_config(I2S_HandleTypeDef *i2s, uint32_t sample_rate, uint32_t mode, bool mck_enable) { + // Set I2S clock source. + RCC_PeriphCLKInitTypeDef pclk_init = {0}; + pclk_init.PLL3.PLL3M = 16; + pclk_init.PLL3.PLL3N = 344; + pclk_init.PLL3.PLL3P = 7; + pclk_init.PLL3.PLL3Q = 5; + pclk_init.PLL3.PLL3R = 5; + pclk_init.PLL3.PLL3FRACN = 0; + pclk_init.PLL3.PLL3RGE = RCC_PLL3VCIRANGE_0; + pclk_init.PLL3.PLL3VCOSEL = RCC_PLL3VCOMEDIUM; + pclk_init.PeriphClockSelection |= RCC_PERIPHCLK_SPI123; + pclk_init.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL3; + + if (HAL_RCCEx_PeriphCLKConfig(&pclk_init) != HAL_OK) { + return -1; + } + + // Enable I2S clock + if (i2s->Instance == SPI1) { + __HAL_RCC_SPI1_CLK_ENABLE(); + } else if (i2s->Instance == SPI2) { + __HAL_RCC_SPI2_CLK_ENABLE(); + } else if (i2s->Instance == SPI3) { + __HAL_RCC_SPI3_CLK_ENABLE(); + } + + i2s->Init.Mode = mode; + i2s->Init.Standard = I2S_STANDARD_PHILIPS; + i2s->Init.DataFormat = I2S_DATAFORMAT_16B_EXTENDED; + i2s->Init.MCLKOutput = mck_enable ? I2S_MCLKOUTPUT_ENABLE : I2S_MCLKOUTPUT_DISABLE; + i2s->Init.AudioFreq = sample_rate; + i2s->Init.CPOL = I2S_CPOL_LOW; + i2s->Init.FirstBit = I2S_FIRSTBIT_MSB; + i2s->Init.WSInversion = I2S_WS_INVERSION_DISABLE; + i2s->Init.Data24BitAlignment = I2S_DATA_24BIT_ALIGNMENT_RIGHT; + i2s->Init.MasterKeepIOState = I2S_MASTER_KEEP_IO_STATE_DISABLE; + + if (HAL_I2S_Init(i2s) != HAL_OK) { + return -1; + } + return 0; +} diff --git a/src/HALConfig.h b/src/HALConfig.h index f366557..7ab6711 100644 --- a/src/HALConfig.h +++ b/src/HALConfig.h @@ -30,5 +30,6 @@ void hal_dma_enable_dbm(DMA_HandleTypeDef *dma, void *m0 = nullptr, void *m1 = n void hal_dma_update_memory(DMA_HandleTypeDef *dma, void *addr); int hal_dac_config(DAC_HandleTypeDef *dac, uint32_t channel, uint32_t trigger); int hal_adc_config(ADC_HandleTypeDef *adc, uint32_t resolution, uint32_t trigger, PinName *adc_pins, uint32_t n_channels); +int hal_i2s_config(I2S_HandleTypeDef *i2s, uint32_t sample_rate, uint32_t mode, bool mck_enable); #endif // __HAL_CONFIG_H__