Hello,
I am new to this forum and I am facing some difficulty in my program. I am taking ADC input from a DSO- a sine wave at 1kHz frequency, and I am processing the received data and applying filters to the received data and sending output on DAC. I am using both ADC and DAC using DMA. Sampling frequency of ADC and DAC is 8kHz. ADC DMA buffer is circular while DAC DMA is normal that I am re-starting the DMA irq handler. I have generated filter coefficients using FDA tool in MATLAB. The data that I am receiving on the DSO is very distorted. I am using the STM32L476 Discovery Board. I have written the code in Atollic Truestudio using STM32CubeMX. I don't know exactly what is wrong here. The code is written below. What changes should I make to the code to make this code work?
main.c :
/* USER CODE BEGIN Header *//********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************* @attention** <h2><center>© Copyright (c) 2019 STMicroelectronics.* All rights reserved.</center></h2>** This software component is licensed by ST under BSD 3-Clause license,* the "License"; You may not use this file except in compliance with the* License. You may obtain a copy of the License at:* opensource.org/licenses/BSD-3-Clause********************************************************************************//* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/#include "main.h"
/* Private includes ----------------------------------------------------------*//* USER CODE BEGIN Includes */#include "stdbool.h"#include "audio.h"#include "cs43l22.h"#include "kaiser_10000_order20.h"#include "arm_math.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*//* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*//* USER CODE BEGIN PD */#define ADC_CALIBRATION_TIMEOUT_MS ((uint32_t) 1)#define ADC_ENABLE_TIMEOUT_MS ((uint32_t) 1)#define ADC_DISABLE_TIMEOUT_MS ((uint32_t) 1)#define ADC_STOP_CONVERSION_TIMEOUT_MS ((uint32_t) 1)#define ADC_CONVERSION_TIMEOUT_MS ((uint32_t) 500)
#define ADC_DELAY_CALIB_ENABLE_CPU_CYCLES (LL_ADC_DELAY_CALIB_ENABLE_ADC_CYCLES * 32)
#define ADC_CONVERTED_DATA_BUFFER_SIZE ((uint32_t) 256)
/* Init variable out of expected ADC conversion data range */#define VAR_CONVERTED_DATA_INIT_VALUE (__LL_ADC_DIGITAL_SCALE(LL_ADC_RESOLUTION_12B) + 1)
/*DAC Private define ---------------------------------------------------------*//* Size of array containing DAC waveform samples */#define WAVEFORM_SAMPLES_SIZE ADC_CONVERTED_DATA_BUFFER_SIZE
#define AUDIO_I2C_ADDRESS ((uint16_t) 0x94)#define I2c1Timeout 3000
#define NUM_TAPS BL#define BLOCK_SIZE 2
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*//* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
SAI_HandleTypeDef hsai_BlockA1;DMA_HandleTypeDef hdma_sai1_a;
/* USER CODE BEGIN PV */AUDIO_DrvTypeDef *audio_drv;
__IO uint16_t aADCxConvertedData[ADC_CONVERTED_DATA_BUFFER_SIZE]; /* ADC group regular conversion data */
/* Variables for ADC conversion data computation to scaled values */float32_t aADCConvertedData_Scaled[ADC_CONVERTED_DATA_BUFFER_SIZE];
/* Variables for FIR Filter Output Data */float32_t aFilteredData[ADC_CONVERTED_DATA_BUFFER_SIZE]; /* FIR Filter Output Values (array of data) */
/* Variables for storing FIR filtered values */static int16_t finalOutput[ADC_CONVERTED_DATA_BUFFER_SIZE];
/* Variable flag to check the status of ADC Conversion*/static bool dacStartFlag = true;volatile bool adcHalfConversionComplete = false;volatile bool adcFullConversionComplete = false;
static float32_t firStateF321[68];
/*Variable for number of blocks based on buffer size and block size*/uint32_t numBlocks = ADC_CONVERTED_DATA_BUFFER_SIZE/BLOCK_SIZE;
/*Pointers for handling ADC data and filter data buffer */float32_t *aADCConverted_Full_Scaled_Ptr, *aADCConverted_Half_Scaled_Ptr, *aFilteredData_Half_Ptr, *aFilteredData_Full_Ptr;
int16_t PlayBuff[ADC_CONVERTED_DATA_BUFFER_SIZE];volatile int16_t UpdatePointer = -1;/* 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_SAI1_Init(void);static void MX_I2C1_Init(void);static void MX_ADC1_Init(void);static void MX_TIM15_Init(void);static void MX_DAC1_Init(void);static void MX_TIM7_Init(void);/* USER CODE BEGIN PFP */void AdcDmaTransferComplete_Callback();void AdcDmaTransferHalf_Callback();
void Activate_ADC(void);void Activate_DAC(void);void Configure_DMA(void);void AdcGrpRegularOverrunError_Callback(void);/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*//* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/*** @brief The application entry point.* @retval int*/int main(void){/* USER CODE BEGIN 1 *///#define blockSize BLOCK_SIZEuint32_t tmp_index_adc_converted_data = 0; //Variable for ADC operation loop indexaADCConverted_Half_Scaled_Ptr = &aADCConvertedData_Scaled[0]; //Assigning ADC buffer starting element's addressaADCConverted_Full_Scaled_Ptr = &aADCConvertedData_Scaled[ADC_CONVERTED_DATA_BUFFER_SIZE/2];//Assigning ADC buffer middle element's addressaFilteredData_Half_Ptr = &aFilteredData[0]; //Assigning filter buffer starting element's addressaFilteredData_Full_Ptr = &aFilteredData[ADC_CONVERTED_DATA_BUFFER_SIZE/2]; //Assigning filter buffer middle element's addressarm_fir_instance_f32 S;arm_fir_init_f32(&S, NUM_TAPS, (float32_t *)&B[0], &firStateF321[0], BLOCK_SIZE);/* 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_SAI1_Init();MX_I2C1_Init();MX_ADC1_Init();MX_TIM15_Init();MX_DAC1_Init();MX_TIM7_Init();/* USER CODE BEGIN 2 */for(int i=0; i < ADC_CONVERTED_DATA_BUFFER_SIZE; i+=1){PlayBuff[i]=finalOutput[i];}
for (tmp_index_adc_converted_data = 0; tmp_index_adc_converted_data < ADC_CONVERTED_DATA_BUFFER_SIZE; tmp_index_adc_converted_data++){aADCxConvertedData[tmp_index_adc_converted_data] = VAR_CONVERTED_DATA_INIT_VALUE;}
Configure_DMA();
//Enable timers of ADC and DACLL_TIM_EnableCounter(TIM15);LL_TIM_EnableCounter(TIM7);
Activate_DAC();Activate_ADC();
if ((LL_ADC_IsEnabled(ADC1) == 1) &&(LL_ADC_IsDisableOngoing(ADC1) == 0) &&(LL_ADC_REG_IsConversionOngoing(ADC1) == 0) ){LL_ADC_REG_StartConversion(ADC1);}else{Error_Handler();}
if(0 != audio_drv->Play(AUDIO_I2C_ADDRESS, NULL, 0)){Error_Handler();}if(HAL_OK != HAL_SAI_Transmit_DMA(&hsai_BlockA1, (uint8_t *)PlayBuff, ADC_CONVERTED_DATA_BUFFER_SIZE)){Error_Handler();}
/* USER CODE END 2 */
/* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(adcFullConversionComplete == true){
if(dacStartFlag == true){dacStartFlag = false;LL_DAC_EnableDMAReq(DAC1,LL_DAC_CHANNEL_2);}
uint32_t tmp_index = 0;
for (tmp_index = 0; tmp_index < ADC_CONVERTED_DATA_BUFFER_SIZE; tmp_index++){aADCConvertedData_Scaled[tmp_index] = ((float32_t)aADCxConvertedData[tmp_index] - 2048.0) / 2048.0 ;}
for (int i=0; i < (numBlocks); i++){
arm_fir_f32((arm_fir_instance_f32*)&S, aADCConverted_Half_Scaled_Ptr + (i * BLOCK_SIZE), aFilteredData_Half_Ptr + (i * BLOCK_SIZE), BLOCK_SIZE);}
//Converting back the filtered data to make it compatible for DAC processingfor (tmp_index =0; tmp_index < ADC_CONVERTED_DATA_BUFFER_SIZE; tmp_index++){finalOutput[tmp_index] = (int16_t)(aFilteredData[tmp_index] * 2048.0) + 2048;}
for (tmp_index =0; tmp_index < ADC_CONVERTED_DATA_BUFFER_SIZE; tmp_index++){PlayBuff[tmp_index]=finalOutput[tmp_index];}
//Reset ADC Full Conversion flagadcFullConversionComplete = false;
}
/* USER CODE END 3 */}
/*** @brief System Clock Configuration* @retval None*/void SystemClock_Config(void){RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the CPU, AHB and APB busses clocks */RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;RCC_OscInitStruct.MSIState = RCC_MSI_ON;RCC_OscInitStruct.MSICalibrationValue = 0;RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;RCC_OscInitStruct.PLL.PLLM = 1;RCC_OscInitStruct.PLL.PLLN = 40;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB busses 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_DIV1;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK){Error_Handler();}PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SAI1|RCC_PERIPHCLK_I2C1|RCC_PERIPHCLK_ADC;PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;PeriphClkInit.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI1;PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI2;PeriphClkInit.PLLSAI2.PLLSAI2Source = RCC_PLLSOURCE_MSI;PeriphClkInit.PLLSAI2.PLLSAI2M = 1;PeriphClkInit.PLLSAI2.PLLSAI2N = 40;PeriphClkInit.PLLSAI2.PLLSAI2P = RCC_PLLP_DIV7;PeriphClkInit.PLLSAI2.PLLSAI2R = RCC_PLLR_DIV2;PeriphClkInit.PLLSAI2.PLLSAI2ClockOut = RCC_PLLSAI2_ADC2CLK;PeriphClkInit.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_MSI;PeriphClkInit.PLLSAI1.PLLSAI1M = 1;PeriphClkInit.PLLSAI1.PLLSAI1N = 40;PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7;PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2;PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2;PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_SAI1CLK;if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK){Error_Handler();}/** Configure the main internal regulator output voltage */if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != 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 */
LL_ADC_InitTypeDef ADC_InitStruct = {0};LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};LL_ADC_CommonInitTypeDef ADC_CommonInitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Peripheral clock enable */LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_ADC);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);/**ADC1 GPIO Configuration PA3 ------> ADC1_IN8 */GPIO_InitStruct.Pin = LL_GPIO_PIN_3;GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
LL_GPIO_EnablePinAnalogControl(GPIOA, LL_GPIO_PIN_3);
/* ADC1 DMA Init */
/* ADC1 Init */LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMA_REQUEST_0);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_HALFWORD);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_HALFWORD);
/* ADC1 interrupt Init */NVIC_SetPriority(ADC1_2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));NVIC_EnableIRQ(ADC1_2_IRQn);
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 *//** Common config */ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B;ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE;LL_ADC_Init(ADC1, &ADC_InitStruct);ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_EXT_TIM15_TRGO;ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_DISABLE;ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_CONTINUOUS;ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED;ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_PRESERVED;LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);LL_ADC_DisableIT_EOC(ADC1);LL_ADC_DisableIT_EOS(ADC1);LL_ADC_DisableDeepPowerDown(ADC1);LL_ADC_EnableInternalRegulator(ADC1);ADC_CommonInitStruct.CommonClock = LL_ADC_CLOCK_ASYNC_DIV1;ADC_CommonInitStruct.Multimode = LL_ADC_MULTI_INDEPENDENT;LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(ADC1), &ADC_CommonInitStruct);LL_ADC_REG_SetTriggerEdge(ADC1, LL_ADC_REG_TRIG_EXT_RISING);/** Configure Regular Channel */LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_8);LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_8, LL_ADC_SAMPLINGTIME_2CYCLES_5);LL_ADC_SetChannelSingleDiff(ADC1, LL_ADC_CHANNEL_8, LL_ADC_SINGLE_ENDED);/* USER CODE BEGIN ADC1_Init 2 */
/* USER CODE END ADC1_Init 2 */
/*** @brief DAC1 Initialization Function* @param None* @retval None*/static void MX_DAC1_Init(void){
/* USER CODE BEGIN DAC1_Init 0 */
/* USER CODE END DAC1_Init 0 */
LL_DAC_InitTypeDef DAC_InitStruct = {0};
/* Peripheral clock enable */LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_DAC1);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);/**DAC1 GPIO Configuration PA5 ------> DAC1_OUT2 */GPIO_InitStruct.Pin = LL_GPIO_PIN_5;GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* DAC1 DMA Init */
/* DAC_CH2 Init */LL_DMA_SetPeriphRequest(DMA2, LL_DMA_CHANNEL_5, LL_DMA_REQUEST_3);
LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_CHANNEL_5, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetChannelPriorityLevel(DMA2, LL_DMA_CHANNEL_5, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA2, LL_DMA_CHANNEL_5, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_CHANNEL_5, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_CHANNEL_5, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA2, LL_DMA_CHANNEL_5, LL_DMA_PDATAALIGN_HALFWORD);
LL_DMA_SetMemorySize(DMA2, LL_DMA_CHANNEL_5, LL_DMA_MDATAALIGN_HALFWORD);
/* USER CODE BEGIN DAC1_Init 1 */
/* USER CODE END DAC1_Init 1 *//** DAC channel OUT2 config */DAC_InitStruct.TriggerSource = LL_DAC_TRIG_EXT_TIM7_TRGO;DAC_InitStruct.WaveAutoGeneration = LL_DAC_WAVE_AUTO_GENERATION_NONE;DAC_InitStruct.OutputBuffer = LL_DAC_OUTPUT_BUFFER_ENABLE;DAC_InitStruct.OutputConnection = LL_DAC_OUTPUT_CONNECT_GPIO;DAC_InitStruct.OutputMode = LL_DAC_OUTPUT_MODE_NORMAL;LL_DAC_Init(DAC1, LL_DAC_CHANNEL_2, &DAC_InitStruct);LL_DAC_EnableTrigger(DAC1, LL_DAC_CHANNEL_2);/* USER CODE BEGIN DAC1_Init 2 */
/* USER CODE END DAC1_Init 2 */
/*** @brief I2C1 Initialization Function* @param None* @retval None*/static void MX_I2C1_Init(void){
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */hi2c1.Instance = I2C1;hi2c1.Init.Timing = 0x50B03644;hi2c1.Init.OwnAddress1 = 0;hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;hi2c1.Init.OwnAddress2 = 0;hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;if (HAL_I2C_Init(&hi2c1) != HAL_OK){Error_Handler();}/** Configure Analogue filter */if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_DISABLE) != HAL_OK){Error_Handler();}/** Configure Digital filter */if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK){Error_Handler();}/* USER CODE BEGIN I2C1_Init 2 */HAL_I2C_MspInit(&hi2c1);HAL_I2C_Init(&hi2c1);/* USER CODE END I2C1_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_TX;hsai_BlockA1.Init.DataSize = SAI_DATASIZE_16;hsai_BlockA1.Init.FirstBit = SAI_FIRSTBIT_MSB;hsai_BlockA1.Init.ClockStrobing = SAI_CLOCKSTROBING_FALLINGEDGE;hsai_BlockA1.Init.Synchro = SAI_ASYNCHRONOUS;hsai_BlockA1.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLE;hsai_BlockA1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;hsai_BlockA1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_8K;hsai_BlockA1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;hsai_BlockA1.Init.MonoStereoMode = SAI_STEREOMODE;hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;hsai_BlockA1.Init.TriState = SAI_OUTPUT_NOTRELEASED;hsai_BlockA1.FrameInit.FrameLength = 32;hsai_BlockA1.FrameInit.ActiveFrameLength = 16;hsai_BlockA1.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION;hsai_BlockA1.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW;hsai_BlockA1.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT;hsai_BlockA1.SlotInit.FirstBitOffset = 0;hsai_BlockA1.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE;hsai_BlockA1.SlotInit.SlotNumber = 2;hsai_BlockA1.SlotInit.SlotActive = 0x00000003;if (HAL_SAI_Init(&hsai_BlockA1) != HAL_OK){Error_Handler();}/* USER CODE BEGIN SAI1_Init 2 */__HAL_SAI_ENABLE(&hsai_BlockA1);
/* Initialize audio driver */if(CS43L22_ID != cs43l22_drv.ReadID(AUDIO_I2C_ADDRESS)){Error_Handler();}audio_drv = &cs43l22_drv;audio_drv->Reset(AUDIO_I2C_ADDRESS);if(0 != audio_drv->Init(AUDIO_I2C_ADDRESS, OUTPUT_DEVICE_HEADPHONE, 90, AUDIO_FREQUENCY_16K)){Error_Handler();}/* USER CODE END SAI1_Init 2 */
/*** @brief TIM7 Initialization Function* @param None* @retval None*/static void MX_TIM7_Init(void){
/* USER CODE BEGIN TIM7_Init 0 */
/* USER CODE END TIM7_Init 0 */
LL_TIM_InitTypeDef TIM_InitStruct = {0};
/* Peripheral clock enable */LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM7);
/* USER CODE BEGIN TIM7_Init 1 */
/* USER CODE END TIM7_Init 1 */TIM_InitStruct.Prescaler = 99;TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;TIM_InitStruct.Autoreload = 99;LL_TIM_Init(TIM7, &TIM_InitStruct);LL_TIM_EnableARRPreload(TIM7);LL_TIM_SetTriggerOutput(TIM7, LL_TIM_TRGO_UPDATE);LL_TIM_DisableMasterSlaveMode(TIM7);/* USER CODE BEGIN TIM7_Init 2 */
/* USER CODE END TIM7_Init 2 */
/*** @brief TIM15 Initialization Function* @param None* @retval None*/static void MX_TIM15_Init(void){
/* USER CODE BEGIN TIM15_Init 0 */
/* USER CODE END TIM15_Init 0 */
LL_TIM_InitTypeDef TIM_InitStruct = {0};LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};LL_TIM_BDTR_InitTypeDef TIM_BDTRInitStruct = {0};
/* Peripheral clock enable */LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM15);
/* USER CODE BEGIN TIM15_Init 1 */
/* USER CODE END TIM15_Init 1 */TIM_InitStruct.Prescaler = 99;TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;TIM_InitStruct.Autoreload = 99;TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;TIM_InitStruct.RepetitionCounter = 0;LL_TIM_Init(TIM15, &TIM_InitStruct);LL_TIM_EnableARRPreload(TIM15);LL_TIM_SetClockSource(TIM15, LL_TIM_CLOCKSOURCE_INTERNAL);TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_FROZEN;TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;TIM_OC_InitStruct.CompareValue = 0;TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;TIM_OC_InitStruct.OCNPolarity = LL_TIM_OCPOLARITY_HIGH;TIM_OC_InitStruct.OCIdleState = LL_TIM_OCIDLESTATE_LOW;TIM_OC_InitStruct.OCNIdleState = LL_TIM_OCIDLESTATE_LOW;LL_TIM_OC_Init(TIM15, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);LL_TIM_OC_DisableFast(TIM15, LL_TIM_CHANNEL_CH1);LL_TIM_SetTriggerInput(TIM15, LL_TIM_TS_ITR0);LL_TIM_SetSlaveMode(TIM15, LL_TIM_SLAVEMODE_DISABLED);LL_TIM_DisableIT_TRIG(TIM15);LL_TIM_DisableDMAReq_TRIG(TIM15);LL_TIM_SetTriggerOutput(TIM15, LL_TIM_TRGO_UPDATE);LL_TIM_DisableMasterSlaveMode(TIM15);TIM_BDTRInitStruct.OSSRState = LL_TIM_OSSR_DISABLE;TIM_BDTRInitStruct.OSSIState = LL_TIM_OSSI_DISABLE;TIM_BDTRInitStruct.LockLevel = LL_TIM_LOCKLEVEL_OFF;TIM_BDTRInitStruct.DeadTime = 0;TIM_BDTRInitStruct.BreakState = LL_TIM_BREAK_DISABLE;TIM_BDTRInitStruct.BreakPolarity = LL_TIM_BREAK_POLARITY_HIGH;TIM_BDTRInitStruct.AutomaticOutput = LL_TIM_AUTOMATICOUTPUT_DISABLE;LL_TIM_BDTR_Init(TIM15, &TIM_BDTRInitStruct);/* USER CODE BEGIN TIM15_Init 2 */
/* USER CODE END TIM15_Init 2 */
/** * Enable DMA controller clock*/static void MX_DMA_Init(void) {
/* DMA controller clock enable */__HAL_RCC_DMA2_CLK_ENABLE();__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init *//* DMA1_Channel1_IRQn interrupt configuration */NVIC_SetPriority(DMA1_Channel1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));NVIC_EnableIRQ(DMA1_Channel1_IRQn);/* DMA2_Channel1_IRQn interrupt configuration */HAL_NVIC_SetPriority(DMA2_Channel1_IRQn, 1, 0);HAL_NVIC_EnableIRQ(DMA2_Channel1_IRQn);/* DMA2_Channel5_IRQn interrupt configuration */NVIC_SetPriority(DMA2_Channel5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));NVIC_EnableIRQ(DMA2_Channel5_IRQn);
/*** @brief GPIO Initialization Function* @param None* @retval None*/static void MX_GPIO_Init(void){LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOE);LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
/**/LL_GPIO_ResetOutputPin(GPIOB, LL_GPIO_PIN_2);
/**/LL_GPIO_ResetOutputPin(GPIOE, LL_GPIO_PIN_8);
/**/GPIO_InitStruct.Pin = LL_GPIO_PIN_2;GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/**/GPIO_InitStruct.Pin = LL_GPIO_PIN_8;GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;LL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/* USER CODE BEGIN 4 */void AdcDmaTransferComplete_Callback(){adcFullConversionComplete = 1;}
void AdcDmaTransferHalf_Callback(){adcHalfConversionComplete = 1 ;}
void Activate_ADC(void){volatile uint32_t wait_loop_index = 0;#if (USE_TIMEOUT == 1)uint32_t Timeout = 0; /* Variable used for timeout management */#endif /* USE_TIMEOUT */
if (LL_ADC_IsEnabled(ADC1) == 0){/* Disable ADC deep power down (enabled by default after reset state) */LL_ADC_DisableDeepPowerDown(ADC1);
/* Enable ADC internal voltage regulator */LL_ADC_EnableInternalRegulator(ADC1);
/* Delay for ADC internal voltage regulator stabilization. *//* Compute number of CPU cycles to wait for, from delay in us. *//* Note: Variable divided by 2 to compensate partially *//* CPU processing cycles (depends on compilation optimization). *//* Note: If system core clock frequency is below 200kHz, wait time *//* is only a few CPU processing cycles. */wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);while(wait_loop_index != 0){wait_loop_index--;}
/* Run ADC self calibration */LL_ADC_StartCalibration(ADC1, LL_ADC_SINGLE_ENDED);
/* Poll for ADC effectively calibrated */#if (USE_TIMEOUT == 1)Timeout = ADC_CALIBRATION_TIMEOUT_MS;#endif /* USE_TIMEOUT */
while (LL_ADC_IsCalibrationOnGoing(ADC1) != 0){#if (USE_TIMEOUT == 1)/* Check Systick counter flag to decrement the time-out value */if (LL_SYSTICK_IsActiveCounterFlag()){if(Timeout-- == 0){/* Time-out occurred. Set LED to blinking mode */LED_Blinking(LED_BLINK_ERROR);}}#endif /* USE_TIMEOUT */}
/* Delay between ADC end of calibration and ADC enable. *//* Note: Variable divided by 2 to compensate partially *//* CPU processing cycles (depends on compilation optimization). */wait_loop_index = (ADC_DELAY_CALIB_ENABLE_CPU_CYCLES >> 1);while(wait_loop_index != 0){wait_loop_index--;}
/* Enable ADC */LL_ADC_Enable(ADC1);
/* Poll for ADC ready to convert */#if (USE_TIMEOUT == 1)Timeout = ADC_ENABLE_TIMEOUT_MS;#endif /* USE_TIMEOUT */
while (LL_ADC_IsActiveFlag_ADRDY(ADC1) == 0){#if (USE_TIMEOUT == 1)/* Check Systick counter flag to decrement the time-out value */if (LL_SYSTICK_IsActiveCounterFlag()){if(Timeout-- == 0){/* Time-out occurred. Set LED to blinking mode */LED_Blinking(LED_BLINK_ERROR);}}#endif /* USE_TIMEOUT */}}}
void Activate_DAC(void){volatile uint32_t wait_loop_index = 0;
/* Enable DAC channel */LL_DAC_Enable(DAC1, LL_DAC_CHANNEL_2);
/* Delay for DAC channel voltage settling time from DAC channel startup. *//* Compute number of CPU cycles to wait for, from delay in us. *//* Note: Variable divided by 2 to compensate partially *//* CPU processing cycles (depends on compilation optimization). *//* Note: If system core clock frequency is below 200kHz, wait time *//* is only a few CPU processing cycles. */wait_loop_index = ((LL_DAC_DELAY_STARTUP_VOLTAGE_SETTLING_US * (SystemCoreClock / (100000 * 2))) / 10);while(wait_loop_index != 0){wait_loop_index--;}
void Configure_DMA(void){//Data Length an address has to be configured before enabling the channelLL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, ADC_CONVERTED_DATA_BUFFER_SIZE);LL_DMA_EnableIT_TC(DMA1,LL_DMA_CHANNEL_1);//LL_DMA_EnableIT_HT(DMA1,LL_DMA_CHANNEL_1);LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1,LL_ADC_DMA_GetRegAddr(ADC1,LL_ADC_DMA_REG_REGULAR_DATA),(uint32_t)aADCxConvertedData, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
//DAC address must be configured and data length must be set before channel is enabledLL_DMA_ConfigAddresses(DMA2,LL_DMA_CHANNEL_5,(uint32_t)&PlayBuff[0],LL_DAC_DMA_GetRegAddr(DAC1,LL_DAC_CHANNEL_2, LL_DAC_DMA_REG_DATA_12BITS_RIGHT_ALIGNED), LL_DMA_DIRECTION_MEMORY_TO_PERIPH );LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_5, ADC_CONVERTED_DATA_BUFFER_SIZE);//Transfer complete and half transfer complete interrupts are enabledLL_DMA_EnableIT_TC(DMA2,LL_DMA_CHANNEL_5);LL_DMA_EnableIT_TE(DMA2,LL_DMA_CHANNEL_5);//Channel is enabledLL_DMA_EnableChannel(DMA2,LL_DMA_CHANNEL_5);
void AdcGrpRegularOverrunError_Callback(void){/* Note: Disable ADC interruption that caused this error before entering in *//* infinite loop below. */
/* Disable ADC group regular overrun interruption */LL_ADC_DisableIT_OVR(ADC1);
/* Error from ADC */}
void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai){/* NOTE : This function Should not be modified, when the callback is needed,the HAL_SAI_TxCpltCallback could be implemented in the user file*/UpdatePointer = ADC_CONVERTED_DATA_BUFFER_SIZE/2;
void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai){/* NOTE : This function Should not be modified, when the callback is needed,the HAL_SAI_TxHalfCpltCallback could be implenetd in the user file*/UpdatePointer = 0;}
/* 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 */
/* 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(char *file, uint32_t line){ /* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */}#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
stm32l4xx_it.c
/* USER CODE BEGIN Header *//********************************************************************************* @file stm32l4xx_it.c* @brief Interrupt Service Routines.******************************************************************************* @attention** <h2><center>© Copyright (c) 2019 STMicroelectronics.* All rights reserved.</center></h2>** This software component is licensed by ST under BSD 3-Clause license,* the "License"; You may not use this file except in compliance with the* License. You may obtain a copy of the License at:* opensource.org/licenses/BSD-3-Clause********************************************************************************//* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/#include "main.h"#include "stm32l4xx_it.h"/* Private includes ----------------------------------------------------------*//* USER CODE BEGIN Includes */
/* Private typedef -----------------------------------------------------------*//* USER CODE BEGIN TD *//* USER CODE END TD */
/* Private define ------------------------------------------------------------*//* USER CODE BEGIN PD */#define ADC_CONVERTED_DATA_BUFFER_SIZE ((uint32_t) 256)/* USER CODE END PD */
/* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*//* USER CODE BEGIN PFP */
extern void AdcDmaTransferComplete_Callback();extern void AdcDmaTransferHalf_Callback();extern void AdcGrpRegularOverrunError_Callback(void);/* USER CODE END PFP */
/* External variables --------------------------------------------------------*/extern DMA_HandleTypeDef hdma_sai1_a;/* USER CODE BEGIN EV */
/* USER CODE END EV */
/******************************************************************************//* Cortex-M4 Processor Interruption and Exception Handlers */ /******************************************************************************//*** @brief This function handles Non maskable interrupt.*/void NMI_Handler(void){/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
/* USER CODE END NonMaskableInt_IRQn 0 *//* USER CODE BEGIN NonMaskableInt_IRQn 1 */
/* USER CODE END NonMaskableInt_IRQn 1 */}
/*** @brief This function handles Hard fault interrupt.*/void HardFault_Handler(void){/* USER CODE BEGIN HardFault_IRQn 0 */
/* USER CODE END HardFault_IRQn 0 */while (1){/* USER CODE BEGIN W1_HardFault_IRQn 0 *//* USER CODE END W1_HardFault_IRQn 0 */}}
/*** @brief This function handles Memory management fault.*/void MemManage_Handler(void){/* USER CODE BEGIN MemoryManagement_IRQn 0 */
/* USER CODE END MemoryManagement_IRQn 0 */while (1){/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 *//* USER CODE END W1_MemoryManagement_IRQn 0 */}}
/*** @brief This function handles Prefetch fault, memory access fault.*/void BusFault_Handler(void){/* USER CODE BEGIN BusFault_IRQn 0 */
/* USER CODE END BusFault_IRQn 0 */while (1){/* USER CODE BEGIN W1_BusFault_IRQn 0 *//* USER CODE END W1_BusFault_IRQn 0 */}}
/*** @brief This function handles Undefined instruction or illegal state.*/void UsageFault_Handler(void){/* USER CODE BEGIN UsageFault_IRQn 0 */
/* USER CODE END UsageFault_IRQn 0 */while (1){/* USER CODE BEGIN W1_UsageFault_IRQn 0 *//* USER CODE END W1_UsageFault_IRQn 0 */}}
/*** @brief This function handles System service call via SWI instruction.*/void SVC_Handler(void){/* USER CODE BEGIN SVCall_IRQn 0 */
/* USER CODE END SVCall_IRQn 0 *//* USER CODE BEGIN SVCall_IRQn 1 */
/* USER CODE END SVCall_IRQn 1 */}
/*** @brief This function handles Debug monitor.*/void DebugMon_Handler(void){/* USER CODE BEGIN DebugMonitor_IRQn 0 */
/* USER CODE END DebugMonitor_IRQn 0 *//* USER CODE BEGIN DebugMonitor_IRQn 1 */
/* USER CODE END DebugMonitor_IRQn 1 */}
/*** @brief This function handles Pendable request for system service.*/void PendSV_Handler(void){/* USER CODE BEGIN PendSV_IRQn 0 */
/* USER CODE END PendSV_IRQn 0 *//* USER CODE BEGIN PendSV_IRQn 1 */
/* USER CODE END PendSV_IRQn 1 */}
/*** @brief This function handles System tick timer.*/void SysTick_Handler(void){/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */HAL_IncTick();/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */}
/******************************************************************************//* STM32L4xx Peripheral Interrupt Handlers *//* Add here the Interrupt Handlers for the used peripherals. *//* For the available peripheral interrupt handler names, *//* please refer to the startup file (startup_stm32l4xx.s). *//******************************************************************************/
/*** @brief This function handles DMA1 channel1 global interrupt.*/void DMA1_Channel1_IRQHandler(void){/* USER CODE BEGIN DMA1_Channel1_IRQn 0 */
if(LL_DMA_IsActiveFlag_TC1(DMA1) == 1){/* Clear flag DMA transfer complete */LL_DMA_ClearFlag_TC1(DMA1);
AdcDmaTransferComplete_Callback();/* Call interruption treatment function */
/* USER CODE END DMA1_Channel1_IRQn 0 */
/* USER CODE BEGIN DMA1_Channel1_IRQn 1 */
/* USER CODE END DMA1_Channel1_IRQn 1 */}
/*** @brief This function handles ADC1 and ADC2 interrupts.*/void ADC1_2_IRQHandler(void){/* USER CODE BEGIN ADC1_2_IRQn 0 */if(LL_ADC_IsActiveFlag_OVR(ADC1) != 0){/* Clear flag ADC group regular overrun */LL_ADC_ClearFlag_OVR(ADC1);
/* Call interruption treatment function */AdcGrpRegularOverrunError_Callback();}/* USER CODE END ADC1_2_IRQn 0 */
/* USER CODE BEGIN ADC1_2_IRQn 1 */
/* USER CODE END ADC1_2_IRQn 1 */}
/*** @brief This function handles DMA2 channel1 global interrupt.*/void DMA2_Channel1_IRQHandler(void){/* USER CODE BEGIN DMA2_Channel1_IRQn 0 */
/* USER CODE END DMA2_Channel1_IRQn 0 */HAL_DMA_IRQHandler(&hdma_sai1_a);/* USER CODE BEGIN DMA2_Channel1_IRQn 1 */
/* USER CODE END DMA2_Channel1_IRQn 1 */}
/*** @brief This function handles DMA2 channel5 global interrupt.*/void DMA2_Channel5_IRQHandler(void){/* USER CODE BEGIN DMA2_Channel5_IRQn 0 */
if(LL_DMA_IsActiveFlag_TC5(DMA2) == 1){LL_DMA_DisableChannel(DMA2,LL_DMA_CHANNEL_5);LL_DMA_ClearFlag_TC5(DMA2);DMA2_Channel5->CNDTR = ADC_CONVERTED_DATA_BUFFER_SIZE;LL_DMA_EnableChannel(DMA2,LL_DMA_CHANNEL_5);}
if(LL_DMA_IsActiveFlag_TE5(DMA2) == 1){LL_GPIO_SetOutputPin(GPIOB,LL_GPIO_PIN_2);}
/* USER CODE END DMA2_Channel5_IRQn 0 */
/* USER CODE BEGIN DMA2_Channel5_IRQn 1 */
/* USER CODE END DMA2_Channel5_IRQn 1 */}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 *//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
but I don't think anyone is going to plough through all that!
What have you done to find the source of the problem(s)?
You've mentioned a number of processes going on in your code - what have you done to make sure each one is working in isolation?
Have you started with ST's examples to make sure you are using the peripherals correctly?
shivphad said:I am taking ADC input from a DSO
What do you mean by that?
"DSO" usually means "Digital Storage Oscilloscope".
shivphad said:The data that I am receiving on the DSO is very distorted
Again, what do you mean by that?