211 lines
6.3 KiB
C++
211 lines
6.3 KiB
C++
#include "audio.h"
|
|
|
|
/* generic I2S example for any STM32duino HAL core F4
|
|
Original code by Rene Böllhoff
|
|
translated to STM32duino by Matthias Diro ("madias" -> STM32duino forum)
|
|
Features: Circular Buffer with DMA IRQ (half full, full), Master Clock enabled
|
|
This example uses the SPI3 port tested on STM32F407VET "black" board. On other boards please define LED0_BUILTIN and LED1_BUILTIN
|
|
*/
|
|
#define I2S_BUFFER_SIZE 64
|
|
I2S_HandleTypeDef hi2s3;
|
|
DMA_HandleTypeDef hdma_spi3_tx;
|
|
uint32_t dma_buffer[I2S_BUFFER_SIZE];
|
|
// sinus oszillator
|
|
float osc_phi = 0;
|
|
float osc_phi_inc = 440.0f / 44100.0f; // generating 440HZ
|
|
|
|
void MX_DMA_Init(void);
|
|
|
|
extern "C" void DMA1_Stream5_IRQHandler(void) // this function must be included to avoid DMA to crash!
|
|
{
|
|
HAL_DMA_IRQHandler(&hdma_spi3_tx);
|
|
}
|
|
|
|
void Error_Handler2(byte errorcode)
|
|
{ // if something goes wrong counter the blinks for rudimentary debugging
|
|
digitalWrite(LED_BUILTIN, 1);
|
|
while (1)
|
|
{
|
|
for (int x = 0; x < errorcode; x++)
|
|
{
|
|
digitalWrite(LED_BUILTIN, 1);
|
|
delay(100);
|
|
digitalWrite(LED_BUILTIN, 0);
|
|
delay(100);
|
|
}
|
|
delay(1000);
|
|
}
|
|
}
|
|
|
|
void FillBuffer(uint32_t *buffer, uint16_t len)
|
|
{
|
|
float a;
|
|
int16_t y;
|
|
uint16_t c;
|
|
for (c = 0; c < len; c++)
|
|
{
|
|
// calculate sin
|
|
a = (float)sin(osc_phi * 6.2832f) * 0.20f;
|
|
osc_phi += osc_phi_inc;
|
|
osc_phi -= (float)((uint16_t)osc_phi);
|
|
// float to integer
|
|
y = (int16_t)(a * 32767.0f);
|
|
// auf beide kanäle
|
|
buffer[c] = ((uint32_t)(uint16_t)y) << 0 |
|
|
((uint32_t)(uint16_t)y) << 16;
|
|
}
|
|
}
|
|
void StartAudioBuffers(I2S_HandleTypeDef *hi2s)
|
|
{
|
|
// clear buffer
|
|
memset(dma_buffer, 0, sizeof(dma_buffer));
|
|
// start circular dma
|
|
HAL_I2S_Transmit_DMA(hi2s, (uint16_t *)dma_buffer, I2S_BUFFER_SIZE << 1);
|
|
}
|
|
extern "C" void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
|
|
{
|
|
// second half finished, filling it up again while first half is playing
|
|
FillBuffer(&(dma_buffer[I2S_BUFFER_SIZE >> 1]), I2S_BUFFER_SIZE >> 1);
|
|
}
|
|
extern "C" void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
|
|
{
|
|
// first half finished, filling it up again while second half is playing
|
|
FillBuffer(&(dma_buffer[0]), I2S_BUFFER_SIZE >> 1);
|
|
}
|
|
// setting up I2S
|
|
extern "C" void MX_I2S3_Init(void)
|
|
{
|
|
hi2s3.Instance = SPI3;
|
|
hi2s3.Init.Mode = I2S_MODE_MASTER_TX;
|
|
hi2s3.Init.Standard = I2S_STANDARD_PHILIPS;
|
|
hi2s3.Init.DataFormat = I2S_DATAFORMAT_16B;
|
|
//hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;
|
|
hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
|
|
hi2s3.Init.AudioFreq = I2S_AUDIOFREQ_44K;
|
|
hi2s3.Init.CPOL = I2S_CPOL_LOW;
|
|
hi2s3.Init.ClockSource = I2S_CLOCK_PLL;
|
|
hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;
|
|
if (HAL_I2S_Init(&hi2s3) != HAL_OK)
|
|
{
|
|
Error_Handler2(1); // on error: one blink
|
|
}
|
|
}
|
|
// setting up DMA
|
|
void MX_DMA_Init(void)
|
|
{
|
|
/* DMA controller clock enable */
|
|
__HAL_RCC_DMA1_CLK_ENABLE();
|
|
/* DMA interrupt init */
|
|
/* DMA1_Stream5_IRQn interrupt configuration */
|
|
HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0);
|
|
HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
|
|
}
|
|
// setting up pins and clocks; routing SPI3 to DMA
|
|
extern "C" void HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s)
|
|
{
|
|
__HAL_RCC_GPIOC_CLK_ENABLE();
|
|
__HAL_RCC_GPIOA_CLK_ENABLE();
|
|
__HAL_RCC_GPIOB_CLK_ENABLE();
|
|
__HAL_RCC_SPI3_CLK_ENABLE();
|
|
GPIO_InitTypeDef GPIO_InitStruct;
|
|
/* I2S standard configurations:
|
|
SPI2
|
|
PB15 DIN
|
|
PB12 LRC
|
|
PB13 SCLK
|
|
PC6 MCK
|
|
SPI3
|
|
PB5 DIN
|
|
PA4 LRC
|
|
PB3 SCLK
|
|
PC7 MCK
|
|
*/
|
|
//I2S3 used GPIO configuration in this example:
|
|
// PB5 DIN / SD
|
|
// PA4 LRC /WD
|
|
// PB3 SCLK /CK
|
|
// PC7 MCK
|
|
GPIO_InitStruct.Pin = GPIO_PIN_4;
|
|
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
|
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
|
|
GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
|
|
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
|
GPIO_InitStruct.Pin = GPIO_PIN_3 | GPIO_PIN_5;
|
|
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
|
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
|
|
GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
|
|
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
|
|
// master clock
|
|
GPIO_InitStruct.Pin = GPIO_PIN_7;
|
|
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
|
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
|
|
GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
|
|
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
|
|
// Peripheral DMA init
|
|
hdma_spi3_tx.Instance = DMA1_Stream5;
|
|
hdma_spi3_tx.Init.Channel = DMA_CHANNEL_0;
|
|
hdma_spi3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
|
|
hdma_spi3_tx.Init.PeriphInc = DMA_PINC_DISABLE;
|
|
hdma_spi3_tx.Init.MemInc = DMA_MINC_ENABLE;
|
|
hdma_spi3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
|
|
hdma_spi3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
|
|
hdma_spi3_tx.Init.Mode = DMA_CIRCULAR;
|
|
hdma_spi3_tx.Init.Priority = DMA_PRIORITY_LOW;
|
|
hdma_spi3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
|
|
if (HAL_DMA_Init(&hdma_spi3_tx) != HAL_OK)
|
|
{
|
|
Error_Handler2(5); // on error: five blinks
|
|
}
|
|
__HAL_LINKDMA(hi2s, hdmatx, hdma_spi3_tx);
|
|
}
|
|
extern "C" void HAL_MspInit(void) // maybe useful, not included in this example
|
|
{
|
|
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
|
|
/* System interrupt init*/
|
|
/* MemoryManagement_IRQn interrupt configuration */
|
|
HAL_NVIC_SetPriority(MemoryManagement_IRQn, 0, 0);
|
|
/* BusFault_IRQn interrupt configuration */
|
|
HAL_NVIC_SetPriority(BusFault_IRQn, 0, 0);
|
|
/* UsageFault_IRQn interrupt configuration */
|
|
HAL_NVIC_SetPriority(UsageFault_IRQn, 0, 0);
|
|
/* SVCall_IRQn interrupt configuration */
|
|
HAL_NVIC_SetPriority(SVCall_IRQn, 0, 0);
|
|
/* DebugMonitor_IRQn interrupt configuration */
|
|
HAL_NVIC_SetPriority(DebugMonitor_IRQn, 0, 0);
|
|
/* PendSV_IRQn interrupt configuration */
|
|
HAL_NVIC_SetPriority(PendSV_IRQn, 0, 0);
|
|
/* SysTick_IRQn interrupt configuration */
|
|
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
|
|
}
|
|
// void initAudio(void)
|
|
// {
|
|
|
|
// }
|
|
|
|
// void handleAudio(void)
|
|
// {
|
|
|
|
// }
|
|
|
|
void initAudio()
|
|
{
|
|
// HAL_MspInit(); // not important by default
|
|
HAL_I2S_MspInit(&hi2s3); // setting up pins and clocks; routing SPI3 to DMA
|
|
MX_DMA_Init();
|
|
MX_I2S3_Init();
|
|
pinMode(LED_BUILTIN, OUTPUT);
|
|
digitalWrite(LED_BUILTIN, 0);
|
|
StartAudioBuffers(&hi2s3);
|
|
digitalWrite(LED_BUILTIN, 1);
|
|
}
|
|
void handleAudio()
|
|
{
|
|
// just a dummy code in loop, audio out is generated by ISR
|
|
digitalWrite(LED_BUILTIN, 1);
|
|
delay(250);
|
|
digitalWrite(LED_BUILTIN, 0);
|
|
delay(250);
|
|
} |