STM32 BLDC Control with HALL Sensor: Unterschied zwischen den Versionen

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche
(Änderung 92803 von 134.220.30.106 (Diskussion) rückgängig gemacht.)
 
(15 dazwischenliegende Versionen von 5 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
== STM32 BLDC Control with HALL Sensor ==
The code is not a complete project, it's to show you how to use the motor timer to control an BLDC Motor in combination with an HALL Sensor on another timer.


The code is not a complete project, it`s to show you how to use the motor timer to control an BLDC Motor in combination with an HALL Sensor on another timer.
== Intro ==


== Intro ==
The system needs two timers. One to control [[PWM]] for the bridge [[FET]]s and another timer to identify the HallSensor singnals. The HallSensor timer triggers the motor timer commutation event.


The system needs two timers. One to control PWM for the Bridge FETs and another Timer to identify the HallSensor singnals. The HallSensor timer triggers the Motor timer commutation event.
The connection betwenn these two timers is done in the background with events. There is no direct need for interrupt handling to commutate the motor timer.
The connection betwenn these two timers is done in the background with events. There is no direct need for interrupt handling to commutate the motor timer.
* BLDC Bridge FETs: TIM1
BLDC Bridge FETs: TIM1
* HallSensor: TIM4
HallSensor: TIM4
Recommendation: For your first projects and to learn more, I suggest to use a protection 3 phase motor driver. In there documents you find good information about shoot-through protection, on-time, off-time, dead-time, hall-steps, rpm and field frequency, cutoff filter frequency and many more. Some of the chips offers also simplified usage of sensorless motor control because they detect the rotorposition and give back a virtual Hall Signal, see the TMC product.
 
* Allegro A4935 [http://www.allegromicro.com/en/Products/Part_Numbers/4935/]
* TMC 603 [http://www.trinamic.com/tmc/render.php?sess_pid=446]
 
If you have a simple FET bridge driver then you must carfully calculate the dead time which is a prameter in the motortimer.
 
;Active Freewheeling: Without active freewheeling the body diodes during PWM OFF time produce a huge amount of power loss. With active freewheeling this power loss can be reduced.


INFO: Check HallSensor or EncoderInputs they often needs PullUp`, use RC Filters to filter bad signals.
;Info: Check HallSensor Inputs. They often needs PullUps, use RC filters to filter bad signals.


== Code for the HallSensor timer ==
== Code for the HallSensor timer ==


Internal Connection from Hall/Enc Timer to Motor Timer. The HALL/Encoder Timer Output is direct connected to the Motor Timer Commutation Trigger. If the correct combination of Motor and Hall/Enc Timer  is selected then this is done for you. If you can not use the internal Connection you have to do this in an Interrupt manually.
Internal Connection from Hall/Enc Timer to Motor Timer. The HALL Timer Output is direct connected to the Motor Timer Commutation Trigger. If the correct combination of Motor and Hall/Enc Timer  is selected then this is done for you. If you can not use the internal Connection you have to do this in an Interrupt manually.
 
Info: Only following Combinations are possible !!


Check STM32 Reference Manual for Internal Trigger 1 to 4 (ITR1 to ITR4)  
Only following Combinations are possible<ref>Check STM32 Reference Manual for Internal Trigger 1 to 4 (ITR1 to ITR4). See Table "TIMx Internal trigger connection".</ref>
see Table: "TIMx Internal trigger connection"


If Motor is on Timer1 this is possible
If Motor is on Timer1 this is possible
  Hall/Enc is Timer 2 --> Motor is Timer 1 ==> use TIM_TS_ITR1
* Hall/Enc is Timer 2 Motor is Timer 1 use TIM_TS_ITR1
  Hall/Enc is Timer 3 --> Motor is Timer 1 ==> use TIM_TS_ITR2
* Hall/Enc is Timer 3 Motor is Timer 1 use TIM_TS_ITR2
  Hall/Enc is Timer 4 --> Motor is Timer 1 ==> use TIM_TS_ITR3
* Hall/Enc is Timer 4 Motor is Timer 1 use TIM_TS_ITR3
  Hall/Enc is Timer 5 --> Motor is Timer 1 ==> use TIM_TS_ITR0
* Hall/Enc is Timer 5 Motor is Timer 1 use TIM_TS_ITR0


If Motor is on Timer8 this is possible
If Motor is on Timer8 this is possible
  Hall/Enc is Timer 1 --> Motor is Timer 1 ==> use TIM_TS_ITR0
* Hall/Enc is Timer 1 Motor is Timer 1 use TIM_TS_ITR0
  Hall/Enc is Timer 2 --> Motor is Timer 1 ==> use TIM_TS_ITR1
* Hall/Enc is Timer 2 Motor is Timer 1 use TIM_TS_ITR1
  Hall/Enc is Timer 4 --> Motor is Timer 1 ==> use TIM_TS_ITR2
* Hall/Enc is Timer 4 Motor is Timer 1 use TIM_TS_ITR2
  Hall/Enc is Timer 5 --> Motor is Timer 1 ==> use TIM_TS_ITR3
* Hall/Enc is Timer 5 Motor is Timer 1 use TIM_TS_ITR3


<c>
<syntaxhighlight lang="c">
void configHallSensorTimer(void) {  
void configHallSensorTimer(void) {  


//   Timer 3 decodes the 3 HallSensor input lines   
// Timer 3 decodes the 3 HallSensor input lines   
// see referenze manual page 305  
// see referenze manual page 305  
    
    
// define timer clock  
// 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
// 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
// define timer counter clock appropriate
…..…


// enable port pins for hall inputs
// enable port pins for hall inputs
RCC_APB2PeriphClockCmd(...);   
RCC_APB2PeriphClockCmd(...);   
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_...;  
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_...;  
GPIO_InitStructure.GPIO_Mode = .
GPIO_InitStructure.GPIO_Mode = ...
GPIO_Init(...., &GPIO_InitStructure);  
GPIO_Init(..., &GPIO_InitStructure);  
 
...….. 


RCC_APB1PeriphClockCmd(TIM4_CLK, ENABLE);  
RCC_APB1PeriphClockCmd(TIM4_CLK, ENABLE);  


// timer base configuration  
  // timer base configuration
   TIM_TimeBaseStructure.TIM_Prescaler = 126; // 126 => 3,5s till overflow ; 285,714kHz TimerClock [36MHz/Prescaler] ;  
   // 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_CounterMode = TIM_CounterMode_Up;  
   TIM_TimeBaseStructure.TIM_Period = 65535;  
   TIM_TimeBaseStructure.TIM_Period = 65535;  
Zeile 65: Zeile 66:
      
      
   // enable hall sensor
   // enable hall sensor
// T1F_ED will be connected to  HallSensoren Imputs TIM4_CH1,TIM4_CH2,TIM4_CH3  
  // T1F_ED will be connected to  HallSensoren Imputs
  // TIM4_CH1,TIM4_CH2,TIM4_CH3  
   TIM_SelectHallSensor(TIM4, ENABLE);  
   TIM_SelectHallSensor(TIM4, ENABLE);  


   //  TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,  
   //  TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,  
   //       uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity)  
   // 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)
  // HallSensor event is delivered with singnal TI1F_ED
// Signal TI1F_ED: falling and rising ddge of the inputs is used  
  // (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);  
   TIM_SelectInputTrigger(TIM4, TIM_TS_TI1F_ED);  
    
    
Zeile 80: Zeile 83:
   // Channel 1 in input capture mode  
   // Channel 1 in input capture mode  
   // on every TCR edge (build from TI1F_ED which is a HallSensor edge)   
   // 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_CC1is fired  
   // 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_Channel = TIM_Channel_1;  
   TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;  
   TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;  
   TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_TRC; // listen to T1, the  HallSensorEvent  
   // listen to T1, the  HallSensorEvent  
   TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; // Div:1, every edge  
   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  
   // noise filter: 1111 => 72000kHz / factor (==1) / 32 / 8 -> 281kHz  
   TIM_ICInitStructure.TIM_ICFilter = 0xF; // input noise filter (reference manual page 322)  
   // input noise filter (reference manual page 322)  
  TIM_ICInitStructure.TIM_ICFilter = 0xF;
   TIM_ICInit(TIM4, &TIM_ICInitStructure);  
   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
   // 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_OCMode = TIM_OCMode_PWM2;  
   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  
   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  
Zeile 105: Zeile 114:
   TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_OC2Ref);  
   TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_OC2Ref);  
    
    
   // Enable channel 2 compate interrupt request  
   // Enable channel 2 compate interrupt request
   TIM_ITConfig(TIM4, TIM_IT_CC1 | TIM_IT_CC2, ENABLE); // TIM_IT_CC1 | TIM_IT_CC2
  // TIM_IT_CC1 | TIM_IT_CC2
   TIM_ITConfig(TIM4, TIM_IT_CC1 | TIM_IT_CC2, ENABLE);
   
   
   // Enable output compare preload  
   // Enable output compare preload  
Zeile 118: Zeile 128:
   //TIM_ITConfig(TIM4, TIM_IT_Update, DISABLE);  
   //TIM_ITConfig(TIM4, TIM_IT_Update, DISABLE);  
   
   
// we use preemption interrupts here,  BLDC Bridge switching and Hall has highest priority  
  // we use preemption interrupts here,  BLDC Bridge switching and
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;  
  // Hall has highest priority  
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;  
  NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;  
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;  
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
Zeile 126: Zeile 137:


   // -------------------
   // -------------------
   // HallSensor is now configured, if BLDC Timer is also configured the after enabling timer 4  
   // HallSensor is now configured, if BLDC Timer is also configured
   // the motor will start after next overflow of the hall timer because this generates the first startup
  // after enabling timer 4  
  // motor cummutation event
   // the motor will start after next overflow of the hall timer because
  // this generates the first startup motor cummutation event
   TIM_Cmd(TIM4, ENABLE);  
   TIM_Cmd(TIM4, ENABLE);  
}
}
</c>


<c>
void incCommutationDelay(void) {  
void incCommutationDelay(void) {  
   TIM4->CCR2 = (TIM4->CCR2) + 1;  
   TIM4->CCR2 = (TIM4->CCR2) + 1;  
  ….
}
}
</c>


<c>
void decCommutationDelay(void) {  
void decCommutationDelay(void) {  
   TIM4->CCR2 = (TIM4->CCR2) - 1;
   TIM4->CCR2 = (TIM4->CCR2) - 1;
  ….
}
}
</c>


<c>
// ------------- HallSensor interrupt handler -----------------
// ------------- HallSensor interrupt handler -----------------


Zeile 160: Zeile 161:
     // calculate motor  speed or else with CCR1 values
     // calculate motor  speed or else with CCR1 values
     hallccr1 = TIM4->CCR1;
     hallccr1 = TIM4->CCR1;
     …...........
     ...
   }  
   }  
   else if (TIM_GetITStatus(TIM4, TIM_IT_CC2) != RESET)  
   else if (TIM_GetITStatus(TIM4, TIM_IT_CC2) != RESET)  
   {  
   {  
     TIM_ClearITPendingBit(MOTOR_TMC603_HALLENC_TIM, TIM_IT_CC2);  
     TIM_ClearITPendingBit(MOTOR_TMC603_HALLENC_TIM, TIM_IT_CC2);  
     // this interrupt handler is called AFTER the motor commutaton event is done
     // this interrupt handler is called AFTER the motor commutaton event
    // is done
     // after commutation the next motor step must be prepared
     // after commutation the next motor step must be prepared
     // use inline functions in irq handlers static __INLINE funct(..) {..}  
     // use inline functions in irq handlers static __INLINE funct(..) {..}  
     BLDCMotorPrepareCommutation();  
     BLDCMotorPrepareCommutation();  
} else {  
  } else {  
     ; //.... this should not happen  
     ; // this should not happen  
   }  
   }  
}
}
</c>
</syntaxhighlight>
 
 
== Code for BLDC motor control timer ==
== Code for BLDC motor control timer ==


Recommendation: For your first projects and to learn more i suggest to use a protection 3 phase motor driver. In there documents you find good information about shoot-through protection, on-time, off-time, dead-time, hall-steps, rpm and field frequency, cutoff filter frequency and many more ...…. Some of the chips offers also simplified usage of sensorless motor control because he detects the rotorposition and gives back a virtual Hall Signal, see the TMC product.
<syntaxhighlight lang="c">
 
Allegro A4935 http://www.allegromicro.com/en/Products/Part_Numbers/4935/
TMC 603 http://www.trinamic.com/tmc/render.php?sess_pid=446
 
If you have a simple FET bridge driver then you must carfully calculate the dead time which is a prameter in the motortimer.
 
Active Freewheeling
Without active freewheeling the body diodes during PWM OFF time produce a huge amount of power loss. With active freewheeling this power loss can be reduced.
 
 
<c>
void configMotorBridgeTimer(void) {  
void configMotorBridgeTimer(void) {  


// define timer clock, motor timer can be TIM1 or TIM8
// define timer clock, motor timer can be TIM1 or TIM8
RCC_APB2PeriphClockCmd(...);
RCC_APB2PeriphClockCmd(...);
..….


// define the 6 output pins for the bridge, if needed define he input pin for emergeny stop
// define the 6 output pins for the bridge, if needed define
…...
// the input pin for emergeny stop


// Chopper Frequency (PWM for the FETs)
// Chopper Frequency (PWM for the FETs)
Zeile 207: Zeile 197:


   TIM_TimeBaseStructure.TIM_Prescaler = 0;
   TIM_TimeBaseStructure.TIM_Prescaler = 0;
   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
   TIM_TimeBaseStructure.TIM_Period = BLDC_CHOPPER_PERIOD;
   TIM_TimeBaseStructure.TIM_Period = BLDC_CHOPPER_PERIOD;
   TIM_TimeBaseStructure.TIM_ClockDivision = 0;
   TIM_TimeBaseStructure.TIM_ClockDivision = 0;
   TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
   TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
   TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
   TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);


   // Channel 1, 2, 3 – set to PWM mode - all 6 outputs
   // Channel 1, 2, 3 – set to PWM mode - all 6 outputs
Zeile 224: Zeile 207:


   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
   TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
   TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
   TIM_OCInitStructure.TIM_Pulse = 0; // BLDC_ccr_val
   TIM_OCInitStructure.TIM_Pulse = 0; // BLDC_ccr_val


   TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
   TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
   TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
   TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
   TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
   TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
   TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set;
   TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set;


   TIM_OC1Init(TIM1, &TIM_OCInitStructure);
   TIM_OC1Init(TIM1, &TIM_OCInitStructure);
   TIM_OC2Init(TIM1, &TIM_OCInitStructure);
   TIM_OC2Init(TIM1, &TIM_OCInitStructure);
   TIM_OC3Init(TIM1, &TIM_OCInitStructure);
   TIM_OC3Init(TIM1, &TIM_OCInitStructure);
 


   // activate preloading the CCR register
   // activate preloading the CCR register
   TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);  
   TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);  
   TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);  
   TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);  
   TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);  
   TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);  
   


   /* automatic output enable, break off, dead time ca. 200ns and  
   /* automatic output enable, break off, dead time ca. 200ns and  
Zeile 262: Zeile 230:


   TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
   TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
   TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
   TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
   TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF;
   TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF;


   // DeadTime value n=1 bis 31: from 14ns to 1,7us
   // DeadTime value n=1 bis 31: from 14ns to 1,7us
   // DeadTime value n=129 bis 159: from 1,7us to 3,5ms
   // DeadTime value n=129 bis 159: from 1,7µs to 3,5ms
   // DeadTime value 7 => 98ns
   // DeadTime value 7 => 98ns
   // ... see programmers reference manual
   // ... see programmers reference manual
Zeile 276: Zeile 242:


   TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
   TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
   
   
   // enabel this if you use emergency stop signal
   // enabel this if you use emergency stop signal
   // TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
   // TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
   // TIM_BDTRInitStructure.TIM_BreakPolarity = MOTOR_TMC603_EMSTOP_POLARITY;
   // TIM_BDTRInitStructure.TIM_BreakPolarity = MOTOR_TMC603_EMSTOP_POLARITY;


Zeile 286: Zeile 250:


   TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);
   TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);


   // preload ARR register
   // preload ARR register
   TIM_CCPreloadControl(TIM1, ENABLE);
   TIM_CCPreloadControl(TIM1, ENABLE);


 
   // activate COM (Commutation) Event from Slave (HallSensor timer)
 
  // through TRGI
   // activate COM (Commutation) Event from Slave (HallSensor timer) through TRGI
   enableHallCommutateSignal();
   enableHallCommutateSignal();
   
   
   // Internal connection from Hall/Enc Timer to Motor Timer
   // Internal connection from Hall/Enc Timer to Motor Timer
 
   // eg. TIM1 (BLDC Motor Timer) is Slave of TIM3 (Hall Timer)
   // eg. TIM1 (BLDC Motor Timer) is Slave of TIM3 (Hall or Encoder TImer)
 
 
   // Internal connection from Hall/Enc Timer to Motor Timer
   // Internal connection from Hall/Enc Timer to Motor Timer


Zeile 308: Zeile 265:
   // check programmers reference manual
   // check programmers reference manual
   // TIM_SelectInputTrigger(TIM1, TIM_TS_ITR0);
   // TIM_SelectInputTrigger(TIM1, TIM_TS_ITR0);
// MotorTimer = TIM1, HallTimer = TIM5
  // MotorTimer = TIM1, HallTimer = TIM5
   // TIM_SelectInputTrigger(TIM1, TIM_TS_ITR1);
   // TIM_SelectInputTrigger(TIM1, TIM_TS_ITR1);
// MotorTimer = TIM1, HallTimer = TIM2
  // MotorTimer = TIM1, HallTimer = TIM2
   // TIM_SelectInputTrigger(TIM1, TIM_TS_ITR2);
   // TIM_SelectInputTrigger(TIM1, TIM_TS_ITR2);
// MotorTimer = TIM1, HallTimer = TIM3
  // MotorTimer = TIM1, HallTimer = TIM3
  TIM_SelectInputTrigger(TIM1, TIM_TS_ITR3);
  TIM_SelectInputTrigger(TIM1, TIM_TS_ITR3);
// MotorTimer = TIM1, HallTimer = TIM4
  // MotorTimer = TIM1, HallTimer = TIM4
   // TIM_SelectInputTrigger(TIM8, TIM_TS_ITR0);
   // TIM_SelectInputTrigger(TIM8, TIM_TS_ITR0);
// MotorTimer = TIM8, HallTimer = TIM1
  // MotorTimer = TIM8, HallTimer = TIM1
   // TIM_SelectInputTrigger(TIM8, TIM_TS_ITR1);
   // TIM_SelectInputTrigger(TIM8, TIM_TS_ITR1);
// MotorTimer = TIM8, HallTimer = TIM2
  // MotorTimer = TIM8, HallTimer = TIM2
   // TIM_SelectInputTrigger(TIM8, TIM_TS_ITR2);
   // TIM_SelectInputTrigger(TIM8, TIM_TS_ITR2);
// MotorTimer = TIM8, HallTimer = TIM4
  // MotorTimer = TIM8, HallTimer = TIM4
   // TIM_SelectInputTrigger(TIM8, TIM_TS_ITR3);
   // TIM_SelectInputTrigger(TIM8, TIM_TS_ITR3);
// MotorTimer = TIM8, HallTimer = TIM5
  // MotorTimer = TIM8, HallTimer = TIM5


   // Enable interrupt, motor commutation has high piority and has a higher subpriority then the hall sensor
   // Enable interrupt, motor commutation has high piority and has
  // a higher subpriority then the hall sensor
   NVIC_InitStructure.NVIC_IRQChannel = TIM1_TRG_COM_IRQn;
   NVIC_InitStructure.NVIC_IRQChannel = TIM1_TRG_COM_IRQn;


   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; // highest priority
  // highest priority
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;


   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
// highest priority
  // highest priority
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;


   NVIC_Init(&NVIC_InitStructure);
   NVIC_Init(&NVIC_InitStructure);
 


   // Interrupt for hardwired EmergencyStop
   // Interrupt for hardwired EmergencyStop
  (if needed)
  (if needed)
   // Timer 1 Motor Emergency Break Input
   // Timer 1 Motor Emergency Break Input
   // NVIC_InitStructure.NVIC_IRQChannel = TIM1_BRK_IRQn;
   // NVIC_InitStructure.NVIC_IRQChannel = TIM1_BRK_IRQn;
   // NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;
   // NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;
   // NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
   // NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
   // NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   // NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   // NVIC_Init(&NVIC_InitStructure);
   // NVIC_Init(&NVIC_InitStructure);


   // --------- activate the bldc bridge ctrl. ----------
   // --------- activate the bldc bridge ctrl. ----------
Zeile 365: Zeile 315:
   // enable motor timer main output (the bridge signals)
   // enable motor timer main output (the bridge signals)
   TIM_CtrlPWMOutputs(TIM1, ENABLE);
   TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
}
</c>


<c>
// enable the connection between HallTimer and MotorTimer
// enable the connection between HallTimer and MotorTimer
void enableHallCommutateSignal() {
void enableHallCommutateSignal() {
   TIM_SelectCOM(TIM1, ENABLE);
   TIM_SelectCOM(TIM1, ENABLE);
}
}
</c>


<c>
// disable the connection between HallTimer and MotorTimer
// disable the connection between HallTimer and MotorTimer
void disableHallCommutateSignal() {
void disableHallCommutateSignal() {
   TIM_SelectCOM(TIM1, DISABLE);
   TIM_SelectCOM(TIM1, DISABLE);
}
}
</c>


<c>
// BLDC motor steps
// BLDC motor steps
// every row from 1 to 6 is called by a hall state
// every row from 1 to 6 is called by a hall state
Zeile 398: Zeile 339:
// cw - rechtslauf - positiv
// cw - rechtslauf - positiv
// {    1H,1L      ,      2H,2L      ,    3H,3L    }
// {    1H,1L      ,      2H,2L      ,    3H,3L    }
static uint8_t BLDC_BRIDGE_STATE_VORWARD[8][6] = {      // Motor step
static const uint8_t BLDC_BRIDGE_STATE_VORWARD[8][6] =   // Motor step
{
   { FALSE,FALSE  ,  FALSE,FALSE  ,  FALSE,FALSE },  // 0
   { FALSE,FALSE  ,  FALSE,FALSE  ,  FALSE,FALSE },  // 0
   { FALSE,TRUE    ,  TRUE ,FALSE  ,  FALSE,FALSE },  // 2
   { FALSE,TRUE    ,  TRUE ,FALSE  ,  FALSE,FALSE },  // 2
Zeile 408: Zeile 350:
   { FALSE,FALSE  ,  FALSE,FALSE  ,  FALSE,FALSE },  // 0
   { FALSE,FALSE  ,  FALSE,FALSE  ,  FALSE,FALSE },  // 0
};
};
</c>


<c>
// This function handles motor timer trigger and commutation interrupts
// This function handles motor timer trigger and commutation interrupts
// can be used for calculation...
// can be used for calculation...
Zeile 419: Zeile 358:
   // commutationCount++;
   // commutationCount++;
}
}
</c>


<c>
/* This is called from HALL timer interrupt handler
// This is called from HALL timer interrupt handler
  remember:
// remember:
    if hall a hall edge is detected  
//  if hall a hall edge is detected  
    first the motor commutation event is done
//  first the motor commutation event is done
    next this routine is called which has to prepare the next motor step
//  next this routine is called which has to prepare the next motor step
    (which FET must be switched on or off)
//  (which FET must be switched on or off)
  active freewhelling is used to minimize power loss
// active freewhelling is used to minimize power loss
 
//
  code should be easy to understand and to debug... for practical use  
// code should be easy to understand and to debug... for practical use  
  you should optimize it */
// you should optimize it


//
static __INLINE void BLDCMotorPrepareCommutation(void)
static __INLINE void BLDCMotorPrepareCommutation(void)
{
{
 
   // next bridge step calculated by HallSensor inputs
   // next bridge step calculated by HallSensor inputs
   // if there was an hall event without changing the hall position, do nothing
   // 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  
   // In principle, on every hall event you can go to the next motor  
   // step but i had sometimes problems that the motor was running
   // step but i had sometimes problems that the motor was running
   // on an harmonic wave (??) when the motor was without load
   // on an harmonic wave (??) when the motor was without load
   uint16_t newhallpos = ((GPIO_ReadInputData(GPIOD) & 0x7000) >> 12);
   uint16_t newhallpos = ((GPIO_ReadInputData(GPIOD) & 0x7000) >> 12);


   if (newhallpos == hallpos) return;
   if (newhallpos == hallpos) return;
Zeile 452: Zeile 385:


   hallpos = newhallpos;
   hallpos = newhallpos;
 


   // this is only for motor direction forward   
   // this is only for motor direction forward   


   BH1 = BLDC_BRIDGE_STATE_VORWARD[hallpos][0];
   BH1 = BLDC_BRIDGE_STATE_VORWARD[hallpos][0];
   BL1 = BLDC_BRIDGE_STATE_VORWARD[hallpos][1];
   BL1 = BLDC_BRIDGE_STATE_VORWARD[hallpos][1];


   BH2 = BLDC_BRIDGE_STATE_VORWARD[hallpos][2];
   BH2 = BLDC_BRIDGE_STATE_VORWARD[hallpos][2];
   BL2 = BLDC_BRIDGE_STATE_VORWARD[hallpos][3];
   BL2 = BLDC_BRIDGE_STATE_VORWARD[hallpos][3];


   BH3 = BLDC_BRIDGE_STATE_VORWARD[hallpos][4];
   BH3 = BLDC_BRIDGE_STATE_VORWARD[hallpos][4];
   BL3 = BLDC_BRIDGE_STATE_VORWARD[hallpos][5];   
   BL3 = BLDC_BRIDGE_STATE_VORWARD[hallpos][5];   
     


   // **** this is with active freewheeling ****   
   // **** this is with active freewheeling ****   
 


   // Bridge FETs for Motor Phase U
   // Bridge FETs for Motor Phase U
Zeile 510: Zeile 434:


   }
   }
 


   // Bridge FETs for Motor Phase V
   // Bridge FETs for Motor Phase V


   if (BH2) {
   if (BH2) {
     TIM_SelectOCxM(TIM1, TIM_Channel_2, TIM_OCMode_PWM1);
     TIM_SelectOCxM(TIM1, TIM_Channel_2, TIM_OCMode_PWM1);
     TIM_CCxCmd(TIM1, TIM_Channel_2, TIM_CCx_Enable);
     TIM_CCxCmd(TIM1, TIM_Channel_2, TIM_CCx_Enable);
     TIM_CCxNCmd(TIM1, TIM_Channel_2, TIM_CCxN_Enable);
     TIM_CCxNCmd(TIM1, TIM_Channel_2, TIM_CCxN_Enable);
   } else {
   } else {
     TIM_CCxCmd(TIM1, TIM_Channel_2, TIM_CCx_Disable);
     TIM_CCxCmd(TIM1, TIM_Channel_2, TIM_CCx_Disable);


     if (BL2){
     if (BL2){
       TIM_SelectOCxM(TIM1, TIM_Channel_2, TIM_ForcedAction_Active);
       TIM_SelectOCxM(TIM1, TIM_Channel_2, TIM_ForcedAction_Active);
       TIM_CCxNCmd(TIM1, TIM_Channel_2, TIM_CCxN_Enable);
       TIM_CCxNCmd(TIM1, TIM_Channel_2, TIM_CCxN_Enable);
     } else {
     } else {
       TIM_CCxNCmd(TIM1, TIM_Channel_2, TIM_CCxN_Disable);
       TIM_CCxNCmd(TIM1, TIM_Channel_2, TIM_CCxN_Disable);
     }
     }
   }
   }
 


   // Bridge FETs for Motor Phase W
   // Bridge FETs for Motor Phase W


    if (BH3) {
  if (BH3) {
 
     TIM_SelectOCxM(TIM1, TIM_Channel_3, TIM_OCMode_PWM1);
     TIM_SelectOCxM(TIM1, TIM_Channel_3, TIM_OCMode_PWM1);
     TIM_CCxCmd(TIM1, TIM_Channel_3, TIM_CCx_Enable);
     TIM_CCxCmd(TIM1, TIM_Channel_3, TIM_CCx_Enable);
     TIM_CCxNCmd(TIM1, TIM_Channel_3, TIM_CCxN_Enable);
     TIM_CCxNCmd(TIM1, TIM_Channel_3, TIM_CCxN_Enable);
   } else {
   } else {
     TIM_CCxCmd(TIM1, TIM_Channel_3, TIM_CCx_Disable);
     TIM_CCxCmd(TIM1, TIM_Channel_3, TIM_CCx_Disable);


    if (BL3){
  if (BL3){
 
       TIM_SelectOCxM(TIM1, TIM_Channel_3, TIM_ForcedAction_Active);
       TIM_SelectOCxM(TIM1, TIM_Channel_3, TIM_ForcedAction_Active);
       TIM_CCxNCmd(TIM1, TIM_Channel_3, TIM_CCxN_Enable);
       TIM_CCxNCmd(TIM1, TIM_Channel_3, TIM_CCxN_Enable);
     } else {
     } else {
       TIM_CCxNCmd(TIM1, TIM_Channel_3, TIM_CCxN_Disable);
       TIM_CCxNCmd(TIM1, TIM_Channel_3, TIM_CCxN_Disable);
     }
     }
   }
   }
}
}
</c>
</syntaxhighlight>


== Some things good to know ==
== Some things good to know ==


<c>
<syntaxhighlight lang="c">
// You can manually generate an commutation event (like the hall sensor)
// You can manually generate an commutation event (like the hall sensor)
TIM_GenerateEvent(TIM1, TIM_EventSource_COM);
TIM_GenerateEvent(TIM1, TIM_EventSource_COM);
Zeile 584: Zeile 481:
TIM1->CCR2 = new_ccr_val;
TIM1->CCR2 = new_ccr_val;
TIM1->CCR3 = new_ccr_val;
TIM1->CCR3 = new_ccr_val;
</c>
</syntaxhighlight>
 
== Footnotes ==
<references/>
 
[[Kategorie:STM32]]

Aktuelle Version vom 21. April 2016, 21:32 Uhr

The code is not a complete project, it's to show you how to use the motor timer to control an BLDC Motor in combination with an HALL Sensor on another timer.

Intro

The system needs two timers. One to control PWM for the bridge FETs and another timer to identify the HallSensor singnals. The HallSensor timer triggers the motor timer commutation event.

The connection betwenn these two timers is done in the background with events. There is no direct need for interrupt handling to commutate the motor timer.

  • BLDC Bridge FETs: TIM1
  • HallSensor: TIM4

Recommendation: For your first projects and to learn more, I suggest to use a protection 3 phase motor driver. In there documents you find good information about shoot-through protection, on-time, off-time, dead-time, hall-steps, rpm and field frequency, cutoff filter frequency and many more. Some of the chips offers also simplified usage of sensorless motor control because they detect the rotorposition and give back a virtual Hall Signal, see the TMC product.

If you have a simple FET bridge driver then you must carfully calculate the dead time which is a prameter in the motortimer.

Active Freewheeling
Without active freewheeling the body diodes during PWM OFF time produce a huge amount of power loss. With active freewheeling this power loss can be reduced.
Info
Check HallSensor Inputs. They often needs PullUps, use RC filters to filter bad signals.

Code for the HallSensor timer

Internal Connection from Hall/Enc Timer to Motor Timer. The HALL Timer Output is direct connected to the Motor Timer Commutation Trigger. If the correct combination of Motor and Hall/Enc Timer is selected then this is done for you. If you can not use the internal Connection you have to do this in an Interrupt manually.

Only following Combinations are possible[1]

If Motor is on Timer1 this is possible

  • Hall/Enc is Timer 2 → Motor is Timer 1 → use TIM_TS_ITR1
  • Hall/Enc is Timer 3 → Motor is Timer 1 → use TIM_TS_ITR2
  • Hall/Enc is Timer 4 → Motor is Timer 1 → use TIM_TS_ITR3
  • Hall/Enc is Timer 5 → Motor is Timer 1 → use TIM_TS_ITR0

If Motor is on Timer8 this is possible

  • Hall/Enc is Timer 1 → Motor is Timer 1 → use TIM_TS_ITR0
  • Hall/Enc is Timer 2 → Motor is Timer 1 → use TIM_TS_ITR1
  • Hall/Enc is Timer 4 → Motor is Timer 1 → use TIM_TS_ITR2
  • Hall/Enc is Timer 5 → Motor is Timer 1 → use TIM_TS_ITR3
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 
  } 
}

Code for BLDC motor control timer

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);
    }
  }
}

Some things good to know

// 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;

Footnotes

  1. Check STM32 Reference Manual for Internal Trigger 1 to 4 (ITR1 to ITR4). See Table "TIMx Internal trigger connection".