Advertisement
teplofizik

i2s.c (dma)

Aug 7th, 2013
486
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 8.82 KB | None | 0 0
  1. // ***********************************************************
  2. // Драйвер I2S
  3. //
  4. // ***********************************************************
  5.  
  6. #include "i2s.h"
  7. #include "gpio.h"
  8.  
  9. #include <stm32f4xx.h>
  10. #include <string.h>
  11.  
  12. // SPI3, альтернативная функция 6
  13. // PC7:  MCK
  14. // PC10: SCKФ
  15. // PC12: SD
  16. // PA4:  WS
  17.  
  18. typedef struct
  19. {
  20.     // Обновление буфера
  21.     bool (*Callback)(int16_t ** Buffer, uint16_t * Length);
  22.    
  23.     // DMA запущен?
  24.     bool      DMA;
  25.    
  26.     // Буфер подготовлен?
  27.     bool      Ready;
  28.    
  29.     // Буфер с аудио
  30.     int16_t * Buffer;
  31.     uint16_t  Length;
  32. } TI2SContext;
  33.  
  34. // Параметры тактирования для некоторого набора частот семплирования
  35. typedef struct
  36. {
  37.     // Параметры ФАПЧ
  38.     uint16_t PLLN;
  39.     uint16_t PLLR;
  40.    
  41.     // предделитель
  42.     uint16_t Div;
  43.     // Добавлять 1 к предделителю?
  44.     bool     Odd;
  45. } TI2SClockParams;
  46.  
  47. TI2SClockParams Params[] = {
  48.   { 256, 8,  5, false }, // 8  кГц (8000  Гц)
  49.   { 256, 4,  5, false }, // 16 кГц (16000 Гц)
  50.   { 256, 2,  5, false }, // 32 кГц (32000 Гц)
  51.   { 384, 4,  2, true  }, // 48 кГц (48000 Гц)
  52.   { 384, 2,  2, true  }, // 96 кГц (96000 Гц)
  53.   { 388, 4,  5, true  }, // 22.05 кГц (22045 Гц)
  54.   { 388, 2,  5, true }, // 44.1 кГц (44090 Гц)
  55. };
  56.  
  57. TI2SContext Context;
  58.  
  59. // Начать передачу буфера по DMA
  60. static void i2s_dma_Start(void);
  61.  
  62. // Вызвать функцию получения следующего буфера
  63. static void i2s_dma_Callback(void);
  64.  
  65. // Передача завершена.
  66. void DMA1_Stream7_IRQHandler()
  67. {
  68.     // Сбросим флаг прерывания (чтобы выйти из него)
  69.     DMA1->HIFCR = DMA_HIFCR_CTCIF7;
  70.  
  71.     // DMA закончился
  72.     Context.DMA = false;
  73.    
  74.     // Попробовать воспроизвести следующий буфер, если он есть, конечно
  75.     i2s_dma_Start();
  76. }
  77.  
  78. // Инициализация
  79. void i2s_Init(TAudioFrequency Frequency)
  80. {
  81.     if(Frequency >= FREQ_LAST) return;
  82.    
  83.     // Очистим переменные
  84.     memset(&Context, 0, sizeof(TI2SContext));
  85.    
  86.     // Разрешим прерывание по DMA
  87.     NVIC_EnableIRQ(DMA1_Stream7_IRQn);
  88.     NVIC_SetPriority(DMA1_Stream7_IRQn, 4);
  89.    
  90.     // Альтернативная функция выводов
  91.     gpio_SetAlternateFunction(PORTC,  7, 6); // PC7
  92.     gpio_SetAlternateFunction(PORTC, 10, 6); // PC10
  93.     gpio_SetAlternateFunction(PORTC, 12, 6); // PC12
  94.     gpio_SetAlternateFunction(PORTA,  4, 6); // PA4
  95.    
  96.     // Скорость: 50 МГц (для линий данных)
  97.     gpio_FastSpeed(PORTC,  7);
  98.     gpio_FastSpeed(PORTC, 10);
  99.     gpio_FastSpeed(PORTC, 12);
  100.    
  101.     // Тактирование на модуль SPI3
  102.     RCC->APB1ENR |= RCC_APB1ENR_SPI3EN;
  103.    
  104.     // Тактирование на модуль DMA1
  105.     RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
  106.    
  107.     // Выключение модуля
  108.     SPI3->I2SCFGR = 0;
  109.    
  110.     // В качестве источника тактирования используется PLLI2S
  111.     RCC->CFGR &= ~RCC_CFGR_I2SSRC;
  112.    
  113.     // Настройка I2C PLL
  114.     {
  115.         uint16_t PLLR = Params[Frequency].PLLR;
  116.         uint16_t PLLN = Params[Frequency].PLLN;
  117.        
  118.         RCC->PLLI2SCFGR = (PLLR << 28) | (PLLN << 6);
  119.     }
  120.    
  121.     // Включить источник тактового сигнала PLLI2S
  122.     RCC->CR |= RCC_CR_PLLI2SON;
  123.  
  124.     // Ожидание готовности
  125.     while (!(RCC->CR & RCC_CR_PLLI2SRDY)) {}
  126.        
  127.     // Инициализация I2S
  128.     {
  129.         // Предделитель и иже с ним
  130.         uint16_t Divider = Params[Frequency].Div;
  131.         uint16_t Odd = (Params[Frequency].Odd) ? SPI_I2SPR_ODD : 0;
  132.        
  133.         // Предделитель
  134.         SPI3->I2SPR = Divider | Odd | SPI_I2SPR_MCKOE;
  135.        
  136.         SPI3->CR1 |= SPI_CR1_SPE;
  137.        
  138.         // Аудио включено
  139.         i2s_AudioOn();
  140.     }
  141. }
  142.  
  143. // Начать передачу буфера по DMA
  144. static void i2s_dma_Start(void)
  145. {
  146.     // Если нечего воспроизводить, остановимся
  147.     if(!Context.Ready) return;
  148.        
  149.     // Настройка потока DMA
  150.     DMA1_Stream7->CR = 0 |
  151.                        DMA_SxCR_PL_0 |    // Приоритет 1
  152.                        DMA_SxCR_PSIZE_0 | // PSIZE = 16 bit
  153.                        DMA_SxCR_MSIZE_0 | // MSIZE = 16 bit
  154.                        DMA_SxCR_MINC |    // Адрес памяти увеличивать
  155.                        DMA_SxCR_DIR_0 |   // Память -> Периферия
  156.                        DMA_SxCR_TCIE;     // Прерывание по завершению передачи
  157.    
  158.     // Количество посылок
  159.     DMA1_Stream7->NDTR = Context.Length;
  160.     // Куда записываем
  161.     DMA1_Stream7->PAR = (uint32_t)&SPI3->DR;
  162.     // Откуда записываем (начинаем записывать, если точнее)
  163.     DMA1_Stream7->M0AR = (uint32_t)Context.Buffer;
  164.     // Direct mode запрещн
  165.     DMA1_Stream7->FCR = DMA_SxFCR_DMDIS;
  166.     // Начать передачу
  167.     DMA1_Stream7->CR |= DMA_SxCR_EN;
  168.    
  169.     // DMA запущен
  170.     Context.DMA = true;
  171.     // Буфер более не актуален
  172.     Context.Ready = false;
  173.     Context.Buffer = 0;
  174.     Context.Length = 0;
  175.    
  176.     // Попробуем запросить следующий буфер, пока переда тся этот
  177.     i2s_dma_Callback();
  178. }
  179.  
  180. // Отключить DMA
  181. static void i2s_dma_Stop(void)
  182. {
  183.     // Завершить поток DMA
  184.     DMA1_Stream7->CR &= ~DMA_SxCR_EN;
  185.    
  186.     // Ожидание завершения DMA-потока
  187.     while (DMA1_Stream7->CR & DMA_SxCR_EN) {}
  188.        
  189.     // DMA выключено
  190.     Context.DMA = false;
  191. }
  192.  
  193. // Вызвать функцию получения следующего буфера
  194. static void i2s_dma_Callback(void)
  195. {
  196.     int16_t * Buffer = 0;
  197.     uint16_t  Length = 0;
  198.    
  199.     // Получим новую пачку
  200.     if(Context.Callback(&Buffer, &Length))
  201.     {
  202.         // Запомним новый буфер и длину
  203.         Context.Buffer = Buffer;
  204.         Context.Length = Length;
  205.        
  206.         // Буфер готов
  207.         Context.Ready = true;
  208.     }
  209.    
  210.     // Если не запущено воспроизведение, запустим
  211.     if(!Context.DMA) i2s_dma_Start();
  212. }
  213.  
  214. // Установить буфер
  215. void i2s_SetBuffer(int16_t * Buffer, uint16_t Length)
  216. {
  217.     NVIC_DisableIRQ(DMA1_Stream7_IRQn);
  218.    
  219.     // Запомним новый буфер и длину
  220.     Context.Buffer = Buffer;
  221.     Context.Length = Length;
  222.        
  223.     // Буфер готов
  224.     Context.Ready = true;
  225.    
  226.     NVIC_EnableIRQ(DMA1_Stream7_IRQn);
  227.    
  228.     // Если не запущено воспроизведение, запустим
  229.     if(!Context.DMA) i2s_dma_Start();
  230. }
  231.  
  232. // Нет следующего буфера?
  233. bool i2s_NeedBuffer(void)
  234. {
  235.     return !Context.Ready;
  236. }
  237.  
  238. // Остановить воспроизведение
  239. void i2s_Stop(void)
  240. {
  241.     // Запретить генерацию запросов DMA
  242.     SPI3->CR2 &= ~SPI_CR2_TXDMAEN;
  243.    
  244.     // Остановить DMA
  245.     i2s_dma_Stop();
  246.    
  247.     // Сброс контекста
  248.     memset(&Context, 0, sizeof(TI2SContext));
  249. }
  250.  
  251. // Начать воспроизведение, для получения данных используется Callback
  252. // bool (*Callback)(uint8_t ** Buffer, uint16_t * Length);
  253. void i2s_Play(void * Callback)
  254. {
  255.     // Остановим DMA
  256.     i2s_dma_Stop();
  257.    
  258.     // Если нет функции, выходим, дальше делать нечего
  259.     if(!Callback) return;
  260.    
  261.     // Разрешить генерацию запросов DMA
  262.     SPI3->CR2 |= SPI_CR2_TXDMAEN;
  263.    
  264.     // Запомним Callback:
  265.     Context.Callback = (bool (*)(int16_t **, uint16_t *))Callback;
  266.    
  267.     // Вызовем его
  268.     i2s_dma_Callback();
  269. }
  270.  
  271. // Включить передачу аудио
  272. void i2s_AudioOn(void)
  273. {
  274.     // Режим I2S, включн
  275.     // Мастер, передача.
  276.     SPI3 ->I2SCFGR = SPI_I2SCFGR_I2SMOD | SPI_I2SCFGR_I2SCFG_1 | SPI_I2SCFGR_I2SE;
  277. }
  278.  
  279. // Выключить передачу аудио
  280. void i2s_AudioOff(void)
  281. {
  282.     SPI3 ->I2SCFGR = 0;
  283. }
  284.  
  285. // Буфер передачи пуст?
  286. bool i2s_IsBufferFree(void)
  287. {
  288.     return (SPI3->SR & SPI_SR_TXE) != 0;
  289. }
  290.  
  291. // Вывести аудиосемпл
  292. void i2s_Send(int16_t sample)
  293. {
  294.     SPI3->DR = sample;
  295. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement