Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ***********************************************************
- // Драйвер I2S
- //
- // ***********************************************************
- #include "i2s.h"
- #include "gpio.h"
- #include <stm32f4xx.h>
- #include <string.h>
- // SPI3, альтернативная функция 6
- // PC7: MCK
- // PC10: SCKФ
- // PC12: SD
- // PA4: WS
- typedef struct
- {
- // Обновление буфера
- bool (*Callback)(int16_t ** Buffer, uint16_t * Length);
- // DMA запущен?
- bool DMA;
- // Буфер подготовлен?
- bool Ready;
- // Буфер с аудио
- int16_t * Buffer;
- uint16_t Length;
- } TI2SContext;
- // Параметры тактирования для некоторого набора частот семплирования
- typedef struct
- {
- // Параметры ФАПЧ
- uint16_t PLLN;
- uint16_t PLLR;
- // предделитель
- uint16_t Div;
- // Добавлять 1 к предделителю?
- bool Odd;
- } TI2SClockParams;
- TI2SClockParams Params[] = {
- { 256, 8, 5, false }, // 8 кГц (8000 Гц)
- { 256, 4, 5, false }, // 16 кГц (16000 Гц)
- { 256, 2, 5, false }, // 32 кГц (32000 Гц)
- { 384, 4, 2, true }, // 48 кГц (48000 Гц)
- { 384, 2, 2, true }, // 96 кГц (96000 Гц)
- { 388, 4, 5, true }, // 22.05 кГц (22045 Гц)
- { 388, 2, 5, true }, // 44.1 кГц (44090 Гц)
- };
- TI2SContext Context;
- // Начать передачу буфера по DMA
- static void i2s_dma_Start(void);
- // Вызвать функцию получения следующего буфера
- static void i2s_dma_Callback(void);
- // Передача завершена.
- void DMA1_Stream7_IRQHandler()
- {
- // Сбросим флаг прерывания (чтобы выйти из него)
- DMA1->HIFCR = DMA_HIFCR_CTCIF7;
- // DMA закончился
- Context.DMA = false;
- // Попробовать воспроизвести следующий буфер, если он есть, конечно
- i2s_dma_Start();
- }
- // Инициализация
- void i2s_Init(TAudioFrequency Frequency)
- {
- if(Frequency >= FREQ_LAST) return;
- // Очистим переменные
- memset(&Context, 0, sizeof(TI2SContext));
- // Разрешим прерывание по DMA
- NVIC_EnableIRQ(DMA1_Stream7_IRQn);
- NVIC_SetPriority(DMA1_Stream7_IRQn, 4);
- // Альтернативная функция выводов
- gpio_SetAlternateFunction(PORTC, 7, 6); // PC7
- gpio_SetAlternateFunction(PORTC, 10, 6); // PC10
- gpio_SetAlternateFunction(PORTC, 12, 6); // PC12
- gpio_SetAlternateFunction(PORTA, 4, 6); // PA4
- // Скорость: 50 МГц (для линий данных)
- gpio_FastSpeed(PORTC, 7);
- gpio_FastSpeed(PORTC, 10);
- gpio_FastSpeed(PORTC, 12);
- // Тактирование на модуль SPI3
- RCC->APB1ENR |= RCC_APB1ENR_SPI3EN;
- // Тактирование на модуль DMA1
- RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
- // Выключение модуля
- SPI3->I2SCFGR = 0;
- // В качестве источника тактирования используется PLLI2S
- RCC->CFGR &= ~RCC_CFGR_I2SSRC;
- // Настройка I2C PLL
- {
- uint16_t PLLR = Params[Frequency].PLLR;
- uint16_t PLLN = Params[Frequency].PLLN;
- RCC->PLLI2SCFGR = (PLLR << 28) | (PLLN << 6);
- }
- // Включить источник тактового сигнала PLLI2S
- RCC->CR |= RCC_CR_PLLI2SON;
- // Ожидание готовности
- while (!(RCC->CR & RCC_CR_PLLI2SRDY)) {}
- // Инициализация I2S
- {
- // Предделитель и иже с ним
- uint16_t Divider = Params[Frequency].Div;
- uint16_t Odd = (Params[Frequency].Odd) ? SPI_I2SPR_ODD : 0;
- // Предделитель
- SPI3->I2SPR = Divider | Odd | SPI_I2SPR_MCKOE;
- SPI3->CR1 |= SPI_CR1_SPE;
- // Аудио включено
- i2s_AudioOn();
- }
- }
- // Начать передачу буфера по DMA
- static void i2s_dma_Start(void)
- {
- // Если нечего воспроизводить, остановимся
- if(!Context.Ready) return;
- // Настройка потока DMA
- DMA1_Stream7->CR = 0 |
- DMA_SxCR_PL_0 | // Приоритет 1
- DMA_SxCR_PSIZE_0 | // PSIZE = 16 bit
- DMA_SxCR_MSIZE_0 | // MSIZE = 16 bit
- DMA_SxCR_MINC | // Адрес памяти увеличивать
- DMA_SxCR_DIR_0 | // Память -> Периферия
- DMA_SxCR_TCIE; // Прерывание по завершению передачи
- // Количество посылок
- DMA1_Stream7->NDTR = Context.Length;
- // Куда записываем
- DMA1_Stream7->PAR = (uint32_t)&SPI3->DR;
- // Откуда записываем (начинаем записывать, если точнее)
- DMA1_Stream7->M0AR = (uint32_t)Context.Buffer;
- // Direct mode запрещн
- DMA1_Stream7->FCR = DMA_SxFCR_DMDIS;
- // Начать передачу
- DMA1_Stream7->CR |= DMA_SxCR_EN;
- // DMA запущен
- Context.DMA = true;
- // Буфер более не актуален
- Context.Ready = false;
- Context.Buffer = 0;
- Context.Length = 0;
- // Попробуем запросить следующий буфер, пока переда тся этот
- i2s_dma_Callback();
- }
- // Отключить DMA
- static void i2s_dma_Stop(void)
- {
- // Завершить поток DMA
- DMA1_Stream7->CR &= ~DMA_SxCR_EN;
- // Ожидание завершения DMA-потока
- while (DMA1_Stream7->CR & DMA_SxCR_EN) {}
- // DMA выключено
- Context.DMA = false;
- }
- // Вызвать функцию получения следующего буфера
- static void i2s_dma_Callback(void)
- {
- int16_t * Buffer = 0;
- uint16_t Length = 0;
- // Получим новую пачку
- if(Context.Callback(&Buffer, &Length))
- {
- // Запомним новый буфер и длину
- Context.Buffer = Buffer;
- Context.Length = Length;
- // Буфер готов
- Context.Ready = true;
- }
- // Если не запущено воспроизведение, запустим
- if(!Context.DMA) i2s_dma_Start();
- }
- // Установить буфер
- void i2s_SetBuffer(int16_t * Buffer, uint16_t Length)
- {
- NVIC_DisableIRQ(DMA1_Stream7_IRQn);
- // Запомним новый буфер и длину
- Context.Buffer = Buffer;
- Context.Length = Length;
- // Буфер готов
- Context.Ready = true;
- NVIC_EnableIRQ(DMA1_Stream7_IRQn);
- // Если не запущено воспроизведение, запустим
- if(!Context.DMA) i2s_dma_Start();
- }
- // Нет следующего буфера?
- bool i2s_NeedBuffer(void)
- {
- return !Context.Ready;
- }
- // Остановить воспроизведение
- void i2s_Stop(void)
- {
- // Запретить генерацию запросов DMA
- SPI3->CR2 &= ~SPI_CR2_TXDMAEN;
- // Остановить DMA
- i2s_dma_Stop();
- // Сброс контекста
- memset(&Context, 0, sizeof(TI2SContext));
- }
- // Начать воспроизведение, для получения данных используется Callback
- // bool (*Callback)(uint8_t ** Buffer, uint16_t * Length);
- void i2s_Play(void * Callback)
- {
- // Остановим DMA
- i2s_dma_Stop();
- // Если нет функции, выходим, дальше делать нечего
- if(!Callback) return;
- // Разрешить генерацию запросов DMA
- SPI3->CR2 |= SPI_CR2_TXDMAEN;
- // Запомним Callback:
- Context.Callback = (bool (*)(int16_t **, uint16_t *))Callback;
- // Вызовем его
- i2s_dma_Callback();
- }
- // Включить передачу аудио
- void i2s_AudioOn(void)
- {
- // Режим I2S, включн
- // Мастер, передача.
- SPI3 ->I2SCFGR = SPI_I2SCFGR_I2SMOD | SPI_I2SCFGR_I2SCFG_1 | SPI_I2SCFGR_I2SE;
- }
- // Выключить передачу аудио
- void i2s_AudioOff(void)
- {
- SPI3 ->I2SCFGR = 0;
- }
- // Буфер передачи пуст?
- bool i2s_IsBufferFree(void)
- {
- return (SPI3->SR & SPI_SR_TXE) != 0;
- }
- // Вывести аудиосемпл
- void i2s_Send(int16_t sample)
- {
- SPI3->DR = sample;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement