F401RE I2S + DMA Issues
Posted: Thu Oct 08, 2020 1:31 am
(moved to general for better visibility)
Hi all!
Back again with more troubleshooting. Trying to figure out why a "simple" I2S + DMA setup I've got isn't working properly.
I've got a WM8731 hooked up to a Nucleo F401RE board with the following I2S connections:
I've also got I2C up and working properly (thanks to the previous thread I posted) and I believe I'm setting up all of its parameters properly to receive the I2S data. However, I can't even get the STM32 to send I2S data so that's step 1.
Right now, it doesn't seem that I2S is getting initialized properly. After the code starts, none of the clock lines have a clock preset and there's no data on the DOUT line. I have simple debug serial prints in the half-full and full callback functions, just to see if they're running, and they're never printed out so I'm assuming DMA also isn't getting set up properly.
I'm following the example posted on the old forums almost verbatim - that thread is here: https://stm32duinoforum.com/forum/viewt ... _4095.html
Here is my i2s.cpp file. The startI2SDMA() function at the bottom is called from my main runtime .ino in the setup() function and includes all of the setup operations performed in the setup() function in the above example.
I'm not getting any compile errors, nor am I getting any debug errors on the serial port, so I believe all the functions are at least being called properly. Something else is amiss and I don't have nearly enough experience to start figuring out what.
I can probe all the I2S pins and none of them have any data. A couple are floating at around 1.6V which is...interesting (maybe still High-Z and the WM8731 is pulling them up a bit?) but there's most certainly not any I2S data on them.
I know there's a lot going on in this post, and that code is pretty long, but hopefully someone will be able to help shed some light on my issues. I'm not a programmer by trade and low-level complex stuff like this is all new territory for me. Hoping it's something dead-simple I'm just missing.
Thanks in advance!
Hi all!
Back again with more troubleshooting. Trying to figure out why a "simple" I2S + DMA setup I've got isn't working properly.
I've got a WM8731 hooked up to a Nucleo F401RE board with the following I2S connections:
Code: Select all
F401RE -> WM8731
PA15 LRCLK
PC7 MCLK
PC10 SCLK
PC11 DIN
PC12 DOUT
Right now, it doesn't seem that I2S is getting initialized properly. After the code starts, none of the clock lines have a clock preset and there's no data on the DOUT line. I have simple debug serial prints in the half-full and full callback functions, just to see if they're running, and they're never printed out so I'm assuming DMA also isn't getting set up properly.
I'm following the example posted on the old forums almost verbatim - that thread is here: https://stm32duinoforum.com/forum/viewt ... _4095.html
Here is my i2s.cpp file. The startI2SDMA() function at the bottom is called from my main runtime .ino in the setup() function and includes all of the setup operations performed in the setup() function in the above example.
Code: Select all
/ self-referential include
#include "i2s.h"
// includes
#include <Arduino.h>
#include "audio.h" // needed for sample types
#include "debugConsole.h" // debug messaging
// I2S setups
#define AUD_BUFFER_SIZE 64
// debug defines
#define DEBUG_I2S
#define DEBUG_I2S_DETAILED
#define DEBUG_I2S_NAME "I2S"
I2S_HandleTypeDef hi2s3;
DMA_HandleTypeDef hdmaSpi3Tx;
uint32_t dmaTxBuffer[AUD_BUFFER_SIZE];
/**
* Setup pin assignments and clocks for I2S3
*/
extern "C" void HAL_I2S_MspInit(I2S_HandleTypeDef* hi2s ) {
#ifdef DEBUG_I2S
debugMsg(3,DEBUG_I2S_NAME,"initializing I2S hardware on hi2s3");
#endif
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
//__HAL_RCC_GPIOB_CLK_ENABLE(); // shouldn't need this, nothing on port B
__HAL_RCC_SPI3_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
/**
* I2S Pin Mappings for F401RE
* PA15 LRCLK
* PC7 MCLK
* PC10 SCLK
* PC11 DIN
* PC12 DOUT
*/
// LRCLK on port A
GPIO_InitStruct.Pin = GPIO_PIN_15;
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);
// Rest of pins on port C
GPIO_InitStruct.Pin = GPIO_PIN_7 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
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);
// Peripheral DMA init
hdmaSpi3Tx.Instance = DMA1_Stream5;
hdmaSpi3Tx.Init.Channel = DMA_CHANNEL_0;
hdmaSpi3Tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdmaSpi3Tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdmaSpi3Tx.Init.MemInc = DMA_MINC_ENABLE;
hdmaSpi3Tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdmaSpi3Tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdmaSpi3Tx.Init.Mode = DMA_CIRCULAR;
hdmaSpi3Tx.Init.Priority = DMA_PRIORITY_LOW;
hdmaSpi3Tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
// Initialize the DMA finally
if (HAL_DMA_Init(&hdmaSpi3Tx) != HAL_OK)
{
debugMsg(1,DEBUG_I2S_NAME,"failed to initialize HAL DMA");
}
__HAL_LINKDMA(hi2s, hdmatx, hdmaSpi3Tx);
}
/**
* Initialize DMA
*/
void MX_DMA_Init(void)
{
#ifdef DEBUG_I2S
debugMsg(3,DEBUG_I2S_NAME,"starting DMA clocks and IRQs");
#endif
// 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);
}
/**
* I2S mode and format setup
*/
extern "C" void MX_I2S3_Init(void)
{
#ifdef DEBUG_I2S
debugMsg(3,DEBUG_I2S_NAME,"initializing I2S3 mode and format");
#endif
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_48K;
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) {
debugMsg(1,DEBUG_I2S_NAME,"Failed to initialize I2S3");
}
}
/**
* starts the I2S circular TX process
*/
void StartAudioBuffers(I2S_HandleTypeDef *hi2s) {
#ifdef DEBUG_I2S
debugMsg(3,DEBUG_I2S_NAME,"Starting I2S audio buffer");
#endif
// clear buffer
memset (dmaTxBuffer,0, sizeof (dmaTxBuffer));
// start circular dma
HAL_I2S_Transmit_DMA (hi2s, (uint16_t *) dmaTxBuffer, AUD_BUFFER_SIZE << 1);
}
/**
* Fills the DMA buffer with a sine wave
*/
void FillBuffer (uint32_t *buffer, uint16_t len) {
float a;
int16_t y;
uint16_t c;
float osc_phi = 0;
float osc_phi_inc = 440.0f / 44100.0f; // generating 440HZ
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;
}
}
/**
* Full TX Buffer Callback
*/
extern "C" void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) {
#ifdef DEBUG_I2S_DETAILED
debugMsg(3,DEBUG_I2S_NAME,"DMA TX Full Buffer Callback");
#endif
// second half finished, filling it up again while first half is playing
FillBuffer (&(dmaTxBuffer [AUD_BUFFER_SIZE >> 1]), AUD_BUFFER_SIZE >> 1);
}
/**
* Half-full TX Buffer Callback
*/
extern "C" void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) {
#ifdef DEBUG_I2S_DETAILED
debugMsg(3,DEBUG_I2S_NAME,"DMA TX Half-Full Buffer Callback");
#endif
// first half finished, filling it up again while second half is playing
FillBuffer (&(dmaTxBuffer [0]), AUD_BUFFER_SIZE >> 1);
}
/**
* needed to prevent a DMA crash
*/
extern "C" void DMA1_Stream5_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdmaSpi3Tx);
}
/**
* Initializes I2S and DMA components
*/
void startI2SDMA() {
#ifdef DEBUG_I2S
debugMsg(3,DEBUG_I2S_NAME,"Starting I2S and DMA");
#endif
// sets up pins and clocks, routes SPI3 to DMA
HAL_I2S_MspInit(&hi2s3);
// initializes DMA clocks and IRQs
MX_DMA_Init();
// sets up I2S format and mode
MX_I2S3_Init();
// starts the audio TX buffer
StartAudioBuffers(&hi2s3);
}
I can probe all the I2S pins and none of them have any data. A couple are floating at around 1.6V which is...interesting (maybe still High-Z and the WM8731 is pulling them up a bit?) but there's most certainly not any I2S data on them.
I know there's a lot going on in this post, and that code is pretty long, but hopefully someone will be able to help shed some light on my issues. I'm not a programmer by trade and low-level complex stuff like this is all new territory for me. Hoping it's something dead-simple I'm just missing.
Thanks in advance!