Files

copied
Last update 6 years 1 month by Olivier Gillet
Fileselementsdrivers
..
codec.cc
codec.h
cv_adc.cc
cv_adc.h
debug_pin.h
debug_port.cc
debug_port.h
gate_input.cc
gate_input.h
leds.cc
leds.h
pots_adc.cc
pots_adc.h
switch.cc
switch.h
system.cc
system.h
codec.cc
// Copyright 2014 Olivier Gillet. // // Author: Olivier Gillet (ol.gillet@gmail.com) // // 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. // // See http://creativecommons.org/licenses/MIT/ for more information. // // ----------------------------------------------------------------------------- // // WM8371 Codec support. #include "elements/drivers/codec.h" #include <string.h> #define CODEC_I2C I2C2 #define CODEC_I2C_CLK RCC_APB1Periph_I2C2 #define CODEC_I2C_GPIO_CLOCK RCC_AHB1Periph_GPIOB #define CODEC_I2C_GPIO_AF GPIO_AF_I2C2 #define CODEC_I2C_GPIO GPIOB #define CODEC_I2C_SCL_PIN GPIO_Pin_10 #define CODEC_I2C_SDA_PIN GPIO_Pin_11 #define CODEC_I2C_SCL_PINSRC GPIO_PinSource10 #define CODEC_I2C_SDA_PINSRC GPIO_PinSource11 #define CODEC_TIMEOUT ((uint32_t)0x1000) #define CODEC_LONG_TIMEOUT ((uint32_t)(300 * CODEC_TIMEOUT)) #define CODEC_I2C_SPEED 100000 #define CODEC_I2S SPI2 #define CODEC_I2S_EXT I2S2ext #define CODEC_I2S_CLK RCC_APB1Periph_SPI2 #define CODEC_I2S_ADDRESS 0x4000380C #define CODEC_I2S_EXT_ADDRESS 0x4000340C #define CODEC_I2S_GPIO_AF GPIO_AF_SPI2 #define CODEC_I2S_IRQ SPI2_IRQn #define CODEC_I2S_EXT_IRQ SPI2_IRQn #define CODEC_I2S_GPIO_CLOCK (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOB) #define CODEC_I2S_WS_PIN GPIO_Pin_12 #define CODEC_I2S_SCK_PIN GPIO_Pin_13 #define CODEC_I2S_SDI_PIN GPIO_Pin_14 #define CODEC_I2S_SDO_PIN GPIO_Pin_15 #define CODEC_I2S_MCK_PIN GPIO_Pin_6 #define CODEC_I2S_WS_PINSRC GPIO_PinSource12 #define CODEC_I2S_SCK_PINSRC GPIO_PinSource13 #define CODEC_I2S_SDI_PINSRC GPIO_PinSource14 #define CODEC_I2S_SDO_PINSRC GPIO_PinSource15 #define CODEC_I2S_MCK_PINSRC GPIO_PinSource6 #define CODEC_I2S_GPIO GPIOB #define CODEC_I2S_MCK_GPIO GPIOC #define AUDIO_I2S_IRQHandler SPI2_IRQHandler #define AUDIO_DMA_PERIPH_DATA_SIZE DMA_PeripheralDataSize_HalfWord #define AUDIO_DMA_MEM_DATA_SIZE DMA_MemoryDataSize_HalfWord #define AUDIO_I2S_DMA_CLOCK RCC_AHB1Periph_DMA1 #define AUDIO_I2S_DMA_STREAM DMA1_Stream4 #define AUDIO_I2S_DMA_DREG CODEC_I2S_ADDRESS #define AUDIO_I2S_DMA_CHANNEL DMA_Channel_0 #define AUDIO_I2S_DMA_IRQ DMA1_Stream4_IRQn #define AUDIO_I2S_DMA_FLAG_TC DMA_FLAG_TCIF4 #define AUDIO_I2S_DMA_FLAG_HT DMA_FLAG_HTIF4 #define AUDIO_I2S_DMA_FLAG_FE DMA_FLAG_FEIF4 #define AUDIO_I2S_DMA_FLAG_TE DMA_FLAG_TEIF4 #define AUDIO_I2S_DMA_FLAG_DME DMA_FLAG_DMEIF4 #define AUDIO_I2S_EXT_DMA_STREAM DMA1_Stream3 #define AUDIO_I2S_EXT_DMA_DREG CODEC_I2S_EXT_ADDRESS #define AUDIO_I2S_EXT_DMA_CHANNEL DMA_Channel_3 #define AUDIO_I2S_EXT_DMA_IRQ DMA1_Stream3_IRQn #define AUDIO_I2S_EXT_DMA_FLAG_TC DMA_FLAG_TCIF3 #define AUDIO_I2S_EXT_DMA_FLAG_HT DMA_FLAG_HTIF3 #define AUDIO_I2S_EXT_DMA_FLAG_FE DMA_FLAG_FEIF3 #define AUDIO_I2S_EXT_DMA_FLAG_TE DMA_FLAG_TEIF3 #define AUDIO_I2S_EXT_DMA_FLAG_DME DMA_FLAG_DMEIF3 #define AUDIO_I2S_EXT_DMA_REG DMA1 #define AUDIO_I2S_EXT_DMA_ISR LISR #define AUDIO_I2S_EXT_DMA_IFCR LIFCR #define W8731_ADDR_0 0x1A #define W8731_ADDR_1 0x1B #define W8731_NUM_REGS 10 #define CODEC_ADDRESS (W8731_ADDR_0 << 1) #define WAIT_LONG(x) { \ uint32_t timeout = CODEC_LONG_TIMEOUT; \ while (x) { if ((timeout--) == 0) return false; } \ } #define WAIT(x) { \ uint32_t timeout = CODEC_TIMEOUT; \ while (x) { if ((timeout--) == 0) return false; } \ } namespace elements { /* static */ Codec* Codec::instance_; enum CodecRegister { CODEC_REG_LEFT_LINE_IN = 0x00, CODEC_REG_RIGHT_LINE_IN = 0x01, CODEC_REG_LEFT_HEADPHONES_OUT = 0x02, CODEC_REG_RIGHT_HEADPHONES_OUT = 0x03, CODEC_REG_ANALOGUE_ROUTING = 0x04, CODEC_REG_DIGITAL_ROUTING = 0x05, CODEC_REG_POWER_MANAGEMENT = 0x06, CODEC_REG_DIGITAL_FORMAT = 0x07, CODEC_REG_SAMPLE_RATE = 0x08, CODEC_REG_ACTIVE = 0x09, CODEC_REG_RESET = 0x0f, }; bool Codec::InitializeGPIO() { GPIO_InitTypeDef gpio_init; // Start GPIO peripheral clocks. RCC_AHB1PeriphClockCmd(CODEC_I2C_GPIO_CLOCK | CODEC_I2S_GPIO_CLOCK, ENABLE); // Initialize I2C pins gpio_init.GPIO_Pin = CODEC_I2C_SCL_PIN | CODEC_I2C_SDA_PIN; gpio_init.GPIO_Mode = GPIO_Mode_AF; gpio_init.GPIO_Speed = GPIO_Speed_25MHz; gpio_init.GPIO_OType = GPIO_OType_OD; gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(CODEC_I2C_GPIO, &gpio_init); // Connect pins to I2C peripheral GPIO_PinAFConfig(CODEC_I2C_GPIO, CODEC_I2C_SCL_PINSRC, CODEC_I2C_GPIO_AF); GPIO_PinAFConfig(CODEC_I2C_GPIO, CODEC_I2C_SDA_PINSRC, CODEC_I2C_GPIO_AF); // Initialize I2S pins gpio_init.GPIO_Pin = CODEC_I2S_SCK_PIN | CODEC_I2S_SDO_PIN | \ CODEC_I2S_SDI_PIN | CODEC_I2S_WS_PIN; gpio_init.GPIO_Mode = GPIO_Mode_AF; gpio_init.GPIO_Speed = GPIO_Speed_50MHz; gpio_init.GPIO_OType = GPIO_OType_PP; gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(CODEC_I2S_GPIO, &gpio_init); gpio_init.GPIO_Pin = CODEC_I2S_MCK_PIN; GPIO_Init(CODEC_I2S_MCK_GPIO, &gpio_init); // Connect pins to I2S peripheral. GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_WS_PINSRC, CODEC_I2S_GPIO_AF); GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_SCK_PINSRC, CODEC_I2S_GPIO_AF); GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_SDO_PINSRC, CODEC_I2S_GPIO_AF); GPIO_PinAFConfig(CODEC_I2S_GPIO, CODEC_I2S_SDI_PINSRC, CODEC_I2S_GPIO_AF); GPIO_PinAFConfig(CODEC_I2S_MCK_GPIO, CODEC_I2S_MCK_PINSRC, CODEC_I2S_GPIO_AF); return true; } bool Codec::InitializeControlInterface() { I2C_InitTypeDef i2c_init; // Initialize I2C RCC_APB1PeriphClockCmd(CODEC_I2C_CLK, ENABLE); I2C_DeInit(CODEC_I2C); i2c_init.I2C_Mode = I2C_Mode_I2C; i2c_init.I2C_DutyCycle = I2C_DutyCycle_2; i2c_init.I2C_OwnAddress1 = 0x33; i2c_init.I2C_Ack = I2C_Ack_Enable; i2c_init.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; i2c_init.I2C_ClockSpeed = CODEC_I2C_SPEED; I2C_Init(CODEC_I2C, &i2c_init); I2C_Cmd(CODEC_I2C, ENABLE); return true; } bool Codec::InitializeAudioInterface( uint32_t sample_rate, CodecProtocol protocol, CodecFormat format) { // Configure PLL and I2S master clock. RCC_I2SCLKConfig(RCC_I2S2CLKSource_PLLI2S); // The following values have been computed for a 8Mhz external crystal! RCC_PLLI2SCmd(DISABLE); if (sample_rate == 48000) { // 47.992kHz RCC_PLLI2SConfig(258, 3); } else if (sample_rate == 44100) { // 44.11kHz RCC_PLLI2SConfig(271, 6); } else if (sample_rate == 32000) { // 32.003kHz RCC_PLLI2SConfig(426, 4); } else if (sample_rate == 96000) { // 95.95 kHz RCC_PLLI2SConfig(393, 4); } else { // Unsupported sample rate! return false; } RCC_PLLI2SCmd(ENABLE); WAIT(RCC_GetFlagStatus(RCC_FLAG_PLLI2SRDY) == RESET); RCC_APB1PeriphClockCmd(CODEC_I2S_CLK, ENABLE); // Initialize I2S I2S_InitTypeDef i2s_init; SPI_I2S_DeInit(CODEC_I2S); i2s_init.I2S_AudioFreq = sample_rate; i2s_init.I2S_Standard = protocol; i2s_init.I2S_DataFormat = format; i2s_init.I2S_CPOL = I2S_CPOL_Low; i2s_init.I2S_Mode = I2S_Mode_MasterTx; i2s_init.I2S_MCLKOutput = I2S_MCLKOutput_Enable; // Initialize the I2S main channel for TX I2S_Init(CODEC_I2S, &i2s_init); // Initialize the I2S extended channel for RX I2S_FullDuplexConfig(CODEC_I2S_EXT, &i2s_init); return true; } bool Codec::WriteControlRegister(uint8_t address, uint16_t data) { uint8_t byte_1 = ((address << 1) & 0xfe) | ((data >> 8) & 0x01); uint8_t byte_2 = data & 0xff; WAIT_LONG(I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_BUSY)); I2C_GenerateSTART(CODEC_I2C, ENABLE); WAIT(!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(CODEC_I2C, CODEC_ADDRESS, I2C_Direction_Transmitter); WAIT(!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(CODEC_I2C, byte_1); WAIT(!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING)); I2C_SendData(CODEC_I2C, byte_2); WAIT(!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING)); WAIT_LONG(!I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_BTF)); I2C_GenerateSTOP(CODEC_I2C, ENABLE); return true; } bool Codec::InitializeCodec( uint32_t sample_rate, CodecProtocol protocol, CodecFormat format) { bool s = true; // success; s = s && WriteControlRegister(CODEC_REG_RESET, 0); // Configure L&R inputs s = s && WriteControlRegister(CODEC_REG_LEFT_LINE_IN, CODEC_INPUT_0_DB); s = s && WriteControlRegister(CODEC_REG_RIGHT_LINE_IN, CODEC_INPUT_0_DB); // Configure L&R headphone outputs s = s && WriteControlRegister(CODEC_REG_LEFT_HEADPHONES_OUT, CODEC_HEADPHONES_MUTE); s = s && WriteControlRegister(CODEC_REG_RIGHT_HEADPHONES_OUT, CODEC_HEADPHONES_MUTE); // Configure analog routing s = s && WriteControlRegister( CODEC_REG_ANALOGUE_ROUTING, CODEC_MIC_MUTE | CODEC_ADC_LINE | CODEC_OUTPUT_DAC_ENABLE); // Configure digital routing s = s && WriteControlRegister(CODEC_REG_DIGITAL_ROUTING, CODEC_DEEMPHASIS_NONE); // Configure power management s = s && WriteControlRegister( CODEC_REG_POWER_MANAGEMENT, CODEC_POWER_DOWN_OSCILLATOR | \ CODEC_POWER_DOWN_CLOCK_OUTPUT | \ CODEC_POWER_DOWN_MIC); uint8_t format_byte = CODEC_FORMAT_SLAVE; if (protocol == CODEC_PROTOCOL_PHILIPS) { format_byte |= CODEC_PROTOCOL_MASK_PHILIPS; } else if (protocol == CODEC_PROTOCOL_MSB_FIRST) { format_byte |= CODEC_PROTOCOL_MASK_MSB_FIRST; } else if (protocol == CODEC_PROTOCOL_LSB_FIRST) { format_byte |= CODEC_PROTOCOL_MASK_LSB_FIRST; } if (format == CODEC_FORMAT_16_BIT) { format_byte |= CODEC_FORMAT_MASK_16_BIT; } else if (format == CODEC_FORMAT_24_BIT) { format_byte |= CODEC_FORMAT_MASK_24_BIT; } else if (format == CODEC_FORMAT_32_BIT) { format_byte |= CODEC_FORMAT_MASK_32_BIT; } s = s && WriteControlRegister(CODEC_REG_DIGITAL_FORMAT, format_byte); uint8_t rate_byte = 0; // According to the WM8731 datasheet, the 32kHz and 96kHz modes require the // master clock to be at 12.288 MHz (384 fs / 128 fs). The STM32F4 I2S clock // is always at 256 fs. So the 32kHz and 96kHz modes are achieved by // pretending that we are doing 48kHz, but with a slower or faster master // clock. rate_byte = sample_rate == 44100 ? CODEC_RATE_44K_44K : CODEC_RATE_48K_48K; s = s && WriteControlRegister(CODEC_REG_SAMPLE_RATE, rate_byte); // For now codec is not active. s = s && WriteControlRegister(CODEC_REG_ACTIVE, 0x00); return s; } bool Codec::InitializeDMA() { RCC_AHB1PeriphClockCmd(AUDIO_I2S_DMA_CLOCK, ENABLE); // DMA setup for TX. DMA_Cmd(AUDIO_I2S_DMA_STREAM, DISABLE); DMA_DeInit(AUDIO_I2S_DMA_STREAM); dma_init_tx_.DMA_Channel = AUDIO_I2S_DMA_CHANNEL; dma_init_tx_.DMA_PeripheralBaseAddr = AUDIO_I2S_DMA_DREG; dma_init_tx_.DMA_Memory0BaseAddr = (uint32_t)0; dma_init_tx_.DMA_DIR = DMA_DIR_MemoryToPeripheral; dma_init_tx_.DMA_BufferSize = (uint32_t)0xFFFE; dma_init_tx_.DMA_PeripheralInc = DMA_PeripheralInc_Disable; dma_init_tx_.DMA_MemoryInc = DMA_MemoryInc_Enable; dma_init_tx_.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; dma_init_tx_.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; dma_init_tx_.DMA_Mode = DMA_Mode_Circular; dma_init_tx_.DMA_Priority = DMA_Priority_High; dma_init_tx_.DMA_FIFOMode = DMA_FIFOMode_Disable; dma_init_tx_.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull; dma_init_tx_.DMA_MemoryBurst = DMA_MemoryBurst_Single; dma_init_tx_.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(AUDIO_I2S_DMA_STREAM, &dma_init_tx_); // DMA setup for RX. DMA_Cmd(AUDIO_I2S_EXT_DMA_STREAM, DISABLE); DMA_DeInit(AUDIO_I2S_EXT_DMA_STREAM); dma_init_rx_.DMA_Channel = AUDIO_I2S_EXT_DMA_CHANNEL; dma_init_rx_.DMA_PeripheralBaseAddr = AUDIO_I2S_EXT_DMA_DREG; dma_init_rx_.DMA_Memory0BaseAddr = (uint32_t)0; dma_init_rx_.DMA_DIR = DMA_DIR_PeripheralToMemory; dma_init_rx_.DMA_BufferSize = (uint32_t)0xFFFE; dma_init_rx_.DMA_PeripheralInc = DMA_PeripheralInc_Disable; dma_init_rx_.DMA_MemoryInc = DMA_MemoryInc_Enable; dma_init_rx_.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; dma_init_rx_.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; dma_init_rx_.DMA_Mode = DMA_Mode_Circular; dma_init_rx_.DMA_Priority = DMA_Priority_High; dma_init_rx_.DMA_FIFOMode = DMA_FIFOMode_Disable; dma_init_rx_.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull; dma_init_rx_.DMA_MemoryBurst = DMA_MemoryBurst_Single; dma_init_rx_.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(AUDIO_I2S_EXT_DMA_STREAM, &dma_init_rx_); // Enable the interrupts. DMA_ITConfig(AUDIO_I2S_EXT_DMA_STREAM, DMA_IT_TC | DMA_IT_HT, ENABLE); // Enable the IRQ. NVIC_EnableIRQ(AUDIO_I2S_EXT_DMA_IRQ); // Start DMA from/to codec. SPI_I2S_DMACmd(CODEC_I2S, SPI_I2S_DMAReq_Tx, ENABLE); SPI_I2S_DMACmd(CODEC_I2S_EXT, SPI_I2S_DMAReq_Rx, ENABLE); return true; } bool Codec::Init( uint32_t sample_rate, CodecProtocol protocol, CodecFormat format) { rx_buffer_.Init(); tx_buffer_.Init(); Frame s; s.l = s.r = 0; for (size_t i = 0; i < rx_buffer_.capacity() >> 1; ++i) { rx_buffer_.Overwrite(s); tx_buffer_.Overwrite(s); } instance_ = this; callback_ = NULL; return InitializeGPIO() && \ InitializeControlInterface() && \ InitializeAudioInterface(sample_rate, protocol, format) && \ InitializeCodec(sample_rate, protocol, format) && \ InitializeDMA(); } bool Codec::Start(FillBufferCallback callback) { // Start the codec. if (!WriteControlRegister(CODEC_REG_ACTIVE, 0x01)) { return false; } callback_ = callback; client_tx_ = NULL; client_rx_ = NULL; transmitted_ = 0; processed_ = 0; // Enable the I2S TX and RX peripherals. if ((CODEC_I2S->I2SCFGR & 0x0400) == 0){ I2S_Cmd(CODEC_I2S, ENABLE); } if ((CODEC_I2S_EXT->I2SCFGR & 0x0400) == 0){ I2S_Cmd(CODEC_I2S_EXT, ENABLE); } dma_init_tx_.DMA_Memory0BaseAddr = (uint32_t)(tx_dma_buffer_); dma_init_rx_.DMA_Memory0BaseAddr = (uint32_t)(rx_dma_buffer_); dma_init_tx_.DMA_BufferSize = kAudioChunkSize * 2 * 2; dma_init_rx_.DMA_BufferSize = kAudioChunkSize * 2 * 2; DMA_Init(AUDIO_I2S_DMA_STREAM, &dma_init_tx_); DMA_Init(AUDIO_I2S_EXT_DMA_STREAM, &dma_init_rx_); DMA_Cmd(AUDIO_I2S_DMA_STREAM, ENABLE); DMA_Cmd(AUDIO_I2S_EXT_DMA_STREAM, ENABLE); return true; } void Codec::Stop() { DMA_Cmd(AUDIO_I2S_DMA_STREAM, DISABLE); DMA_Cmd(AUDIO_I2S_EXT_DMA_STREAM, DISABLE); } void Codec::Fill(size_t offset) { if (kNumFIFOChunks) { // Write input samples to FIFO, Read output samples from FIFO rx_buffer_.Overwrite(&rx_dma_buffer_[offset], kAudioChunkSize); tx_buffer_.ImmediateRead(&tx_dma_buffer_[offset], kAudioChunkSize); } else if (callback_) { (*callback_)( &rx_dma_buffer_[offset], &tx_dma_buffer_[offset], kAudioChunkSize); } else { // Inform the client that some data is ready, and store pointers to the // valid section of the ring buffer. ++transmitted_; client_rx_ = &rx_dma_buffer_[offset]; client_tx_ = &tx_dma_buffer_[offset]; } } } // namespace elements extern "C" { // Do not call into the firmware library to save on calls/jumps. // if (DMA_GetFlagStatus(AUDIO_I2S_EXT_DMA_STREAM, AUDIO_I2S_EXT_DMA_FLAG_TC) != RESET) { // DMA_ClearFlag(AUDIO_I2S_EXT_DMA_STREAM, AUDIO_I2S_EXT_DMA_FLAG_TC); void DMA1_Stream3_IRQHandler(void) { if (AUDIO_I2S_EXT_DMA_REG->AUDIO_I2S_EXT_DMA_ISR & AUDIO_I2S_EXT_DMA_FLAG_TC) { AUDIO_I2S_EXT_DMA_REG->AUDIO_I2S_EXT_DMA_IFCR = AUDIO_I2S_EXT_DMA_FLAG_TC; elements::Codec::GetInstance()->Fill(elements::kAudioChunkSize); } if (AUDIO_I2S_EXT_DMA_REG->AUDIO_I2S_EXT_DMA_ISR & AUDIO_I2S_EXT_DMA_FLAG_HT) { AUDIO_I2S_EXT_DMA_REG->AUDIO_I2S_EXT_DMA_IFCR = AUDIO_I2S_EXT_DMA_FLAG_HT; elements::Codec::GetInstance()->Fill(0); } } }
Report a bug