2024-02-05 09:08 PM - last edited on 2024-09-24 05:17 AM by Amel NASRI
Hi,
I have a board with an STM32U575 at the core.
I'm attempting to set up SAI receiver in I2S mode, with 32-bit 48kHz sampling. See my configuration of SAI A below:
I also want to receive the data continuously in circular mode via DMA (or GPDMA in the case of this MCU).
Reading other sources, I've concluded that the following setup should be established:
The audio DMA functions can be seen below. The Audio_Init() function is called once in the main function, and the CpltCallback functions are called by DMA interrupts.
static uint16_t audio_rx[AUDIO_BUFFER_SIZE];
static uint16_t audio_tx[AUDIO_BUFFER_SIZE];
void Audio_Init(){
audio_ready_flag = AUDIO_DATA_FREE;
if(HAL_SAI_Receive_DMA(&AUDIO_RX_PORT, (uint8_t *)audio_rx, AUDIO_BUFFER_SIZE) != HAL_OK){
Error_Handler();
}
if(HAL_SAI_Transmit_DMA(&AUDIO_TX_PORT, (uint8_t *)audio_tx, AUDIO_BUFFER_SIZE) != HAL_OK){
Error_Handler();
}
}
void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hi2s){
audio_ready_flag = AUDIO_DATA_READY_HALF;
}
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hi2s){
audio_ready_flag = AUDIO_DATA_READY_FULL;
}
(I'm omitting all the HAL stuff, since I've already showed the setup in the above images. I want to note that all of the HAL code seems to be configured correctly --> no errors or warnings)
When my hardware has no audio source connected to it, I would assume that the audio data stored via DMA in the audio_rx buffer should be equal to 0 (and maybe some weak noise).
However, my buffer is filled with what appears to be 16-bit garbage values:
Why is this? I've successfully implemented this code on my old build, which used a STM32G4.
I'm very thankful for any help I can receive on this issue!
Best regards,
Max Borglowe
Solved! Go to Solution.
2024-02-26 10:45 PM - edited 2024-02-26 10:56 PM
You are lucky, your SAI and your ADC are working correctly.
Now your 32bit buffer shows perfect noise values around 0, with about 100 dB SNR, the least 15 to 16 bits noisy only.
Check the 2's complement data format: https://en.wikipedia.org/wiki/Two%27s_complement
Your ADC ist just noisy around 0, example for 2's complement:
8 bit 2'sc:
1111 1111 = -1 !
0000 0000 = 0
0111 1111 = +127
1000 000 = -127
When working with audio formats, you should have heard about that. ;)
If not, now you know... :D
So it was mostly a 16 / 32 bit problem.
2024-02-08 02:46 AM
What hardware is connected, a real codec? If yes, codec setup correct?
I would do the following:
1) initialize the buffers with 0
2) check all the I2S clocks and data lines with a scope
3) connect I2S RX and TX data lines directly, so that RX buffer should exactly display TX
4) for sync'd starting, start the SAI slaves first, then the master
2024-02-09 08:59 PM
Hi LCE, thank you for your response.
1. I have initialized audio_rx and audio_tx buffers to 0, but this does not change the outcome.
2. All the I2S signals appear have the correct format. The transferred data seems just as erratic as the 16-bit garbage values shown in my first post. So maybe the error stems from software?
3. What do you mean with this?
4. This system only has SAI masters - one for ADC and one for DAC.
2024-02-11 06:11 AM
Hello, I have a similar problem with my nucleo H503RB. I'm trying to make it work with Pmod I2S2 module (ADC+DAC). For now I've connected only ADC via I2S1. This is my HW configuration:
I2S config:
GPDMA config:
Code:
Output:
As you can see, I have no data in the buffer. I've checked clocks and data line with scope and it's okay here. The DMA interrupt also fires. So what can I do with it?
2024-02-11 11:55 PM
> 3. What do you mean with this?
> 4. This system only has SAI masters - one for ADC and one for DAC.
To get the external codec / ADC / DAC out of the equation, you could try to connect the SD pins directly.
What do you have connected? Especially at the TX, it's better to start simple, like outputting tx_buf[i++] = i
I'm not sure if having 2 masters really makes sense, because they will not be synchronized a 100%, check the LRCKs on the scope. If you have some other reasons for not syncing, that might be okay. But unsynced, you need all the I2S clocks to the outside world twice.
2024-02-12 03:22 AM
Could you help me too, please?
2024-02-12 08:25 PM
What do you mean by "connect the SD pins directly"? I have two separate audio transceivers - one ADC (PCM1821) and one DAC (PCM5100). Seems pointless to connect the SD of these two, if that's what you're implying?
Syncing is not an issue for me, as I want to alter the audio received from the ADC (so some delay is OK). I have used two I2S masters for my previous board, but in that case I did not have to use SAI.
Below is the pinout on my STM32U575, if that is of any help:
@sebxx4 respectfully, get your own thread instead of cluttering mine with information irrelevant to the original post.
2024-02-12 09:58 PM
Okay, then forget about the syncing.
> What do you mean by "connect the SD pins directly"?
> I have two separate audio transceivers - one ADC (PCM1821) and one DAC (PCM5100).
> Seems pointless to connect the SD of these two, if that's what you're implying?
That's how I test the SAI without external components. If they are synced and SAI setup is okay, I find the TX data in the RX buffers.
Right now you have the DAC and the ADC in the equation, are you 100 % sure that these are set up correctly?
Anyway, check further:
1) GPIO settings correct? Check & compare alt functions with datasheet.
2) What do you see on the scope? All clocks as they should be?
3) Scope again, what's happening on ADC data line? Maybe the ADC data output is still high impedance, so is your GPIO SD input -> noise, maybe try a pull-down R.
4) Maybe the external components need a master clock?
5) DMA interrupts enabled and ISRs called?
6) RX data is ***, but is it changing? (means it is at least running)
I just see:
> I want to note that all of the HAL code seems to be configured correctly --> no errors or warnings
Haha, good one... successful compilation and correct functioning is not the same.
2024-02-22 08:43 PM
I've excluded the DAC for the time being, trying to evaluate only the ADC.
1. The GPIOs are correct according to the datasheet.
2. Here's what I'm getting on the scope. Every frame is transferred in 8 bits, which kind of makes sense when you look at the way pData is setup in this function:
HAL_StatusTypeDef HAL_SAI_Receive_DMA(SAI_HandleTypeDef *hsai, uint8_t *pData, uint16_t Size)
Could it be that I'm messing up here?
#define AUDIO_BUFFER_SIZE 64
uint32_t audio_rx[AUDIO_BUFFER_SIZE];
#define AUDIO_RX_PORT hsai_BlockA1 //hsai_BlockA1 pre-defined by HAL
//initializing call of the SAI receive via DMA function
HAL_SAI_Receive_DMA(&AUDIO_RX_PORT, (uint8_t *)audio_rx, AUDIO_BUFFER_SIZE);
3. Tried pulling down the data line, but still the same result as above. I'm ruling out tristate, since the data line seems to be driven high.
4. I've used this ADC with an STM32G4 MCU, where I used a simple I2S interface without needing a master clock. On this device, everything worked perfectly.
5. Here's the NVIC, where the ISRs are enabled both for the SAI1, and for the GPDMA channel it's using.
6. The RX data changes randomly and constantly.
Here's how the HAL SAI code is setup, generated from CubeMX:
SAI_HandleTypeDef hsai_BlockA1;
SAI_HandleTypeDef hsai_BlockB1;
DMA_NodeTypeDef Node_GPDMA1_Channel14;
DMA_QListTypeDef List_GPDMA1_Channel14;
DMA_HandleTypeDef handle_GPDMA1_Channel14;
/* SAI1 init function */
void MX_SAI1_Init(void)
{
/* USER CODE BEGIN SAI1_Init 0 */
/* USER CODE END SAI1_Init 0 */
/* USER CODE BEGIN SAI1_Init 1 */
/* USER CODE END SAI1_Init 1 */
/* USER CODE BEGIN SAI1_Init 1 */
/* USER CODE END SAI1_Init 1 */
hsai_BlockA1.Instance = SAI1_Block_A;
hsai_BlockA1.Init.AudioMode = SAI_MODEMASTER_RX;
hsai_BlockA1.Init.Synchro = SAI_ASYNCHRONOUS;
hsai_BlockA1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
hsai_BlockA1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
hsai_BlockA1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_48K;
hsai_BlockA1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
hsai_BlockA1.Init.MckOutput = SAI_MCK_OUTPUT_DISABLE;
hsai_BlockA1.Init.MonoStereoMode = SAI_STEREOMODE;
hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;
if (HAL_SAI_InitProtocol(&hsai_BlockA1, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_32BIT, 2) != HAL_OK)
{
Error_Handler();
}
hsai_BlockB1.Instance = SAI1_Block_B;
hsai_BlockB1.Init.AudioMode = SAI_MODEMASTER_TX;
hsai_BlockB1.Init.Synchro = SAI_ASYNCHRONOUS;
hsai_BlockB1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
hsai_BlockB1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
hsai_BlockB1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
hsai_BlockB1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_48K;
hsai_BlockB1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
hsai_BlockB1.Init.MckOutput = SAI_MCK_OUTPUT_DISABLE;
hsai_BlockB1.Init.MonoStereoMode = SAI_STEREOMODE;
hsai_BlockB1.Init.CompandingMode = SAI_NOCOMPANDING;
hsai_BlockB1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
if (HAL_SAI_InitProtocol(&hsai_BlockB1, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_32BIT, 2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SAI1_Init 2 */
/* USER CODE END SAI1_Init 2 */
}
static uint32_t SAI1_client =0;
void HAL_SAI_MspInit(SAI_HandleTypeDef* saiHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
DMA_NodeConfTypeDef NodeConfig;
/* SAI1 */
if(saiHandle->Instance==SAI1_Block_A)
{
/* SAI1 clock enable */
if (SAI1_client == 0)
{
__HAL_RCC_SAI1_CLK_ENABLE();
/* Peripheral interrupt init*/
HAL_NVIC_SetPriority(SAI1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SAI1_IRQn);
}
SAI1_client ++;
/**SAI1_A_Block_A GPIO Configuration
PA8 ------> SAI1_SCK_A
PA9 ------> SAI1_FS_A
PA10 ------> SAI1_SD_A
*/
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Peripheral DMA init*/
NodeConfig.NodeType = DMA_GPDMA_2D_NODE;
NodeConfig.Init.Request = GPDMA1_REQUEST_SAI1_A;
NodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
NodeConfig.Init.Direction = DMA_PERIPH_TO_MEMORY;
NodeConfig.Init.SrcInc = DMA_SINC_FIXED;
NodeConfig.Init.DestInc = DMA_DINC_INCREMENTED;
NodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
NodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
NodeConfig.Init.SrcBurstLength = 1;
NodeConfig.Init.DestBurstLength = 1;
NodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
NodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
NodeConfig.Init.Mode = DMA_NORMAL;
NodeConfig.RepeatBlockConfig.RepeatCount = 1;
NodeConfig.RepeatBlockConfig.SrcAddrOffset = 0;
NodeConfig.RepeatBlockConfig.DestAddrOffset = 0;
NodeConfig.RepeatBlockConfig.BlkSrcAddrOffset = 0;
NodeConfig.RepeatBlockConfig.BlkDestAddrOffset = 0;
NodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
NodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
NodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
if (HAL_DMAEx_List_BuildNode(&NodeConfig, &Node_GPDMA1_Channel14) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMAEx_List_InsertNode(&List_GPDMA1_Channel14, NULL, &Node_GPDMA1_Channel14) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMAEx_List_SetCircularMode(&List_GPDMA1_Channel14) != HAL_OK)
{
Error_Handler();
}
handle_GPDMA1_Channel14.Instance = GPDMA1_Channel14;
handle_GPDMA1_Channel14.InitLinkedList.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
handle_GPDMA1_Channel14.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
handle_GPDMA1_Channel14.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
handle_GPDMA1_Channel14.InitLinkedList.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
handle_GPDMA1_Channel14.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel14) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel14, &List_GPDMA1_Channel14) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(saiHandle, hdmarx, handle_GPDMA1_Channel14);
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel14, DMA_CHANNEL_PRIV) != HAL_OK)
{
Error_Handler();
}
}
if(saiHandle->Instance==SAI1_Block_B)
{
/* SAI1 clock enable */
if (SAI1_client == 0)
{
__HAL_RCC_SAI1_CLK_ENABLE();
/* Peripheral interrupt init*/
HAL_NVIC_SetPriority(SAI1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SAI1_IRQn);
}
SAI1_client ++;
/**SAI1_B_Block_B GPIO Configuration
PB3 (JTDO/TRACESWO) ------> SAI1_SCK_B
PB5 ------> SAI1_SD_B
PB6 ------> SAI1_FS_B
*/
GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_5|GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
}
2024-02-22 10:57 PM
Oh my, the DMA setup is completely different from the G4 / F7 / H7 that I know.
But there is definitely one problem:
SCLK should be 4 times higher for 2 x 32 clock cycles within 1 LRCK cycle, but you actually seem to set it up correctly with SAI_PROTOCOL_DATASIZE_32BIT.
I would read the SAI registers where all the clock settings are done. Maybe there's a HAL bug?
Are there any SAI examples from ST for that U575? I would compare DMA and SAI setup.
And check if your ADC really runs as SAI_I2S_STANDARD, not as left justified (as most audio ADCs that I have used).