#include #include "stm32f4xx.h" #include "iis.h" #ifdef TARGET_I2S_TX_DMA_BUFSIZ #define TX_DMA_BUFFER_SIZE TARGET_I2S_TX_DMA_BUFSIZ #else #define TX_DMA_BUFFER_SIZE 512 #endif struct iis_descr { SPI_TypeDef *base; I2S_InitTypeDef conf; void (*setup)(struct iis_descr *); int8_t tx_irq; int8_t tx_irq_priority; uint32_t dma_rcc; uint32_t tx_dma_channel; DMA_Stream_TypeDef *tx_dma_stream; uint32_t tx_dma_tcflag; DMA_InitTypeDef tx_dma_conf; volatile uint16_t *tx_dma_buffer; uint16_t tx_buffer_size; volatile uint8_t *tx_buffer; volatile uint16_t tx_head; volatile uint16_t tx_tail; void (*tx_completion)(struct iis_descr *, void *); void *user; }; int iis_setup(iis_t tag, uint16_t standard, uint32_t freq) { DMA_InitTypeDef dma_conf; NVIC_InitTypeDef irq_conf; I2S_StructInit(&tag->conf); tag->conf.I2S_Mode = I2S_Mode_MasterTx; tag->conf.I2S_Standard = standard; tag->conf.I2S_AudioFreq = freq; tag->conf.I2S_MCLKOutput = I2S_MCLKOutput_Enable; tag->setup(tag); SPI_I2S_DeInit(tag->base); I2S_Init(tag->base, &tag->conf); // tx dma RCC_AHB1PeriphClockCmd(tag->dma_rcc, ENABLE); DMA_Cmd(tag->tx_dma_stream, DISABLE); DMA_DeInit(tag->tx_dma_stream); DMA_StructInit(&dma_conf); dma_conf.DMA_Channel = tag->tx_dma_channel; dma_conf.DMA_PeripheralBaseAddr = (uint32_t)&tag->base->DR; dma_conf.DMA_Memory0BaseAddr = (uint32_t)tag->tx_dma_buffer; dma_conf.DMA_BufferSize = (TX_DMA_BUFFER_SIZE / 2); dma_conf.DMA_DIR = DMA_DIR_MemoryToPeripheral; dma_conf.DMA_MemoryInc = DMA_MemoryInc_Enable; dma_conf.DMA_Priority = DMA_Priority_High; dma_conf.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; dma_conf.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; dma_conf.DMA_Mode = DMA_Mode_Circular; DMA_Init(tag->tx_dma_stream, &dma_conf); DMA_DoubleBufferModeConfig(tag->tx_dma_stream, (uint32_t)(&tag->tx_dma_buffer[TX_DMA_BUFFER_SIZE / 2]), DMA_Memory_0); DMA_DoubleBufferModeCmd(tag->tx_dma_stream, ENABLE); tag->tx_dma_conf = dma_conf; DMA_ITConfig(tag->tx_dma_stream, DMA_IT_TC, ENABLE); SPI_I2S_DMACmd(tag->base, SPI_I2S_DMAReq_Tx, ENABLE); memset(&irq_conf, 0, sizeof irq_conf); irq_conf.NVIC_IRQChannel = tag->tx_irq; if (tag->tx_irq_priority > 0) { irq_conf.NVIC_IRQChannelPreemptionPriority = tag->tx_irq_priority; } irq_conf.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&irq_conf); I2S_Cmd(tag->base, ENABLE); return 0; } static int tx_buffer_empty(struct iis_descr *tag) { return tag->tx_head == tag->tx_tail; } static void tx_buffer_put(struct iis_descr *tag, uint8_t data) { tag->tx_buffer[tag->tx_tail % tag->tx_buffer_size] = data; tag->tx_tail++; } static uint8_t tx_buffer_get(struct iis_descr *tag) { uint8_t data = tag->tx_buffer[tag->tx_head % tag->tx_buffer_size]; tag->tx_head++; return data; } static int tx_buffer_full(struct iis_descr *tag) { return (uint16_t)(tag->tx_tail - tag->tx_head) == tag->tx_buffer_size; } void iis_start_tx(struct iis_descr *tag) { DMA_Cmd(tag->tx_dma_stream, ENABLE); } void iis_stop_tx(struct iis_descr *tag) { DMA_Cmd(tag->tx_dma_stream, DISABLE); } void iis_set_tx_completion(struct iis_descr *tag, void (*callback)(struct iis_descr *, void *), void *user) { tag->tx_completion = callback; tag->user = user; } size_t iis_send(struct iis_descr *tag, const void* _data, size_t num_bytes) { size_t bytes_written = 0; const uint8_t *data = _data; while (!tx_buffer_full(tag) && (bytes_written < num_bytes)) { tx_buffer_put(tag, data[bytes_written++]); } return bytes_written; } size_t iis_send_blocking(struct iis_descr *tag, const void* _data, size_t num_bytes) { size_t r = num_bytes, n; const uint8_t *ptr = _data; while (r > 0) { n = iis_send(tag, ptr, r); ptr += n; r -= n; } return num_bytes; } static __attribute__((unused)) void __dma_tx_isr(struct iis_descr *tag) { int n = 0; volatile uint8_t *ptr; if (DMA_GetITStatus(tag->tx_dma_stream, tag->tx_dma_tcflag)) { DMA_ClearITPendingBit(tag->tx_dma_stream, tag->tx_dma_tcflag); if (tag->tx_completion) tag->tx_completion(tag, tag->user); if (DMA_GetCurrentMemoryTarget(tag->tx_dma_stream) == 1) { /* current using buffer 1, buffer 0 idle */ ptr = (volatile uint8_t *)tag->tx_dma_buffer; } else { ptr = (volatile uint8_t *)&tag->tx_dma_buffer[TX_DMA_BUFFER_SIZE >> 1]; } if (!tx_buffer_empty(tag)) { for (n = 0; n < TX_DMA_BUFFER_SIZE && !tx_buffer_empty(tag); ++n) { ptr[n] = tx_buffer_get(tag); } } if (n < TX_DMA_BUFFER_SIZE) { memset((void *)(ptr + n), 0, TX_DMA_BUFFER_SIZE - n); } } } #if (TARGET_HAS_I2S0) void __attribute__((weak)) target_i2s0_setup() { GPIO_InitTypeDef gpio_conf; RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC, ENABLE); RCC_I2SCLKConfig(RCC_I2S2CLKSource_PLLI2S); RCC_PLLI2SCmd(ENABLE); while (!RCC_GetFlagStatus(RCC_FLAG_PLLI2SRDY)) ; GPIO_StructInit(&gpio_conf); gpio_conf.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_10; /* PB12-> LRCLK, PB10 SCLK */ gpio_conf.GPIO_Mode = GPIO_Mode_AF; gpio_conf.GPIO_OType = GPIO_OType_PP; gpio_conf.GPIO_PuPd = GPIO_PuPd_UP; gpio_conf.GPIO_Speed = GPIO_High_Speed; GPIO_Init(GPIOB, &gpio_conf); gpio_conf.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_3; /* PC6 -> MCLK, PC3 -> SD */ GPIO_Init(GPIOC, &gpio_conf); GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_SPI2); GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_SPI2); GPIO_PinAFConfig(GPIOC, GPIO_PinSource3, GPIO_AF_SPI2); GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_SPI2); } static void i2s0_setup(struct iis_descr *tag) { target_i2s0_setup(); } static volatile uint8_t __i2s0_tx_buffer[TARGET_I2S0_TX_BUFFER_SIZE] __attribute__((section(".txbuf.i2s0,\"aw\",%nobits@"))); static volatile uint16_t __i2s0_tx_dma_buffer[TX_DMA_BUFFER_SIZE] __attribute__((section(".txdma.i2s0,\"aw\",%nobits@"))); struct iis_descr i2s0 = { .base = SPI2, .setup = i2s0_setup, .tx_irq = DMA1_Stream4_IRQn, #ifdef TARGET_I2S0_TX_IRQ_PRIORITY .tx_irq_priority = TARGET_I2S0_TX_IRQ_PRIORITY, #else .tx_irq_priority = -1, #endif .dma_rcc = RCC_AHB1Periph_DMA1, .tx_dma_channel = DMA_Channel_0, .tx_dma_stream = DMA1_Stream4, .tx_dma_tcflag = DMA_IT_TCIF4, .tx_dma_buffer = __i2s0_tx_dma_buffer, .tx_buffer_size = TARGET_I2S0_TX_BUFFER_SIZE, .tx_buffer = __i2s0_tx_buffer, .tx_head = 0, .tx_tail = 0, }; void DMA1_Stream4_IRQHandler(void) { __dma_tx_isr(&i2s0); } #endif