Skocz do zawartości

Czy mikrokontroler podoła FFT przy jednoczesnej filtracji IIR - STM32 F407


Pomocna odpowiedź

Dobry wieczór, kiedyś wspominałem, że zabiorę się za wizualną stronę equalizera. Padło ostatecznie na matrycę diod led 8x32, ale zanim zacznę wszystko budować to zastanawiam się czy mikrokontroler filtrując obecnie 10 częstotliwości za pomocą filtrów IIR będzie w stanie podołać dyskretnej transformacie. Docelowo płynność efektów na wyświetlaczu ma wynosić albo 30 albo nawet 60fps jeśli będzie to możliwe. Czy można ocenić na podstawie kodu ile czasu mikrokontroler nic nie robi i czy będzie nadążał z fft? Tutaj kod, może ktoś kiedyś sobie go nawet wykorzysta bo działa całkiem nieźle:

/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file           : main.c
 * @brief          : Main program body
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2024 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "arm_math.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
I2S_HandleTypeDef hi2s2;
DMA_HandleTypeDef hdma_i2s2_ext_rx;
DMA_HandleTypeDef hdma_spi2_tx;

/* USER CODE BEGIN PV */
#define BLOCK_SIZE_FLOAT 512
#define BLOCK_SIZE_U16 2048

#define numStages 10

float iir_l_state[4 * numStages];
float iir_r_state[4 * numStages];

float iir_coeffs[5 * numStages];

uint16_t rxBuf[BLOCK_SIZE_U16 * 2];
uint16_t txBuf[BLOCK_SIZE_U16 * 2];
float l_buf_in[BLOCK_SIZE_FLOAT * 2];
float r_buf_in[BLOCK_SIZE_FLOAT * 2];
float l_buf_out[BLOCK_SIZE_FLOAT * 2];
float r_buf_out[BLOCK_SIZE_FLOAT * 2];

uint8_t callback_state = 0;
uint8_t change_coeffs = 1;

typedef struct {
	uint16_t f_40, f_75, f_125, f_250, f_500, f_1000, f_2000, f_4000, f_8000, f_16000;
	int8_t g_40, g_75, g_125, g_250, g_500, g_1000, g_2000, g_4000, g_8000, g_16000;
} dsp_equalizer;

dsp_equalizer equalizer;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_I2S2_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

void setStructData(void) {
	equalizer.f_40 = 40;
	equalizer.f_75 = 75;
	equalizer.f_125 = 125;
	equalizer.f_250 = 250;
	equalizer.f_500 = 500;
	equalizer.f_1000 = 1000;
	equalizer.f_2000 = 2000;
	equalizer.f_4000 = 4000;
	equalizer.f_8000 = 8000;
	equalizer.f_16000 = 16000;
	equalizer.g_40 = 7;
	equalizer.g_75 = 4;
	equalizer.g_125 = -4;
	equalizer.g_250 = -2;
	equalizer.g_500 = 1;
	equalizer.g_1000 = 1;
	equalizer.g_2000 = 4;
	equalizer.g_4000 = 1;
	equalizer.g_8000 = 2;
	equalizer.g_16000 = 0;
}

void calcPeakCoeefs(int8_t peakGain, uint16_t Fc, float *b0, float *b1,
		float *b2, float *a1, float *a2) {
	if (peakGain == 0) {
		*b0 = 1;
		*b1 = 0;
		*b2 = 0;
		*a1 = 0;
		*a2 = 0;
	} else {
		double norm = 0;
		int8_t x = 0;
		if (peakGain >= 0) {
			x = peakGain;
		} else {
			x = 0 - peakGain;
		}
		float V = pow(10, (x / 40.0));
		float K = tan(M_PI * (Fc / 96000.0));
		if (peakGain > 0.0 && Fc != 0) {
			norm = 1.0f / (1.0f + 1.0f / 0.707 * K + K * K);
			*b0 = (1.0f + V / 0.707f * K + K * K) * norm;
			*b1 = 2.0f * (K * K - 1.0f) * norm;
			*b2 = (1.0f - V / 0.707f * K + K * K) * norm;
			*a1 = *b1 * (-1);
			*a2 = (1.0f - 1.0f / 0.707f * K + K * K) * norm * (-1);
		} else if (peakGain < 0.0 && Fc != 0) {
			norm = 1.0f / (1.0f + V / 0.707f * K + K * K);
			*b0 = (1.0f + 1.0f / 0.707f * K + K * K) * norm;
			*b1 = 2.0f * (K * K - 1.0f) * norm;
			*b2 = (1.0f - 1.0f / 0.707f * K + K * K) * norm;
			*a1 = *b1 * (-1);
			*a2 = (1.0f - V / 0.707f * K + K * K) * norm * (-1);
		}
	}
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2S2_Init();
  /* USER CODE BEGIN 2 */
	arm_biquad_casd_df1_inst_f32 iirsettings_l, iirsettings_r;

	arm_biquad_cascade_df1_init_f32(&iirsettings_l, numStages, &iir_coeffs[0],
			&iir_l_state[0]);
	arm_biquad_cascade_df1_init_f32(&iirsettings_r, numStages, &iir_coeffs[0],
			&iir_r_state[0]);

	if (HAL_I2SEx_TransmitReceive_DMA(&hi2s2, txBuf, rxBuf, BLOCK_SIZE_U16)
			!= HAL_OK) {
		Error_Handler();
	}

	setStructData();

	int offset_r_ptr;
	int offset_w_ptr, w_ptr;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	while (1) {
		if (change_coeffs == 1) {
		    float32_t g_values[] = {equalizer.g_40, equalizer.g_75, equalizer.g_125, equalizer.g_250, equalizer.g_500,
		                            equalizer.g_1000, equalizer.g_2000, equalizer.g_4000, equalizer.g_8000, equalizer.g_16000};
		    float32_t f_values[] = {equalizer.f_40, equalizer.f_75, equalizer.f_125, equalizer.f_250, equalizer.f_500,
		                            equalizer.f_1000, equalizer.f_2000, equalizer.f_4000, equalizer.f_8000, equalizer.f_16000};

		    for (int i = 0; i < sizeof(g_values) / sizeof(g_values[0]); ++i) {
		        float32_t *coeffs_ptr = &iir_coeffs[i * 5];
		        calcPeakCoeefs(g_values[i], f_values[i], coeffs_ptr, coeffs_ptr + 1, coeffs_ptr + 2, coeffs_ptr + 3, coeffs_ptr + 4);
		    }
		    change_coeffs = 0;
		}
		if (callback_state != 0) {

			if (callback_state == 1) {
				offset_r_ptr = 0;
				offset_w_ptr = 0;
				w_ptr = 0;
			}

			else if (callback_state == 2) {
				offset_r_ptr = BLOCK_SIZE_U16;
				offset_w_ptr = BLOCK_SIZE_FLOAT;
				w_ptr = BLOCK_SIZE_FLOAT;
			}

			for (int i = offset_r_ptr; i < offset_r_ptr + BLOCK_SIZE_U16;
					i = i + 4) {
				l_buf_in[w_ptr] =
						(float) ((int) (rxBuf[i] << 16) | rxBuf[i + 1]);
				r_buf_in[w_ptr] = (float) ((int) (rxBuf[i + 2] << 16)
						| rxBuf[i + 3]);
				w_ptr++;
			}

			arm_biquad_cascade_df1_f32(&iirsettings_l, &l_buf_in[offset_w_ptr],
					&l_buf_out[offset_w_ptr],
					BLOCK_SIZE_FLOAT);
			arm_biquad_cascade_df1_f32(&iirsettings_r, &r_buf_in[offset_w_ptr],
					&r_buf_out[offset_w_ptr],
					BLOCK_SIZE_FLOAT);

			w_ptr = offset_w_ptr;

			for (int i = offset_r_ptr; i < offset_r_ptr + BLOCK_SIZE_U16;
					i = i + 4) {
				txBuf[i] = (((int) l_buf_out[w_ptr]) >> 16) & 0xFFFF;
				txBuf[i + 1] = ((int) l_buf_out[w_ptr]) & 0xFFFF;
				txBuf[i + 2] = (((int) r_buf_out[w_ptr]) >> 16) & 0xFFFF;
				txBuf[i + 3] = ((int) r_buf_out[w_ptr]) & 0xFFFF;
				w_ptr++;
			}

			callback_state = 0;

		}
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	}
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = 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_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief I2S2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2S2_Init(void)
{

  /* USER CODE BEGIN I2S2_Init 0 */

  /* USER CODE END I2S2_Init 0 */

  /* USER CODE BEGIN I2S2_Init 1 */

  /* USER CODE END I2S2_Init 1 */
  hi2s2.Instance = SPI2;
  hi2s2.Init.Mode = I2S_MODE_MASTER_TX;
  hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
  hi2s2.Init.DataFormat = I2S_DATAFORMAT_24B;
  hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
  hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_96K;
  hi2s2.Init.CPOL = I2S_CPOL_LOW;
  hi2s2.Init.ClockSource = I2S_CLOCK_PLL;
  hi2s2.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_ENABLE;
  if (HAL_I2S_Init(&hi2s2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2S2_Init 2 */

  /* USER CODE END I2S2_Init 2 */

}

/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Stream3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);
  /* DMA1_Stream4_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

void HAL_I2SEx_TxRxHalfCpltCallback(I2S_HandleTypeDef *hi2s) {

	callback_state = 1;

}

void HAL_I2SEx_TxRxCpltCallback(I2S_HandleTypeDef *hi2s) {

	callback_state = 2;

}

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
	/* User can add his own implementation to report the HAL error return state */
	__disable_irq();
	while (1) {
	}
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

Edit: oczywiście 10 częstotliwości na kanał. Filtrów jest dokładnie 20, więc będą przeprowadzane dwie transformaty - jedna dla lewego kanału i druga dla prawego kanału.

Edytowano przez DeadGeneratio
Link do komentarza
Share on other sites

@DeadGeneratio cześć!

Myślę, że bez problemu. Biblioteka CMSIS DSP ma co trzeba. Nie wiem ile chcesz sampli, ale jak robiłem kiedyś 1024 na F4 to zajmowało kilka us.

Ostatnio udało mi się zabrać za udoskonalenie swojego wyświetlacza widma 19x21 diod programowalnych i działa na ESP32 S3. Jest tam sterowanie matrycą, sterowanie wyświetlaczem LCD, sampling mikrofonów I2S 16kHz + 4096 FFT, serwer UDP i to wszystko daje radę. F4 to nie te S3 ale FFT to nie problem.

https://github.com/Gieneq/esp32s3_asd/blob/master/main/gsampler.c

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.