void configHallSensorTimer(void) { // Timer 3 decodes the 3 HallSensor input lines // see referenze manual page 305 // define timer clock // between two changes on the hall sensor lines on the lowest rotation // speed (eg. 1/100 from max. speed) the timer must not overflow // define timer counter clock appropriate // enable port pins for hall inputs RCC_APB2PeriphClockCmd(...); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_...; GPIO_InitStructure.GPIO_Mode = ... GPIO_Init(..., &GPIO_InitStructure); RCC_APB1PeriphClockCmd(TIM4_CLK, ENABLE); // timer base configuration // 126 => 3,5s till overflow ; 285,714kHz TimerClock [36MHz/Prescaler] TIM_TimeBaseStructure.TIM_Prescaler = 126; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = 65535; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); // enable hall sensor // T1F_ED will be connected to HallSensoren Imputs // TIM4_CH1,TIM4_CH2,TIM4_CH3 TIM_SelectHallSensor(TIM4, ENABLE); // TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode, // uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity) // HallSensor event is delivered with singnal TI1F_ED // (this is XOR of the three hall sensor lines) // Signal TI1F_ED: falling and rising ddge of the inputs is used TIM_SelectInputTrigger(TIM4, TIM_TS_TI1F_ED); // On every TI1F_ED event the counter is resetted and update is tiggered TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset); // Channel 1 in input capture mode // on every TCR edge (build from TI1F_ED which is a HallSensor edge) // the timervalue is copied into ccr register and a CCR1 Interrupt // TIM_IT_CC1 is fired TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // listen to T1, the HallSensorEvent TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_TRC; // Div:1, every edge TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; // noise filter: 1111 => 72000kHz / factor (==1) / 32 / 8 -> 281kHz // input noise filter (reference manual page 322) TIM_ICInitStructure.TIM_ICFilter = 0xF; TIM_ICInit(TIM4, &TIM_ICInitStructure); // channel 2 can be use for commution delay between hallsensor edge // and switching the FET into the next step. if this delay time is // over the channel 2 generates the commutation signal to the motor timer TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_Pulse = 1; // 1 is no delay; 2000 = 7ms TIM_OC2Init(TIM4, &TIM_OCInitStructure); // clear interrupt flag TIM_ClearFlag(TIM4, TIM_FLAG_CC2); //TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable); // TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_OC1); // timer2 output compate signal is connected to TRIGO TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_OC2Ref); // Enable channel 2 compate interrupt request // TIM_IT_CC1 | TIM_IT_CC2 TIM_ITConfig(TIM4, TIM_IT_CC1 | TIM_IT_CC2, ENABLE); // Enable output compare preload //TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable); // Enable ARR preload //TIM_ARRPreloadConfig(TIM4, ENABLE); // Enable update event //TIM_ClearFlag(TIM4, TIM_FLAG_Update); //TIM_ITConfig(TIM4, TIM_IT_Update, DISABLE); // we use preemption interrupts here, BLDC Bridge switching and // Hall has highest priority NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // ------------------- // HallSensor is now configured, if BLDC Timer is also configured // after enabling timer 4 // the motor will start after next overflow of the hall timer because // this generates the first startup motor cummutation event TIM_Cmd(TIM4, ENABLE); } void incCommutationDelay(void) { TIM4->CCR2 = (TIM4->CCR2) + 1; } void decCommutationDelay(void) { TIM4->CCR2 = (TIM4->CCR2) - 1; } // ------------- HallSensor interrupt handler ----------------- // this handles TIM4 irqs (from HallSensor) void TIM4_IRQHandler(void) { if (TIM_GetITStatus(TIM4, TIM_IT_CC1) != RESET) { TIM_ClearITPendingBit(MOTOR_TMC603_HALLENC_TIM, TIM_IT_CC1); // calculate motor speed or else with CCR1 values hallccr1 = TIM4->CCR1; ... } else if (TIM_GetITStatus(TIM4, TIM_IT_CC2) != RESET) { TIM_ClearITPendingBit(MOTOR_TMC603_HALLENC_TIM, TIM_IT_CC2); // this interrupt handler is called AFTER the motor commutaton event // is done // after commutation the next motor step must be prepared // use inline functions in irq handlers static __INLINE funct(..) {..} BLDCMotorPrepareCommutation(); } else { ; // this should not happen } }
void configMotorBridgeTimer(void) { // define timer clock, motor timer can be TIM1 or TIM8 RCC_APB2PeriphClockCmd(...); // define the 6 output pins for the bridge, if needed define // the input pin for emergeny stop // Chopper Frequency (PWM for the FETs) // 18kHz was good in empiric tests // ARR = SystemCoreClock / ChopperFreq // ARR defines also the resolution of the Chopper PWM #define BLDC_CHOPPER_PERIOD ((uint16_t)4000) // Time Base configuration TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = BLDC_CHOPPER_PERIOD; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); // Channel 1, 2, 3 – set to PWM mode - all 6 outputs // per channel on output is low side fet, the opposite is for high side fet TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; // BLDC_ccr_val TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set; TIM_OC1Init(TIM1, &TIM_OCInitStructure); TIM_OC2Init(TIM1, &TIM_OCInitStructure); TIM_OC3Init(TIM1, &TIM_OCInitStructure); // activate preloading the CCR register TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable); /* automatic output enable, break off, dead time ca. 200ns and // no lock of configuration */ TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable; TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF; // DeadTime value n=1 bis 31: from 14ns to 1,7us // DeadTime value n=129 bis 159: from 1,7µs to 3,5ms // DeadTime value 7 => 98ns // ... see programmers reference manual // DeadTime[ns] = value * (1/SystemCoreFreq) (on 72MHz: 7 is 98ns) TIM_BDTRInitStructure.TIM_DeadTime = 7; // 98ns TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable; // enabel this if you use emergency stop signal // TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable; // TIM_BDTRInitStructure.TIM_BreakPolarity = MOTOR_TMC603_EMSTOP_POLARITY; TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable; TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure); // preload ARR register TIM_CCPreloadControl(TIM1, ENABLE); // activate COM (Commutation) Event from Slave (HallSensor timer) // through TRGI enableHallCommutateSignal(); // Internal connection from Hall/Enc Timer to Motor Timer // eg. TIM1 (BLDC Motor Timer) is Slave of TIM3 (Hall Timer) // Internal connection from Hall/Enc Timer to Motor Timer // Choose carefully from the following possible combination // check programmers reference manual // TIM_SelectInputTrigger(TIM1, TIM_TS_ITR0); // MotorTimer = TIM1, HallTimer = TIM5 // TIM_SelectInputTrigger(TIM1, TIM_TS_ITR1); // MotorTimer = TIM1, HallTimer = TIM2 // TIM_SelectInputTrigger(TIM1, TIM_TS_ITR2); // MotorTimer = TIM1, HallTimer = TIM3 TIM_SelectInputTrigger(TIM1, TIM_TS_ITR3); // MotorTimer = TIM1, HallTimer = TIM4 // TIM_SelectInputTrigger(TIM8, TIM_TS_ITR0); // MotorTimer = TIM8, HallTimer = TIM1 // TIM_SelectInputTrigger(TIM8, TIM_TS_ITR1); // MotorTimer = TIM8, HallTimer = TIM2 // TIM_SelectInputTrigger(TIM8, TIM_TS_ITR2); // MotorTimer = TIM8, HallTimer = TIM4 // TIM_SelectInputTrigger(TIM8, TIM_TS_ITR3); // MotorTimer = TIM8, HallTimer = TIM5 // Enable interrupt, motor commutation has high piority and has // a higher subpriority then the hall sensor NVIC_InitStructure.NVIC_IRQChannel = TIM1_TRG_COM_IRQn; // highest priority NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // highest priority NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // Interrupt for hardwired EmergencyStop (if needed) // Timer 1 Motor Emergency Break Input // NVIC_InitStructure.NVIC_IRQChannel = TIM1_BRK_IRQn; // NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; // NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // NVIC_Init(&NVIC_InitStructure); // --------- activate the bldc bridge ctrl. ---------- // in a project this will be done late after complete // configuration of other peripherie // enable COM (commutation) IRQ TIM_ITConfig(TIM1, TIM_IT_COM, ENABLE); // enable motor timer TIM_Cmd(TIM1, ENABLE); // enable motor timer main output (the bridge signals) TIM_CtrlPWMOutputs(TIM1, ENABLE); } // enable the connection between HallTimer and MotorTimer void enableHallCommutateSignal() { TIM_SelectCOM(TIM1, ENABLE); } // disable the connection between HallTimer and MotorTimer void disableHallCommutateSignal() { TIM_SelectCOM(TIM1, DISABLE); } // BLDC motor steps // every row from 1 to 6 is called by a hall state // every column a FET from 3-phase bridge // motor off is at row 0 BLDC_BRIDGE_STATE_VORWARD[0] // cw - rechtslauf - positiv // { 1H,1L , 2H,2L , 3H,3L } // BLDC motor steps // every row from 1 to 6 is one of the 6 motor vector state // every column a FET from 3-phase bridge // all FETs off at row 0 or 8 (this pattern should not come from the hallsensor) // cw - rechtslauf - positiv // { 1H,1L , 2H,2L , 3H,3L } static const uint8_t BLDC_BRIDGE_STATE_VORWARD[8][6] = // Motor step { { FALSE,FALSE , FALSE,FALSE , FALSE,FALSE }, // 0 { FALSE,TRUE , TRUE ,FALSE , FALSE,FALSE }, // 2 { FALSE,FALSE , FALSE,TRUE , TRUE ,FALSE }, // 4 { FALSE,TRUE , FALSE,FALSE , TRUE ,FALSE }, // 3 { TRUE ,FALSE , FALSE,FALSE , FALSE,TRUE } // 6 { FALSE,FALSE , TRUE ,FALSE , FALSE,TRUE }, // 1 { TRUE ,FALSE , FALSE,TRUE , FALSE,FALSE }, // 5 { FALSE,FALSE , FALSE,FALSE , FALSE,FALSE }, // 0 }; // This function handles motor timer trigger and commutation interrupts // can be used for calculation... void TIM1_TRG_COM_IRQHandler(void) { TIM_ClearITPendingBit(TIM1, TIM_IT_COM); // commutationCount++; } /* This is called from HALL timer interrupt handler remember: if hall a hall edge is detected first the motor commutation event is done next this routine is called which has to prepare the next motor step (which FET must be switched on or off) active freewhelling is used to minimize power loss code should be easy to understand and to debug... for practical use you should optimize it */ static __INLINE void BLDCMotorPrepareCommutation(void) { // next bridge step calculated by HallSensor inputs // if there was an hall event without changing the hall position, // do nothing. // // In principle, on every hall event you can go to the next motor // step but i had sometimes problems that the motor was running // on an harmonic wave (??) when the motor was without load uint16_t newhallpos = ((GPIO_ReadInputData(GPIOD) & 0x7000) >> 12); if (newhallpos == hallpos) return; lasthallpos = hallpos; hallpos = newhallpos; // this is only for motor direction forward BH1 = BLDC_BRIDGE_STATE_VORWARD[hallpos][0]; BL1 = BLDC_BRIDGE_STATE_VORWARD[hallpos][1]; BH2 = BLDC_BRIDGE_STATE_VORWARD[hallpos][2]; BL2 = BLDC_BRIDGE_STATE_VORWARD[hallpos][3]; BH3 = BLDC_BRIDGE_STATE_VORWARD[hallpos][4]; BL3 = BLDC_BRIDGE_STATE_VORWARD[hallpos][5]; // **** this is with active freewheeling **** // Bridge FETs for Motor Phase U if (BH1) { // PWM at low side FET of bridge U // active freewheeling at high side FET of bridge U // if low side FET is in PWM off mode then the hide side FET // is ON for active freewheeling. This mode needs correct definition // of dead time otherwise we have shoot-through problems TIM_SelectOCxM(TIM1, TIM_Channel_1, TIM_OCMode_PWM1); TIM_CCxCmd(TIM1, TIM_Channel_1, TIM_CCx_Enable); TIM_CCxNCmd(TIM1, TIM_Channel_1, TIM_CCxN_Enable); } else { // Low side FET: OFF TIM_CCxCmd(TIM1, TIM_Channel_1, TIM_CCx_Disable); if (BL1){ // High side FET: ON TIM_SelectOCxM(TIM1, TIM_Channel_1, TIM_ForcedAction_Active); TIM_CCxNCmd(TIM1, TIM_Channel_1, TIM_CCxN_Enable); } else { // High side FET: OFF TIM_CCxNCmd(TIM1, TIM_Channel_1, TIM_CCxN_Disable); } } // Bridge FETs for Motor Phase V if (BH2) { TIM_SelectOCxM(TIM1, TIM_Channel_2, TIM_OCMode_PWM1); TIM_CCxCmd(TIM1, TIM_Channel_2, TIM_CCx_Enable); TIM_CCxNCmd(TIM1, TIM_Channel_2, TIM_CCxN_Enable); } else { TIM_CCxCmd(TIM1, TIM_Channel_2, TIM_CCx_Disable); if (BL2){ TIM_SelectOCxM(TIM1, TIM_Channel_2, TIM_ForcedAction_Active); TIM_CCxNCmd(TIM1, TIM_Channel_2, TIM_CCxN_Enable); } else { TIM_CCxNCmd(TIM1, TIM_Channel_2, TIM_CCxN_Disable); } } // Bridge FETs for Motor Phase W if (BH3) { TIM_SelectOCxM(TIM1, TIM_Channel_3, TIM_OCMode_PWM1); TIM_CCxCmd(TIM1, TIM_Channel_3, TIM_CCx_Enable); TIM_CCxNCmd(TIM1, TIM_Channel_3, TIM_CCxN_Enable); } else { TIM_CCxCmd(TIM1, TIM_Channel_3, TIM_CCx_Disable); if (BL3){ TIM_SelectOCxM(TIM1, TIM_Channel_3, TIM_ForcedAction_Active); TIM_CCxNCmd(TIM1, TIM_Channel_3, TIM_CCxN_Enable); } else { TIM_CCxNCmd(TIM1, TIM_Channel_3, TIM_CCxN_Disable); } } }
// You can manually generate an commutation event (like the hall sensor) TIM_GenerateEvent(TIM1, TIM_EventSource_COM); // how to change the PWM value TIM1->CCR1 = new_ccr_val; TIM1->CCR2 = new_ccr_val; TIM1->CCR3 = new_ccr_val;