Hello,
I have a project where I need to setup I2S with DMA on an STM32 microcontroller. I did get it to work using STCube IDE. Now I need to port the code over to platformio. I want to use the Arduino framework.
I tried to just copy over the functions from the main.c file, which did compile but not work on the microcontroller. Copying all h and c files from inc and src into the src folder of pio as suggested in some other threads does not compile.
Does anyone has experience with porting code over?
I really only need the setups made by STCube to be ported. I did not get the I2S to work without the STCube, that is the whole reason for me to port the Code. So if someone knows how to properly setup I2S and DMA directly in pio with the arduino framework this would be my preferred way of doing things here.
Thanks!
Which functions did you copy exactly, and in which functions do you call? There might also be other functions in other files, e.g., HAL_I2S_MspInit() and HAL_I2S_MspDeInit(), HAL_DMA_MspInit(), MX_I2S_Init(), …, and especially the interrupt handling functions in the stm32xxxxx_it.c files. Special care must then be taken to copy those into either also .c files or in .cpp files but with extern "C" marking, otherwise those functions will not be found and respected during linking.
Also, stuff like the clock initialization for SAI is important; The SystemClock_Config() function is overridable if you need a different version from the usual clock init, which may miss SAI clock setup. (See example).
Without having the original working STM32Cube project and your ported project, this is like looking into a cloudy crystal ball. It could be very many things. If you can show the actual code, tracking the problem done would be much easier.
Hello Max,
thanks for your reply. The Code from STMCube that does work is this:
#include "main.h"
#include <stdio.h>
#include <string.h>
SAI_HandleTypeDef hsai_BlockA1;
DMA_HandleTypeDef hdma_sai1_a;
UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_tx;
#define AUDIO_BUFFER_SIZE 64 // Size in samples (adjust based on your needs)
int32_t audioBuffer[AUDIO_BUFFER_SIZE]; // Buffer to store I2S data
uint8_t uartBuffer[AUDIO_BUFFER_SIZE * 4]; // Buffer for UART transmission (4 bytes per sample)
uint8_t transferComplete = 0; // Flag to indicate DMA transfer completion
void SystemClock_Config(void);
void PeriphCommonClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_SAI1_Init(void);
static void MX_USART1_UART_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_SAI1_Init();
MX_USART1_UART_Init();
char startMsg[] = "I2S Microphone Test Starting\r\n";
HAL_UART_Transmit(&huart1, (uint8_t*)startMsg, sizeof(startMsg)-1, 1000);
if (HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t*)audioBuffer, AUDIO_BUFFER_SIZE) != HAL_OK) {
char errorMsg[] = "I2S Microphone Test Failed\r\n";
HAL_UART_Transmit(&huart1, (uint8_t*)errorMsg, sizeof(errorMsg)-1, 1000);
Error_Handler();
}
while (1)
{
if (transferComplete) {
transferComplete = 0;
HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t*)audioBuffer, AUDIO_BUFFER_SIZE);
}
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
RCC_OscInitStruct.PLL.PLLN = 32;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK4|RCC_CLOCKTYPE_HCLK2
|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.AHBCLK2Divider = RCC_SYSCLK_DIV2;
RCC_ClkInitStruct.AHBCLK4Divider = RCC_SYSCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
{
Error_Handler();
}
}
void PeriphCommonClock_Config(void)
{
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SMPS;
PeriphClkInitStruct.SmpsClockSelection = RCC_SMPSCLKSOURCE_HSI;
PeriphClkInitStruct.SmpsDivSelection = RCC_SMPSCLKDIV_RANGE1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
}
static void MX_SAI1_Init(void)
{
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.MckOverSampling = SAI_MCK_OVERSAMPLING_DISABLE;
hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
hsai_BlockA1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_16K;
hsai_BlockA1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
hsai_BlockA1.Init.MonoStereoMode = SAI_MONOMODE;
hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;
if (HAL_SAI_InitProtocol(&hsai_BlockA1, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_16BIT, 2) != HAL_OK)
{
Error_Handler();
}
}
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;//1000000;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_DisableFifoMode(&huart1) != HAL_OK)
{
Error_Handler();
}
}
static void MX_DMA_Init(void)
{
__HAL_RCC_DMAMUX1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
/* DMA1_Channel2_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
}
static void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
}
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
{
static uint32_t callbackCount = 0;
callbackCount++;
if (callbackCount % 1 == 0) {
if (huart1.gState == HAL_UART_STATE_READY) {
char buffer[20]; // Buffer for each sample
// Get raw 24-bit sample from the microphone
int32_t sample = audioBuffer[0];
sprintf(buffer, "%d\r\n", sample);
HAL_UART_Transmit(&huart1, (uint8_t*)buffer, strlen(buffer), 100);
}
}
HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t*)audioBuffer, AUDIO_BUFFER_SIZE);
}
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
This simple I2S test to read data from the INMP441 and print it to via UART does work when compiled and uploaded through the STCube IDE. But when I simply try to move it over to platformio my STM32WB55 gets stuck. I think around the SystemClock_Config. I am definetley not getting the same result.
How would you port this over to platformio and the Arduino framework?
Thanks!