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
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
|
|
|
|
|