Simple voice recorder - Page 1
Forum section: electronics / projects

author: spin7ion
author_rank: Newbie
timestamp: May 28, 2023, 12:29:58 am
content: Hi, everyone! I'm struggling to make a voice recorder with stm32f401, sd card over spi and I2S microphone. I've successfully make file read and file write with fatfs, but I still can't find out why I'm recording some noise. The logic analyzer shows that clock, frame and data are kind of ok as I can understand.Here is the code I've made so far. Can anyone help me, please?Code:/* Includes ------------------------------------------------------------------*/#include "main.h"#include "fatfs.h"#include "usb_device.h"/* Private includes ----------------------------------------------------------*//* USER CODE BEGIN Includes */#include "stdio.h"/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*//* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*//* USER CODE BEGIN PD */#define I2S_DATA_WORD_LENGTH (24) // industry-standard 24-bit I2S#define I2S_FRAME (32) // bits per sample/* USER CODE END PD *//* Private macro -------------------------------------------------------------*//* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*/ADC_HandleTypeDef hadc1;I2S_HandleTypeDef hi2s3;DMA_HandleTypeDef hdma_spi3_rx;SPI_HandleTypeDef hspi1;TIM_HandleTypeDef htim4;UART_HandleTypeDef huart1;/* USER CODE BEGIN PV */// ===FILES===FATFS fs;FIL fil;FRESULT res;uint32_t temp_number;// === State ===#define STATE_START_REC 0#define STATE_REC1#define STATE_STOP_REC 2#define STATE_WAITING 3volatile uint8_t state=STATE_START_REC;// === Audio ===#define WAV_WRITE_SAMPLE_COUNT 512uint8_t data_i2s;static uint8_t first_time = 0;volatile uint8_t half_i2s, full_i2s;static uint8_t wav_file_header={0x52, 0x49, 0x46, 0x46, 0xa4, 0xa9, 0x03, 0x00, 0x57 ,0x41, 0x56, 0x45, 0x66, 0x6d,0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x80, 0x7d, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00,0x04, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x80, 0xa9, 0x03, 0x00};static uint32_t wav_file_size;/*uint16_t aud_buf[2*BUFFER_SIZE]; // Double bufferingunsigned int written=0;unsigned long fileSize = 0L;unsigned long waveChunk = 16; //bit sizeunsigned int waveType = 1; //unsigned int numChannels = 1; //1:mono 2:stereounsigned long sampleRate = 15625; //samples per secunsigned long bytesPerSec = 46875; //samplerate*NumChannels*(Bitpersample/8)unsigned int blockAlign = 3; //NumChannels*Bitpersample/8unsigned int bitsPerSample = 24;unsigned long dataSize = 0L;unsigned long recByteSaved = 0L;char bytes;int lenOfBufferToWrite = 0;int temp = 0;*//* 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_ADC1_Init(void);static void MX_SPI1_Init(void);static void MX_USART1_UART_Init(void);static void MX_TIM4_Init(void);static void MX_I2S3_Init(void);/* USER CODE BEGIN PFP */void errorHandler();/* USER CODE END PFP *//* Private user code ---------------------------------------------------------*//* USER CODE BEGIN 0 */void convert_endianness(uint32_t *array, uint16_t Size) {for (int i = 0; i < Size; i++) {array = __REV(array);}}void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s){full_i2s = 1;}void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s){half_i2s = 1;}void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){if (htim->Instance == htim4.Instance){if(state==STATE_REC){state=STATE_STOP_REC;}}}int _write(int file, char *ptr, int len){int DataIdx;for (DataIdx = 0; DataIdx < len; DataIdx++){ITM_SendChar(*ptr++);}return len;}void start_recording_fname(char *filename, uint32_t frequency){uint32_t byte_rate = frequency * 2 * 2;wav_file_header = (uint8_t)frequency;wav_file_header = (uint8_t)(frequency >> 8);wav_file_header = (uint8_t)(frequency >> 16);wav_file_header = (uint8_t)(frequency >> 24);wav_file_header = (uint8_t)byte_rate;wav_file_header = (uint8_t)(byte_rate >> 8);wav_file_header = (uint8_t)(byte_rate >> 16);wav_file_header = (uint8_t)(byte_rate >> 24);// creating a fileres = f_open(&fil ,filename, FA_OPEN_ALWAYS | FA_WRITE | FA_READ);if(res != 0){printf("error in creating a file: %d \n", res);Error_Handler();}else{res = f_write(&fil,wav_file_header, 44,(UINT*)&temp_number);printf("Created wav \n");}wav_file_size = 0;}void write2wave_file(uint8_t *data, uint16_t data_size){uint32_t temp_number;printf("w\n");if(first_time == 0){for(int i = 0; i < 44; i++){*(data + i) = wav_file_header;}first_time = 1;}res = f_write(&fil,(void *)data, data_size,(UINT*)&temp_number);if(res != 0){printf("error in writing to the file: %d \n", res);Error_Handler();}res=f_sync(&fil);wav_file_size += data_size;}void stop_recording(){HAL_I2S_DMAStop(&hi2s3);half_i2s = 0;full_i2s = 0;// updating data size sectorwav_file_size -= 8;wav_file_header = (uint8_t)wav_file_size;wav_file_header = (uint8_t)(wav_file_size >> 8);wav_file_header = (uint8_t)(wav_file_size >> 16);wav_file_header = (uint8_t)(wav_file_size >> 24);wav_file_size -= 36;wav_file_header = (uint8_t)wav_file_size;wav_file_header = (uint8_t)(wav_file_size >> 8);wav_file_header = (uint8_t)(wav_file_size >> 16);wav_file_header = (uint8_t)(wav_file_size >> 24);// moving to the beginning of the file to update the file formatf_lseek(&fil, 0);res=f_write(&fil,(void *)wav_file_header, sizeof(wav_file_header),(UINT*)&temp_number);if(res != 0){printf("error in updating the first sector: %d \n", res);Error_Handler();}f_sync(&fil);f_close(&fil);first_time = 0;printf("closed the file \n");}void incrementRunCounter(){FILINFO fno;res=f_stat("init.txt", &fno);uint8_t a=0;char test;if(res==FR_NO_FILE){res=f_open(&fil, "init.txt", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);res=f_puts("1", &fil);}else{res=f_open(&fil, "init.txt", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);f_gets(test, 15, &fil);a=atoi(test);a++;sprintf(test,"%d",a);f_lseek(&fil, 0);//res=f_puts(test, &fil);res = f_write(&fil, test, strlen(test),(UINT*)&temp_number);}f_sync(&fil);res=f_close(&fil);}/* 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_ADC1_Init();MX_SPI1_Init();MX_USART1_UART_Init();MX_FATFS_Init();MX_USB_DEVICE_Init();MX_TIM4_Init();MX_I2S3_Init();/* USER CODE BEGIN 2 *///HAL_I2S_DMAStop(&hi2s2);HAL_Delay(500);printf("Hello\n");res=f_mount(&fs, "0", 0);if(res != 0){printf("can't mount %d\n", res);Error_Handler();}incrementRunCounter();HAL_GPIO_WritePin(PIN_LED_GPIO_Port, PIN_LED_Pin, GPIO_PIN_RESET);for(uint8_t i=0;i<3;i++){HAL_GPIO_WritePin(PIN_LED_GPIO_Port, PIN_LED_Pin, GPIO_PIN_RESET);HAL_Delay(500);HAL_GPIO_WritePin(PIN_LED_GPIO_Port, PIN_LED_Pin, GPIO_PIN_SET);HAL_Delay(500);}start_recording_fname("test2.wav", I2S_AUDIOFREQ_32K);HAL_GPIO_WritePin(PIN_LED_GPIO_Port, PIN_LED_Pin, GPIO_PIN_RESET);HAL_TIM_Base_Start_IT(&htim4);state=STATE_REC;// divide by 32 because we have full word data width DMA but buffer is 1 byte arrayHAL_I2S_Receive_DMA(&hi2s3, (uint16_t *)data_i2s, WAV_WRITE_SAMPLE_COUNT/32);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){if(state==STATE_REC){if(half_i2s == 1) {write2wave_file(data_i2s,WAV_WRITE_SAMPLE_COUNT/2);half_i2s = 0;}if(full_i2s == 1) {write2wave_file(data_i2s + WAV_WRITE_SAMPLE_COUNT/2, WAV_WRITE_SAMPLE_COUNT/2);full_i2s = 0;}} else if(state==STATE_STOP_REC) {stop_recording();state=STATE_WAITING;HAL_GPIO_WritePin(PIN_LED_GPIO_Port, PIN_LED_Pin, GPIO_PIN_SET);}/* 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_SCALE2);/** 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 = 25;RCC_OscInitStruct.PLL.PLLN = 336;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;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_DIV2;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK){Error_Handler();}}/*** @brief ADC1 Initialization Function* @param None* @retval None*/static void MX_ADC1_Init(void){/* USER CODE BEGIN ADC1_Init 0 *//* USER CODE END ADC1_Init 0 */ADC_ChannelConfTypeDef sConfig = {0};/* USER CODE BEGIN ADC1_Init 1 *//* USER CODE END ADC1_Init 1 *//** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)*/hadc1.Instance = ADC1;hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;hadc1.Init.Resolution = ADC_RESOLUTION_12B;hadc1.Init.ScanConvMode = DISABLE;hadc1.Init.ContinuousConvMode = DISABLE;hadc1.Init.DiscontinuousConvMode = DISABLE;hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;hadc1.Init.NbrOfConversion = 1;hadc1.Init.DMAContinuousRequests = DISABLE;hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;if (HAL_ADC_Init(&hadc1) != HAL_OK){Error_Handler();}/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.*/sConfig.Channel = ADC_CHANNEL_0;sConfig.Rank = 1;sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){Error_Handler();}/* USER CODE BEGIN ADC1_Init 2 *//* USER CODE END ADC1_Init 2 */}/*** @brief I2S3 Initialization Function* @param None* @retval None*/static void MX_I2S3_Init(void){/* USER CODE BEGIN I2S3_Init 0 *//* USER CODE END I2S3_Init 0 *//* USER CODE BEGIN I2S3_Init 1 *//* USER CODE END I2S3_Init 1 */hi2s3.Instance = SPI3;hi2s3.Init.Mode = I2S_MODE_MASTER_RX;hi2s3.Init.Standard = I2S_STANDARD_PHILIPS;hi2s3.Init.DataFormat = I2S_DATAFORMAT_24B;hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;hi2s3.Init.AudioFreq = I2S_AUDIOFREQ_32K;hi2s3.Init.CPOL = I2S_CPOL_HIGH;hi2s3.Init.ClockSource = I2S_CLOCK_PLL;hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;if (HAL_I2S_Init(&hi2s3) != HAL_OK){Error_Handler();}/* USER CODE BEGIN I2S3_Init 2 *//* USER CODE END I2S3_Init 2 */}/*** @brief SPI1 Initialization Function* @param None* @retval None*/static void MX_SPI1_Init(void){/* USER CODE BEGIN SPI1_Init 0 *//* USER CODE END SPI1_Init 0 *//* USER CODE BEGIN SPI1_Init 1 *//* USER CODE END SPI1_Init 1 *//* SPI1 parameter configuration*/hspi1.Instance = SPI1;hspi1.Init.Mode = SPI_MODE_MASTER;hspi1.Init.Direction = SPI_DIRECTION_2LINES;hspi1.Init.DataSize = SPI_DATASIZE_8BIT;hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;hspi1.Init.NSS = SPI_NSS_SOFT;hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;hspi1.Init.TIMode = SPI_TIMODE_DISABLE;hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;hspi1.Init.CRCPolynomial = 10;if (HAL_SPI_Init(&hspi1) != HAL_OK){Error_Handler();}/* USER CODE BEGIN SPI1_Init 2 *//* USER CODE END SPI1_Init 2 */}/*** @brief TIM4 Initialization Function* @param None* @retval None*/static void MX_TIM4_Init(void){/* USER CODE BEGIN TIM4_Init 0 *//* USER CODE END TIM4_Init 0 */TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};/* USER CODE BEGIN TIM4_Init 1 *//* USER CODE END TIM4_Init 1 */htim4.Instance = TIM4;htim4.Init.Prescaler = 21000;htim4.Init.CounterMode = TIM_COUNTERMODE_UP;htim4.Init.Period = 32000;htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4;htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;if (HAL_TIM_Base_Init(&htim4) != HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM4_Init 2 *//* USER CODE END TIM4_Init 2 */}/*** @brief USART1 Initialization Function* @param None* @retval None*/static void MX_USART1_UART_Init(void){/* USER CODE BEGIN USART1_Init 0 *//* USER CODE END USART1_Init 0 *//* USER CODE BEGIN USART1_Init 1 *//* USER CODE END USART1_Init 1 */huart1.Instance = USART1;huart1.Init.BaudRate = 115200;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;if (HAL_UART_Init(&huart1) != HAL_OK){Error_Handler();}/* USER CODE BEGIN USART1_Init 2 *//* USER CODE END USART1_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};/* GPIO Ports Clock Enable */__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_GPIOH_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(PIN_LED_GPIO_Port, PIN_LED_Pin, GPIO_PIN_RESET);/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);/*Configure GPIO pin : PIN_LED_Pin */GPIO_InitStruct.Pin = PIN_LED_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(PIN_LED_GPIO_Port, &GPIO_InitStruct);/*Configure GPIO pin : PA4 */GPIO_InitStruct.Pin = GPIO_PIN_4;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/*Configure GPIO pin : PA8 */GPIO_InitStruct.Pin = GPIO_PIN_8;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);}/* USER CODE BEGIN 4 *//* 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){HAL_GPIO_WritePin(PIN_LED_GPIO_Port, PIN_LED_Pin, GPIO_PIN_RESET);HAL_Delay(100);HAL_GPIO_WritePin(PIN_LED_GPIO_Port, PIN_LED_Pin, GPIO_PIN_SET);HAL_Delay(100);}/* 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 */

author: Georgy.Moshkin
author_rank: Frequent Contributor
timestamp: May 28, 2023, 03:59:28 am
content: 1) check recorded waveform using audio editor, e.g. Audacity.2) try to fill data_i2s manually and check if data is written on sd card correctly3) if you can hear something through noise, check if it is endianness problem4) your aproach may work, but it is not guaranteed. Easiest way is to check with a two channel scooe: make two GPIO outputs, toggle first one in dma half complete / complete interrupt, and toggle second one before and after sdcard write procedure, look at gpio outputs on a scope and see if sdcard write completes before next dma interrupt.