/*
**
******************************************************************************
* @file : MESCBLDC.c
* @brief : BLDC running code
******************************************************************************
* @attention
*
*
© Copyright (c) 2020 David Molony.
* All rights reserved.
*
* This software component is licensed under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
* MESCBLDC.c
*
* Created on: 25 Jul 2020
* Author: David Molony
*/
#include "MESCBLDC.h"
#include "MESCfoc.h"
#include "MESCpwm.h"
#include "MESChw_setup.h"
#include "MESCmotor_state.h"
foc_measurement_t measurement_buffers;
extern TIM_HandleTypeDef htim1;
MESCBLDCVars_s BLDCVars;
MESCBLDCState_e BLDCState;
void BLDCInit() {
BLDCVars.ReqCurrent = 0; // Start the motor at 0 current
BLDCVars.BLDCduty = 0;
BLDCVars.CurrentChannel = 0;
BLDCVars.currentCurrent = 0;
BLDCVars.pGain =
1023 * mtr[0].m.R /
8; // wtf should I set the gain as by default... V/Amp error...Perhaps
// base it on Rphase and the bus voltage (nominally 48V)? But we don;t
// know the exact bus voltage yet...
BLDCVars.iGain = BLDCVars.pGain; // After experimentation, igain of pgain
// seems to work well.
BLDCVars.BLDCEstate = GetHallState();
BLDCState = BLDC_FORWARDS;
}
void BLDCCommuteHall() {
int CurrentHallState =
GetHallState(); // Borrow the hall state detection from the FOC system
static int LastHallState = 7;
if (BLDCState == BLDC_FORWARDS) {
BLDCVars.BLDCEstate = (CurrentHallState + 2) % 6;
writeBLDC(); // Write the PWM values for the next state to generate
// forward torque
if (!(BLDCVars.BLDCEstate == (CurrentHallState + 1))) {
// ToDo Fix if the writeBLDC command is put in here, the PWM duty
// gets stuck at 0.
}
} else if (BLDCState == BLDC_BACKWARDS) {
BLDCVars.BLDCEstate = (CurrentHallState + 4) % 6;
writeBLDC(); // Write the PWM values for the previous state to generate
// reverse torque
// FIXME: what is this supposed to accomplish?
// commented out since this code does nothing and is likely removed by
// the compiler. if(!(CurrentHallState==CurrentHallState)){
// }
} else if (BLDCState == BLDC_BRAKE) {
int hallStateChange = CurrentHallState - LastHallState;
// ToDo Logic to always be on synch or hanging 1 step in front or
// behind... ToDo this does not cope with the roll-over, making for a
// very jerky brake
// TODO: the expression inside if() statement is very hard to read.
// Create separate variable.
if (((hallStateChange) % 6) > 1) {
BLDCVars.BLDCEstate = (CurrentHallState + 5) % 6;
} else if (((CurrentHallState - LastHallState) % 6) < -1) {
BLDCVars.BLDCEstate = (CurrentHallState + 1) % 6;
LastHallState = CurrentHallState;
}
writeBLDC();
} else {
// Disable the drivers, freewheel
// fixme: misleading function name. If this is freewheel, then it should
// be named as such.
MESCpwm_phU_Break(&mtr[0]);
MESCpwm_phV_Break(&mtr[0]);
MESCpwm_phW_Break(&mtr[0]);
}
}
void BLDCCurrentController() {
// Implement a simple PI controller
static float CurrentError = 0;
static float CurrentIntegralError = 0;
static int Duty = 0;
BLDCVars.currentCurrent =
measurement_buffers.ConvertedADC[BLDCVars.CurrentChannel][0];
CurrentError = (BLDCVars.ReqCurrent - BLDCVars.currentCurrent);
// measurement_buffers.ConvertedADC[BLDCVars.CurrentChannel][0]);
CurrentIntegralError =
CurrentIntegralError +
CurrentError *
0.0027f; // 37kHz PWM, so the integral portion should
// be multiplied by 1/37k before accumulating Interesting
// behaviour with gating - increasing the factor here and
// changing the pgain have different results - need to have
// high gain prior to gating and stability becomes much
// better.
if (CurrentIntegralError > 20) CurrentIntegralError = 20; // Magic numbers
if (CurrentIntegralError < -20) CurrentIntegralError = -20; // Magic numbers
Duty = (int)(CurrentError * BLDCVars.pGain +
CurrentIntegralError * BLDCVars.iGain);
if (Duty > 1023) {
Duty = 1023;
} else if (Duty < 0) {
Duty = 0;
}
BLDCVars.BLDCduty = Duty;
}
void writeBLDC() {
switch (BLDCVars.BLDCEstate) {
case 0:
// disable phase first
MESCpwm_phW_Break(&mtr[0]);
// WritePWM values
htim1.Instance->CCR1 = BLDCVars.BLDCduty;
htim1.Instance->CCR2 = 0;
MESCpwm_phU_Enable(&mtr[0]);
MESCpwm_phV_Enable(&mtr[0]);
BLDCVars.CurrentChannel =
1; // Write the field into which the lowside current will flow,
// to be retrieved from the FOC_measurement_vars
break;
case 1:
MESCpwm_phV_Break(&mtr[0]);
htim1.Instance->CCR1 = BLDCVars.BLDCduty;
htim1.Instance->CCR3 = 0;
MESCpwm_phU_Enable(&mtr[0]);
MESCpwm_phW_Enable(&mtr[0]);
BLDCVars.CurrentChannel = 2;
break;
case 2:
MESCpwm_phU_Break(&mtr[0]);
htim1.Instance->CCR2 = BLDCVars.BLDCduty;
htim1.Instance->CCR3 = 0;
MESCpwm_phV_Enable(&mtr[0]);
MESCpwm_phW_Enable(&mtr[0]);
BLDCVars.CurrentChannel = 2;
break;
case 3:
MESCpwm_phW_Break(&mtr[0]);
htim1.Instance->CCR1 = 0;
htim1.Instance->CCR2 = BLDCVars.BLDCduty;
MESCpwm_phU_Enable(&mtr[0]);
MESCpwm_phV_Enable(&mtr[0]);
BLDCVars.CurrentChannel = 0;
break;
case 4:
MESCpwm_phV_Break(&mtr[0]);
htim1.Instance->CCR1 = 0;
htim1.Instance->CCR3 = BLDCVars.BLDCduty;
MESCpwm_phU_Enable(&mtr[0]);
MESCpwm_phW_Enable(&mtr[0]);
BLDCVars.CurrentChannel = 0;
break;
case 5:
MESCpwm_phU_Break(&mtr[0]);
htim1.Instance->CCR2 = 0;
htim1.Instance->CCR3 = BLDCVars.BLDCduty;
MESCpwm_phV_Enable(&mtr[0]);
MESCpwm_phW_Enable(&mtr[0]);
BLDCVars.CurrentChannel = 1;
break;
default:
break;
}
}
int GetHallState() {
switch (getHallState()) {
case 0:
return 7; // 7 is the no hall sensor detected state (all low)
break;
case 7:
return 6; // 6 is the no hall sensor detected state (all high)
break;
// Implement the hall table order here, depending how the hall
// sensors are configured
case 1:
return 0;
break;
case 3:
return 1;
break;
case 2:
return 2;
break;
case 6:
return 3;
break;
case 4:
return 4;
break;
case 5:
return 5;
break;
default:
return 8;
break;
}
}
void BLDCCommute(MESC_motor_typedef *_motor){
//Collect the variables required
switch (_motor->BLDC.sector){
case 0:
_motor->BLDC.I_meas = _motor->Conv.Iu;
// _motor->BLDC.V_meas = _motor->Conv.Vv;
// _motor->BLDC.rising_int = _motor->BLDC.rising_int + _motor->BLDC.V_meas*_motor->BLDC.PWM_period;
break;
case 1:
_motor->BLDC.I_meas = _motor->Conv.Iu;
// _motor->BLDC.V_meas = _motor->Conv.Vw;
// _motor->BLDC.falling_int = _motor->BLDC.rising_int + _motor->BLDC.V_meas*_motor->BLDC.PWM_period;
break;
case 2:
_motor->BLDC.I_meas = _motor->Conv.Iw;
// _motor->BLDC.V_meas = _motor->Conv.Vu;
// _motor->BLDC.rising_int = _motor->BLDC.rising_int + _motor->BLDC.V_meas*_motor->BLDC.PWM_period;
break;
case 3:
_motor->BLDC.I_meas = _motor->Conv.Iw;
// _motor->BLDC.V_meas = _motor->Conv.Vv;
// _motor->BLDC.falling_int = _motor->BLDC.rising_int + _motor->BLDC.V_meas*_motor->BLDC.PWM_period;
break;
case 4:
_motor->BLDC.I_meas = _motor->Conv.Iv;
// _motor->BLDC.V_meas = _motor->Conv.Vw;
// _motor->BLDC.rising_int = _motor->BLDC.rising_int + _motor->BLDC.V_meas*_motor->BLDC.PWM_period;
break;
case 5:
_motor->BLDC.I_meas = _motor->Conv.Iv;
// _motor->BLDC.V_meas = _motor->Conv.Vu;
// _motor->BLDC.falling_int = _motor->BLDC.rising_int + _motor->BLDC.V_meas*_motor->BLDC.PWM_period;
break;
}
// //Reduce the rising and falling integrals
// _motor->BLDC.rising_int = _motor->BLDC.rising_int * 0.999f;
// _motor->BLDC.falling_int = _motor->BLDC.rising_int * 0.999f;
//Invert the current since we are measuring the current leaving the motor but controlling the voltage where current is going in
_motor->BLDC.I_meas = -_motor->BLDC.I_meas;
//////Run PID
_motor->BLDC.I_pgain = _motor->FOC.Iq_pgain;//Borrow from FOC for now
_motor->BLDC.I_igain = _motor->FOC.Iq_igain;//Borrow from FOC for now
_motor->BLDC.PWM_period = _motor->FOC.pwm_period;//Borrow from FOC for now
//Calculate the error
_motor->BLDC.I_error = (_motor->BLDC.I_set-_motor->BLDC.I_meas)*_motor->BLDC.I_pgain;
_motor->BLDC.int_I_error = //Calculate the integral error
_motor->BLDC.int_I_error + _motor->BLDC.I_error * _motor->BLDC.I_igain * _motor->BLDC.PWM_period;
_motor->BLDC.V_bldc = _motor->BLDC.int_I_error + _motor->BLDC.I_error;
//Bounding
if(_motor->BLDC.V_bldc > 0.95f * _motor->Conv.Vbus){
_motor->BLDC.V_bldc = 0.95f * _motor->Conv.Vbus;
if(_motor->BLDC.int_I_error > _motor->Conv.Vbus){
_motor->BLDC.int_I_error = _motor->Conv.Vbus;
_motor->BLDC.I_error = 0.05f*_motor->BLDC.int_I_error;
}
}
//Determine the conversion from volts to PWM
_motor->BLDC.V_bldc_to_PWM = _motor->mtimer->Instance->ARR/_motor->Conv.Vbus;
//Convert to PWM value
_motor->BLDC.BLDC_PWM = _motor->BLDC.V_bldc*_motor->BLDC.V_bldc_to_PWM;
//////Integrate and determine if commutation ready, VBEMF = Vbldc-2*I*Rphase
_motor->BLDC.flux_integral = _motor->BLDC.flux_integral + (_motor->BLDC.V_bldc - _motor->BLDC.I_meas * 2.0f*_motor->m.R)* _motor->BLDC.PWM_period; //Volt seconds
//FUDGED
_motor->BLDC.closed_loop = 1;
if(_motor->BLDC.closed_loop){
//If the flux reaches a limit then commute
if(_motor->BLDC.flux_integral<0.0f){_motor->BLDC.flux_integral = 0.0f;}
if(_motor->BLDC.flux_integral>_motor->BLDC.com_flux){
_motor->BLDC.V_meas_sect[_motor->BLDC.sector] = _motor->BLDC.V_meas;
_motor->BLDC.sector = _motor->BLDC.sector + _motor->BLDC.direction;
_motor->BLDC.last_flux_integral = _motor->BLDC.flux_integral;
_motor->BLDC.flux_integral = 0.0f; //Reset the integrator
_motor->BLDC.last_p_error = _motor->BLDC.I_error;
//Run a vague tuning mechanism, needs a lot of work.
if((_motor->BLDC.int_I_error>_motor->Conv.Vbus*0.4f) && (_motor->BLDC.int_I_error<_motor->Conv.Vbus*0.9f)){
if(_motor->BLDC.I_error>0.05f*_motor->BLDC.int_I_error){
_motor->BLDC.com_flux = _motor->BLDC.com_flux*1.005f;
}
if(_motor->BLDC.I_error<0.05f*_motor->BLDC.int_I_error){
_motor->BLDC.com_flux = _motor->BLDC.com_flux*0.99f;
}
}
// _motor->BLDC.rising_int_st =_motor->BLDC.rising_int;
// _motor->BLDC.rising_int = 0.0f;
// _motor->BLDC.falling_int_st = _motor->BLDC.falling_int;
// _motor->BLDC.falling_int = 0.0f;
// if(_motor->BLDC.falling_int_st > _motor->BLDC.falling_int_st){
// _motor->BLDC.com_flux = _motor->BLDC.com_flux * 1.01f;
// }else{
// _motor->BLDC.com_flux = _motor->BLDC.com_flux * 0.99f;
// }
//
// if(_motor->BLDC.com_flux<0.018f){_motor->BLDC.com_flux=0.018f;}
// if(_motor->BLDC.com_flux>0.022f){_motor->BLDC.com_flux=0.022f;}
}
}else{
_motor->BLDC.OL_countdown--;
if(_motor->BLDC.OL_countdown == 0){
_motor->BLDC.OL_countdown =_motor->BLDC.OL_periods;
_motor->BLDC.sector++;
_motor->BLDC.last_flux_integral = _motor->BLDC.flux_integral;
_motor->BLDC.flux_integral = 0.0f; //Reset the integrator
}
}
//////Wrap the sector
if(_motor->BLDC.sector>5){
_motor->BLDC.sector = 0;
}else if(_motor->BLDC.sector<0){
_motor->BLDC.sector = 5;
}
//////Write PWMs
switch (_motor->BLDC.sector){
case 0:
MESCpwm_phV_Break(_motor);
MESCpwm_phU_Enable(_motor);
MESCpwm_phW_Enable(_motor);
_motor->mtimer->Instance->CCR1 = 0;
_motor->mtimer->Instance->CCR3 = _motor->BLDC.BLDC_PWM;
break;
case 1:
MESCpwm_phW_Break(_motor);
MESCpwm_phU_Enable(_motor);
MESCpwm_phV_Enable(_motor);
_motor->mtimer->Instance->CCR1 = 0;
_motor->mtimer->Instance->CCR2 = _motor->BLDC.BLDC_PWM;
break;
case 2:
MESCpwm_phU_Break(_motor);
MESCpwm_phV_Enable(_motor);
MESCpwm_phW_Enable(_motor);
_motor->mtimer->Instance->CCR3 = 0;
_motor->mtimer->Instance->CCR2 = _motor->BLDC.BLDC_PWM;
break;
case 3:
MESCpwm_phV_Break(_motor);
MESCpwm_phU_Enable(_motor);
MESCpwm_phW_Enable(_motor);
_motor->mtimer->Instance->CCR3 = 0;
_motor->mtimer->Instance->CCR1 = _motor->BLDC.BLDC_PWM;
break;
case 4:
MESCpwm_phW_Break(_motor);
MESCpwm_phU_Enable(_motor);
MESCpwm_phV_Enable(_motor);
_motor->mtimer->Instance->CCR2 = 0;
_motor->mtimer->Instance->CCR1 = _motor->BLDC.BLDC_PWM;
break;
case 5:
MESCpwm_phU_Break(_motor);
MESCpwm_phV_Enable(_motor);
MESCpwm_phW_Enable(_motor);
_motor->mtimer->Instance->CCR2 = 0;
_motor->mtimer->Instance->CCR3 = _motor->BLDC.BLDC_PWM;
break;
default:
//Reset to 0, something went wrong...
_motor->BLDC.sector = 0;
break;
}
}
void CalculateBLDCGains(MESC_motor_typedef *_motor){
}