You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

260 lines
6.5 KiB

#include <string.h>
#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