FMAC 디지털 필터 구현 방법 (with the STM32 G4 MCU Package)(6) - 3p3z code

Configuring the FMAC

FMAC 레지스터에 액세스하기 전에 FMAC 클록을 활성화해야 합니다.

__HAL_RCC_FMAC_CLK_ENABLE();
 

A 및 B 계수를 위해 시스템 메모리 영역을 할당해야 합니다.

/* Array of filter coefficients A (feedback taps) in Q1.15 format */
static int16_t aFilterCoeffA[COEFF_VECTOR_A_SIZE] = {A1,A2,A3};
/* Array of filter coefficients B (feed-forward taps) in Q1.15 format */
static int16_t aFilterCoeffB[COEFF_VECTOR_B_SIZE] = {-B0,-B1,-B2,-B3};
 

Note : B 계수는 부정됩니다. 이것은 ADC가 변환 결과에서 오프셋(기준)을 뺀다는 사실을 보상하기 위한 것입니다. 즉, 보상기 입력의 오류 신호가 반전된다는 의미입니다.

 

또한 FMAC 매개변수를 포함하는 구조체를 선언해야 합니다.

FMAC_HandleTypeDef hfmac;
 

먼저 인터럽트 컨트롤러(NVIC)에서 FMAC 인터럽트를 활성화합니다.

HAL_NVIC_SetPriority(FMAC_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(FMAC_IRQn);
 

이제 HAL_FMAC_FilterConfig() 함수를 사용하여 FMAC를 구성할 수 있습니다.

/* declare a filter configuration structure */
FMAC_FilterConfigTypeDef sFmacConfig;
/* Set the coefficient buffer base address */
sFmacConfig.CoeffBaseAddress = 0;
/* Set the coefficient buffer size to the number of coeffs */
sFmacConfig.CoeffBufferSize = 7;
/* Set the Input buffer base address to the next free address */
sFmacConfig.InputBaseAddress = 7;
/* Set the input buffer size greater than the number of coeffs */
sFmacConfig.InputBufferSize = 5;
/* Set the input watermark to zero since we are using DMA */
sFmacConfig.InputThreshold = 0;
/* Set the Output buffer base address to the next free address */
sFmacConfig.OutputBaseAddress = 12;
/* Set the output buffer size greater than the number of coeffs */
sFmacConfig.OutputBufferSize = 5;
/* Set the output watermark to zero since we are using DMA */
sFmacConfig.OutputThreshold = 0;
/* Pointer to A coefficients in memory*/
sFmacConfig.pCoeffA = aFilterCoeffA;
/* Number of A coefficients */
sFmacConfig.CoeffASize = 3;
/* Pointer to Bcoefficients in memory */
sFmacConfig.pCoeffB = aFilterCoeffB;
/* Number of B coefficients */
sFmacConfig.CoeffBSize = 4;
/* Select IIR filter function */
sFmacConfig.Filter = FMAC_FUNC_IIR_DIRECT_FORM_1;
/* Disable DMA and interrupt requests for input */
sFmacConfig.InputAccess = FMAC_BUFFER_ACCESS_NONE;
/* Enable FMAC read interrupt for output transfer */
sFmacConfig.OutputAccess = FMAC_BUFFER_ACCESS_IT;
/* Enable clipping of the output at 0x7FFF and 0x8000 */
sFmacConfig.Clip = FMAC_CLIP_ENABLED;
/* P parameter contains number of B coefficients */
sFmacConfig.P = 4;
/* Q parameter contains number of A coefficients */
sFmacConfig.Q = 3;
/* R parameter contains the post-shift value */
sFmacConfig.R = KC_shift;
/* Configure the FMAC */
if (HAL_FMAC_FilterConfig(&hfmac, &sFmacConfig) != HAL_OK)
  /* Configuration Error */
  Error_Handler();
 

HAL_FMAC_FilterConfig() 함수는 구성 및 제어 레지스터를 프로그래밍하고 계수를 FMAC 로컬 메모리(X2 버퍼)에 로드합니다.

 

처음부터 예측할 수 없는 일시적인 값을 피하기 위해 Y 버퍼에 0을 미리 로드하는 것이 좋습니다.

/* Array of output data to preload in Q1.15 format */
static int16_t aOutputDataToPreload[COEFF_VECTOR_A_SIZE] = {0x0000, 0x0000, 0x0000};
 
if (HAL_FMAC_FilterPreload(&hfmac, NULL, INPUT_BUFFER_SIZE, aOutputDataToPreload, COEFF_VECTOR_A_SIZE) != HAL_OK)
  /* Configuration Error */
  Error_Handler();
 

Configuring the ADC

ADC는 STM32Cube-FW-G4 패키지의 HAL 드라이버를 사용하여 구성할 수 있습니다.

ADC 레지스터에 액세스하기 전에 ADC 클럭을 활성화해야 합니다.

__HAL_RCC_ADC12_CLK_ENABLE();
 

ADC 매개변수에 대한 구조체를 만들고 HAL_ADC_Init() 함수를 사용하여 초기화합니다.

ADC_HandleTypeDef hadc1;
hadc1.Instance = ADC1;
/* ADC max allowed clock frequency is 80MHz */
/* ADC clock configured to use asynchronous clock */
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
/* 12-bit samples */
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
/* left aligned data equivalent to 8dB gain */
hadc1.Init.DataAlign = ADC_DATAALIGN_LEFT;
hadc1.Init.GainCompensation = 0;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DiscontinuousConvMode = DISABLE;
/* Conversion triggered by HRTimer Trig 1 rising edge */
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_HRTIM_TRG1;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
/* Allow ADC to generate DMA requests after every conversion */
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc1.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
  _Error_Handler();
 

ADC 채널 구성을 위한 구조체를 선언해야 합니다.

ADC_ChannelConfTypeDef sConfig;
 

HAL_ADC_ConfigChannel() 함수를 사용하여 초기화합니다.

sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
/* Enable offset feature */
sConfig.OffsetNumber = ADC_OFFSET_1;
/* Set offset to fixed reference */
sConfig.Offset = REF;
/* Offset is subtracted from conversion result */
sConfig.OffsetSign = ADC_OFFSET_SIGN_NEGATIVE;
sConfig.OffsetSaturation = DISABLE;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  _Error_Handler();
 

오프셋은 참조입니다. 코드에서 상수로 정의됩니다. #define REF (778)

다음으로 ADC 출력에서 FMAC 입력으로 오류 값을 전송하는 데 사용할 DMA 채널 구조체를 선언하고 초기화합니다.

DMA_HandleTypeDef hdma_adc;
hdma_adc.Instance = DMA1_Channel1;
/* DMA request comes from the ADC */
hdma_adc.Init.Request = DMA_REQUEST_ADC1;
/* Peripheral (source) is ADC; Memory (dest) is FMAC */
hdma_adc.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc.Init.MemInc = DMA_MINC_DISABLE;
hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_adc.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_adc.Init.Mode = DMA_CIRCULAR;
hdma_adc.Init.Priority = DMA_PRIORITY_HIGH;
if (HAL_DMA_Init(&hdma_adc) != HAL_OK)
  Error_Handler();
/* Connect the DMA channel to the ADC structure */
__HAL_LINKDMA(&hadc1,DMA_Handle,hdma_adc);
 

Configuring the high-resolution timer

먼저 HR 타이머 매개변수에 대한 구조체를 만듭니다.

 

HRTIM_HandleTypeDef hhrtim1;
 

HRTimer는 200kHz에서 차동 PWM 신호를 생성하도록 구성됩니다. 32 x 170MHz = 5440MHz에서 실행되는 하나의 카운터인 타이머 A가 구성됩니다.

HRTIM_TimeBaseCfgTypeDef pTimeBaseCfg;
pTimeBaseCfg.Period = 27200; /* 200kHz (32 * 170 / 0.2 = 27200) */
pTimeBaseCfg.RepetitionCounter = 0x00;
pTimeBaseCfg.PrescalerRatio = HRTIM_PRESCALERRATIO_MUL32;
pTimeBaseCfg.Mode = HRTIM_MODE_CONTINUOUS;
if (HAL_HRTIM_TimeBaseConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_C, &pTimeBaseCfg) != HAL_OK)
  _Error_Handler();
 

다음을 위해 각각 사용되는 세 개의 비교 단위를 활성화해야 합니다.

1. 듀티 사이클 적용 듀티 사이클은 0으로 초기화할 수 있습니다. 컨트롤러의 출력으로 PWM 주기마다 업데이트됩니다.

HRTIM_CompareCfgTypeDef pCompareCfg;
pCompareCfg.CompareValue = 0; /* Will be updated */
pCompareCfg.AutoDelayedMode = HRTIM_AUTODELAYEDMODE_REGULAR;
if (HAL_HRTIM_WaveformCompareConfig(&hhrtim1,
HRTIM_TIMERINDEX_TIMER_C, HRTIM_COMPAREUNIT_1, &pCompareCfg) != HAL_OK)
  _Error_Handler();
 

2. ADC 변환 트리거

최대 듀티 사이클은 Compensator output and PWM duty cycle 섹션에 정의되어 있습니다.

pCompareCfg.CompareValue = DUTY_TICKS_MAX; /* Max duty cycle */
if (HAL_HRTIM_WaveformCompareConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_C, 
HRTIM_COMPAREUNIT_2, &pCompareCfg) != HAL_OK)
  _Error_Handler();
 

3. ADC 변환 트리거

이 예에서 ADC는 PWM 사이클이 시작될 때 트리거됩니다. 이것은 컨트롤러가 한 샘플 주기의 시간 지연을 도입하여 0dB 교차 주파수에서 18도의 위상 침식을 초래함을 의미합니다.

 

위상 마진을 개선하기 위해 컨트롤러를 실행하는 데 걸리는 시간에 따라 ADC가 나중에 트리거될 수 있습니다.

pCompareCfg.CompareValue = 60;
if (HAL_HRTIM_WaveformCompareConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_C, HRTIM_COMPAREUNIT_3, &pCompareCfg) != HAL_OK)
 _ Error_Handler();
 

비교기 3은 ADC 트리거에 연결됩니다.

HRTIM_ADCTriggerCfgTypeDef pADCTriggerCfg;
pADCTriggerCfg.UpdateSource = HRTIM_ADCTRIGGERUPDATE_TIMER_C;
pADCTriggerCfg.Trigger = HRTIM_ADCTRIGGEREVENT13_TIMERC_CMP3;
if (HAL_HRTIM_ADCTriggerConfig(&hhrtim1, HRTIM_ADCTRIGGER_1, &pADCTriggerCfg) != HAL_OK)
  _Error_Handler();
 

하이사이드 및 로우사이드 스위치를 구동하려면 보완 출력이 필요합니다.

 

75 x 8 PWM 클록 사이클(110ns)의 데드 타임이 2차측의 하강 에지와 1차측의 상승 에지 사이에 삽입되고 300 x 8 사이클(441ns)이 1차측의 하강 에지와 상승 에지 사이에 삽입됩니다.

 

이 값은 디스커버리 키트에 대해 특별히 선택된 값입니다.

HRTIM_TimerCfgTypeDef pTimerCfg;
HRTIM_DeadTimeCfgTypeDef pDeadTimeCfg;
/* Enable complementary outputs with dead time insertion */
pTimerCfg.DeadTimeInsertion = HRTIM_TIMDEADTIMEINSERTION_ENABLED;
if (HAL_HRTIM_WaveformTimerConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_C, &pTimerCfg) != HAL_OK)
  _Error_Handler();
pDeadTimeCfg.Prescaler = HRTIM_TIMDEADTIME_PRESCALERRATIO_MUL8;
pDeadTimeCfg.RisingValue = 75;
pDeadTimeCfg.RisingSign = HRTIM_TIMDEADTIME_RISINGSIGN_POSITIVE;
pDeadTimeCfg.RisingLock = HRTIM_TIMDEADTIME_RISINGLOCK_WRITE;
pDeadTimeCfg.RisingSignLock =
HRTIM_TIMDEADTIME_RISINGSIGNLOCK_WRITE;
pDeadTimeCfg.FallingValue = 300;
pDeadTimeCfg.FallingSign = HRTIM_TIMDEADTIME_FALLINGSIGN_POSITIVE;
pDeadTimeCfg.FallingLock = HRTIM_TIMDEADTIME_FALLINGLOCK_WRITE;
pDeadTimeCfg.FallingSignLock =
HRTIM_TIMDEADTIME_FALLINGSIGNLOCK_WRITE;
if (HAL_HRTIM_DeadTimeConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_C, &pDeadTimeCfg) != HAL_OK)
  _Error_Handler();
 

카운터가 주기 카운트에 도달하고 0으로 돌아갈 때 하이 사이드 스위치 출력이 설정되도록 프로그래밍됩니다. 카운터가 듀티 사이클 카운트(비교기 1) 또는 최대 듀티 사이클(비교기 2)에 도달하면 리셋됩니다.

HRTIM_OutputCfgTypeDef pOutputCfg;
pOutputCfg.Polarity = HRTIM_OUTPUTPOLARITY_HIGH;
/* Set the PWM output with the period count */
pOutputCfg.SetSource = HRTIM_OUTPUTSET_TIMPER;
/* Reset the PWM output with comparator 1 or 2 */
pOutputCfg.ResetSource = HRTIM_OUTPUTRESET_TIMCMP1|HRTIM_OUTPUTRESET_TIMCMP2;
pOutputCfg.IdleMode = HRTIM_OUTPUTIDLEMODE_NONE;
pOutputCfg.IdleLevel = HRTIM_OUTPUTIDLELEVEL_INACTIVE;
pOutputCfg.FaultLevel = HRTIM_OUTPUTFAULTLEVEL_INACTIVE;
pOutputCfg.ChopperModeEnable = HRTIM_OUTPUTCHOPPERMODE_DISABLED;
pOutputCfg.BurstModeEntryDelayed =
HRTIM_OUTPUTBURSTMODEENTRY_REGULAR;
/* Configure the high side output */
if (HAL_HRTIM_WaveformOutputConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_C, HRTIM_OUTPUT_TC1, &pOutputCfg) != HAL_OK)
  _Error_Handler();
 

로우 사이드 스위치 출력은 하이 사이드의 역이므로 데드 타임이 허용됩니다. 이것은 데드 타임이 활성화되면 자동으로 선택되므로 소스를 설정하고 재설정할 필요가 없습니다.

pOutputCfg.SetSource = HRTIM_OUTPUTSET_NONE;
pOutputCfg.ResetSource = HRTIM_OUTPUTRESET_NONE;
/* configure the low side output */
if (HAL_HRTIM_WaveformOutputConfig(&hhrtim1, HRTIM_TIMERINDEX_TIMER_C, HRTIM_OUTPUT_TC2, &pOutputCfg) != HAL_OK)
  _Error_Handler();
 

Starting the controller

FMAC를 시작하기 전에 FMAC 출력을 위해 예약된 메모리 공간의 크기와 위치를 포함하는 두 개의 HAL 드라이버 매개변수를 더 정의해야 합니다. 이 경우 크기는 1이고 위치는 16비트 더미 변수입니다.

 

실제로 이것은 인터럽트 핸들러가 데이터 전송을 직접 수행하기 때문에 사용되지 않습니다(Compensator output and PWM duty cycle 섹션 참조).

uint16_t ExpectedCalculatedOutputSize = (uint16_t) 1;
int16_t Fmac_output;
 

이제 FMAC를 시작할 수 있습니다.

if (HAL_FMAC_FilterStart(&hfmac,&Fmac_output, &ExpectedCalculatedOutputSize) != HAL_OK)
  Error_Handler();
 

다음으로 ADC를 보정(Calibration)하고 시작합니다.

/* A pointer to the FMAC input data register for the DMA */
uint32_t *Fmac_Wdata;
Fmac_Wdata = (uint32_t *) FMAC_WDATA;
/* Do an auto-calibration */
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
/* Start the ADC in DMA mode */
HAL_ADC_Start_DMA(&hadc1,Fmac_Wdata,1);
 

마지막으로 HRTimer를 시작합니다.

HAL_HRTIM_WaveformOutputStart(&hhrtim1, HRTIM_OUTPUT_TC1 | HRTIM_OUTPUT_TC2);
HAL_HRTIM_WaveformCounterStart(&hhrtim1, HRTIM_TIMERID_TIMER_C);
 

<계속>