/** ****************************************************************************** * @file r3_f0xx_pwm_curr_fdbk.c * @author Motor Control SDK Team, ST Microelectronics * @brief This file provides firmware functions that implement current sensor * class to be stantiated when the three shunts current sensing * topology is used. * * It is specifically designed for STM32F0XX * microcontrollers and implements the successive sampling of two motor * current using only one ADC. * + MCU peripheral and handle initialization fucntion * + three shunt current sensing * + space vector modulation function * + ADC sampling function * ****************************************************************************** * @attention * *

© Copyright (c) 2024 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under Ultimate Liberty license * SLA0044, the "License"; You may not use this file except in compliance with * the License. You may obtain a copy of the License at: * www.st.com/SLA0044 * ****************************************************************************** * @ingroup R3_F0XX_pwm_curr_fdbk */ /* Includes ------------------------------------------------------------------*/ #include "r3_f0xx_pwm_curr_fdbk.h" #include "pwm_common.h" #include "mc_type.h" /** @addtogroup MCSDK * @{ */ /** @addtogroup pwm_curr_fdbk * @{ */ /** @addtogroup R3_1_pwm_curr_fdbk * @{ */ /* Private defines -----------------------------------------------------------*/ #define TIMxCCER_MASK_CH123 (LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH1N | LL_TIM_CHANNEL_CH2|LL_TIM_CHANNEL_CH2N |\ LL_TIM_CHANNEL_CH3 | LL_TIM_CHANNEL_CH3N) /* Private function prototypes -----------------------------------------------*/ void R3_1_HFCurrentsCalibrationAB( PWMC_Handle_t * pHdl, ab_t * pStator_Currents ); void R3_1_HFCurrentsCalibrationC( PWMC_Handle_t * pHdl, ab_t * pStator_Currents ); uint16_t R3_1_WriteTIMRegisters( PWMC_Handle_t * pHdl, uint16_t hCCR4Reg ); /* Private functions ---------------------------------------------------------*/ /* * @brief Initializes TIM1, ADC1, GPIO, DMA1 and NVIC for three shunt current * reading configuration using STM32F0X. * * @param pHandle: Handler of the current instance of the PWM component. */ __weak void R3_1_Init( PWMC_R3_1_Handle_t * pHandle ) { if ( ( uint32_t )pHandle == ( uint32_t )&pHandle->_Super ) { /* disable IT and flags in case of LL driver usage * workaround for unwanted interrupt enabling done by LL driver */ LL_ADC_DisableIT_EOC( ADC1 ); LL_ADC_ClearFlag_EOC( ADC1 ); LL_ADC_DisableIT_EOS( ADC1 ); LL_ADC_ClearFlag_EOS( ADC1 ); /* Enable the CCS */ LL_RCC_HSE_EnableCSS(); /* Peripheral clocks enabling END ----------------------------------------*/ /* Clear TIMx break flag. */ LL_TIM_ClearFlag_BRK( TIM1 ); LL_TIM_EnableIT_BRK( TIM1 ); LL_TIM_SetCounter( TIM1, ( uint32_t )( pHandle->Half_PWMPeriod ) - 1u ); /* TIM1 Counter Clock stopped when the core is halted */ LL_APB1_GRP2_EnableClock (LL_APB1_GRP2_PERIPH_DBGMCU); LL_DBGMCU_APB1_GRP2_FreezePeriph( LL_DBGMCU_APB1_GRP2_TIM1_STOP ); /* ADC Calibration */ LL_ADC_StartCalibration( ADC1 ); while ((LL_ADC_IsCalibrationOnGoing(ADC1) == SET) || (LL_ADC_REG_IsConversionOngoing(ADC1) == SET) || (LL_ADC_REG_IsStopConversionOngoing(ADC1) == SET) || (LL_ADC_IsDisableOngoing(ADC1) == SET)) { /* wait */ } /* Enables the ADC peripheral */ LL_ADC_Enable( ADC1 ); /* Wait ADC Ready */ while ( LL_ADC_IsActiveFlag_ADRDY( ADC1 ) == RESET ) { /* wait */ } /* DMA1 Channel1 Config */ LL_DMA_SetMemoryAddress( DMA1, LL_DMA_CHANNEL_1, ( uint32_t )pHandle->ADC1_DMA_converted ); LL_DMA_SetPeriphAddress( DMA1, LL_DMA_CHANNEL_1, ( uint32_t )&ADC1->DR ); LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 3); /* Enables the DMA1 Channel1 peripheral */ LL_DMA_EnableChannel( DMA1, LL_DMA_CHANNEL_1 ); /* disable ADC source trigger */ LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_RESET); /* Enable PWM channel */ LL_TIM_CC_EnableChannel( TIM1, TIMxCCER_MASK_CH123 ); /* set default triggering edge */ pHandle->ADCTriggerEdge = LL_ADC_REG_TRIG_EXT_RISING; /* We allow ADC usage for regular conversion on Systick*/ pHandle->ADCRegularLocked=false; LL_TIM_EnableCounter( TIM1 ); } } /* * @brief Stores in @p pHdl handler the calibrated @p offsets. * */ __weak void R3_1_SetOffsetCalib(PWMC_Handle_t *pHdl, PolarizationOffsets_t *offsets) { PWMC_R3_1_Handle_t *pHandle = (PWMC_R3_1_Handle_t *)pHdl; //cstat !MISRAC2012-Rule-11.3 pHandle->PhaseAOffset = offsets->phaseAOffset; pHandle->PhaseBOffset = offsets->phaseBOffset; pHandle->PhaseCOffset = offsets->phaseCOffset; pHdl->offsetCalibStatus = true; } /* * @brief Reads the calibrated @p offsets stored in @p pHdl. * */ __weak void R3_1_GetOffsetCalib(PWMC_Handle_t *pHdl, PolarizationOffsets_t *offsets) { PWMC_R3_1_Handle_t *pHandle = (PWMC_R3_1_Handle_t *)pHdl; //cstat !MISRAC2012-Rule-11.3 offsets->phaseAOffset = pHandle->PhaseAOffset; offsets->phaseBOffset = pHandle->PhaseBOffset; offsets->phaseCOffset = pHandle->PhaseCOffset; } /* * @brief Stores into the @p pHdl handler the voltage present on Ia and * Ib current feedback analog channels when no current is flowing into the * motor. * */ __weak void R3_1_CurrentReadingCalibration( PWMC_Handle_t * pHdl ) { #if defined (__ICCARM__) #pragma cstat_disable = "MISRAC2012-Rule-11.3" #endif PWMC_R3_1_Handle_t * pHandle = ( PWMC_R3_1_Handle_t * )pHdl; #if defined (__ICCARM__) #pragma cstat_restore = "MISRAC2012-Rule-11.3" #endif TIM_TypeDef * TIMx = TIM1; volatile PWMC_GetPhaseCurr_Cb_t GetPhaseCurrCbSave; volatile PWMC_SetSampPointSectX_Cb_t SetSampPointSectXCbSave; if (false == pHandle->_Super.offsetCalibStatus) { /* Save callback routines */ GetPhaseCurrCbSave = pHandle->_Super.pFctGetPhaseCurrents; SetSampPointSectXCbSave = pHandle->_Super.pFctSetADCSampPointSectX; pHandle->PhaseAOffset = 0u; pHandle->PhaseBOffset = 0u; pHandle->PhaseCOffset = 0u; pHandle->PolarizationCounter = 0u; /* It forces inactive level on TIMx CHy and CHyN */ LL_TIM_CC_DisableChannel( TIMx, TIMxCCER_MASK_CH123 ); /* Offset calibration for A B c phases */ /* Change function to be executed in ADCx_ISR */ pHandle->_Super.pFctGetPhaseCurrents = &R3_1_HFCurrentsCalibrationAB; pHandle->_Super.pFctSetADCSampPointSectX = &R3_1_SetADCSampPointCalibration; pHandle->PolarizationSector = SECTOR_5; /* Mandatory to force first polarization conversion on SECTOR_5*/ pHandle->_Super.Sector = SECTOR_5; R3_1_SwitchOnPWM( &pHandle->_Super ); /* Wait for NB_CONVERSIONS to be executed */ waitForPolarizationEnd( TIMx, &pHandle->_Super.SWerror, pHandle->pParams_str->RepetitionCounter, &pHandle->PolarizationCounter ); R3_1_SwitchOffPWM( &pHandle->_Super ); pHandle->_Super.pFctGetPhaseCurrents = &R3_1_HFCurrentsCalibrationC; pHandle->PolarizationSector = SECTOR_1; /* Mandatory to force first polarization conversion on SECTOR_1*/ pHandle->_Super.Sector = SECTOR_1; pHandle->PolarizationCounter = 0; R3_1_SwitchOnPWM( &pHandle->_Super ); /* Wait for NB_CONVERSIONS to be executed */ waitForPolarizationEnd( TIMx, &pHandle->_Super.SWerror, pHandle->pParams_str->RepetitionCounter, &pHandle->PolarizationCounter ); R3_1_SwitchOffPWM( &pHandle->_Super ); /* Mean Value of PhaseCurrents Offset calculation by 4bit shifting operation * instead division by NB_CONVERSIONS value fixed to 16. */ pHandle->PhaseAOffset = pHandle->PhaseAOffset / NB_CONVERSIONS; pHandle->PhaseBOffset = pHandle->PhaseBOffset / NB_CONVERSIONS; pHandle->PhaseCOffset = pHandle->PhaseCOffset / NB_CONVERSIONS; if (0U == pHandle->_Super.SWerror) { pHandle->_Super.offsetCalibStatus = true; } else { /* nothing to do */ } /* Restore functions to be executed in ADCx_ISR */ pHandle->_Super.pFctGetPhaseCurrents = GetPhaseCurrCbSave; pHandle->_Super.pFctSetADCSampPointSectX = SetSampPointSectXCbSave; } /* It over write TIMx CCRy wrongly written by FOC during calibration so as to force 50% duty cycle on the three inverer legs */ /* Disable TIMx preload */ LL_TIM_OC_SetCompareCH1 (TIMx, pHandle->Half_PWMPeriod >> 1u); LL_TIM_OC_SetCompareCH2 (TIMx, pHandle->Half_PWMPeriod >> 1u); LL_TIM_OC_SetCompareCH3 (TIMx, pHandle->Half_PWMPeriod >> 1u); /* generate COM event to apply new CC values */ LL_TIM_GenerateEvent_COM( TIMx ); /* It re-enable drive of TIMx CHy and CHyN by TIMx CHyRef*/ LL_TIM_CC_EnableChannel( TIMx, TIMxCCER_MASK_CH123 ); pHandle->_Super.Sector = SECTOR_5; pHandle->_Super.BrakeActionLock = false; } /* * @brief Computes and stores in @p pHdl handler the latest converted motor phase currents in @p pStator_Currents ab_t format. * */ __weak void R3_1_GetPhaseCurrents( PWMC_Handle_t * pHdl, ab_t * pStator_Currents ) { #if defined (__ICCARM__) #pragma cstat_disable = "MISRAC2012-Rule-11.3" #endif PWMC_R3_1_Handle_t * pHandle = ( PWMC_R3_1_Handle_t * )pHdl; #if defined (__ICCARM__) #pragma cstat_restore = "MISRAC2012-Rule-11.3" #endif int32_t wAux; uint16_t hReg1; uint16_t hReg2; uint8_t bSector; /* disable ADC trigger source */ LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_RESET); bSector = ( uint8_t ) pHandle->_Super.Sector; LL_ADC_REG_SetSequencerScanDirection(ADC1, LL_ADC_REG_SEQ_SCAN_DIR_FORWARD); hReg1 = *pHandle->pParams_str->ADCDataReg1[bSector]; hReg2 = *pHandle->pParams_str->ADCDataReg2[bSector]; switch ( bSector ) { case SECTOR_4: case SECTOR_5: /* Current on Phase C is not accessible */ /* Ia = PhaseAOffset - ADC converted value) ------------------------------*/ wAux = ( int32_t )( pHandle->PhaseAOffset ) - ( int32_t )( hReg1 ); /* Saturation of Ia */ if ( wAux < -INT16_MAX ) { pStator_Currents->a = -INT16_MAX; } else if ( wAux > INT16_MAX ) { pStator_Currents->a = INT16_MAX; } else { pStator_Currents->a = ( int16_t )wAux; } /* Ib = PhaseBOffset - ADC converted value) ------------------------------*/ wAux = ( int32_t )( pHandle->PhaseBOffset ) - ( int32_t )( hReg2 ); /* Saturation of Ib */ if ( wAux < -INT16_MAX ) { pStator_Currents->b = -INT16_MAX; } else if ( wAux > INT16_MAX ) { pStator_Currents->b = INT16_MAX; } else { pStator_Currents->b = ( int16_t )wAux; } break; case SECTOR_6: case SECTOR_1: /* Current on Phase A is not accessible */ /* Ib = (PhaseBOffset - ADC converted value) ------------------------------*/ wAux = ( int32_t )( pHandle->PhaseBOffset ) - ( int32_t )( hReg1 ); /* Saturation of Ib */ if ( wAux < -INT16_MAX ) { pStator_Currents->b = -INT16_MAX; } else if ( wAux > INT16_MAX ) { pStator_Currents->b = INT16_MAX; } else { pStator_Currents->b = ( int16_t )wAux; } /* Ic = PhaseCOffset - ADC converted value) */ /* Ia = -Ic -Ib */ wAux = ( int32_t )( pHandle->PhaseCOffset ) - ( int32_t )( hReg2 ); wAux = -wAux - ( int32_t )pStator_Currents->b; /* Saturation of Ia */ if ( wAux > INT16_MAX ) { pStator_Currents->a = INT16_MAX; } else if ( wAux < -INT16_MAX ) { pStator_Currents->a = -INT16_MAX; } else { pStator_Currents->a = ( int16_t )wAux; } break; case SECTOR_2: case SECTOR_3: /* Current on Phase B is not accessible */ /* Ia = PhaseAOffset - ADC converted value) ------------------------------*/ wAux = ( int32_t )( pHandle->PhaseAOffset ) - ( int32_t )( hReg1 ); /* Saturation of Ia */ if ( wAux < -INT16_MAX ) { pStator_Currents->a = -INT16_MAX; } else if ( wAux > INT16_MAX ) { pStator_Currents->a = INT16_MAX; } else { pStator_Currents->a = ( int16_t )wAux; } /* Ic = PhaseCOffset - ADC converted value) ------------------------------*/ wAux = ( int32_t )( pHandle->PhaseCOffset ) - ( int32_t )( hReg2 ); /* Ib = -Ic -Ia */ wAux = -wAux - ( int32_t )pStator_Currents->a; /* Ib */ /* Saturation of Ib */ if ( wAux > INT16_MAX ) { pStator_Currents->b = INT16_MAX; } else if ( wAux < -INT16_MAX ) { pStator_Currents->b = -INT16_MAX; } else { pStator_Currents->b = ( int16_t )wAux; } break; default: break; } pHandle->_Super.Ia = pStator_Currents->a; pHandle->_Super.Ib = pStator_Currents->b; pHandle->_Super.Ic = -pStator_Currents->a - pStator_Currents->b; } /* * @brief Computes and stores in @p pHdl handler the latest converted motor phase currents in @p Iab ab_t format. Specific to overmodulation. * */ __weak void R3_1_GetPhaseCurrents_OVM( PWMC_Handle_t * pHdl, ab_t * Iab ) { #if defined (__ICCARM__) #pragma cstat_disable = "MISRAC2012-Rule-11.3" #endif /* __ICCARM__ */ PWMC_R3_1_Handle_t * pHandle = ( PWMC_R3_1_Handle_t * )pHdl; #if defined (__ICCARM__) #pragma cstat_restore = "MISRAC2012-Rule-11.3" #endif /* __ICCARM__ */ uint8_t Sector; int32_t Aux; uint32_t ADCDataReg1; uint32_t ADCDataReg2; /* disable ADC trigger source */ LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_RESET); LL_ADC_REG_SetSequencerScanDirection(ADC1, LL_ADC_REG_SEQ_SCAN_DIR_FORWARD); Sector = ( uint8_t )pHandle->_Super.Sector; ADCDataReg1 = *pHandle->pParams_str->ADCDataReg1[Sector]; ADCDataReg2 = *pHandle->pParams_str->ADCDataReg2[Sector]; switch ( Sector ) { case SECTOR_4: /* Current on Phase C is not accessible */ /* Ia = PhaseAOffset - ADC converted value) */ Aux = ( int32_t )( pHandle->PhaseAOffset ) - ( int32_t )( ADCDataReg1 ); /* Saturation of Ia */ if ( Aux < -INT16_MAX ) { Iab->a = -INT16_MAX; } else if ( Aux > INT16_MAX ) { Iab->a = INT16_MAX; } else { Iab->a = ( int16_t )Aux; } if (pHandle->_Super.useEstCurrent == true) { // Ib not available, use estimated Ib Aux = ( int32_t )( pHandle->_Super.IbEst ); } else { /* Ib = PhaseBOffset - ADC converted value) */ Aux = ( int32_t )( pHandle->PhaseBOffset ) - ( int32_t )( ADCDataReg2 ); } /* Saturation of Ib */ if ( Aux < -INT16_MAX ) { Iab->b = -INT16_MAX; } else if ( Aux > INT16_MAX ) { Iab->b = INT16_MAX; } else { Iab->b = ( int16_t )Aux; } break; case SECTOR_5: /* Current on Phase C is not accessible */ /* Ia = PhaseAOffset - ADC converted value) */ if (pHandle->_Super.useEstCurrent == true) { // Ia not available, use estimated Ia Aux = ( int32_t )( pHandle->_Super.IaEst ); } else { Aux = ( int32_t )( pHandle->PhaseAOffset ) - ( int32_t )( ADCDataReg1 ); } /* Saturation of Ia */ if ( Aux < -INT16_MAX ) { Iab->a = -INT16_MAX; } else if ( Aux > INT16_MAX ) { Iab->a = INT16_MAX; } else { Iab->a = ( int16_t )Aux; } /* Ib = PhaseBOffset - ADC converted value) */ Aux = ( int32_t )( pHandle->PhaseBOffset ) - ( int32_t )( ADCDataReg2 ); /* Saturation of Ib */ if ( Aux < -INT16_MAX ) { Iab->b = -INT16_MAX; } else if ( Aux > INT16_MAX ) { Iab->b = INT16_MAX; } else { Iab->b = ( int16_t )Aux; } break; case SECTOR_6: /* Current on Phase A is not accessible */ /* Ib = PhaseBOffset - ADC converted value) */ Aux = ( int32_t )( pHandle->PhaseBOffset ) - ( int32_t )( ADCDataReg1 ); /* Saturation of Ib */ if ( Aux < -INT16_MAX ) { Iab->b = -INT16_MAX; } else if ( Aux > INT16_MAX ) { Iab->b = INT16_MAX; } else { Iab->b = ( int16_t )Aux; } if (pHandle->_Super.useEstCurrent == true) { Aux = ( int32_t ) pHandle->_Super.IcEst ; /* -Ic */ Aux -= ( int32_t )Iab->b; } else { /* Ia = -Ic -Ib */ Aux = ( int32_t )( ADCDataReg2 ) - ( int32_t )( pHandle->PhaseCOffset ); /* -Ic */ Aux -= ( int32_t )Iab->b; /* Ia */ } /* Saturation of Ia */ if ( Aux > INT16_MAX ) { Iab->a = INT16_MAX; } else if ( Aux < -INT16_MAX ) { Iab->a = -INT16_MAX; } else { Iab->a = ( int16_t )Aux; } break; case SECTOR_1: /* Current on Phase A is not accessible */ /* Ib = PhaseBOffset - ADC converted value) */ if (pHandle->_Super.useEstCurrent == true) { Aux = ( int32_t ) pHandle->_Super.IbEst; } else { Aux = ( int32_t )( pHandle->PhaseBOffset ) - ( int32_t )( ADCDataReg1 ); } /* Saturation of Ib */ if ( Aux < -INT16_MAX ) { Iab->b = -INT16_MAX; } else if ( Aux > INT16_MAX ) { Iab->b = INT16_MAX; } else { Iab->b = ( int16_t )Aux; } /* Ia = -Ic -Ib */ Aux = ( int32_t )( ADCDataReg2 ) - ( int32_t )( pHandle->PhaseCOffset ); /* -Ic */ Aux -= ( int32_t )Iab->b; /* Ia */ /* Saturation of Ia */ if ( Aux > INT16_MAX ) { Iab->a = INT16_MAX; } else if ( Aux < -INT16_MAX ) { Iab->a = -INT16_MAX; } else { Iab->a = ( int16_t )Aux; } break; case SECTOR_2: /* Current on Phase B is not accessible */ /* Ia = PhaseAOffset - ADC converted value) */ if (pHandle->_Super.useEstCurrent == true) { Aux = ( int32_t ) pHandle->_Super.IaEst; } else { Aux = ( int32_t )( pHandle->PhaseAOffset ) - ( int32_t )( ADCDataReg1 ); } /* Saturation of Ia */ if ( Aux < -INT16_MAX ) { Iab->a = -INT16_MAX; } else if ( Aux > INT16_MAX ) { Iab->a = INT16_MAX; } else { Iab->a = ( int16_t )Aux; } /* Ib = -Ic -Ia */ Aux = ( int32_t )( ADCDataReg2 ) - ( int32_t )( pHandle->PhaseCOffset ); /* -Ic */ Aux -= ( int32_t )Iab->a; /* Ib */ /* Saturation of Ib */ if ( Aux > INT16_MAX ) { Iab->b = INT16_MAX; } else if ( Aux < -INT16_MAX ) { Iab->b = -INT16_MAX; } else { Iab->b = ( int16_t )Aux; } break; case SECTOR_3: /* Current on Phase B is not accessible */ /* Ia = PhaseAOffset - ADC converted value) */ Aux = ( int32_t )( pHandle->PhaseAOffset ) - ( int32_t )( ADCDataReg1 ); /* Saturation of Ia */ if ( Aux < -INT16_MAX ) { Iab->a = -INT16_MAX; } else if ( Aux > INT16_MAX ) { Iab->a = INT16_MAX; } else { Iab->a = ( int16_t )Aux; } if (pHandle->_Super.useEstCurrent == true) { /* Ib = -Ic -Ia */ Aux = ( int32_t ) pHandle->_Super.IcEst; /* -Ic */ Aux -= ( int32_t )Iab->a; /* Ib */ } else { /* Ib = -Ic -Ia */ Aux = ( int32_t )( ADCDataReg2 ) - ( int32_t )( pHandle->PhaseCOffset ); /* -Ic */ Aux -= ( int32_t )Iab->a; /* Ib */ } /* Saturation of Ib */ if ( Aux > INT16_MAX ) { Iab->b = INT16_MAX; } else if ( Aux < -INT16_MAX ) { Iab->b = -INT16_MAX; } else { Iab->b = ( int16_t )Aux; } break; default: break; } pHandle->_Super.Ia = Iab->a; pHandle->_Super.Ib = Iab->b; pHandle->_Super.Ic = -Iab->a - Iab->b; } /* * @brief Configures the ADC for the current sampling during calibration. * * It sets the ADC sequence length and channels, and the sampling point via TIMx_Ch4 value and polarity. * It then calls the WriteTIMRegisters method. * * @param pHdl: Handler of the current instance of the PWM component. * @retval Return value of R3_1_WriteTIMRegisters. */ __weak uint16_t R3_1_SetADCSampPointCalibration( PWMC_Handle_t * pHdl ) { #if defined (__ICCARM__) #pragma cstat_disable = "MISRAC2012-Rule-11.3" #endif PWMC_R3_1_Handle_t * pHandle = ( PWMC_R3_1_Handle_t * )pHdl; #if defined (__ICCARM__) #pragma cstat_restore = "MISRAC2012-Rule-11.3" #endif pHandle->ADCTriggerEdge = LL_ADC_REG_TRIG_EXT_RISING; pHandle->_Super.Sector = pHandle->PolarizationSector; return R3_1_WriteTIMRegisters( pHdl, ( uint16_t )( pHandle->Half_PWMPeriod ) - 1u); } /* * @brief Configures the ADC for the current sampling related to sector X (X = [1..6] ). * * Sets the ADC sequence length and channels and sets the sampling point via TIMx_Ch4 value and polarity. * The WriteTIMRegisters method is then called. * * @param pHdl: Handler of the current instance of the PWM component. * @retval uint16_t Returns the value of R3_1_WriteTIMRegisters. */ __weak uint16_t R3_1_SetADCSampPointSectX( PWMC_Handle_t * pHdl ) { #if defined (__ICCARM__) #pragma cstat_disable = "MISRAC2012-Rule-11.3" #endif PWMC_R3_1_Handle_t * pHandle = ( PWMC_R3_1_Handle_t * )pHdl; #if defined (__ICCARM__) #pragma cstat_restore = "MISRAC2012-Rule-11.3" #endif uint16_t SamplingPoint; uint16_t DeltaDuty; /* Check if sampling the AB phases in the middle of the PWM period is possible */ if ((uint16_t)(pHandle->Half_PWMPeriod - pHdl->lowDuty) > pHandle->pParams_str->Tafter) { /* When it is possible to sample in the middle of the PWM period, always sample the same phases * (AB are chosen) for all sectors in order to not induce current discontinuities when there are * differences between offsets */ /* Sector number needed by GetPhaseCurrent, phase A and B are sampled which corresponds to * sector 4 or 5 */ pHandle->_Super.Sector = SECTOR_5; /* Set the sampling point in the middle of the PWM period */ SamplingPoint = pHandle->Half_PWMPeriod - 1u; } else { /* In this case it is necessary to sample phases with maximum and variable complementary duty cycle */ /* In every sector there is always one phase with maximum complementary duty, one with minimum * complementary duty and one with variable complementary duty. In this case, phases with variable * and maximum complementary duty are converted, always starting with the one with variable * complementary duty cycle */ if ( (uint16_t )( pHandle->Half_PWMPeriod - pHdl->lowDuty ) > pHandle->pParams_str->Tcase2 ) { SamplingPoint = pHdl->lowDuty + pHandle->pParams_str->Tbefore; if( SamplingPoint >= pHandle->Half_PWMPeriod ) { SamplingPoint = ( 2u * pHandle->Half_PWMPeriod ) - SamplingPoint - 1u; } else { /* Trigger direction must be changed from rising to falling * edge to sample after the PWM period center */ pHandle->ADCTriggerEdge = LL_ADC_REG_TRIG_EXT_FALLING; } } else { DeltaDuty = ( uint16_t )( pHdl->lowDuty - pHdl->midDuty ); /* There is no time to sample during the minimum duty window, sampling is executed between * variable and minimum complementary duty */ if ( DeltaDuty > pHandle->pParams_str->Tcase3 ) { SamplingPoint = pHdl->lowDuty - pHandle->pParams_str->Tbefore; } else { /* No suitable sampling window has been found, sampling is executed in the middle of the * PWM Period. Current instabilities are to be expected */ SamplingPoint = pHandle->Half_PWMPeriod - 1u; } } } return R3_1_WriteTIMRegisters( &pHandle->_Super, SamplingPoint ); } /* * @brief Configures the ADC for the current sampling related to sector X (X = [1..6] ) in case of overmodulation. * * Sets the ADC sequence length and channels and sets the sampling point via TIMx_Ch4 value and polarity. * The WriteTIMRegisters method is then called. * * @param pHdl: Handler of the current instance of the PWM component. * @retval uint16_t Returns the value of R3_1_WriteTIMRegisters. */ uint16_t R3_1_SetADCSampPointSectX_OVM( PWMC_Handle_t * pHdl ) { #if defined (__ICCARM__) #pragma cstat_disable = "MISRAC2012-Rule-11.3" #endif /* __ICCARM__ */ PWMC_R3_1_Handle_t * pHandle = ( PWMC_R3_1_Handle_t * )pHdl; #if defined (__ICCARM__) #pragma cstat_restore = "MISRAC2012-Rule-11.3" #endif /* __ICCARM__ */ uint16_t SamplingPoint; uint16_t DeltaDuty; pHandle->_Super.useEstCurrent = false; /* Check if sampling the AB phases in the middle of the PWM period is possible */ if ( ( uint16_t )( pHandle->Half_PWMPeriod - pHdl->lowDuty ) > pHandle->pParams_str->Tafter ) { /* When it is possible to sample in the middle of the PWM period, always sample the same phases * (AB are chosen) for all sectors in order to not induce current discontinuities when there are differences * between offsets */ /* Sector number needed by GetPhaseCurrent, phase A and B are sampled which corresponds to * sector 4 or 5 */ pHandle->_Super.Sector = SECTOR_5; /* Set the sampling point in the middle of the PWM period */ SamplingPoint = pHandle->Half_PWMPeriod - 1u; } else { /* In this case it is necessary to sample phases with maximum and variable complementary duty cycle.*/ /* In every sector there is always one phase with maximum complementary duty, one with minimum * complementary duty and one with variable complementary duty. In this case, phases with variable * complementary duty and with maximum duty are converted and the first will be always the phase * with variable complementary duty cycle */ if ( ( uint16_t )( pHandle->Half_PWMPeriod - pHdl->lowDuty ) > pHandle->pParams_str->Tcase2 ) { SamplingPoint = pHdl->lowDuty + pHandle->pParams_str->Tbefore; if( SamplingPoint >= pHandle->Half_PWMPeriod ) { SamplingPoint = ( 2u * pHandle->Half_PWMPeriod ) - SamplingPoint - 1u; } else { /* Trigger direction must be changed from rising to falling * edge to sample after the PWM period center */ pHandle->ADCTriggerEdge = LL_ADC_REG_TRIG_EXT_FALLING; } } else { DeltaDuty = ( uint16_t )( pHdl->lowDuty - pHdl->midDuty ); /* If there is no time to sample during the minimum duty window, sampling is executed between * variable duty and maximum duty (cf Case 3 User Manual) */ if ( DeltaDuty > pHandle->pParams_str->Tcase3 ) { SamplingPoint = pHdl->lowDuty - pHandle->pParams_str->Tbefore; } else { /* No suitable sampling window has been found, sampling is executed in the middle of the PWM * Period. Sampled currents will be disregarded and estimated currents will be used instead */ SamplingPoint = pHandle->Half_PWMPeriod - 1u; pHandle->_Super.useEstCurrent = true; } } } return R3_1_WriteTIMRegisters( &pHandle->_Super, SamplingPoint ); } /* * @brief Writes into peripheral registers the new duty cycles and sampling point. * * @param pHdl: Handler of the current instance of the PWM component. * @param hCCR4Reg: New capture/compare register value, written in timer clock counts. * @retval Returns #MC_NO_ERROR if no error occurred or #MC_DURATION if the duty cycles were * set too late for being taken into account in the next PWM cycle. */ __weak uint16_t R3_1_WriteTIMRegisters( PWMC_Handle_t * pHdl, uint16_t hCCR4Reg) { #if defined (__ICCARM__) #pragma cstat_disable = "MISRAC2012-Rule-11.3" #endif PWMC_R3_1_Handle_t * pHandle = ( PWMC_R3_1_Handle_t * )pHdl; #if defined (__ICCARM__) #pragma cstat_restore = "MISRAC2012-Rule-11.3" #endif TIM_TypeDef * TIMx = pHandle->pParams_str->TIMx; uint16_t hAux; LL_TIM_OC_SetCompareCH1 ( TIMx, (uint32_t)pHandle->_Super.CntPhA ); LL_TIM_OC_SetCompareCH2 ( TIMx, (uint32_t)pHandle->_Super.CntPhB ); LL_TIM_OC_SetCompareCH3 ( TIMx, (uint32_t)pHandle->_Super.CntPhC ); LL_TIM_OC_SetCompareCH4 ( TIMx, (uint32_t)hCCR4Reg ); /* Re-configuration of CCR4 must be done before the timer update to be taken into account at the next PWM cycle. Otherwise we are too late, we flag a FOC_DURATION error */ if (((TIMx->CR2) & TIM_CR2_MMS_Msk) != LL_TIM_TRGO_RESET ) { hAux = MC_DURATION; } else { hAux = MC_NO_ERROR; } if ( pHandle->_Super.SWerror == 1u ) { hAux = MC_DURATION; pHandle->_Super.SWerror = 0u; } return hAux; } /* * @brief Implementation of PWMC_GetPhaseCurrents to be performed during calibration. * * It sums up injected conversion data into PhaseAOffset and wPhaseBOffset * to compute the offset introduced in the current feedback network. It is required to * properly configure ADC inputs before in order to enable offset computation. * * @param pHdl: Pointer on the target component instance. * @param pStator_Currents: Pointer to the structure that will receive motor current * of phase A and B in ab_t format. */ __weak void R3_1_HFCurrentsCalibrationAB( PWMC_Handle_t * pHdl, ab_t * pStator_Currents ) { #if defined (__ICCARM__) #pragma cstat_disable = "MISRAC2012-Rule-11.3" #endif PWMC_R3_1_Handle_t * pHandle = ( PWMC_R3_1_Handle_t * )pHdl; #if defined (__ICCARM__) #pragma cstat_restore = "MISRAC2012-Rule-11.3" #endif TIM_TypeDef * TIMx = TIM1; uint8_t bSector = pHandle->PolarizationSector; /* disable ADC trigger source */ LL_TIM_SetTriggerOutput(TIMx, LL_TIM_TRGO_RESET); if ( pHandle->PolarizationCounter < NB_CONVERSIONS ) { pHandle->PhaseAOffset += *pHandle->pParams_str->ADCDataReg1[bSector]; pHandle->PhaseBOffset += *pHandle->pParams_str->ADCDataReg2[bSector]; pHandle->PolarizationCounter++; } /* during offset calibration no current is flowing in the phases */ pStator_Currents->a = 0; pStator_Currents->b = 0; } /* * @brief Implementation of PWMC_GetPhaseCurrents to be performed during calibration. * * It sums up injected conversion data into PhaseCOffset to compute the offset * introduced in the current feedback network. It is required to properly configure * ADC inputs before in order to enable offset computation. * * @param pHdl: Pointer on the target component instance. * @param pStator_Currents: Pointer to the structure that will receive motor current * of phase A and B in ab_t format. */ __weak void R3_1_HFCurrentsCalibrationC( PWMC_Handle_t * pHdl, ab_t * pStator_Currents ) { #if defined (__ICCARM__) #pragma cstat_disable = "MISRAC2012-Rule-11.3" #endif PWMC_R3_1_Handle_t * pHandle = ( PWMC_R3_1_Handle_t * )pHdl; #if defined (__ICCARM__) #pragma cstat_restore = "MISRAC2012-Rule-11.3" #endif TIM_TypeDef * TIMx = TIM1; uint8_t bSector = pHandle->PolarizationSector; /* disable ADC trigger source */ LL_TIM_SetTriggerOutput(TIMx, LL_TIM_TRGO_RESET); pHandle->_Super.Sector = SECTOR_1; if ( pHandle->PolarizationCounter < NB_CONVERSIONS ) { pHandle->PhaseCOffset += *pHandle->pParams_str->ADCDataReg2[bSector]; pHandle->PolarizationCounter++; } /* during offset calibration no current is flowing in the phases */ pStator_Currents->a = 0; pStator_Currents->b = 0; } /* * @brief Turns on low sides switches. * * This function is intended to be used for charging boot capacitors of driving section. It has to be * called each motor start-up when using high voltage drivers. * * @param pHdl: Handler of the current instance of the PWM component * @param ticks: Timer ticks value to be applied * Min value: 0 (low sides ON) * Max value: PWM_PERIOD_CYCLES/2 (low sides OFF) */ __weak void R3_1_TurnOnLowSides( PWMC_Handle_t * pHdl, uint32_t ticks) { #if defined (__ICCARM__) #pragma cstat_disable = "MISRAC2012-Rule-11.3" #endif PWMC_R3_1_Handle_t * pHandle = ( PWMC_R3_1_Handle_t * )pHdl; #if defined (__ICCARM__) #pragma cstat_restore = "MISRAC2012-Rule-11.3" #endif TIM_TypeDef * TIMx = TIM1; pHandle->_Super.TurnOnLowSidesAction = true; /* Clear Update Flag */ LL_TIM_ClearFlag_UPDATE( TIMx ); /*Turn on the three low side switches */ LL_TIM_OC_SetCompareCH1(TIMx, ticks); LL_TIM_OC_SetCompareCH2(TIMx, ticks); LL_TIM_OC_SetCompareCH3(TIMx, ticks); /* Wait until next update */ while ( LL_TIM_IsActiveFlag_UPDATE( TIMx ) == RESET ) {} /* Main PWM Output Enable */ LL_TIM_EnableAllOutputs(TIMx); if ( ( pHandle->_Super.LowSideOutputs ) == ES_GPIO ) { /* Enable signals activation */ LL_GPIO_SetOutputPin( pHandle->_Super.pwm_en_u_port, pHandle->_Super.pwm_en_u_pin ); LL_GPIO_SetOutputPin( pHandle->_Super.pwm_en_v_port, pHandle->_Super.pwm_en_v_pin ); LL_GPIO_SetOutputPin( pHandle->_Super.pwm_en_w_port, pHandle->_Super.pwm_en_w_pin ); } return; } /* * @brief Enables PWM generation on the proper Timer peripheral acting on MOE bit. * * @param pHdl: Handler of the current instance of the PWM component. */ __weak void R3_1_SwitchOnPWM( PWMC_Handle_t * pHdl ) { #if defined (__ICCARM__) #pragma cstat_disable = "MISRAC2012-Rule-11.3" #endif PWMC_R3_1_Handle_t * pHandle = ( PWMC_R3_1_Handle_t * )pHdl; #if defined (__ICCARM__) #pragma cstat_restore = "MISRAC2012-Rule-11.3" #endif TIM_TypeDef * TIMx = TIM1; pHandle->_Super.TurnOnLowSidesAction = false; /* We forbid ADC usage for regular conversion on Systick*/ pHandle->ADCRegularLocked=true; /* Set all duty to 50% */ LL_TIM_OC_SetCompareCH1(TIMx, (uint32_t)(pHandle->Half_PWMPeriod >> 1)); LL_TIM_OC_SetCompareCH2(TIMx, (uint32_t)(pHandle->Half_PWMPeriod >> 1)); LL_TIM_OC_SetCompareCH3(TIMx, (uint32_t)(pHandle->Half_PWMPeriod >> 1)); LL_TIM_OC_SetCompareCH4(TIMx, (uint32_t)(pHandle->Half_PWMPeriod - 5u)); /* Wait for a new PWM period */ LL_TIM_ClearFlag_UPDATE( TIMx ); while ( LL_TIM_IsActiveFlag_UPDATE( TIMx ) == RESET ) {} /* Clear Update Flag */ LL_TIM_ClearFlag_UPDATE( TIMx ); /* Main PWM Output Enable */ TIMx->BDTR |= LL_TIM_OSSI_ENABLE; LL_TIM_EnableAllOutputs(TIMx); if ( ( pHandle->_Super.LowSideOutputs ) == ES_GPIO ) { if ( LL_TIM_CC_IsEnabledChannel(TIMx, TIMxCCER_MASK_CH123) != 0u ) { LL_GPIO_SetOutputPin( pHandle->_Super.pwm_en_u_port, pHandle->_Super.pwm_en_u_pin ); LL_GPIO_SetOutputPin( pHandle->_Super.pwm_en_v_port, pHandle->_Super.pwm_en_v_pin ); LL_GPIO_SetOutputPin( pHandle->_Super.pwm_en_w_port, pHandle->_Super.pwm_en_w_pin ); } else { /* It is executed during calibration phase the EN signal shall stay off */ LL_GPIO_ResetOutputPin( pHandle->_Super.pwm_en_u_port, pHandle->_Super.pwm_en_u_pin ); LL_GPIO_ResetOutputPin( pHandle->_Super.pwm_en_v_port, pHandle->_Super.pwm_en_v_pin ); LL_GPIO_ResetOutputPin( pHandle->_Super.pwm_en_w_port, pHandle->_Super.pwm_en_w_pin ); } } /* Configuration of DMA and ADC to next conversions */ /* Overwriting the CHSELR resgister is allowed because the ADC conversion is stopped by the R3_1_SwitchOffPWM function */ LL_ADC_SetSamplingTimeCommonChannels ( ADC1, pHandle->pParams_str->b_ISamplingTime ); /* Setting of the DMA Buffer Size.*/ /* NOTE. This register (CNDTRx) must not be written when the DMAy Channel x is ENABLED */ LL_DMA_DisableChannel( DMA1, LL_DMA_CHANNEL_1 ); /* Write the Buffer size on CNDTR register */ LL_DMA_SetDataLength( DMA1, LL_DMA_CHANNEL_1, 2u ); /* DMA Enabling */ LL_DMA_EnableChannel( DMA1, LL_DMA_CHANNEL_1 ); /* Clear EOC */ LL_ADC_ClearFlag_EOC( ADC1 ); /* Enable ADC DMA request*/ LL_ADC_REG_SetDMATransfer( ADC1, LL_ADC_REG_DMA_TRANSFER_LIMITED ); /* Clear Pending Interrupt Bits */ LL_DMA_ClearFlag_HT1( DMA1 ); // TBC: for TC1, GL1 (not cleared ...) /* DMA Interrupt Event configuration */ LL_DMA_EnableIT_TC( DMA1, LL_DMA_CHANNEL_1 ); /* Clear Update Flag */ LL_TIM_ClearFlag_UPDATE( TIMx ); /* Enable Update IRQ */ LL_TIM_EnableIT_UPDATE( TIMx ); return; } /* * @brief Disables PWM generation on the proper Timer peripheral acting on MOE bit. * * @param pHdl: Handler of the current instance of the PWM component. */ __weak void R3_1_SwitchOffPWM( PWMC_Handle_t * pHdl ) { #if defined (__ICCARM__) #pragma cstat_disable = "MISRAC2012-Rule-11.3" #endif PWMC_R3_1_Handle_t * pHandle = ( PWMC_R3_1_Handle_t * )pHdl; #if defined (__ICCARM__) #pragma cstat_restore = "MISRAC2012-Rule-11.3" #endif TIM_TypeDef * TIMx = TIM1; /* Enable Update IRQ */ LL_TIM_DisableIT_UPDATE( TIMx ); pHandle->_Super.TurnOnLowSidesAction = false; /* Main PWM Output Disable */ LL_TIM_DisableAllOutputs(TIMx); if ( pHandle->_Super.BrakeActionLock == true ) { } else { if ( ( pHandle->_Super.LowSideOutputs ) == ES_GPIO ) { LL_GPIO_ResetOutputPin( pHandle->_Super.pwm_en_u_port, pHandle->_Super.pwm_en_u_pin ); LL_GPIO_ResetOutputPin( pHandle->_Super.pwm_en_v_port, pHandle->_Super.pwm_en_v_pin ); LL_GPIO_ResetOutputPin( pHandle->_Super.pwm_en_w_port, pHandle->_Super.pwm_en_w_pin ); } } /* Disabling of DMA Interrupt Event configured */ LL_DMA_DisableIT_TC( DMA1, LL_DMA_CHANNEL_1 ); LL_ADC_REG_StopConversion( ADC1 ); /* Disable ADC DMA request*/ ADC1->CFGR1 &= ~ADC_CFGR1_DMAEN; /* Clear Transmission Complete Flag of DMA1 Channel1 */ LL_DMA_ClearFlag_TC1( DMA1 ); /* Clear EOC */ LL_ADC_ClearFlag_EOC( ADC1 ); /* The ADC is not triggered anymore by the PWM timer */ LL_ADC_REG_SetTriggerSource (ADC1, LL_ADC_REG_TRIG_SOFTWARE); /* We allow ADC usage for regular conversion on Systick*/ pHandle->ADCRegularLocked=false; /* Wait for a new PWM period */ LL_TIM_ClearFlag_UPDATE( TIMx ); while ( LL_TIM_IsActiveFlag_UPDATE( TIMx ) == RESET ) {} /* Clear Update Flag */ LL_TIM_ClearFlag_UPDATE( TIMx ); return; } /* * @brief Contains the TIMx Update event interrupt. * * @param pHandle: Handler of the current instance of the PWM component. */ void * R3_1_TIMx_UP_IRQHandler( PWMC_R3_1_Handle_t * pHandle ) { /* Set the trigger polarity as computed inside SetADCSampPointSectX*/ LL_ADC_REG_SetTriggerEdge (ADC1, pHandle->ADCTriggerEdge); /* Set ADC trigger source */ LL_ADC_REG_SetTriggerSource(ADC1, LL_ADC_REG_TRIG_EXT_TIM1_TRGO); /* Set scan direction according to the sector */ LL_ADC_REG_SetSequencerScanDirection(ADC1, pHandle->pParams_str->ADCScandir[pHandle->_Super.Sector]<CHSELR = pHandle->pParams_str->ADCConfig[pHandle->_Super.Sector]; /* ReConfigure sampling time, as deconfigured by reg_conv_manager */ LL_ADC_SetSamplingTimeCommonChannels ( ADC1, pHandle->pParams_str->b_ISamplingTime ); /* Disable ADC source trigger */ LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_OC4REF); /* ADC needs to be restarted because DMA is configured as limited */ LL_ADC_REG_StartConversion( ADC1 ); /* Reset the ADC trigger edge for next conversion */ pHandle->ADCTriggerEdge = LL_ADC_REG_TRIG_EXT_RISING; return &pHandle->_Super.Motor; } /** * @} */ /** * @} */ /** * @} */ /************************ (C) COPYRIGHT 2024 STMicroelectronics *****END OF FILE****/