cancel
Showing results for 
Search instead for 
Did you mean: 

PDM Mono Mic on SAI_A with DMA Transfer

LGR
Visitor

Hi, this is my first post here and one of my first experiences with the STM32.

I'm working on an STM32H7A3ZI-Q development board. I've connected an Adafruit PDM microphone to SAI1_A with clock on PE2 (SAI1_CK1) and data on PE6 (SAI1_D1). I'm not an expert on PDM microphones; I used to acquire analog data from an amplified microphone with an ADC and timer 2 (everything worked perfectly) and then transferred the data via UART to my PC, where I listened to it with a Python script.

Now I need to switch to a PDM microphone.

Now, I've configured this PDM MONO microphone to be used in PDM mode with CK1, with an 8-bit data size and a 16-bit frame, the same slot size as the data, 2 slots (the IOC forces me to use even numbers), free protocol, and asynchronous sync input.
I get the peripheral clock from PLL2 and it's 40.96 MHz. I set the frequency divider after init (the HAL code generator doesn't generate the correct divider) to get a 1.024 MHz output to the microphone, and the oscilloscope shows the frequency is correct.

Reading the reference manual, it seems I need to acquire two slots, one of which is dummy (I assume slot1) and one working slot (I assume slot0).

I configured the DMA to transfer data from the SAI to RAM with automatic increments in MEM. I use the HALF and FULL DMA callbacks to process the data via software sinc1 (when it works, I'll switch to PDM2PCM or DFSDM).

Now the problem is that I only see noise in the data from slot0 and all zeros in the data from slot1, so I've made some mistake somewhere. I suspect some misconfiguration in the SAI or PDM settings.
Any suggestions?

 

/* USER CODE BEGIN Header */
/**
 *
 * Questo programma registra 1 secondo di audio a 16 kHz
 * da un microfono PDM collegato a:
 * CLK -> PE2 (SAI1_CK1)
 * DATA -> PE6 (SAI1_D1)
 *
 * e invia i dati via UART virtuale (USB) a 230400 baud.
 *
 * Usa SAI1 con DMA per acquisire il bitstream PDM (16-bit Half-Word),
 * estrae solo i dati utili (Slot 0), decima in software 64 bit -> 1 campione PCM (16 bit) e popola audio_data[].
 *
 ******************************************************************************
 * @file           : main.c
 * @brief          : Main program body
 ******************************************************************************
 */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "pdm2pcm.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h> // Necessario per printf
/* USER CODE END Includes */

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

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define BUFFER_SIZE        		16000 // Campioni PCM finali (1 secondo a 16kHz)
#define SAFE_RELAUNCH_LIMIT		BUFFER_SIZE - 128	// (limite per rilancio manuale DMA) dimensione buffer - samples per 2 DMA blocks

// Buffer DMA dimensionato in HALF-WORD (16-bit). Per un'elaborazione efficiente:
// 2048 Half-Word = 4096 byte totali. Equivalente a 128 campioni PCM per callback.
#define DMA_HALF_WORDS      1024                 /* numero di half-word (16-bit) del buffer DMA — multiplo di 8 e 2 */
/* USER CODE END PD */

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

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

COM_InitTypeDef BspCOMInit;
__IO uint32_t BspButtonState = BUTTON_RELEASED;

CRC_HandleTypeDef hcrc;

SAI_HandleTypeDef hsai_BlockA1;
DMA_HandleTypeDef hdma_sai1_a;

TIM_HandleTypeDef htim2;

/* USER CODE BEGIN PV */
/* buffers e parametri per PDM->PCM */
// ATTENZIONE: Il buffer deve essere definito come uint16_t (Half-Word) per il DMA.
static volatile uint16_t pdm_dma_buf[DMA_HALF_WORDS]; // buffer scritto dal DMA (16-bit Half-Words)

static volatile int16_t audio_data[BUFFER_SIZE]; /* buffer PCM finale (16-bit) */
static volatile int32_t sample_index = 0;
static volatile uint8_t recording = 0; /* 0 idle, 1 recording, 2 done */

/* stato per decimatore semplice */
static uint32_t pdm_acc_ones = 0; /* accumulo popcount */
static uint32_t pdm_bits_acc = 0; /* quanti bit accumulati (0..64) */

uint32_t indirizzo = 0; // Variabile di debug
uint8_t armed = false;// variabile stampa debug solo se è intervenuta callback DMA
uint8_t position = 'X';

// var debug registri
uint32_t cr1, cr2, pll, frame, slot, status ;



/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MPU_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_TIM2_Init(void);
static void MX_SAI1_Init(void);
static void MX_CRC_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

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

//-------------------------------------------------------------------------------------------------------------------------------------
// INVIO DATI AUDIO SU UART
//-------------------------------------------------------------------------------------------------------------------------------------
static void SendData(void) {
	for (int i = 0; i < BUFFER_SIZE; i++) {
		printf("%d\n", (int16_t) audio_data[i]);
	}
}

//-------------------------------------------------------------------------------------------------------------------------------------
// STAMPA DATI FORMATO BIN-32bit SU UART
//-------------------------------------------------------------------------------------------------------------------------------------
void print_bin32(const char* label, uint32_t value) {
	printf("%s: ", label); // Print the label string here
    int i;
    for (i = 31; i >= 0; i--) { // Itera dal bit più significativo (31) al meno significativo (0)
        // Stampa '1' se il bit i-esimo è settato, altrimenti '0'
        printf("%c", (value & (1 << i)) ? '1' : '0');

        // Aggiunge uno spazio ogni 4 bit per leggibilità (opzionale)
        if (i % 4 == 0) {
            printf(" ");
        }
    }
    printf("\r\n"); // Va a capo alla fine
}

//-------------------------------------------------------------------------------------------------------------------------------------
// DEBUG DATI PDM SAI
//-------------------------------------------------------------------------------------------------------------------------------------
// Funzione temporanea per il debug dei dati PDM grezzi
void DebugPDMBuffer() {

    // Stampa l'intestazione
    printf("Indice\tSlot0(HEX)\tSlot1(HEX)\tOnes(Slot0)\tOnes(Slot1)\r\n");
    printf("------\t----------\t----------\t-----------\t-----------\r\n");

    for (size_t i = 0; i < 256; i++) {
        uint16_t current_half_word = pdm_dma_buf[i];

        // 1. Separazione dei byte tramite maschera (Little-Endian/DMA a seconda della configurazione):
        // Di solito il SAI/DMA su STM32 accetta Half-Word con il primo slot nel byte basso.

        // Byte Basso: Slot 0 (il primo campionamento PDM)
        uint8_t slot0_byte = (uint8_t)(current_half_word & 0x00FF);

        // Byte Alto: Slot 1 (il secondo campionamento PDM/spazzatura)
        uint8_t slot1_byte = (uint8_t)((current_half_word >> 8) & 0x00FF);

        // 2. Calcolo Popcount
        uint32_t ones_slot0 = __builtin_popcount((unsigned)slot0_byte);
        uint32_t ones_slot1 = __builtin_popcount((unsigned)slot1_byte);

        // 3. Stampa Formattata con Tabulazioni
        printf("%d\t0x%02X\t\t0x%02X\t\t%u\t\t%u\r\n",
               i,               // Indice Half-Word (dec)
               slot0_byte,      // Slot 0 Byte (hex)
               slot1_byte,      // Slot 1 Byte (hex)
               ones_slot0,      // Popcount Slot 0 (dec)
               ones_slot1       // Popcount Slot 1 (dec)
        );
    }
    printf("----------------------------------------------------------------\r\n");
}


//-------------------------------------------------------------------------------------------------------------------------------------
// PDM2PCM DECIMATION (64)
//-------------------------------------------------------------------------------------------------------------------------------------
/*
 * @brief Estrae i dati PDM utili (Slot 0) e li converte in PCM.
 * @PAram src: Puntatore all'inizio del blocco DMA (metà buffer).
 * @PAram nHalfWords: Numero di Half-Words (16 bit) da processare (DMA_HALF_WORDS/2).
 */
static void process_pdm_block(volatile uint16_t *src, uint16_t nHalfWords) {
	// Il buffer DMA è composto da Half-Word [Slot 0 | Slot 1].
	// Convertiamo in uint8_t* per accedere direttamente ai byte.
	// Dobbiamo processare nHalfWords * 2 byte totali.
	uint16_t nBytes = nHalfWords * 2;
	volatile uint8_t *pdm_raw_bytes = (volatile uint8_t*) src;

	// L'estrazione e la decimazione sono integrate nel loop.
	// Il loop scorre tutti i byte trasferiti (nBytes)
	for (uint16_t i = 0; i < nBytes; i ++) {

		// Estrazione: prendiamo SOLO il Byte PDM utile (Slot 0), che si trova nel byte basso (bit 7-0)
		// La parte alta (bit 15-8) contiene lo Slot 1 (spazzatura) e viene ignorato.
		uint8_t pdm_byte = pdm_raw_bytes[i];

		// --- Decimazione e Accumulo ---
		// Contiamo gli "uno" in questo byte PDM utile (8 bit)
		pdm_acc_ones += __builtin_popcount((unsigned) pdm_byte);

		// Aggiorna il numero di bit accumulati finora
		pdm_bits_acc += 8; // Abbiamo aggiunto 8 bit PDM utili

		// Se abbiamo accumulato almeno 64 bit (8 byte utili) -> produciamo un campione PCM
		if (pdm_bits_acc >= 64) {
			// Centro il valore degli 1 intorno a zero [-32..+32], poiché 64/2 = 32
			// Questa è la funzione di filtro passa-basso digitale più semplice (filtro Sinc 1)
			int32_t centered = (int32_t) pdm_acc_ones - 32;

			// Scala il valore centrato su int16 [-32767..32767], approssimando il fattore 32767/32 ≈ 1024
			int32_t pcm = centered * 1024;

			// Clamp per sicurezza: non uscire dai limiti di int16
			if (pcm > 32767)
				pcm = 32767;
			if (pcm < -32768)
				pcm = -32768;

			// Salva il campione PCM nel buffer finale audio_data[]
			audio_data[sample_index++] = (int16_t) pcm;

			// Resetta gli accumuli per la prossima decimazione
			pdm_acc_ones = 0;
			pdm_bits_acc = 0;

			// Se il buffer finale è pieno, ferma il DMA e segnala che la registrazione è completa
			if (sample_index >= 15999) {
				//printf("buffer_pieno ! ! !\r\n");
				//printf("buffer_pos %08ld | caller %c\r\n", (uint32_t) sample_index, (uint8_t) position);
				// Ferma il DMA SAI in modo pulito
				HAL_SAI_Abort(&hsai_BlockA1);

				// Segnala che il buffer è pieno
				recording = 2;

				// Esci dalla funzione
				return;
			}
		}
	}
}

/* USER CODE END 0 */

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

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MPU Configuration--------------------------------------------------------*/
  MPU_Config();

  /* 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_TIM2_Init();
  MX_SAI1_Init();
  MX_CRC_Init();
  MX_PDM2PCM_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Initialize leds */
  BSP_LED_Init(LED_GREEN);
  BSP_LED_Init(LED_YELLOW);
  BSP_LED_Init(LED_RED);

  /* Initialize USER push-button, will be used to trigger an interrupt each time it's pressed.*/
  BSP_PB_Init(BUTTON_USER, BUTTON_MODE_EXTI);

  /* Initialize COM1 port (115200, 8 bits (7-bit data + 1 stop bit), no parity */
  BspCOMInit.BaudRate   = 115200;
  BspCOMInit.WordLength = COM_WORDLENGTH_8B;
  BspCOMInit.StopBits   = COM_STOPBITS_1;
  BspCOMInit.Parity     = COM_PARITY_NONE;
  BspCOMInit.HwFlowCtl  = COM_HWCONTROL_NONE;
  if (BSP_COM_Init(COM1, &BspCOMInit) != BSP_ERROR_NONE)
  {
    Error_Handler();
  }

  /* USER CODE BEGIN BSP */

	/* -- Sample board code to send message over COM1 port ---- */
	//printf("SAI1_Block_A->CR1 = 0x%08ld\r\n", (uint32_t) SAFE_RELAUNCH_LIMIT); //SAI1_Block_A->CR1);

	/* -- Sample board code to switch on leds ---- */
	BSP_LED_Off(LED_GREEN);
	BSP_LED_Off(LED_YELLOW);
	BSP_LED_Off(LED_RED);

  /* USER CODE END BSP */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	while (1) {
		//HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, 1); // LED giallo ON, abilitare solo per test
		if ((BspButtonState == BUTTON_PRESSED) && (recording == 0)) {
			BspButtonState = BUTTON_RELEASED;
			HAL_Delay(200);

			BSP_LED_On(LED_GREEN); // LED verde ON
			sample_index = 0;
			pdm_acc_ones = 0;
			pdm_bits_acc = 0;
			recording = 1;

			/* avvia DMA: il buffer è pdm_dma_buf con DMA_HALF_WORDS elementi (Half-word 16-bit) */
			if (HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t*) pdm_dma_buf, DMA_HALF_WORDS) != HAL_OK)
				Error_Handler();

			// debug registri
			cr1 = SAI1_Block_A->CR1;
			cr2 = SAI1_Block_A->CR2;
			slot = SAI1_Block_A->SLOTR;
			frame = SAI1_Block_A->FRCR;
			pll = RCC->PLLCFGR;
			status = SAI1_Block_A->SR;
		}
		/*if (recording == 1)
			if (armed) {
				armed = false;
				printf("buffer_pos %08ld | caller %c\r\n", (uint32_t) sample_index, (uint8_t) position);
			}*/
		if (recording == 2) {
			BSP_LED_Off(LED_GREEN); // LED verde OFF
			BSP_LED_On(LED_YELLOW); // LED giallo ON

			// debug registri
			/*
			print_bin32("CR1 ", cr1);
			print_bin32("CR2 ", cr2);
			print_bin32("FRAME ", frame);
			print_bin32("SLOT ", slot);
			print_bin32("STATUS ", status);
			print_bin32("PLL ", pll);
			*/

			// Stampiamo tutto il buffer DMA da 1024 Half-Words (metà del tuo buffer DMA era 512)
			//DebugPDMBuffer();

			// Invio dati via UART
			SendData();

			BSP_LED_Off(LED_YELLOW);   // LED giallo OFF
			HAL_Delay(1000);
			recording = 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};

  /*AXI clock gating */
  RCC->CKGAENR = 0xE003FFFF;

  /** Supply configuration update enable
  */
  HAL_PWREx_ConfigSupply(PWR_DIRECT_SMPS_SUPPLY);

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

  while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

  /** Macro to configure the PLL clock source
  */
  __HAL_RCC_PLL_PLLSOURCE_CONFIG(RCC_PLLSOURCE_HSI);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_DIV1;
  RCC_OscInitStruct.HSICalibrationValue = 64;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  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_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV1;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV1;

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

/**
  * @brief CRC Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_CRC_Init(void)
{

  /* USER CODE BEGIN CRC_Init 0 */

  /* USER CODE END CRC_Init 0 */

  /* USER CODE BEGIN CRC_Init 1 */

  /* USER CODE END CRC_Init 1 */
  hcrc.Instance = CRC;
  hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_ENABLE;
  hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE;
  hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE;
  hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE;
  hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
  if (HAL_CRC_Init(&hcrc) != HAL_OK)
  {
    Error_Handler();
  }
  __HAL_CRC_DR_RESET(&hcrc);
  /* USER CODE BEGIN CRC_Init 2 */

  /* USER CODE END CRC_Init 2 */

}

/**
  * @brief SAI1 Initialization Function
  * @PAram None
  * @retval None
  */
static 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 */
  hsai_BlockA1.Instance = SAI1_Block_A;
  hsai_BlockA1.Init.Protocol = SAI_FREE_PROTOCOL;
  hsai_BlockA1.Init.AudioMode = SAI_MODEMASTER_RX;
  hsai_BlockA1.Init.DataSize = SAI_DATASIZE_8;
  hsai_BlockA1.Init.FirstBit = SAI_FIRSTBIT_LSB;
  hsai_BlockA1.Init.ClockStrobing = SAI_CLOCKSTROBING_FALLINGEDGE;
  hsai_BlockA1.Init.Synchro = SAI_ASYNCHRONOUS;
  hsai_BlockA1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
  hsai_BlockA1.Init.NoDivider = SAI_MASTERDIVIDER_DISABLE;
  hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_HF;
  hsai_BlockA1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_16K;
  hsai_BlockA1.Init.MonoStereoMode = SAI_MONOMODE;
  hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;
  hsai_BlockA1.Init.PdmInit.Activation = ENABLE;
  hsai_BlockA1.Init.PdmInit.MicPairsNbr = 1;
  hsai_BlockA1.Init.PdmInit.ClockEnable = SAI_PDM_CLOCK1_ENABLE;
  hsai_BlockA1.FrameInit.FrameLength = 16;
  hsai_BlockA1.FrameInit.ActiveFrameLength = 1;
  hsai_BlockA1.FrameInit.FSDefinition = SAI_FS_STARTFRAME;
  hsai_BlockA1.FrameInit.FSPolarity = SAI_FS_ACTIVE_HIGH;
  hsai_BlockA1.FrameInit.FSOffset = SAI_FS_FIRSTBIT;
  hsai_BlockA1.SlotInit.FirstBitOffset = 0;
  hsai_BlockA1.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE;
  hsai_BlockA1.SlotInit.SlotNumber = 2;
  hsai_BlockA1.SlotInit.SlotActive = 0x00000001;
  if (HAL_SAI_Init(&hsai_BlockA1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SAI1_Init 2 */

  SAI1_Block_A->CR1 &= ~(1U << 27);  	// Disattiva Master Clock (Bit 27)
  SAI1_Block_A->CR1 &= ~(1U << 16);		// Disattiva SAI Block (Bit 16)

  // 3. Modifica MCKDIV (Clear & Set)
  //    a. Maschera MCKDIV (6 bit) = 0x3F, posizione bit 20
  uint32_t cr1_val = SAI1_Block_A->CR1;
  cr1_val &= ~(0x3FUL << 20); // Pulisce i bit [25:20]
  cr1_val |= (20U << 20);     // Imposta MCKDIV = 20
  SAI1_Block_A->CR1 = cr1_val;

  SAI1_Block_A->CR1 |= (1U << 27); 		// Riattiva Master Clock (Bit 27)
  SAI1_Block_A->CR1 |= (1U << 16);		// Riattiva SAI Block (Bit 16)

  /* USER CODE END SAI1_Init 2 */

}

/**
  * @brief TIM2 Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_TIM2_Init(void)
{

  /* USER CODE BEGIN TIM2_Init 0 */

  /* USER CODE END TIM2_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 1999;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_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_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);

}

/**
  * @brief GPIO Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  /* USER CODE BEGIN MX_GPIO_Init_1 */
	// Inizializza LED GPIO (Assumendo definizioni standard come per i board support packages)
	__HAL_RCC_GPIOB_CLK_ENABLE();
  /* USER CODE END MX_GPIO_Init_1 */

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

  /*Configure GPIO pin : PA0 */
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN MX_GPIO_Init_2 */
	// Configurazione pin LED per debug (da adattare al proprio hardware)
	GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_14; // LED Verde (PB0) e Rosso (PB14)
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = GPIO_PIN_1; // LED Giallo (PE1)
	HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  /* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* ----------------- CALLBACKS DMA/SAI ------------------ */

void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai) {
	armed = true;
	position = 'H';
	if (hsai->Instance == SAI1_Block_A) {
		// Elabora la prima metà del buffer (0 - DMA_HALF_WORDS/2)
		process_pdm_block(pdm_dma_buf, DMA_HALF_WORDS / 2);
	}
}

void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai) {
	armed = true;
	position = 'F';
	if (hsai->Instance == SAI1_Block_A) {
		// Elabora la seconda metà del buffer (DMA_HALF_WORDS/2 - END)
		process_pdm_block(pdm_dma_buf + (DMA_HALF_WORDS / 2), DMA_HALF_WORDS / 2);
		// Se sono in registrazione ed entro i limiti del buffer rilancio il DMA
		if (recording == 1)
			if (sample_index <= SAFE_RELAUNCH_LIMIT) {
				// Riavvia il DMA in modalità LINEARE per il blocco successivo
				if (HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t*) pdm_dma_buf, DMA_HALF_WORDS) != HAL_OK)
					Error_Handler();
			}
	}
}

void HAL_SAI_ErrorCallback(SAI_HandleTypeDef *hsai) {
	if (hsai->Instance == SAI1_Block_A) {
		printf("SAI Error\r\n");
		/* tenta fermare in modo pulito */
		HAL_SAI_Abort(hsai);
		recording = 0;
		// Accende il LED Rosso per indicare un errore
		BSP_LED_On(LED_RED);
	}
}

/* USER CODE END 4 */

 /* MPU Configuration */

void MPU_Config(void)
{
  MPU_Region_InitTypeDef MPU_InitStruct = {0};

  /* Disables the MPU */
  HAL_MPU_Disable();

  /** Initializes and configures the Region and the memory to be protected
  */
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER0;
  MPU_InitStruct.BaseAddress = 0x0;
  MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
  MPU_InitStruct.SubRegionDisable = 0x87;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
  MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

  HAL_MPU_ConfigRegion(&MPU_InitStruct);
  /* Enables the MPU */
  HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);

}

/**
  * @brief BSP Push Button callback
  * @PAram Button Specifies the pressed button
  * @retval None
  */
void BSP_PB_Callback(Button_TypeDef Button)
{
  if (Button == BUTTON_USER)
  {
    BspButtonState = BUTTON_PRESSED;
  }
}

/**
  * @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) {
		BSP_LED_On(LED_RED);
		HAL_Delay(500);
		BSP_LED_Off(LED_RED);
		HAL_Delay(500);
	}
  /* 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 */

Immagine.pngImmagine2.pngImmagine3.png

0 REPLIES 0