try to fix submodule
This commit is contained in:
92
firmware/lib/Arduino-FOC/src/drivers/BLDCDriver3PWM.cpp
Normal file
92
firmware/lib/Arduino-FOC/src/drivers/BLDCDriver3PWM.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "BLDCDriver3PWM.h"
|
||||
|
||||
BLDCDriver3PWM::BLDCDriver3PWM(int phA, int phB, int phC, int en1, int en2, int en3){
|
||||
// Pin initialization
|
||||
pwmA = phA;
|
||||
pwmB = phB;
|
||||
pwmC = phC;
|
||||
|
||||
// enable_pin pin
|
||||
enableA_pin = en1;
|
||||
enableB_pin = en2;
|
||||
enableC_pin = en3;
|
||||
|
||||
// default power-supply value
|
||||
voltage_power_supply = DEF_POWER_SUPPLY;
|
||||
voltage_limit = NOT_SET;
|
||||
pwm_frequency = NOT_SET;
|
||||
|
||||
}
|
||||
|
||||
// enable motor driver
|
||||
void BLDCDriver3PWM::enable(){
|
||||
// enable_pin the driver - if enable_pin pin available
|
||||
if ( _isset(enableA_pin) ) digitalWrite(enableA_pin, enable_active_high);
|
||||
if ( _isset(enableB_pin) ) digitalWrite(enableB_pin, enable_active_high);
|
||||
if ( _isset(enableC_pin) ) digitalWrite(enableC_pin, enable_active_high);
|
||||
// set zero to PWM
|
||||
setPwm(0,0,0);
|
||||
}
|
||||
|
||||
// disable motor driver
|
||||
void BLDCDriver3PWM::disable()
|
||||
{
|
||||
// set zero to PWM
|
||||
setPwm(0, 0, 0);
|
||||
// disable the driver - if enable_pin pin available
|
||||
if ( _isset(enableA_pin) ) digitalWrite(enableA_pin, !enable_active_high);
|
||||
if ( _isset(enableB_pin) ) digitalWrite(enableB_pin, !enable_active_high);
|
||||
if ( _isset(enableC_pin) ) digitalWrite(enableC_pin, !enable_active_high);
|
||||
|
||||
}
|
||||
|
||||
// init hardware pins
|
||||
int BLDCDriver3PWM::init() {
|
||||
// PWM pins
|
||||
pinMode(pwmA, OUTPUT);
|
||||
pinMode(pwmB, OUTPUT);
|
||||
pinMode(pwmC, OUTPUT);
|
||||
if( _isset(enableA_pin)) pinMode(enableA_pin, OUTPUT);
|
||||
if( _isset(enableB_pin)) pinMode(enableB_pin, OUTPUT);
|
||||
if( _isset(enableC_pin)) pinMode(enableC_pin, OUTPUT);
|
||||
|
||||
|
||||
// sanity check for the voltage limit configuration
|
||||
if(!_isset(voltage_limit) || voltage_limit > voltage_power_supply) voltage_limit = voltage_power_supply;
|
||||
|
||||
// Set the pwm frequency to the pins
|
||||
// hardware specific function - depending on driver and mcu
|
||||
params = _configure3PWM(pwm_frequency, pwmA, pwmB, pwmC);
|
||||
initialized = (params!=SIMPLEFOC_DRIVER_INIT_FAILED);
|
||||
return params!=SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Set voltage to the pwm pin
|
||||
void BLDCDriver3PWM::setPhaseState(PhaseState sa, PhaseState sb, PhaseState sc) {
|
||||
// disable if needed
|
||||
if( _isset(enableA_pin) && _isset(enableB_pin) && _isset(enableC_pin) ){
|
||||
digitalWrite(enableA_pin, sa == PhaseState::PHASE_ON ? enable_active_high:!enable_active_high);
|
||||
digitalWrite(enableB_pin, sb == PhaseState::PHASE_ON ? enable_active_high:!enable_active_high);
|
||||
digitalWrite(enableC_pin, sc == PhaseState::PHASE_ON ? enable_active_high:!enable_active_high);
|
||||
}
|
||||
}
|
||||
|
||||
// Set voltage to the pwm pin
|
||||
void BLDCDriver3PWM::setPwm(float Ua, float Ub, float Uc) {
|
||||
|
||||
// limit the voltage in driver
|
||||
Ua = _constrain(Ua, 0.0f, voltage_limit);
|
||||
Ub = _constrain(Ub, 0.0f, voltage_limit);
|
||||
Uc = _constrain(Uc, 0.0f, voltage_limit);
|
||||
// calculate duty cycle
|
||||
// limited in [0,1]
|
||||
dc_a = _constrain(Ua / voltage_power_supply, 0.0f , 1.0f );
|
||||
dc_b = _constrain(Ub / voltage_power_supply, 0.0f , 1.0f );
|
||||
dc_c = _constrain(Uc / voltage_power_supply, 0.0f , 1.0f );
|
||||
|
||||
// hardware specific writing
|
||||
// hardware specific function - depending on driver and mcu
|
||||
_writeDutyCycle3PWM(dc_a, dc_b, dc_c, params);
|
||||
}
|
||||
64
firmware/lib/Arduino-FOC/src/drivers/BLDCDriver3PWM.h
Normal file
64
firmware/lib/Arduino-FOC/src/drivers/BLDCDriver3PWM.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef BLDCDriver3PWM_h
|
||||
#define BLDCDriver3PWM_h
|
||||
|
||||
#include "../common/base_classes/BLDCDriver.h"
|
||||
#include "../common/foc_utils.h"
|
||||
#include "../common/time_utils.h"
|
||||
#include "../common/defaults.h"
|
||||
#include "hardware_api.h"
|
||||
|
||||
/**
|
||||
3 pwm bldc driver class
|
||||
*/
|
||||
class BLDCDriver3PWM: public BLDCDriver
|
||||
{
|
||||
public:
|
||||
/**
|
||||
BLDCDriver class constructor
|
||||
@param phA A phase pwm pin
|
||||
@param phB B phase pwm pin
|
||||
@param phC C phase pwm pin
|
||||
@param en1 enable pin (optional input)
|
||||
@param en2 enable pin (optional input)
|
||||
@param en3 enable pin (optional input)
|
||||
*/
|
||||
BLDCDriver3PWM(int phA,int phB,int phC, int en1 = NOT_SET, int en2 = NOT_SET, int en3 = NOT_SET);
|
||||
|
||||
/** Motor hardware init function */
|
||||
int init() override;
|
||||
/** Motor disable function */
|
||||
void disable() override;
|
||||
/** Motor enable function */
|
||||
void enable() override;
|
||||
|
||||
// hardware variables
|
||||
int pwmA; //!< phase A pwm pin number
|
||||
int pwmB; //!< phase B pwm pin number
|
||||
int pwmC; //!< phase C pwm pin number
|
||||
int enableA_pin; //!< enable pin number
|
||||
int enableB_pin; //!< enable pin number
|
||||
int enableC_pin; //!< enable pin number
|
||||
bool enable_active_high = true;
|
||||
|
||||
/**
|
||||
* Set phase voltages to the harware
|
||||
*
|
||||
* @param Ua - phase A voltage
|
||||
* @param Ub - phase B voltage
|
||||
* @param Uc - phase C voltage
|
||||
*/
|
||||
void setPwm(float Ua, float Ub, float Uc) override;
|
||||
|
||||
/**
|
||||
* Set phase voltages to the harware
|
||||
*
|
||||
* @param sc - phase A state : active / disabled ( high impedance )
|
||||
* @param sb - phase B state : active / disabled ( high impedance )
|
||||
* @param sa - phase C state : active / disabled ( high impedance )
|
||||
*/
|
||||
virtual void setPhaseState(PhaseState sa, PhaseState sb, PhaseState sc) override;
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
103
firmware/lib/Arduino-FOC/src/drivers/BLDCDriver6PWM.cpp
Normal file
103
firmware/lib/Arduino-FOC/src/drivers/BLDCDriver6PWM.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "BLDCDriver6PWM.h"
|
||||
|
||||
BLDCDriver6PWM::BLDCDriver6PWM(int phA_h,int phA_l,int phB_h,int phB_l,int phC_h,int phC_l, int en){
|
||||
// Pin initialization
|
||||
pwmA_h = phA_h;
|
||||
pwmB_h = phB_h;
|
||||
pwmC_h = phC_h;
|
||||
pwmA_l = phA_l;
|
||||
pwmB_l = phB_l;
|
||||
pwmC_l = phC_l;
|
||||
|
||||
// enable_pin pin
|
||||
enable_pin = en;
|
||||
|
||||
// default power-supply value
|
||||
voltage_power_supply = DEF_POWER_SUPPLY;
|
||||
voltage_limit = NOT_SET;
|
||||
pwm_frequency = NOT_SET;
|
||||
|
||||
// dead zone initial - 2%
|
||||
dead_zone = 0.02f;
|
||||
|
||||
}
|
||||
|
||||
// enable motor driver
|
||||
void BLDCDriver6PWM::enable(){
|
||||
// enable_pin the driver - if enable_pin pin available
|
||||
if ( _isset(enable_pin) ) digitalWrite(enable_pin, enable_active_high);
|
||||
// set phase state enabled
|
||||
setPhaseState(PhaseState::PHASE_ON, PhaseState::PHASE_ON, PhaseState::PHASE_ON);
|
||||
// set zero to PWM
|
||||
setPwm(0, 0, 0);
|
||||
}
|
||||
|
||||
// disable motor driver
|
||||
void BLDCDriver6PWM::disable()
|
||||
{
|
||||
// set phase state to disabled
|
||||
setPhaseState(PhaseState::PHASE_OFF, PhaseState::PHASE_OFF, PhaseState::PHASE_OFF);
|
||||
// set zero to PWM
|
||||
setPwm(0, 0, 0);
|
||||
// disable the driver - if enable_pin pin available
|
||||
if ( _isset(enable_pin) ) digitalWrite(enable_pin, !enable_active_high);
|
||||
|
||||
}
|
||||
|
||||
// init hardware pins
|
||||
int BLDCDriver6PWM::init() {
|
||||
|
||||
// PWM pins
|
||||
pinMode(pwmA_h, OUTPUT);
|
||||
pinMode(pwmB_h, OUTPUT);
|
||||
pinMode(pwmC_h, OUTPUT);
|
||||
pinMode(pwmA_l, OUTPUT);
|
||||
pinMode(pwmB_l, OUTPUT);
|
||||
pinMode(pwmC_l, OUTPUT);
|
||||
if(_isset(enable_pin)) pinMode(enable_pin, OUTPUT);
|
||||
|
||||
|
||||
// sanity check for the voltage limit configuration
|
||||
if( !_isset(voltage_limit) || voltage_limit > voltage_power_supply) voltage_limit = voltage_power_supply;
|
||||
|
||||
// set phase state to disabled
|
||||
phase_state[0] = PhaseState::PHASE_OFF;
|
||||
phase_state[1] = PhaseState::PHASE_OFF;
|
||||
phase_state[2] = PhaseState::PHASE_OFF;
|
||||
|
||||
// set zero to PWM
|
||||
dc_a = dc_b = dc_c = 0;
|
||||
|
||||
// configure 6pwm
|
||||
// hardware specific function - depending on driver and mcu
|
||||
params = _configure6PWM(pwm_frequency, dead_zone, pwmA_h,pwmA_l, pwmB_h,pwmB_l, pwmC_h,pwmC_l);
|
||||
initialized = (params!=SIMPLEFOC_DRIVER_INIT_FAILED);
|
||||
return params!=SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
|
||||
|
||||
// Set voltage to the pwm pin
|
||||
void BLDCDriver6PWM::setPwm(float Ua, float Ub, float Uc) {
|
||||
// limit the voltage in driver
|
||||
Ua = _constrain(Ua, 0, voltage_limit);
|
||||
Ub = _constrain(Ub, 0, voltage_limit);
|
||||
Uc = _constrain(Uc, 0, voltage_limit);
|
||||
// calculate duty cycle
|
||||
// limited in [0,1]
|
||||
dc_a = _constrain(Ua / voltage_power_supply, 0.0f , 1.0f );
|
||||
dc_b = _constrain(Ub / voltage_power_supply, 0.0f , 1.0f );
|
||||
dc_c = _constrain(Uc / voltage_power_supply, 0.0f , 1.0f );
|
||||
// hardware specific writing
|
||||
// hardware specific function - depending on driver and mcu
|
||||
_writeDutyCycle6PWM(dc_a, dc_b, dc_c, phase_state, params);
|
||||
}
|
||||
|
||||
|
||||
// Set the phase state
|
||||
// actually changing the state is only done on the next call to setPwm, and depends
|
||||
// on the hardware capabilities of the driver and MCU.
|
||||
void BLDCDriver6PWM::setPhaseState(PhaseState sa, PhaseState sb, PhaseState sc) {
|
||||
phase_state[0] = sa;
|
||||
phase_state[1] = sb;
|
||||
phase_state[2] = sc;
|
||||
}
|
||||
70
firmware/lib/Arduino-FOC/src/drivers/BLDCDriver6PWM.h
Normal file
70
firmware/lib/Arduino-FOC/src/drivers/BLDCDriver6PWM.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef BLDCDriver6PWM_h
|
||||
#define BLDCDriver6PWM_h
|
||||
|
||||
#include "../common/base_classes/BLDCDriver.h"
|
||||
#include "../common/foc_utils.h"
|
||||
#include "../common/time_utils.h"
|
||||
#include "../common/defaults.h"
|
||||
#include "hardware_api.h"
|
||||
|
||||
/**
|
||||
6 pwm bldc driver class
|
||||
*/
|
||||
class BLDCDriver6PWM: public BLDCDriver
|
||||
{
|
||||
public:
|
||||
/**
|
||||
BLDCDriver class constructor
|
||||
@param phA_h A phase pwm pin
|
||||
@param phA_l A phase pwm pin
|
||||
@param phB_h B phase pwm pin
|
||||
@param phB_l A phase pwm pin
|
||||
@param phC_h C phase pwm pin
|
||||
@param phC_l A phase pwm pin
|
||||
@param en enable pin (optional input)
|
||||
*/
|
||||
BLDCDriver6PWM(int phA_h,int phA_l,int phB_h,int phB_l,int phC_h,int phC_l, int en = NOT_SET);
|
||||
|
||||
/** Motor hardware init function */
|
||||
int init() override;
|
||||
/** Motor disable function */
|
||||
void disable() override;
|
||||
/** Motor enable function */
|
||||
void enable() override;
|
||||
|
||||
// hardware variables
|
||||
int pwmA_h,pwmA_l; //!< phase A pwm pin number
|
||||
int pwmB_h,pwmB_l; //!< phase B pwm pin number
|
||||
int pwmC_h,pwmC_l; //!< phase C pwm pin number
|
||||
int enable_pin; //!< enable pin number
|
||||
bool enable_active_high = true;
|
||||
|
||||
float dead_zone; //!< a percentage of dead-time(zone) (both high and low side in low) for each pwm cycle [0,1]
|
||||
|
||||
PhaseState phase_state[3]; //!< phase state (active / disabled)
|
||||
|
||||
|
||||
/**
|
||||
* Set phase voltages to the harware
|
||||
*
|
||||
* @param Ua - phase A voltage
|
||||
* @param Ub - phase B voltage
|
||||
* @param Uc - phase C voltage
|
||||
*/
|
||||
void setPwm(float Ua, float Ub, float Uc) override;
|
||||
|
||||
/**
|
||||
* Set phase voltages to the harware
|
||||
*
|
||||
* @param sc - phase A state : active / disabled ( high impedance )
|
||||
* @param sb - phase B state : active / disabled ( high impedance )
|
||||
* @param sa - phase C state : active / disabled ( high impedance )
|
||||
*/
|
||||
virtual void setPhaseState(PhaseState sa, PhaseState sb, PhaseState sc) override;
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
107
firmware/lib/Arduino-FOC/src/drivers/StepperDriver2PWM.cpp
Normal file
107
firmware/lib/Arduino-FOC/src/drivers/StepperDriver2PWM.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "StepperDriver2PWM.h"
|
||||
|
||||
StepperDriver2PWM::StepperDriver2PWM(int _pwm1, int* _in1, int _pwm2, int* _in2, int en1, int en2){
|
||||
// Pin initialization
|
||||
pwm1 = _pwm1; // phase 1 pwm pin number
|
||||
dir1a = _in1[0]; // phase 1 INA pin number
|
||||
dir1b = _in1[1]; // phase 1 INB pin number
|
||||
pwm2 = _pwm2; // phase 2 pwm pin number
|
||||
dir2a = _in2[0]; // phase 2 INA pin number
|
||||
dir2b = _in2[1]; // phase 2 INB pin number
|
||||
|
||||
// enable_pin pin
|
||||
enable_pin1 = en1;
|
||||
enable_pin2 = en2;
|
||||
|
||||
// default power-supply value
|
||||
voltage_power_supply = DEF_POWER_SUPPLY;
|
||||
voltage_limit = NOT_SET;
|
||||
pwm_frequency = NOT_SET;
|
||||
|
||||
}
|
||||
|
||||
StepperDriver2PWM::StepperDriver2PWM(int _pwm1, int _dir1, int _pwm2, int _dir2, int en1, int en2){
|
||||
// Pin initialization
|
||||
pwm1 = _pwm1; // phase 1 pwm pin number
|
||||
dir1a = _dir1; // phase 1 direction pin
|
||||
pwm2 = _pwm2; // phase 2 pwm pin number
|
||||
dir2a = _dir2; // phase 2 direction pin
|
||||
// these pins are not used
|
||||
dir1b = NOT_SET;
|
||||
dir2b = NOT_SET;
|
||||
|
||||
// enable_pin pin
|
||||
enable_pin1 = en1;
|
||||
enable_pin2 = en2;
|
||||
|
||||
// default power-supply value
|
||||
voltage_power_supply = DEF_POWER_SUPPLY;
|
||||
voltage_limit = NOT_SET;
|
||||
pwm_frequency = NOT_SET;
|
||||
|
||||
}
|
||||
|
||||
// enable motor driver
|
||||
void StepperDriver2PWM::enable(){
|
||||
// enable_pin the driver - if enable_pin pin available
|
||||
if ( _isset(enable_pin1) ) digitalWrite(enable_pin1, HIGH);
|
||||
if ( _isset(enable_pin2) ) digitalWrite(enable_pin2, HIGH);
|
||||
// set zero to PWM
|
||||
setPwm(0,0);
|
||||
}
|
||||
|
||||
// disable motor driver
|
||||
void StepperDriver2PWM::disable()
|
||||
{
|
||||
// set zero to PWM
|
||||
setPwm(0, 0);
|
||||
// disable the driver - if enable_pin pin available
|
||||
if ( _isset(enable_pin1) ) digitalWrite(enable_pin1, LOW);
|
||||
if ( _isset(enable_pin2) ) digitalWrite(enable_pin2, LOW);
|
||||
|
||||
}
|
||||
|
||||
// init hardware pins
|
||||
int StepperDriver2PWM::init() {
|
||||
// PWM pins
|
||||
pinMode(pwm1, OUTPUT);
|
||||
pinMode(pwm2, OUTPUT);
|
||||
pinMode(dir1a, OUTPUT);
|
||||
pinMode(dir2a, OUTPUT);
|
||||
if( _isset(dir1b) ) pinMode(dir1b, OUTPUT);
|
||||
if( _isset(dir2b) ) pinMode(dir2b, OUTPUT);
|
||||
|
||||
if( _isset(enable_pin1) ) pinMode(enable_pin1, OUTPUT);
|
||||
if( _isset(enable_pin2) ) pinMode(enable_pin2, OUTPUT);
|
||||
|
||||
// sanity check for the voltage limit configuration
|
||||
if( !_isset(voltage_limit) || voltage_limit > voltage_power_supply) voltage_limit = voltage_power_supply;
|
||||
|
||||
// Set the pwm frequency to the pins
|
||||
// hardware specific function - depending on driver and mcu
|
||||
params = _configure2PWM(pwm_frequency, pwm1, pwm2);
|
||||
initialized = (params!=SIMPLEFOC_DRIVER_INIT_FAILED);
|
||||
return params!=SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
|
||||
|
||||
// Set voltage to the pwm pin
|
||||
void StepperDriver2PWM::setPwm(float Ua, float Ub) {
|
||||
float duty_cycle1(0.0f),duty_cycle2(0.0f);
|
||||
// limit the voltage in driver
|
||||
Ua = _constrain(Ua, -voltage_limit, voltage_limit);
|
||||
Ub = _constrain(Ub, -voltage_limit, voltage_limit);
|
||||
// hardware specific writing
|
||||
duty_cycle1 = _constrain(abs(Ua)/voltage_power_supply,0.0f,1.0f);
|
||||
duty_cycle2 = _constrain(abs(Ub)/voltage_power_supply,0.0f,1.0f);
|
||||
|
||||
// phase 1 direction
|
||||
digitalWrite(dir1a, Ua >= 0 ? LOW : HIGH);
|
||||
if( _isset(dir1b) ) digitalWrite(dir1b, Ua >= 0 ? HIGH : LOW );
|
||||
// phase 2 direction
|
||||
digitalWrite(dir2a, Ub >= 0 ? LOW : HIGH);
|
||||
if( _isset(dir2b) ) digitalWrite(dir2b, Ub >= 0 ? HIGH : LOW );
|
||||
|
||||
// write to hardware
|
||||
_writeDutyCycle2PWM(duty_cycle1, duty_cycle2, params);
|
||||
}
|
||||
68
firmware/lib/Arduino-FOC/src/drivers/StepperDriver2PWM.h
Normal file
68
firmware/lib/Arduino-FOC/src/drivers/StepperDriver2PWM.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef STEPPER_DRIVER_2PWM_h
|
||||
#define STEPPER_DRIVER_2PWM_h
|
||||
|
||||
#include "../common/base_classes/StepperDriver.h"
|
||||
#include "../common/foc_utils.h"
|
||||
#include "../common/time_utils.h"
|
||||
#include "../common/defaults.h"
|
||||
#include "hardware_api.h"
|
||||
|
||||
/**
|
||||
2 pwm stepper driver class
|
||||
*/
|
||||
class StepperDriver2PWM: public StepperDriver
|
||||
{
|
||||
public:
|
||||
/**
|
||||
StepperMotor class constructor
|
||||
@param pwm1 PWM1 phase pwm pin
|
||||
@param in1 IN1A phase dir pin
|
||||
@param pwm2 PWM2 phase pwm pin
|
||||
@param in2 IN2A phase dir
|
||||
@param en1 enable pin phase 1 (optional input)
|
||||
@param en2 enable pin phase 2 (optional input)
|
||||
*/
|
||||
StepperDriver2PWM(int pwm1, int* in1, int pwm2, int* in2, int en1 = NOT_SET, int en2 = NOT_SET);
|
||||
|
||||
/**
|
||||
StepperMotor class constructor
|
||||
@param pwm1 PWM1 phase pwm pin
|
||||
@param dir1 DIR1 phase dir pin
|
||||
@param pwm2 PWM2 phase pwm pin
|
||||
@param dir2 DIR2 phase dir pin
|
||||
@param en1 enable pin phase 1 (optional input)
|
||||
@param en2 enable pin phase 2 (optional input)
|
||||
*/
|
||||
StepperDriver2PWM(int pwm1, int dir1, int pwm2, int dir2, int en1 = NOT_SET, int en2 = NOT_SET);
|
||||
|
||||
/** Motor hardware init function */
|
||||
int init() override;
|
||||
/** Motor disable function */
|
||||
void disable() override;
|
||||
/** Motor enable function */
|
||||
void enable() override;
|
||||
|
||||
// hardware variables
|
||||
int pwm1; //!< phase 1 pwm pin number
|
||||
int dir1a; //!< phase 1 INA pin number
|
||||
int dir1b; //!< phase 1 INB pin number
|
||||
int pwm2; //!< phase 2 pwm pin number
|
||||
int dir2a; //!< phase 2 INA pin number
|
||||
int dir2b; //!< phase 2 INB pin number
|
||||
int enable_pin1; //!< enable pin number phase 1
|
||||
int enable_pin2; //!< enable pin number phase 2
|
||||
|
||||
/**
|
||||
* Set phase voltages to the harware
|
||||
*
|
||||
* @param Ua phase A voltage
|
||||
* @param Ub phase B voltage
|
||||
*/
|
||||
void setPwm(float Ua, float Ub) override;
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
81
firmware/lib/Arduino-FOC/src/drivers/StepperDriver4PWM.cpp
Normal file
81
firmware/lib/Arduino-FOC/src/drivers/StepperDriver4PWM.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "StepperDriver4PWM.h"
|
||||
|
||||
StepperDriver4PWM::StepperDriver4PWM(int ph1A,int ph1B,int ph2A,int ph2B,int en1, int en2){
|
||||
// Pin initialization
|
||||
pwm1A = ph1A;
|
||||
pwm1B = ph1B;
|
||||
pwm2A = ph2A;
|
||||
pwm2B = ph2B;
|
||||
|
||||
// enable_pin pin
|
||||
enable_pin1 = en1;
|
||||
enable_pin2 = en2;
|
||||
|
||||
// default power-supply value
|
||||
voltage_power_supply = DEF_POWER_SUPPLY;
|
||||
voltage_limit = NOT_SET;
|
||||
pwm_frequency = NOT_SET;
|
||||
|
||||
}
|
||||
|
||||
// enable motor driver
|
||||
void StepperDriver4PWM::enable(){
|
||||
// enable_pin the driver - if enable_pin pin available
|
||||
if ( _isset(enable_pin1) ) digitalWrite(enable_pin1, HIGH);
|
||||
if ( _isset(enable_pin2) ) digitalWrite(enable_pin2, HIGH);
|
||||
// set zero to PWM
|
||||
setPwm(0,0);
|
||||
}
|
||||
|
||||
// disable motor driver
|
||||
void StepperDriver4PWM::disable()
|
||||
{
|
||||
// set zero to PWM
|
||||
setPwm(0, 0);
|
||||
// disable the driver - if enable_pin pin available
|
||||
if ( _isset(enable_pin1) ) digitalWrite(enable_pin1, LOW);
|
||||
if ( _isset(enable_pin2) ) digitalWrite(enable_pin2, LOW);
|
||||
|
||||
}
|
||||
|
||||
// init hardware pins
|
||||
int StepperDriver4PWM::init() {
|
||||
|
||||
// PWM pins
|
||||
pinMode(pwm1A, OUTPUT);
|
||||
pinMode(pwm1B, OUTPUT);
|
||||
pinMode(pwm2A, OUTPUT);
|
||||
pinMode(pwm2B, OUTPUT);
|
||||
if( _isset(enable_pin1) ) pinMode(enable_pin1, OUTPUT);
|
||||
if( _isset(enable_pin2) ) pinMode(enable_pin2, OUTPUT);
|
||||
|
||||
// sanity check for the voltage limit configuration
|
||||
if( !_isset(voltage_limit) || voltage_limit > voltage_power_supply) voltage_limit = voltage_power_supply;
|
||||
|
||||
// Set the pwm frequency to the pins
|
||||
// hardware specific function - depending on driver and mcu
|
||||
params = _configure4PWM(pwm_frequency, pwm1A, pwm1B, pwm2A, pwm2B);
|
||||
initialized = (params!=SIMPLEFOC_DRIVER_INIT_FAILED);
|
||||
return params!=SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
|
||||
|
||||
// Set voltage to the pwm pin
|
||||
void StepperDriver4PWM::setPwm(float Ualpha, float Ubeta) {
|
||||
float duty_cycle1A(0.0f),duty_cycle1B(0.0f),duty_cycle2A(0.0f),duty_cycle2B(0.0f);
|
||||
// limit the voltage in driver
|
||||
Ualpha = _constrain(Ualpha, -voltage_limit, voltage_limit);
|
||||
Ubeta = _constrain(Ubeta, -voltage_limit, voltage_limit);
|
||||
// hardware specific writing
|
||||
if( Ualpha > 0 )
|
||||
duty_cycle1B = _constrain(abs(Ualpha)/voltage_power_supply,0.0f,1.0f);
|
||||
else
|
||||
duty_cycle1A = _constrain(abs(Ualpha)/voltage_power_supply,0.0f,1.0f);
|
||||
|
||||
if( Ubeta > 0 )
|
||||
duty_cycle2B = _constrain(abs(Ubeta)/voltage_power_supply,0.0f,1.0f);
|
||||
else
|
||||
duty_cycle2A = _constrain(abs(Ubeta)/voltage_power_supply,0.0f,1.0f);
|
||||
// write to hardware
|
||||
_writeDutyCycle4PWM(duty_cycle1A, duty_cycle1B, duty_cycle2A, duty_cycle2B, params);
|
||||
}
|
||||
55
firmware/lib/Arduino-FOC/src/drivers/StepperDriver4PWM.h
Normal file
55
firmware/lib/Arduino-FOC/src/drivers/StepperDriver4PWM.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef STEPPER_DRIVER_4PWM_h
|
||||
#define STEPPER_DRIVER_4PWM_h
|
||||
|
||||
#include "../common/base_classes/StepperDriver.h"
|
||||
#include "../common/foc_utils.h"
|
||||
#include "../common/time_utils.h"
|
||||
#include "../common/defaults.h"
|
||||
#include "hardware_api.h"
|
||||
|
||||
/**
|
||||
4 pwm stepper driver class
|
||||
*/
|
||||
class StepperDriver4PWM: public StepperDriver
|
||||
{
|
||||
public:
|
||||
/**
|
||||
StepperMotor class constructor
|
||||
@param ph1A 1A phase pwm pin
|
||||
@param ph1B 1B phase pwm pin
|
||||
@param ph2A 2A phase pwm pin
|
||||
@param ph2B 2B phase pwm pin
|
||||
@param en1 enable pin phase 1 (optional input)
|
||||
@param en2 enable pin phase 2 (optional input)
|
||||
*/
|
||||
StepperDriver4PWM(int ph1A,int ph1B,int ph2A,int ph2B, int en1 = NOT_SET, int en2 = NOT_SET);
|
||||
|
||||
/** Motor hardware init function */
|
||||
int init() override;
|
||||
/** Motor disable function */
|
||||
void disable() override;
|
||||
/** Motor enable function */
|
||||
void enable() override;
|
||||
|
||||
// hardware variables
|
||||
int pwm1A; //!< phase 1A pwm pin number
|
||||
int pwm1B; //!< phase 1B pwm pin number
|
||||
int pwm2A; //!< phase 2A pwm pin number
|
||||
int pwm2B; //!< phase 2B pwm pin number
|
||||
int enable_pin1; //!< enable pin number phase 1
|
||||
int enable_pin2; //!< enable pin number phase 2
|
||||
|
||||
/**
|
||||
* Set phase voltages to the harware
|
||||
*
|
||||
* @param Ua phase A voltage
|
||||
* @param Ub phase B voltage
|
||||
*/
|
||||
void setPwm(float Ua, float Ub) override;
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
180
firmware/lib/Arduino-FOC/src/drivers/hardware_api.h
Normal file
180
firmware/lib/Arduino-FOC/src/drivers/hardware_api.h
Normal file
@@ -0,0 +1,180 @@
|
||||
#ifndef HARDWARE_UTILS_DRIVER_H
|
||||
#define HARDWARE_UTILS_DRIVER_H
|
||||
|
||||
#include "../common/foc_utils.h"
|
||||
#include "../common/time_utils.h"
|
||||
#include "../communication/SimpleFOCDebug.h"
|
||||
#include "../common/base_classes/BLDCDriver.h"
|
||||
|
||||
|
||||
// these defines determine the polarity of the PWM output. Normally, the polarity is active-high,
|
||||
// i.e. a high-level PWM output is expected to switch on the MOSFET. But should your driver design
|
||||
// require inverted polarity, you can change the defines below, or set them via your build environment
|
||||
// or board definition files.
|
||||
|
||||
// used for 1-PWM, 2-PWM, 3-PWM, and 4-PWM modes
|
||||
#ifndef SIMPLEFOC_PWM_ACTIVE_HIGH
|
||||
#define SIMPLEFOC_PWM_ACTIVE_HIGH true
|
||||
#endif
|
||||
// used for 6-PWM mode, high-side
|
||||
#ifndef SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH
|
||||
#define SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH true
|
||||
#endif
|
||||
// used for 6-PWM mode, low-side
|
||||
#ifndef SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH
|
||||
#define SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH true
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
// flag returned if driver init fails
|
||||
#define SIMPLEFOC_DRIVER_INIT_FAILED ((void*)-1)
|
||||
|
||||
// generic implementation of the hardware specific structure
|
||||
// containing all the necessary driver parameters
|
||||
// will be returned as a void pointer from the _configurexPWM functions
|
||||
// will be provided to the _writeDutyCyclexPWM() as a void pointer
|
||||
typedef struct GenericDriverParams {
|
||||
int pins[6];
|
||||
long pwm_frequency;
|
||||
float dead_zone;
|
||||
} GenericDriverParams;
|
||||
|
||||
|
||||
/**
|
||||
* Configuring PWM frequency, resolution and alignment
|
||||
* - Stepper driver - 2PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param pwm_frequency - frequency in hertz - if applicable
|
||||
* @param pinA pinA pwm pin
|
||||
*
|
||||
* @return -1 if failed, or pointer to internal driver parameters struct if successful
|
||||
*/
|
||||
void* _configure1PWM(long pwm_frequency, const int pinA);
|
||||
|
||||
/**
|
||||
* Configuring PWM frequency, resolution and alignment
|
||||
* - Stepper driver - 2PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param pwm_frequency - frequency in hertz - if applicable
|
||||
* @param pinA pinA bldc driver
|
||||
* @param pinB pinB bldc driver
|
||||
*
|
||||
* @return -1 if failed, or pointer to internal driver parameters struct if successful
|
||||
*/
|
||||
void* _configure2PWM(long pwm_frequency, const int pinA, const int pinB);
|
||||
|
||||
/**
|
||||
* Configuring PWM frequency, resolution and alignment
|
||||
* - BLDC driver - 3PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param pwm_frequency - frequency in hertz - if applicable
|
||||
* @param pinA pinA bldc driver
|
||||
* @param pinB pinB bldc driver
|
||||
* @param pinC pinC bldc driver
|
||||
*
|
||||
* @return -1 if failed, or pointer to internal driver parameters struct if successful
|
||||
*/
|
||||
void* _configure3PWM(long pwm_frequency, const int pinA, const int pinB, const int pinC);
|
||||
|
||||
/**
|
||||
* Configuring PWM frequency, resolution and alignment
|
||||
* - Stepper driver - 4PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param pwm_frequency - frequency in hertz - if applicable
|
||||
* @param pin1A pin1A stepper driver
|
||||
* @param pin1B pin1B stepper driver
|
||||
* @param pin2A pin2A stepper driver
|
||||
* @param pin2B pin2B stepper driver
|
||||
*
|
||||
* @return -1 if failed, or pointer to internal driver parameters struct if successful
|
||||
*/
|
||||
void* _configure4PWM(long pwm_frequency, const int pin1A, const int pin1B, const int pin2A, const int pin2B);
|
||||
|
||||
/**
|
||||
* Configuring PWM frequency, resolution and alignment
|
||||
* - BLDC driver - 6PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param pwm_frequency - frequency in hertz - if applicable
|
||||
* @param dead_zone duty cycle protection zone [0, 1] - both low and high side low - if applicable
|
||||
* @param pinA_h pinA high-side bldc driver
|
||||
* @param pinA_l pinA low-side bldc driver
|
||||
* @param pinB_h pinA high-side bldc driver
|
||||
* @param pinB_l pinA low-side bldc driver
|
||||
* @param pinC_h pinA high-side bldc driver
|
||||
* @param pinC_l pinA low-side bldc driver
|
||||
*
|
||||
* @return -1 if failed, or pointer to internal driver parameters struct if successful
|
||||
*/
|
||||
void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l);
|
||||
|
||||
/**
|
||||
* Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
* - Stepper driver - 2PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param dc_a duty cycle phase A [0, 1]
|
||||
* @param dc_b duty cycle phase B [0, 1]
|
||||
* @param params the driver parameters
|
||||
*/
|
||||
void _writeDutyCycle1PWM(float dc_a, void* params);
|
||||
|
||||
/**
|
||||
* Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
* - Stepper driver - 2PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param dc_a duty cycle phase A [0, 1]
|
||||
* @param dc_b duty cycle phase B [0, 1]
|
||||
* @param params the driver parameters
|
||||
*/
|
||||
void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params);
|
||||
|
||||
/**
|
||||
* Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
* - BLDC driver - 3PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param dc_a duty cycle phase A [0, 1]
|
||||
* @param dc_b duty cycle phase B [0, 1]
|
||||
* @param dc_c duty cycle phase C [0, 1]
|
||||
* @param params the driver parameters
|
||||
*/
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params);
|
||||
|
||||
/**
|
||||
* Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
* - Stepper driver - 4PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param dc_1a duty cycle phase 1A [0, 1]
|
||||
* @param dc_1b duty cycle phase 1B [0, 1]
|
||||
* @param dc_2a duty cycle phase 2A [0, 1]
|
||||
* @param dc_2b duty cycle phase 2B [0, 1]
|
||||
* @param params the driver parameters
|
||||
*/
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params);
|
||||
|
||||
|
||||
/**
|
||||
* Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
* - BLDC driver - 6PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param dc_a duty cycle phase A [0, 1]
|
||||
* @param dc_b duty cycle phase B [0, 1]
|
||||
* @param dc_c duty cycle phase C [0, 1]
|
||||
* @param phase_state pointer to PhaseState[3] array
|
||||
* @param params the driver parameters
|
||||
*
|
||||
*/
|
||||
void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void* params);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,278 @@
|
||||
#include "../../hardware_api.h"
|
||||
|
||||
#if defined(__AVR_ATmega2560__) || defined(AVR_ATmega1280)
|
||||
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for Arduino/ATmega2560 or Arduino/ATmega1280")
|
||||
#pragma message("")
|
||||
|
||||
|
||||
#define _PWM_FREQUENCY 32000
|
||||
#define _PWM_FREQUENCY_MAX 32000
|
||||
#define _PWM_FREQUENCY_MIN 4000
|
||||
|
||||
// set pwm frequency to 32KHz
|
||||
void _pinHighFrequency(const int pin, const long frequency){
|
||||
bool high_fq = false;
|
||||
// set 32kHz frequency if requested freq is higher than the middle of the range (14kHz)
|
||||
// else set the 4kHz
|
||||
if(frequency >= 0.5*(_PWM_FREQUENCY_MAX-_PWM_FREQUENCY_MIN)) high_fq=true;
|
||||
// High PWM frequency
|
||||
// https://sites.google.com/site/qeewiki/books/avr-guide/timers-on-the-ATmega2560
|
||||
// https://forum.arduino.cc/index.php?topic=72092.0
|
||||
if (pin == 13 || pin == 4 ) {
|
||||
TCCR0A = ((TCCR0A & 0b11111100) | 0x01); // configure the pwm phase-corrected mode
|
||||
if(high_fq) TCCR0B = ((TCCR0B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR0B = ((TCCR0B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
}
|
||||
else if (pin == 12 || pin == 11 )
|
||||
if(high_fq) TCCR1B = ((TCCR1B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR1B = ((TCCR1B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
else if (pin == 10 || pin == 9 )
|
||||
if(high_fq) TCCR2B = ((TCCR2B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR2B = ((TCCR2B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
else if (pin == 5 || pin == 3 || pin == 2)
|
||||
if(high_fq) TCCR3B = ((TCCR3B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR3B = ((TCCR3B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
else if (pin == 8 || pin == 7 || pin == 6)
|
||||
if(high_fq) TCCR4B = ((TCCR4B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR4B = ((TCCR4B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
else if (pin == 44 || pin == 45 || pin == 46)
|
||||
if(high_fq) TCCR5B = ((TCCR5B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR5B = ((TCCR5B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
|
||||
}
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware specific
|
||||
// supports Arduino/ATmega2560
|
||||
void* _configure1PWM(long pwm_frequency,const int pinA) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 32kHz
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX); // constrain to 4-32kHz max
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
_pinHighFrequency(pinA, pwm_frequency);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware specific
|
||||
// supports Arduino/ATmega2560
|
||||
void* _configure2PWM(long pwm_frequency,const int pinA, const int pinB) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 32kHz
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX); // constrain to 4-32kHz max
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
_pinHighFrequency(pinA, pwm_frequency);
|
||||
_pinHighFrequency(pinB, pwm_frequency);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware specific
|
||||
// supports Arduino/ATmega2560
|
||||
void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 32kHz
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX); // constrain to 4-32kHz max
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
_pinHighFrequency(pinA, pwm_frequency);
|
||||
_pinHighFrequency(pinB, pwm_frequency);
|
||||
_pinHighFrequency(pinC, pwm_frequency);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB, pinC },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
// _syncAllTimers();
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware specific
|
||||
void _writeDutyCycle1PWM(float dc_a, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware specific
|
||||
void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_b);
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware specific
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_b);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[2], 255.0f*dc_c);
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware specific
|
||||
void* _configure4PWM(long pwm_frequency,const int pin1A, const int pin1B, const int pin2A, const int pin2B) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 32kHz
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX); // constrain to 4-32kHz max
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
_pinHighFrequency(pin1A,pwm_frequency);
|
||||
_pinHighFrequency(pin1B,pwm_frequency);
|
||||
_pinHighFrequency(pin2A,pwm_frequency);
|
||||
_pinHighFrequency(pin2B,pwm_frequency);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pin1A, pin1B, pin2A, pin2B },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware specific
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params) {
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_1a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_1b);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[2], 255.0f*dc_2a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[3], 255.0f*dc_2b);
|
||||
}
|
||||
|
||||
|
||||
// function configuring pair of high-low side pwm channels, 32khz frequency and center aligned pwm
|
||||
// supports Arduino/ATmega2560
|
||||
// https://ww1.microchip.com/downloads/en/devicedoc/atmel-2549-8-bit-avr-microcontroller-atmega640-1280-1281-2560-2561_datasheet.pdf
|
||||
// https://docs.arduino.cc/hacking/hardware/PinMapping2560
|
||||
int _configureComplementaryPair(const int pinH,const int pinL, long frequency) {
|
||||
bool high_fq = false;
|
||||
// set 32kHz frequency if requested freq is higher than the middle of the range (14kHz)
|
||||
// else set the 4kHz
|
||||
if(frequency >= 0.5*(_PWM_FREQUENCY_MAX-_PWM_FREQUENCY_MIN)) high_fq=true;
|
||||
|
||||
// configure pin pairs
|
||||
if( (pinH == 4 && pinL == 13 ) || (pinH == 13 && pinL == 4 ) ){
|
||||
// configure the pwm phase-corrected mode
|
||||
TCCR0A = ((TCCR0A & 0b11111100) | 0x01);
|
||||
// configure complementary pwm on low side
|
||||
if(pinH == 13 ) TCCR0A = 0b10110000 | (TCCR0A & 0b00001111) ;
|
||||
else TCCR0A = 0b11100000 | (TCCR0A & 0b00001111) ;
|
||||
// set frequency
|
||||
if(high_fq) TCCR0B = ((TCCR0B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR0B = ((TCCR0B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
}else if( (pinH == 11 && pinL == 12 ) || (pinH == 12 && pinL == 11 ) ){
|
||||
// set frequency
|
||||
if(high_fq) TCCR1B = ((TCCR1B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR1B = ((TCCR1B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
// configure complementary pwm on low side
|
||||
if(pinH == 11 ) TCCR1A = 0b10110000 | (TCCR1A & 0b00001111) ;
|
||||
else TCCR1A = 0b11100000 | (TCCR1A & 0b00001111) ;
|
||||
}else if((pinH == 10 && pinL == 9 ) || (pinH == 9 && pinL == 10 ) ){
|
||||
// set frequency
|
||||
if(high_fq) TCCR2B = ((TCCR2B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR2B = ((TCCR2B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
// configure complementary pwm on low side
|
||||
if(pinH == 10 ) TCCR2A = 0b10110000 | (TCCR2A & 0b00001111) ;
|
||||
else TCCR2A = 0b11100000 | (TCCR2A & 0b00001111) ;
|
||||
}else if((pinH == 5 && pinL == 2 ) || (pinH == 2 && pinL == 5 ) ){
|
||||
// set frequency
|
||||
if(high_fq) TCCR3B = ((TCCR3B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR3B = ((TCCR3B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
// configure complementary pwm on low side
|
||||
if(pinH == 5 ) TCCR3A = 0b10110000 | (TCCR3A & 0b00001111) ;
|
||||
else TCCR3A = 0b11100000 | (TCCR3A & 0b00001111) ;
|
||||
}else if((pinH == 6 && pinL == 7 ) || (pinH == 7 && pinL == 6 ) ){
|
||||
// set frequency
|
||||
if(high_fq) TCCR4B = ((TCCR4B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR4B = ((TCCR4B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
// configure complementary pwm on low side
|
||||
if(pinH == 6 ) TCCR4A = 0b10110000 | (TCCR4A & 0b00001111) ;
|
||||
else TCCR4A = 0b11100000 | (TCCR4A & 0b00001111) ;
|
||||
}else if((pinH == 46 && pinL == 45 ) || (pinH == 45 && pinL == 46 ) ){
|
||||
// set frequency
|
||||
if(high_fq) TCCR5B = ((TCCR5B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR5B = ((TCCR5B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
// configure complementary pwm on low side
|
||||
if(pinH == 46 ) TCCR5A = 0b10110000 | (TCCR5A & 0b00001111) ;
|
||||
else TCCR5A = 0b11100000 | (TCCR5A & 0b00001111) ;
|
||||
}else{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Configuring PWM frequency, resolution and alignment
|
||||
// - BLDC driver - setting
|
||||
// - hardware specific
|
||||
// supports Arduino/ATmega2560
|
||||
void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 32kHz
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX); // constrain to 4-32kHz max
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
int ret_flag = 0;
|
||||
ret_flag += _configureComplementaryPair(pinA_h, pinA_l, pwm_frequency);
|
||||
ret_flag += _configureComplementaryPair(pinB_h, pinB_l, pwm_frequency);
|
||||
ret_flag += _configureComplementaryPair(pinC_h, pinC_l, pwm_frequency);
|
||||
if (ret_flag!=0) return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA_h, pinA_l, pinB_h, pinB_l, pinC_h, pinC_l },
|
||||
.pwm_frequency = pwm_frequency,
|
||||
.dead_zone = dead_zone
|
||||
};
|
||||
// _syncAllTimers();
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the
|
||||
void _setPwmPair(int pinH, int pinL, float val, int dead_time, PhaseState ps)
|
||||
{
|
||||
int pwm_h = _constrain(val-dead_time/2,0,255);
|
||||
int pwm_l = _constrain(val+dead_time/2,0,255);
|
||||
// determine the phase state and set the pwm accordingly
|
||||
// deactivate phases if needed
|
||||
if((ps == PhaseState::PHASE_OFF) || (ps == PhaseState::PHASE_LO)){
|
||||
digitalWrite(pinH, LOW);
|
||||
}else{
|
||||
analogWrite(pinH, pwm_h);
|
||||
}
|
||||
if((ps == PhaseState::PHASE_OFF) || (ps == PhaseState::PHASE_HI)){
|
||||
digitalWrite(pinL, LOW);
|
||||
}else{
|
||||
if(pwm_l == 255 || pwm_l == 0)
|
||||
digitalWrite(pinL, pwm_l ? LOW : HIGH);
|
||||
else
|
||||
analogWrite(pinL, pwm_l);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
// - BLDC driver - 6PWM setting
|
||||
// - hardware specific
|
||||
// supports Arduino/ATmega328
|
||||
void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void* params){
|
||||
_setPwmPair(((GenericDriverParams*)params)->pins[0], ((GenericDriverParams*)params)->pins[1], dc_a*255.0, ((GenericDriverParams*)params)->dead_zone*255.0, phase_state[0]);
|
||||
_setPwmPair(((GenericDriverParams*)params)->pins[2], ((GenericDriverParams*)params)->pins[3], dc_b*255.0, ((GenericDriverParams*)params)->dead_zone*255.0, phase_state[1]);
|
||||
_setPwmPair(((GenericDriverParams*)params)->pins[4], ((GenericDriverParams*)params)->pins[5], dc_c*255.0, ((GenericDriverParams*)params)->dead_zone*255.0, phase_state[2]);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,255 @@
|
||||
#include "../../hardware_api.h"
|
||||
|
||||
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega328PB__)
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for Arduino/ATmega328 ATmega168 ATmega328PB")
|
||||
#pragma message("")
|
||||
|
||||
#define _PWM_FREQUENCY 32000
|
||||
#define _PWM_FREQUENCY_MAX 32000
|
||||
#define _PWM_FREQUENCY_MIN 4000
|
||||
|
||||
// set pwm frequency to 32KHz
|
||||
void _pinHighFrequency(const int pin, const long frequency){
|
||||
bool high_fq = false;
|
||||
// set 32kHz frequency if requested freq is higher than the middle of the range (14kHz)
|
||||
// else set the 4kHz
|
||||
if(frequency >= 0.5*(_PWM_FREQUENCY_MAX-_PWM_FREQUENCY_MIN)) high_fq=true;
|
||||
// High PWM frequency
|
||||
// https://www.arxterra.com/9-atmega328p-timers/
|
||||
if (pin == 5 || pin == 6 ) {
|
||||
TCCR0A = ((TCCR0A & 0b11111100) | 0x01); // configure the pwm phase-corrected mode
|
||||
if(high_fq) TCCR0B = ((TCCR0B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR0B = ((TCCR0B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
}else if (pin == 9 || pin == 10 ){
|
||||
if(high_fq) TCCR1B = ((TCCR1B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR1B = ((TCCR1B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
}else if (pin == 3 || pin == 11){
|
||||
if(high_fq) TCCR2B = ((TCCR2B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR2B = ((TCCR2B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
}
|
||||
}
|
||||
|
||||
void _syncAllTimers(){
|
||||
GTCCR = (1<<TSM)|(1<<PSRASY)|(1<<PSRSYNC); // halt all timers
|
||||
// set all timers to the same value
|
||||
TCNT0 = 0; // set timer0 to 0
|
||||
TCNT1H = 0; // set timer1 high byte to 0
|
||||
TCNT1L = 0; // set timer1 low byte to 0
|
||||
TCNT2 = 0; // set timer2 to 0
|
||||
GTCCR = 0; // release all timers
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware specific
|
||||
// supports Arduino/ATmega328
|
||||
void* _configure1PWM(long pwm_frequency,const int pinA) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 32kHz
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX); // constrain to 4-32kHz max
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
_pinHighFrequency(pinA, pwm_frequency);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware specific
|
||||
// supports Arduino/ATmega328
|
||||
void* _configure2PWM(long pwm_frequency,const int pinA, const int pinB) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 32kHz
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX); // constrain to 4-32kHz max
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
_pinHighFrequency(pinA, pwm_frequency);
|
||||
_pinHighFrequency(pinB, pwm_frequency);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware specific
|
||||
// supports Arduino/ATmega328
|
||||
void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 32kHz
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX); // constrain to 4-32kHz max
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
_pinHighFrequency(pinA, pwm_frequency);
|
||||
_pinHighFrequency(pinB, pwm_frequency);
|
||||
_pinHighFrequency(pinC, pwm_frequency);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB, pinC },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
_syncAllTimers();
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware specific
|
||||
void _writeDutyCycle1PWM(float dc_a, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
}
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware specific
|
||||
void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_b);
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware specific
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_b);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[2], 255.0f*dc_c);
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware specific
|
||||
// supports Arduino/ATmega328
|
||||
void* _configure4PWM(long pwm_frequency,const int pin1A, const int pin1B, const int pin2A, const int pin2B) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 32kHz
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX); // constrain to 4-32kHz max
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
_pinHighFrequency(pin1A,pwm_frequency);
|
||||
_pinHighFrequency(pin1B,pwm_frequency);
|
||||
_pinHighFrequency(pin2A,pwm_frequency);
|
||||
_pinHighFrequency(pin2B,pwm_frequency);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pin1A, pin1B, pin2A, pin2B },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware specific
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_1a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_1b);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[2], 255.0f*dc_2a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[3], 255.0f*dc_2b);
|
||||
}
|
||||
|
||||
|
||||
// function configuring pair of high-low side pwm channels, 32khz frequency and center aligned pwm
|
||||
int _configureComplementaryPair(const int pinH, const int pinL, long frequency) {
|
||||
bool high_fq = false;
|
||||
// set 32kHz frequency if requested freq is higher than the middle of the range (14kHz)
|
||||
// else set the 4kHz
|
||||
if(frequency >= 0.5*(_PWM_FREQUENCY_MAX-_PWM_FREQUENCY_MIN)) high_fq=true;
|
||||
|
||||
// configure pins
|
||||
if( (pinH == 5 && pinL == 6 ) || (pinH == 6 && pinL == 5 ) ){
|
||||
// configure the pwm phase-corrected mode
|
||||
TCCR0A = ((TCCR0A & 0b11111100) | 0x01);
|
||||
// configure complementary pwm on low side
|
||||
if(pinH == 6 ) TCCR0A = 0b10110000 | (TCCR0A & 0b00001111) ;
|
||||
else TCCR0A = 0b11100000 | (TCCR0A & 0b00001111) ;
|
||||
// set frequency
|
||||
if(high_fq) TCCR0B = ((TCCR0B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR0B = ((TCCR0B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
}else if( (pinH == 9 && pinL == 10 ) || (pinH == 10 && pinL == 9 ) ){
|
||||
// set frequency
|
||||
if(high_fq) TCCR1B = ((TCCR1B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR1B = ((TCCR1B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
// configure complementary pwm on low side
|
||||
if(pinH == 9 ) TCCR1A = 0b10110000 | (TCCR1A & 0b00001111) ;
|
||||
else TCCR1A = 0b11100000 | (TCCR1A & 0b00001111) ;
|
||||
}else if((pinH == 3 && pinL == 11 ) || (pinH == 11 && pinL == 3 ) ){
|
||||
// set frequency
|
||||
if(high_fq) TCCR2B = ((TCCR2B & 0b11110000) | 0x01); // set prescaler to 1 - 32kHz
|
||||
else TCCR2B = ((TCCR2B & 0b11110000) | 0x02); // set prescaler to 2 - 4kHz
|
||||
// configure complementary pwm on low side
|
||||
if(pinH == 11 ) TCCR2A = 0b10110000 | (TCCR2A & 0b00001111) ;
|
||||
else TCCR2A = 0b11100000 | (TCCR2A & 0b00001111) ;
|
||||
}else{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Configuring PWM frequency, resolution and alignment
|
||||
// - BLDC driver - setting
|
||||
// - hardware specific
|
||||
// supports Arduino/ATmega328
|
||||
void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 32kHz
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX); // constrain to 4-32kHz max
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
int ret_flag = 0;
|
||||
ret_flag += _configureComplementaryPair(pinA_h, pinA_l, pwm_frequency);
|
||||
ret_flag += _configureComplementaryPair(pinB_h, pinB_l, pwm_frequency);
|
||||
ret_flag += _configureComplementaryPair(pinC_h, pinC_l, pwm_frequency);
|
||||
if (ret_flag!=0) return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA_h, pinA_l, pinB_h, pinB_l, pinC_h, pinC_l },
|
||||
.pwm_frequency = pwm_frequency,
|
||||
.dead_zone = dead_zone
|
||||
};
|
||||
_syncAllTimers();
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the
|
||||
void _setPwmPair(int pinH, int pinL, float val, int dead_time, PhaseState ps)
|
||||
{
|
||||
int pwm_h = _constrain(val-dead_time/2,0,255);
|
||||
int pwm_l = _constrain(val+dead_time/2,0,255);
|
||||
// determine the phase state and set the pwm accordingly
|
||||
// deactivate phases if needed
|
||||
if((ps == PhaseState::PHASE_OFF) || (ps == PhaseState::PHASE_LO)){
|
||||
digitalWrite(pinH, LOW);
|
||||
}else{
|
||||
analogWrite(pinH, pwm_h);
|
||||
}
|
||||
if((ps == PhaseState::PHASE_OFF) || (ps == PhaseState::PHASE_HI)){
|
||||
digitalWrite(pinL, LOW);
|
||||
}else{
|
||||
if(pwm_l == 255 || pwm_l == 0)
|
||||
digitalWrite(pinL, pwm_l ? LOW : HIGH);
|
||||
else
|
||||
analogWrite(pinL, pwm_l);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
// - BLDC driver - 6PWM setting
|
||||
// - hardware specific
|
||||
// supports Arduino/ATmega328
|
||||
void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void* params){
|
||||
_setPwmPair(((GenericDriverParams*)params)->pins[0], ((GenericDriverParams*)params)->pins[1], dc_a*255.0, ((GenericDriverParams*)params)->dead_zone*255.0, phase_state[0]);
|
||||
_setPwmPair(((GenericDriverParams*)params)->pins[2], ((GenericDriverParams*)params)->pins[3], dc_b*255.0, ((GenericDriverParams*)params)->dead_zone*255.0, phase_state[1]);
|
||||
_setPwmPair(((GenericDriverParams*)params)->pins[4], ((GenericDriverParams*)params)->pins[5], dc_c*255.0, ((GenericDriverParams*)params)->dead_zone*255.0, phase_state[2]);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,222 @@
|
||||
|
||||
#include "../../hardware_api.h"
|
||||
|
||||
#if defined(__AVR_ATmega32U4__)
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for Arduino/ATmega32U4")
|
||||
#pragma message("")
|
||||
|
||||
// set pwm frequency to 32KHz
|
||||
void _pinHighFrequency(const int pin){
|
||||
// High PWM frequency
|
||||
// reference: http://r6500.blogspot.com/2014/12/fast-pwm-on-arduino-leonardo.html
|
||||
if (pin == 3 || pin == 11 ) {
|
||||
TCCR0A = ((TCCR0A & 0b11111100) | 0x01); // configure the pwm phase-corrected mode
|
||||
TCCR0B = ((TCCR0B & 0b11110000) | 0x01); // set prescaler to 1
|
||||
}
|
||||
else if (pin == 9 || pin == 10 )
|
||||
TCCR1B = ((TCCR1B & 0b11111000) | 0x01); // set prescaler to 1
|
||||
else if (pin == 5 )
|
||||
TCCR3B = ((TCCR3B & 0b11111000) | 0x01); // set prescaler to 1
|
||||
else if ( pin == 6 || pin == 13 ) { // a bit more complicated 10 bit timer
|
||||
// PLL Configuration
|
||||
PLLFRQ= ((PLLFRQ & 0b11001111) | 0x20); // Use 96MHz / 1.5 = 64MHz
|
||||
TCCR4B = ((TCCR4B & 0b11110000) | 0xB); // configure prescaler to get 64M/2/1024 = 31.25 kHz
|
||||
TCCR4D = ((TCCR4D & 0b11111100) | 0x01); // configure the pwm phase-corrected mode
|
||||
|
||||
if (pin == 6) TCCR4A = 0x82; // activate channel A - pin 13
|
||||
else if (pin == 13) TCCR4C |= 0x09; // Activate channel D - pin 6
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure1PWM(long pwm_frequency,const int pinA) {
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
_pinHighFrequency(pinA);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure2PWM(long pwm_frequency,const int pinA, const int pinB) {
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
_pinHighFrequency(pinA);
|
||||
_pinHighFrequency(pinB);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
_pinHighFrequency(pinA);
|
||||
_pinHighFrequency(pinB);
|
||||
_pinHighFrequency(pinC);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB, pinC },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle1PWM(float dc_a, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
}
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_b);
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, int pinA, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_b);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[2], 255.0f*dc_c);
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure4PWM(long pwm_frequency,const int pin1A, const int pin1B, const int pin2A, const int pin2B) {
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
_pinHighFrequency(pin1A);
|
||||
_pinHighFrequency(pin1B);
|
||||
_pinHighFrequency(pin2A);
|
||||
_pinHighFrequency(pin2B);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pin1A, pin1B, pin2A, pin2B },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_1a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_1b);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[2], 255.0f*dc_2a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[3], 255.0f*dc_2b);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// function configuring pair of high-low side pwm channels, 32khz frequency and center aligned pwm
|
||||
int _configureComplementaryPair(int pinH, int pinL) {
|
||||
if( (pinH == 3 && pinL == 11 ) || (pinH == 11 && pinL == 3 ) ){
|
||||
// configure the pwm phase-corrected mode
|
||||
TCCR0A = ((TCCR0A & 0b11111100) | 0x01);
|
||||
// configure complementary pwm on low side
|
||||
if(pinH == 11 ) TCCR0A = 0b10110000 | (TCCR0A & 0b00001111) ;
|
||||
else TCCR0A = 0b11100000 | (TCCR0A & 0b00001111) ;
|
||||
// set prescaler to 1 - 32kHz freq
|
||||
TCCR0B = ((TCCR0B & 0b11110000) | 0x01);
|
||||
}else if( (pinH == 9 && pinL == 10 ) || (pinH == 10 && pinL == 9 ) ){
|
||||
// set prescaler to 1 - 32kHz freq
|
||||
TCCR1B = ((TCCR1B & 0b11111000) | 0x01);
|
||||
// configure complementary pwm on low side
|
||||
if(pinH == 9 ) TCCR1A = 0b10110000 | (TCCR1A & 0b00001111) ;
|
||||
else TCCR1A = 0b11100000 | (TCCR1A & 0b00001111) ;
|
||||
}else if((pinH == 6 && pinL == 13 ) || (pinH == 13 && pinL == 6 ) ){
|
||||
// PLL Configuration
|
||||
PLLFRQ= ((PLLFRQ & 0b11001111) | 0x20); // Use 96MHz / 1.5 = 64MHz
|
||||
TCCR4B = ((TCCR4B & 0b11110000) | 0xB); // configure prescaler to get 64M/2/1024 = 31.25 kHz
|
||||
TCCR4D = ((TCCR4D & 0b11111100) | 0x01); // configure the pwm phase-corrected mode
|
||||
|
||||
// configure complementary pwm on low side
|
||||
if(pinH == 13 ){
|
||||
TCCR4A = 0x82; // activate channel A - pin 13
|
||||
TCCR4C |= 0x0d; // Activate complementary channel D - pin 6
|
||||
}else {
|
||||
TCCR4C |= 0x09; // Activate channel D - pin 6
|
||||
TCCR4A = 0xc2; // activate complementary channel A - pin 13
|
||||
}
|
||||
}else{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Configuring PWM frequency, resolution and alignment
|
||||
// - BLDC driver - 6PWM setting
|
||||
// - hardware specific
|
||||
void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) {
|
||||
// High PWM frequency
|
||||
// - always max 32kHz
|
||||
int ret_flag = 0;
|
||||
ret_flag += _configureComplementaryPair(pinA_h, pinA_l);
|
||||
ret_flag += _configureComplementaryPair(pinB_h, pinB_l);
|
||||
ret_flag += _configureComplementaryPair(pinC_h, pinC_l);
|
||||
if (ret_flag!=0) return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA_h, pinA_l, pinB_h, pinB_l, pinC_h, pinC_l },
|
||||
.pwm_frequency = pwm_frequency,
|
||||
.dead_zone = dead_zone
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the
|
||||
void _setPwmPair(int pinH, int pinL, float val, int dead_time)
|
||||
{
|
||||
int pwm_h = _constrain(val-dead_time/2,0,255);
|
||||
int pwm_l = _constrain(val+dead_time/2,0,255);
|
||||
|
||||
analogWrite(pinH, pwm_h);
|
||||
if(pwm_l == 255 || pwm_l == 0)
|
||||
digitalWrite(pinL, pwm_l ? LOW : HIGH);
|
||||
else
|
||||
analogWrite(pinL, pwm_l);
|
||||
}
|
||||
|
||||
// Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
// - BLDC driver - 6PWM setting
|
||||
// - hardware specific
|
||||
// supports Arudino/ATmega328
|
||||
void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void* params){
|
||||
_setPwmPair(((GenericDriverParams*)params)->pins[0], ((GenericDriverParams*)params)->pins[1], dc_a*255.0, ((GenericDriverParams*)params)->dead_zone*255.0);
|
||||
_setPwmPair(((GenericDriverParams*)params)->pins[2], ((GenericDriverParams*)params)->pins[3], dc_b*255.0, ((GenericDriverParams*)params)->dead_zone*255.0);
|
||||
_setPwmPair(((GenericDriverParams*)params)->pins[4], ((GenericDriverParams*)params)->pins[5], dc_c*255.0, ((GenericDriverParams*)params)->dead_zone*255.0);
|
||||
|
||||
_UNUSED(phase_state);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,449 @@
|
||||
#include "../hardware_api.h"
|
||||
|
||||
#if defined(__arm__) && defined(__SAM3X8E__)
|
||||
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for Arduino/Due")
|
||||
#pragma message("")
|
||||
|
||||
|
||||
#define _PWM_FREQUENCY 25000 // 25khz
|
||||
#define _PWM_FREQUENCY_MAX 50000 // 50khz
|
||||
|
||||
#define _PWM_RES_MIN 255 // 50khz
|
||||
|
||||
// pwm frequency and max duty cycle
|
||||
static unsigned long _pwm_frequency;
|
||||
static int _max_pwm_value = 1023;
|
||||
|
||||
// array mapping the timer values to the interrupt handlers
|
||||
static IRQn_Type irq_type[] = {TC0_IRQn, TC0_IRQn, TC1_IRQn, TC1_IRQn, TC2_IRQn, TC2_IRQn, TC3_IRQn, TC3_IRQn, TC4_IRQn, TC4_IRQn, TC5_IRQn, TC5_IRQn, TC6_IRQn, TC6_IRQn, TC7_IRQn, TC7_IRQn, TC8_IRQn, TC8_IRQn};
|
||||
// current counter values
|
||||
static volatile uint32_t pwm_counter_vals[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
|
||||
// variables copied from wiring_analog.cpp for arduino due
|
||||
static uint8_t PWMEnabled = 0;
|
||||
static uint8_t TCChanEnabled[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
static const uint32_t channelToChNo[] = { 0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2 };
|
||||
static const uint32_t channelToAB[] = { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 };
|
||||
static Tc *channelToTC[] = {
|
||||
TC0, TC0, TC0, TC0, TC0, TC0,
|
||||
TC1, TC1, TC1, TC1, TC1, TC1,
|
||||
TC2, TC2, TC2, TC2, TC2, TC2 };
|
||||
static const uint32_t channelToId[] = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8 };
|
||||
|
||||
|
||||
// function setting the CMR register
|
||||
static void TC_SetCMR_ChannelA(Tc *tc, uint32_t chan, uint32_t v){ tc->TC_CHANNEL[chan].TC_CMR = (tc->TC_CHANNEL[chan].TC_CMR & 0xFFF0FFFF) | v;}
|
||||
static void TC_SetCMR_ChannelB(Tc *tc, uint32_t chan, uint32_t v){ tc->TC_CHANNEL[chan].TC_CMR = (tc->TC_CHANNEL[chan].TC_CMR & 0xF0FFFFFF) | v; }
|
||||
|
||||
|
||||
// function which starts and syncs the timers
|
||||
// if the pin is the true PWM pin this function does not do anything
|
||||
void syncTimers(uint32_t ulPin1,uint32_t ulPin2, uint32_t ulPin3 = -1, uint32_t ulPin4 = -1){
|
||||
uint32_t chNo1,chNo2,chNo3,chNo4;
|
||||
Tc *chTC1 = nullptr,*chTC2 = nullptr,*chTC3 = nullptr,*chTC4 = nullptr;
|
||||
|
||||
// configure timer channel for the first pin if it is a timer pin
|
||||
uint32_t attr = g_APinDescription[ulPin1].ulPinAttribute;
|
||||
if ((attr & PIN_ATTR_TIMER) == PIN_ATTR_TIMER) {
|
||||
ETCChannel channel1 = g_APinDescription[ulPin1].ulTCChannel;
|
||||
chNo1 = channelToChNo[channel1];
|
||||
chTC1 = channelToTC[channel1];
|
||||
TCChanEnabled[channelToId[channel1]] = 1;
|
||||
}
|
||||
|
||||
// configure timer channel for the first pin if it is a timer pin
|
||||
attr = g_APinDescription[ulPin2].ulPinAttribute;
|
||||
if ((attr & PIN_ATTR_TIMER) == PIN_ATTR_TIMER) {
|
||||
ETCChannel channel2 = g_APinDescription[ulPin2].ulTCChannel;
|
||||
chNo2 = channelToChNo[channel2];
|
||||
chTC2 = channelToTC[channel2];
|
||||
TCChanEnabled[channelToId[channel2]] = 1;
|
||||
}
|
||||
if(ulPin3 > 0 ){
|
||||
// configure timer channel for the first pin if it is a timer pin
|
||||
attr = g_APinDescription[ulPin3].ulPinAttribute;
|
||||
if ((attr & PIN_ATTR_TIMER) == PIN_ATTR_TIMER) {
|
||||
ETCChannel channel3 = g_APinDescription[ulPin3].ulTCChannel;
|
||||
chNo3 = channelToChNo[channel3];
|
||||
chTC3 = channelToTC[channel3];
|
||||
TCChanEnabled[channelToId[channel3]] = 1;
|
||||
}
|
||||
}
|
||||
if(ulPin4 > 0 ){
|
||||
// configure timer channel for the first pin if it is a timer pin
|
||||
attr = g_APinDescription[ulPin4].ulPinAttribute;
|
||||
if ((attr & PIN_ATTR_TIMER) == PIN_ATTR_TIMER) {
|
||||
ETCChannel channel4 = g_APinDescription[ulPin4].ulTCChannel;
|
||||
chNo4 = channelToChNo[channel4];
|
||||
chTC4 = channelToTC[channel4];
|
||||
TCChanEnabled[channelToId[channel4]] = 1;
|
||||
}
|
||||
}
|
||||
// start timers and make them synced
|
||||
if(chTC1){
|
||||
TC_Start(chTC1, chNo1);
|
||||
chTC1->TC_BCR = TC_BCR_SYNC;
|
||||
}
|
||||
if(chTC2){
|
||||
TC_Start(chTC2, chNo2);
|
||||
chTC2->TC_BCR = TC_BCR_SYNC;
|
||||
}
|
||||
if(chTC3 && ulPin3){
|
||||
TC_Start(chTC3, chNo3);
|
||||
chTC3->TC_BCR = TC_BCR_SYNC;
|
||||
}
|
||||
if(chTC4 && ulPin4){
|
||||
TC_Start(chTC4, chNo4);
|
||||
chTC4->TC_BCR = TC_BCR_SYNC;
|
||||
}
|
||||
}
|
||||
|
||||
// function configuring the pwm frequency for given pin
|
||||
// possible to supply the pwm pin and the timer pin
|
||||
void initPWM(uint32_t ulPin, uint32_t pwm_freq){
|
||||
// check which pin type
|
||||
uint32_t attr = g_APinDescription[ulPin].ulPinAttribute;
|
||||
if ((attr & PIN_ATTR_PWM) == PIN_ATTR_PWM) { // if pwm pin
|
||||
|
||||
if (!PWMEnabled) {
|
||||
// PWM Startup code
|
||||
pmc_enable_periph_clk(PWM_INTERFACE_ID);
|
||||
// this function does not work too well - I'll rewrite it
|
||||
// PWMC_ConfigureClocks(PWM_FREQUENCY * _max_pwm_value, 0, VARIANT_MCK);
|
||||
|
||||
// finding the divisors an prescalers form FindClockConfiguration function
|
||||
uint32_t divisors[11] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
|
||||
uint8_t divisor = 0;
|
||||
uint32_t prescaler;
|
||||
|
||||
/* Find prescaler and divisor values */
|
||||
prescaler = (VARIANT_MCK / divisors[divisor]) / (pwm_freq*_max_pwm_value);
|
||||
while ((prescaler > 255) && (divisor < 11)) {
|
||||
divisor++;
|
||||
prescaler = (VARIANT_MCK / divisors[divisor]) / (pwm_freq*_max_pwm_value);
|
||||
}
|
||||
// update the divisor*prescaler value
|
||||
prescaler = prescaler | (divisor << 8);
|
||||
|
||||
// now calculate the real resolution timer period necessary (pwm resolution)
|
||||
// pwm_res = bus_freq / (pwm_freq * (prescaler))
|
||||
_max_pwm_value = (double)VARIANT_MCK / (double)pwm_freq / (double)(prescaler);
|
||||
// set the prescaler value
|
||||
PWM->PWM_CLK = prescaler;
|
||||
|
||||
PWMEnabled = 1;
|
||||
}
|
||||
|
||||
uint32_t chan = g_APinDescription[ulPin].ulPWMChannel;
|
||||
if ((g_pinStatus[ulPin] & 0xF) != PIN_STATUS_PWM) {
|
||||
// Setup PWM for this pin
|
||||
PIO_Configure(g_APinDescription[ulPin].pPort,
|
||||
g_APinDescription[ulPin].ulPinType,
|
||||
g_APinDescription[ulPin].ulPin,
|
||||
g_APinDescription[ulPin].ulPinConfiguration);
|
||||
// PWM_CMR_CALG - center align
|
||||
// PWMC_ConfigureChannel(PWM_INTERFACE, chan, PWM_CMR_CPRE_CLKA, PWM_CMR_CALG, 0);
|
||||
PWMC_ConfigureChannel(PWM_INTERFACE, chan, PWM_CMR_CPRE_CLKA, 0, 0);
|
||||
PWMC_SetPeriod(PWM_INTERFACE, chan, _max_pwm_value);
|
||||
PWMC_SetDutyCycle(PWM_INTERFACE, chan, 0);
|
||||
PWMC_EnableChannel(PWM_INTERFACE, chan);
|
||||
g_pinStatus[ulPin] = (g_pinStatus[ulPin] & 0xF0) | PIN_STATUS_PWM;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((attr & PIN_ATTR_TIMER) == PIN_ATTR_TIMER) { // if timer pin
|
||||
// We use MCLK/2 as clock.
|
||||
const uint32_t TC = VARIANT_MCK / 2 / pwm_freq ;
|
||||
// Setup Timer for this pin
|
||||
ETCChannel channel = g_APinDescription[ulPin].ulTCChannel;
|
||||
uint32_t chNo = channelToChNo[channel];
|
||||
uint32_t chA = channelToAB[channel];
|
||||
Tc *chTC = channelToTC[channel];
|
||||
uint32_t interfaceID = channelToId[channel];
|
||||
|
||||
if (!TCChanEnabled[interfaceID]) {
|
||||
pmc_enable_periph_clk(TC_INTERFACE_ID + interfaceID);
|
||||
TC_Configure(chTC, chNo,
|
||||
TC_CMR_TCCLKS_TIMER_CLOCK1 |
|
||||
TC_CMR_WAVE | // Waveform mode
|
||||
TC_CMR_WAVSEL_UP_RC | // Counter running up and reset when equals to RC
|
||||
TC_CMR_EEVT_XC0 | // Set external events from XC0 (this setup TIOB as output)
|
||||
TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
|
||||
TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR);
|
||||
TC_SetRC(chTC, chNo, TC);
|
||||
}
|
||||
|
||||
// disable the counter on start
|
||||
if (chA){
|
||||
TC_SetCMR_ChannelA(chTC, chNo, TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET);
|
||||
}else{
|
||||
TC_SetCMR_ChannelB(chTC, chNo, TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_SET);
|
||||
}
|
||||
// configure input-ouput structure
|
||||
if ((g_pinStatus[ulPin] & 0xF) != PIN_STATUS_PWM) {
|
||||
PIO_Configure(g_APinDescription[ulPin].pPort,
|
||||
g_APinDescription[ulPin].ulPinType,
|
||||
g_APinDescription[ulPin].ulPin,
|
||||
g_APinDescription[ulPin].ulPinConfiguration);
|
||||
g_pinStatus[ulPin] = (g_pinStatus[ulPin] & 0xF0) | PIN_STATUS_PWM;
|
||||
}
|
||||
// enable interrupts
|
||||
chTC->TC_CHANNEL[chNo].TC_IER = TC_IER_CPAS // interrupt on RA compare match
|
||||
| TC_IER_CPBS // interrupt on RB compare match
|
||||
| TC_IER_CPCS; // interrupt on RC compare match
|
||||
chTC->TC_CHANNEL[chNo].TC_IDR = ~TC_IER_CPAS // interrupt on RA compare match
|
||||
& ~TC_IER_CPBS // interrupt on RB compare match
|
||||
& ~ TC_IER_CPCS; // interrupt on RC compare match
|
||||
// enable interrupts for this timer
|
||||
NVIC_EnableIRQ(irq_type[channel]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// pwm setting function
|
||||
// it sets the duty cycle for pwm pin or timer pin
|
||||
void setPwm(uint32_t ulPin, uint32_t ulValue) {
|
||||
// check pin type
|
||||
uint32_t attr = g_APinDescription[ulPin].ulPinAttribute;
|
||||
if ((attr & PIN_ATTR_PWM) == PIN_ATTR_PWM) { // if pwm
|
||||
uint32_t chan = g_APinDescription[ulPin].ulPWMChannel;
|
||||
PWMC_SetDutyCycle(PWM_INTERFACE, chan, ulValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((attr & PIN_ATTR_TIMER) == PIN_ATTR_TIMER) { // if timer pin
|
||||
// get the timer variables
|
||||
ETCChannel channel = g_APinDescription[ulPin].ulTCChannel;
|
||||
Tc *chTC = channelToTC[channel];
|
||||
uint32_t chNo = channelToChNo[channel];
|
||||
if(!ulValue) {
|
||||
// if the value 0 disable counter
|
||||
if (channelToAB[channel])
|
||||
TC_SetCMR_ChannelA(chTC, chNo, TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR);
|
||||
else
|
||||
TC_SetCMR_ChannelB(chTC, chNo, TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR);
|
||||
}else{
|
||||
// if the value not zero
|
||||
// calculate clock
|
||||
const uint32_t TC = VARIANT_MCK / 2 / _pwm_frequency;
|
||||
// Map value to Timer ranges 0..max_duty_cycle => 0..TC
|
||||
// Setup Timer for this pin
|
||||
ulValue = ulValue * TC ;
|
||||
pwm_counter_vals[channel] = ulValue / _max_pwm_value;
|
||||
// enable counter
|
||||
if (channelToAB[channel])
|
||||
TC_SetCMR_ChannelA(chTC, chNo, TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET);
|
||||
else
|
||||
TC_SetCMR_ChannelB(chTC, chNo, TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_SET);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// interrupt handlers for seamless pwm duty-cycle setting
|
||||
void TC0_Handler()
|
||||
{
|
||||
// read/clear interrupt status
|
||||
TC_GetStatus(TC0, 0);
|
||||
// update the counters
|
||||
if(pwm_counter_vals[0]) TC_SetRA(TC0, 0, pwm_counter_vals[0]);
|
||||
if(pwm_counter_vals[1]) TC_SetRB(TC0, 0, pwm_counter_vals[1]);
|
||||
}
|
||||
|
||||
void TC1_Handler()
|
||||
{
|
||||
// read/clear interrupt status
|
||||
TC_GetStatus(TC0, 1);
|
||||
// update the counters
|
||||
if(pwm_counter_vals[2]) TC_SetRA(TC0, 1, pwm_counter_vals[2]);
|
||||
if(pwm_counter_vals[3]) TC_SetRB(TC0, 1, pwm_counter_vals[3]);
|
||||
}
|
||||
|
||||
void TC2_Handler()
|
||||
{
|
||||
// read/clear interrupt status
|
||||
TC_GetStatus(TC0, 2);
|
||||
// update the counters
|
||||
if(pwm_counter_vals[4]) TC_SetRA(TC0, 2, pwm_counter_vals[4]);
|
||||
if(pwm_counter_vals[5]) TC_SetRB(TC0, 2, pwm_counter_vals[5]);
|
||||
}
|
||||
void TC3_Handler()
|
||||
{
|
||||
// read/clear interrupt status
|
||||
TC_GetStatus(TC1, 0);
|
||||
// update the counters
|
||||
if(pwm_counter_vals[6]) TC_SetRA(TC1, 0, pwm_counter_vals[6]);
|
||||
if(pwm_counter_vals[7]) TC_SetRB(TC1, 0, pwm_counter_vals[7]);
|
||||
}
|
||||
|
||||
void TC4_Handler()
|
||||
{
|
||||
// read/clear interrupt status
|
||||
TC_GetStatus(TC1, 1);
|
||||
// update the counters
|
||||
if(pwm_counter_vals[8]) TC_SetRA(TC1, 1, pwm_counter_vals[8]);
|
||||
if(pwm_counter_vals[9]) TC_SetRB(TC1, 1, pwm_counter_vals[9]);
|
||||
}
|
||||
|
||||
void TC5_Handler()
|
||||
{
|
||||
// read/clear interrupt status
|
||||
TC_GetStatus(TC1, 2);
|
||||
// update the counters
|
||||
if(pwm_counter_vals[10]) TC_SetRA(TC1, 2, pwm_counter_vals[10]);
|
||||
if(pwm_counter_vals[11]) TC_SetRB(TC1, 2, pwm_counter_vals[11]);
|
||||
}
|
||||
void TC6_Handler()
|
||||
{
|
||||
// read/clear interrupt status
|
||||
TC_GetStatus(TC2, 0);
|
||||
// update the counters
|
||||
if(pwm_counter_vals[12]) TC_SetRA(TC2, 0, pwm_counter_vals[12]);
|
||||
if(pwm_counter_vals[13]) TC_SetRB(TC2, 0, pwm_counter_vals[13]);
|
||||
}
|
||||
|
||||
void TC7_Handler()
|
||||
{
|
||||
// read/clear interrupt status
|
||||
TC_GetStatus(TC2, 1);
|
||||
// update the counters
|
||||
if(pwm_counter_vals[14]) TC_SetRA(TC2, 1, pwm_counter_vals[14]);
|
||||
if(pwm_counter_vals[15]) TC_SetRB(TC2, 1, pwm_counter_vals[15]);
|
||||
}
|
||||
|
||||
void TC8_Handler()
|
||||
{
|
||||
// read/clear interrupt status
|
||||
TC_GetStatus(TC2, 2);
|
||||
// update the counters
|
||||
if(pwm_counter_vals[16]) TC_SetRA(TC2, 2, pwm_counter_vals[16]);
|
||||
if(pwm_counter_vals[17]) TC_SetRB(TC2, 2, pwm_counter_vals[17]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// implementation of the hardware_api.cpp
|
||||
// ---------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware specific
|
||||
void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 50khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
// save the pwm frequency
|
||||
_pwm_frequency = pwm_frequency;
|
||||
// cinfigure pwm pins
|
||||
initPWM(pinA, _pwm_frequency);
|
||||
initPWM(pinB, _pwm_frequency);
|
||||
initPWM(pinC, _pwm_frequency);
|
||||
// sync the timers if possible
|
||||
syncTimers(pinA, pinB, pinC);
|
||||
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB, pinC },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Configuring PWM frequency, resolution and alignment
|
||||
//- Stepper driver - 2PWM setting
|
||||
// - hardware specific
|
||||
void* _configure2PWM(long pwm_frequency, const int pinA, const int pinB) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency)) pwm_frequency = _PWM_FREQUENCY; // default frequency 50khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
// save the pwm frequency
|
||||
_pwm_frequency = pwm_frequency;
|
||||
// cinfigure pwm pins
|
||||
initPWM(pinA, _pwm_frequency);
|
||||
initPWM(pinB, _pwm_frequency);
|
||||
// sync the timers if possible
|
||||
syncTimers(pinA, pinB);
|
||||
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure4PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC, const int pinD) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency)) pwm_frequency = _PWM_FREQUENCY; // default frequency 50khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
// save the pwm frequency
|
||||
_pwm_frequency = pwm_frequency;
|
||||
// cinfigure pwm pins
|
||||
initPWM(pinA, _pwm_frequency);
|
||||
initPWM(pinB, _pwm_frequency);
|
||||
initPWM(pinC, _pwm_frequency);
|
||||
initPWM(pinD, _pwm_frequency);
|
||||
// sync the timers if possible
|
||||
syncTimers(pinA, pinB, pinC, pinD);
|
||||
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB, pinC, pinD },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* param){
|
||||
// transform duty cycle from [0,1] to [0,_max_pwm_value]
|
||||
GenericDriverParams* p = (GenericDriverParams*)param;
|
||||
setPwm(p->pins[0], _max_pwm_value*dc_a);
|
||||
setPwm(p->pins[1], _max_pwm_value*dc_b);
|
||||
setPwm(p->pins[2], _max_pwm_value*dc_c);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* param){
|
||||
// transform duty cycle from [0,1] to [0,_max_pwm_value]
|
||||
GenericDriverParams* p = (GenericDriverParams*)param;
|
||||
setPwm(p->pins[0], _max_pwm_value*dc_1a);
|
||||
setPwm(p->pins[1], _max_pwm_value*dc_1b);
|
||||
setPwm(p->pins[2], _max_pwm_value*dc_2a);
|
||||
setPwm(p->pins[3], _max_pwm_value*dc_2b);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
// - Stepper driver - 2PWM setting
|
||||
// - hardware specific
|
||||
void _writeDutyCycle2PWM(float dc_a, float dc_b, void* param){
|
||||
// transform duty cycle from [0,1] to [0,_max_pwm_value]
|
||||
GenericDriverParams* p = (GenericDriverParams*)param;
|
||||
setPwm(p->pins[0], _max_pwm_value*dc_a);
|
||||
setPwm(p->pins[1], _max_pwm_value*dc_b);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
#ifndef ESP32_DRIVER_MCPWM_H
|
||||
#define ESP32_DRIVER_MCPWM_H
|
||||
|
||||
#include "../../hardware_api.h"
|
||||
|
||||
#if defined(ESP_H) && defined(ARDUINO_ARCH_ESP32) && defined(SOC_MCPWM_SUPPORTED) && !defined(SIMPLEFOC_ESP32_USELEDC)
|
||||
|
||||
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for ESP32 MCPWM driver")
|
||||
#pragma message("")
|
||||
|
||||
|
||||
#include "driver/mcpwm.h"
|
||||
#include "soc/mcpwm_reg.h"
|
||||
#include "soc/mcpwm_struct.h"
|
||||
|
||||
// empty motor slot
|
||||
#define _EMPTY_SLOT -20
|
||||
#define _TAKEN_SLOT -21
|
||||
|
||||
// ABI bus frequency - would be better to take it from somewhere
|
||||
// but I did nto find a good exposed variable
|
||||
#define _MCPWM_FREQ 160e6f
|
||||
|
||||
// preferred pwm resolution default
|
||||
#define _PWM_RES_DEF 4096
|
||||
// min resolution
|
||||
#define _PWM_RES_MIN 3000
|
||||
// max resolution
|
||||
#define _PWM_RES_MAX 8000
|
||||
// pwm frequency
|
||||
#define _PWM_FREQUENCY 25000 // default
|
||||
#define _PWM_FREQUENCY_MAX 50000 // mqx
|
||||
|
||||
// structure containing motor slot configuration
|
||||
// this library supports up to 4 motors
|
||||
typedef struct {
|
||||
int pinA;
|
||||
mcpwm_dev_t* mcpwm_num;
|
||||
mcpwm_unit_t mcpwm_unit;
|
||||
mcpwm_operator_t mcpwm_operator;
|
||||
mcpwm_io_signals_t mcpwm_a;
|
||||
mcpwm_io_signals_t mcpwm_b;
|
||||
mcpwm_io_signals_t mcpwm_c;
|
||||
} bldc_3pwm_motor_slots_t;
|
||||
|
||||
typedef struct {
|
||||
int pin1A;
|
||||
mcpwm_dev_t* mcpwm_num;
|
||||
mcpwm_unit_t mcpwm_unit;
|
||||
mcpwm_operator_t mcpwm_operator1;
|
||||
mcpwm_operator_t mcpwm_operator2;
|
||||
mcpwm_io_signals_t mcpwm_1a;
|
||||
mcpwm_io_signals_t mcpwm_1b;
|
||||
mcpwm_io_signals_t mcpwm_2a;
|
||||
mcpwm_io_signals_t mcpwm_2b;
|
||||
} stepper_4pwm_motor_slots_t;
|
||||
|
||||
typedef struct {
|
||||
int pin1pwm;
|
||||
mcpwm_dev_t* mcpwm_num;
|
||||
mcpwm_unit_t mcpwm_unit;
|
||||
mcpwm_operator_t mcpwm_operator;
|
||||
mcpwm_io_signals_t mcpwm_a;
|
||||
mcpwm_io_signals_t mcpwm_b;
|
||||
} stepper_2pwm_motor_slots_t;
|
||||
|
||||
typedef struct {
|
||||
int pinAH;
|
||||
mcpwm_dev_t* mcpwm_num;
|
||||
mcpwm_unit_t mcpwm_unit;
|
||||
mcpwm_operator_t mcpwm_operator1;
|
||||
mcpwm_operator_t mcpwm_operator2;
|
||||
mcpwm_io_signals_t mcpwm_ah;
|
||||
mcpwm_io_signals_t mcpwm_bh;
|
||||
mcpwm_io_signals_t mcpwm_ch;
|
||||
mcpwm_io_signals_t mcpwm_al;
|
||||
mcpwm_io_signals_t mcpwm_bl;
|
||||
mcpwm_io_signals_t mcpwm_cl;
|
||||
} bldc_6pwm_motor_slots_t;
|
||||
|
||||
|
||||
typedef struct ESP32MCPWMDriverParams {
|
||||
long pwm_frequency;
|
||||
mcpwm_dev_t* mcpwm_dev;
|
||||
mcpwm_unit_t mcpwm_unit;
|
||||
mcpwm_operator_t mcpwm_operator1;
|
||||
mcpwm_operator_t mcpwm_operator2;
|
||||
float deadtime;
|
||||
} ESP32MCPWMDriverParams;
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,185 @@
|
||||
#include "../../hardware_api.h"
|
||||
|
||||
#if defined(ESP_H) && defined(ARDUINO_ARCH_ESP32) && ( !defined(SOC_MCPWM_SUPPORTED) || defined(SIMPLEFOC_ESP32_USELEDC) )
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for ESP32 LEDC driver")
|
||||
#pragma message("")
|
||||
|
||||
#include "driver/ledc.h"
|
||||
|
||||
#define _PWM_FREQUENCY 25000 // 25khz
|
||||
#define _PWM_FREQUENCY_MAX 38000 // 38khz max to be able to have 10 bit pwm resolution
|
||||
#define _PWM_RES_BIT 10 // 10 bir resolution
|
||||
#define _PWM_RES 1023 // 2^10-1 = 1023-1
|
||||
|
||||
|
||||
// empty motor slot
|
||||
#define _EMPTY_SLOT -20
|
||||
#define _TAKEN_SLOT -21
|
||||
|
||||
// figure out how many ledc channels are avaible
|
||||
// esp32 - 2x8=16
|
||||
// esp32s2 - 8
|
||||
// esp32c3 - 6
|
||||
#include "soc/soc_caps.h"
|
||||
#ifdef SOC_LEDC_SUPPORT_HS_MODE
|
||||
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM<<1)
|
||||
#else
|
||||
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM)
|
||||
#endif
|
||||
|
||||
|
||||
// current channel stack index
|
||||
// support for multiple motors
|
||||
// esp32 has 16 channels
|
||||
// esp32s2 has 8 channels
|
||||
// esp32c3 has 6 channels
|
||||
int channel_index = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct ESP32LEDCDriverParams {
|
||||
int channels[6];
|
||||
long pwm_frequency;
|
||||
} ESP32LEDCDriverParams;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// configure High PWM frequency
|
||||
void _setHighFrequency(const long freq, const int pin, const int channel){
|
||||
ledcSetup(channel, freq, _PWM_RES_BIT );
|
||||
ledcAttachPin(pin, channel);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void* _configure1PWM(long pwm_frequency, const int pinA) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
|
||||
// check if enough channels available
|
||||
if ( channel_index + 1 >= LEDC_CHANNELS ) return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
int ch1 = channel_index++;
|
||||
_setHighFrequency(pwm_frequency, pinA, ch1);
|
||||
|
||||
ESP32LEDCDriverParams* params = new ESP32LEDCDriverParams {
|
||||
.channels = { ch1 },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void* _configure2PWM(long pwm_frequency, const int pinA, const int pinB) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
|
||||
// check if enough channels available
|
||||
if ( channel_index + 2 >= LEDC_CHANNELS ) return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
int ch1 = channel_index++;
|
||||
int ch2 = channel_index++;
|
||||
_setHighFrequency(pwm_frequency, pinA, ch1);
|
||||
_setHighFrequency(pwm_frequency, pinB, ch2);
|
||||
|
||||
ESP32LEDCDriverParams* params = new ESP32LEDCDriverParams {
|
||||
.channels = { ch1, ch2 },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
|
||||
// check if enough channels available
|
||||
if ( channel_index + 3 >= LEDC_CHANNELS ) return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
int ch1 = channel_index++;
|
||||
int ch2 = channel_index++;
|
||||
int ch3 = channel_index++;
|
||||
_setHighFrequency(pwm_frequency, pinA, ch1);
|
||||
_setHighFrequency(pwm_frequency, pinB, ch2);
|
||||
_setHighFrequency(pwm_frequency, pinC, ch3);
|
||||
|
||||
ESP32LEDCDriverParams* params = new ESP32LEDCDriverParams {
|
||||
.channels = { ch1, ch2, ch3 },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void* _configure4PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC, const int pinD) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
|
||||
// check if enough channels available
|
||||
if ( channel_index + 4 >= LEDC_CHANNELS ) return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
int ch1 = channel_index++;
|
||||
int ch2 = channel_index++;
|
||||
int ch3 = channel_index++;
|
||||
int ch4 = channel_index++;
|
||||
_setHighFrequency(pwm_frequency, pinA, ch1);
|
||||
_setHighFrequency(pwm_frequency, pinB, ch2);
|
||||
_setHighFrequency(pwm_frequency, pinC, ch3);
|
||||
_setHighFrequency(pwm_frequency, pinD, ch4);
|
||||
|
||||
ESP32LEDCDriverParams* params = new ESP32LEDCDriverParams {
|
||||
.channels = { ch1, ch2, ch3, ch4 },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void _writeDutyCycle1PWM(float dc_a, void* params){
|
||||
ledcWrite(((ESP32LEDCDriverParams*)params)->channels[0], _constrain(_PWM_RES*dc_a, 0, _PWM_RES));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params){
|
||||
ledcWrite(((ESP32LEDCDriverParams*)params)->channels[0], _constrain(_PWM_RES*dc_a, 0, _PWM_RES));
|
||||
ledcWrite(((ESP32LEDCDriverParams*)params)->channels[1], _constrain(_PWM_RES*dc_b, 0, _PWM_RES));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params){
|
||||
ledcWrite(((ESP32LEDCDriverParams*)params)->channels[0], _constrain(_PWM_RES*dc_a, 0, _PWM_RES));
|
||||
ledcWrite(((ESP32LEDCDriverParams*)params)->channels[1], _constrain(_PWM_RES*dc_b, 0, _PWM_RES));
|
||||
ledcWrite(((ESP32LEDCDriverParams*)params)->channels[2], _constrain(_PWM_RES*dc_c, 0, _PWM_RES));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params){
|
||||
ledcWrite(((ESP32LEDCDriverParams*)params)->channels[0], _constrain(_PWM_RES*dc_1a, 0, _PWM_RES));
|
||||
ledcWrite(((ESP32LEDCDriverParams*)params)->channels[1], _constrain(_PWM_RES*dc_1b, 0, _PWM_RES));
|
||||
ledcWrite(((ESP32LEDCDriverParams*)params)->channels[2], _constrain(_PWM_RES*dc_2a, 0, _PWM_RES));
|
||||
ledcWrite(((ESP32LEDCDriverParams*)params)->channels[3], _constrain(_PWM_RES*dc_2b, 0, _PWM_RES));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,431 @@
|
||||
#include "esp32_driver_mcpwm.h"
|
||||
|
||||
#if defined(ESP_H) && defined(ARDUINO_ARCH_ESP32) && defined(SOC_MCPWM_SUPPORTED) && !defined(SIMPLEFOC_ESP32_USELEDC)
|
||||
|
||||
#ifndef SIMPLEFOC_ESP32_HW_DEADTIME
|
||||
#define SIMPLEFOC_ESP32_HW_DEADTIME true // TODO: Change to false when sw-deadtime & phase_state is approved ready for general use.
|
||||
#endif
|
||||
|
||||
// define bldc motor slots array
|
||||
bldc_3pwm_motor_slots_t esp32_bldc_3pwm_motor_slots[4] = {
|
||||
{_EMPTY_SLOT, &MCPWM0, MCPWM_UNIT_0, MCPWM_OPR_A, MCPWM0A, MCPWM1A, MCPWM2A}, // 1st motor will be MCPWM0 channel A
|
||||
{_EMPTY_SLOT, &MCPWM0, MCPWM_UNIT_0, MCPWM_OPR_B, MCPWM0B, MCPWM1B, MCPWM2B}, // 2nd motor will be MCPWM0 channel B
|
||||
{_EMPTY_SLOT, &MCPWM1, MCPWM_UNIT_1, MCPWM_OPR_A, MCPWM0A, MCPWM1A, MCPWM2A}, // 3rd motor will be MCPWM1 channel A
|
||||
{_EMPTY_SLOT, &MCPWM1, MCPWM_UNIT_1, MCPWM_OPR_B, MCPWM0B, MCPWM1B, MCPWM2B} // 4th motor will be MCPWM1 channel B
|
||||
};
|
||||
|
||||
// define stepper motor slots array
|
||||
bldc_6pwm_motor_slots_t esp32_bldc_6pwm_motor_slots[2] = {
|
||||
{_EMPTY_SLOT, &MCPWM0, MCPWM_UNIT_0, MCPWM_OPR_A, MCPWM_OPR_B, MCPWM0A, MCPWM1A, MCPWM2A, MCPWM0B, MCPWM1B, MCPWM2B}, // 1st motor will be on MCPWM0
|
||||
{_EMPTY_SLOT, &MCPWM1, MCPWM_UNIT_1, MCPWM_OPR_A, MCPWM_OPR_B, MCPWM0A, MCPWM1A, MCPWM2A, MCPWM0B, MCPWM1B, MCPWM2B}, // 1st motor will be on MCPWM0
|
||||
};
|
||||
|
||||
// define 4pwm stepper motor slots array
|
||||
stepper_4pwm_motor_slots_t esp32_stepper_4pwm_motor_slots[2] = {
|
||||
{_EMPTY_SLOT, &MCPWM0, MCPWM_UNIT_0, MCPWM_OPR_A, MCPWM_OPR_B, MCPWM0A, MCPWM1A, MCPWM0B, MCPWM1B}, // 1st motor will be on MCPWM0
|
||||
{_EMPTY_SLOT, &MCPWM1, MCPWM_UNIT_1, MCPWM_OPR_A, MCPWM_OPR_B, MCPWM0A, MCPWM1A, MCPWM0B, MCPWM1B}, // 1st motor will be on MCPWM1
|
||||
};
|
||||
|
||||
// define 2pwm stepper motor slots array
|
||||
stepper_2pwm_motor_slots_t esp32_stepper_2pwm_motor_slots[4] = {
|
||||
{_EMPTY_SLOT, &MCPWM0, MCPWM_UNIT_0, MCPWM_OPR_A, MCPWM0A, MCPWM1A}, // 1st motor will be MCPWM0 channel A
|
||||
{_EMPTY_SLOT, &MCPWM0, MCPWM_UNIT_0, MCPWM_OPR_B, MCPWM0B, MCPWM1B}, // 2nd motor will be MCPWM0 channel B
|
||||
{_EMPTY_SLOT, &MCPWM1, MCPWM_UNIT_1, MCPWM_OPR_A, MCPWM0A, MCPWM1A}, // 3rd motor will be MCPWM1 channel A
|
||||
{_EMPTY_SLOT, &MCPWM1, MCPWM_UNIT_1, MCPWM_OPR_B, MCPWM0B, MCPWM1B} // 4th motor will be MCPWM1 channel B
|
||||
};
|
||||
|
||||
|
||||
// configuring high frequency pwm timer
|
||||
// a lot of help from this post from Paul Gauld
|
||||
// https://hackaday.io/project/169905-esp-32-bldc-robot-actuator-controller
|
||||
// a short tutorial for v2.0.1+
|
||||
// https://kzhead.info/sun/q6uFktWgkYeMeZ8/esp32-arduino.html
|
||||
//
|
||||
void _configureTimerFrequency(long pwm_frequency, mcpwm_dev_t* mcpwm_num, mcpwm_unit_t mcpwm_unit, float dead_zone = NOT_SET){
|
||||
|
||||
mcpwm_config_t pwm_config;
|
||||
pwm_config.counter_mode = MCPWM_UP_DOWN_COUNTER; // Up-down counter (triangle wave)
|
||||
pwm_config.duty_mode = (_isset(dead_zone) || SIMPLEFOC_PWM_ACTIVE_HIGH == true) ? MCPWM_DUTY_MODE_0 : MCPWM_DUTY_MODE_1; // Normally Active HIGH (MCPWM_DUTY_MODE_0)
|
||||
pwm_config.frequency = 2*pwm_frequency; // set the desired freq - just a placeholder for now https://github.com/simplefoc/Arduino-FOC/issues/76
|
||||
mcpwm_init(mcpwm_unit, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B with above settings
|
||||
mcpwm_init(mcpwm_unit, MCPWM_TIMER_1, &pwm_config); //Configure PWM1A & PWM1B with above settings
|
||||
mcpwm_init(mcpwm_unit, MCPWM_TIMER_2, &pwm_config); //Configure PWM2A & PWM2B with above settings
|
||||
|
||||
if (_isset(dead_zone)){
|
||||
// dead zone is configured
|
||||
|
||||
// When using hardware deadtime, setting the phase_state parameter is not supported.
|
||||
#if SIMPLEFOC_ESP32_HW_DEADTIME == true
|
||||
float dead_time = (float)(_MCPWM_FREQ / (pwm_frequency)) * dead_zone;
|
||||
mcpwm_deadtime_type_t pwm_mode;
|
||||
if ((SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH == true) && (SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH == true)) {pwm_mode = MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE;} // Normal, noninverting driver
|
||||
else if ((SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH == true) && (SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH == false)){pwm_mode = MCPWM_ACTIVE_HIGH_MODE;} // Inverted lowside driver
|
||||
else if ((SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH == false) && (SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH == true)) {pwm_mode = MCPWM_ACTIVE_LOW_MODE;} // Inverted highside driver
|
||||
else if ((SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH == false) && (SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH == false)){pwm_mode = MCPWM_ACTIVE_LOW_COMPLIMENT_MODE;} // Inverted low- & highside driver. Caution: This may short the FETs on reset of the ESP32, as both pins get pulled low!
|
||||
mcpwm_deadtime_enable(mcpwm_unit, MCPWM_TIMER_0, pwm_mode, dead_time/2.0, dead_time/2.0);
|
||||
mcpwm_deadtime_enable(mcpwm_unit, MCPWM_TIMER_1, pwm_mode, dead_time/2.0, dead_time/2.0);
|
||||
mcpwm_deadtime_enable(mcpwm_unit, MCPWM_TIMER_2, pwm_mode, dead_time/2.0, dead_time/2.0);
|
||||
#else // Software deadtime
|
||||
for (int i = 0; i < 3; i++){
|
||||
if (SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH == true) {mcpwm_set_duty_type(mcpwm_unit, (mcpwm_timer_t) i, MCPWM_GEN_A, MCPWM_DUTY_MODE_0);} // Normal, noninverted highside
|
||||
else if (SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH == false) {mcpwm_set_duty_type(mcpwm_unit, (mcpwm_timer_t) i, MCPWM_GEN_A, MCPWM_DUTY_MODE_1);} // Inverted highside driver
|
||||
|
||||
if (SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH == true) {mcpwm_set_duty_type(mcpwm_unit, (mcpwm_timer_t) i, MCPWM_GEN_B, MCPWM_DUTY_MODE_1);} // Normal, complementary lowside
|
||||
else if (SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH == false) {mcpwm_set_duty_type(mcpwm_unit, (mcpwm_timer_t) i, MCPWM_GEN_B, MCPWM_DUTY_MODE_0);} // Inverted lowside driver
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
_delay(100);
|
||||
|
||||
mcpwm_stop(mcpwm_unit, MCPWM_TIMER_0);
|
||||
mcpwm_stop(mcpwm_unit, MCPWM_TIMER_1);
|
||||
mcpwm_stop(mcpwm_unit, MCPWM_TIMER_2);
|
||||
|
||||
// manual configuration due to the lack of config flexibility in mcpwm_init()
|
||||
mcpwm_num->clk_cfg.clk_prescale = 0;
|
||||
// calculate prescaler and period
|
||||
// step 1: calculate the prescaler using the default pwm resolution
|
||||
// prescaler = bus_freq / (pwm_freq * default_pwm_res) - 1
|
||||
int prescaler = ceil((double)_MCPWM_FREQ / (double)_PWM_RES_DEF / 2.0f / (double)pwm_frequency) - 1;
|
||||
// constrain prescaler
|
||||
prescaler = _constrain(prescaler, 0, 128);
|
||||
// now calculate the real resolution timer period necessary (pwm resolution)
|
||||
// pwm_res = bus_freq / (pwm_freq * (prescaler + 1))
|
||||
int resolution_corrected = (double)_MCPWM_FREQ / 2.0f / (double)pwm_frequency / (double)(prescaler + 1);
|
||||
// if pwm resolution too low lower the prescaler
|
||||
if(resolution_corrected < _PWM_RES_MIN && prescaler > 0 )
|
||||
resolution_corrected = (double)_MCPWM_FREQ / 2.0f / (double)pwm_frequency / (double)(--prescaler + 1);
|
||||
resolution_corrected = _constrain(resolution_corrected, _PWM_RES_MIN, _PWM_RES_MAX);
|
||||
|
||||
// set prescaler
|
||||
mcpwm_num->timer[0].timer_cfg0.timer_prescale = prescaler;
|
||||
mcpwm_num->timer[1].timer_cfg0.timer_prescale = prescaler;
|
||||
mcpwm_num->timer[2].timer_cfg0.timer_prescale = prescaler;
|
||||
_delay(1);
|
||||
//set period
|
||||
mcpwm_num->timer[0].timer_cfg0.timer_period = resolution_corrected;
|
||||
mcpwm_num->timer[1].timer_cfg0.timer_period = resolution_corrected;
|
||||
mcpwm_num->timer[2].timer_cfg0.timer_period = resolution_corrected;
|
||||
_delay(1);
|
||||
mcpwm_num->timer[0].timer_cfg0.timer_period_upmethod = 0;
|
||||
mcpwm_num->timer[1].timer_cfg0.timer_period_upmethod = 0;
|
||||
mcpwm_num->timer[2].timer_cfg0.timer_period_upmethod = 0;
|
||||
_delay(1);
|
||||
// _delay(1);
|
||||
//restart the timers
|
||||
mcpwm_start(mcpwm_unit, MCPWM_TIMER_0);
|
||||
mcpwm_start(mcpwm_unit, MCPWM_TIMER_1);
|
||||
mcpwm_start(mcpwm_unit, MCPWM_TIMER_2);
|
||||
_delay(1);
|
||||
|
||||
mcpwm_sync_config_t sync_conf = {
|
||||
.sync_sig = MCPWM_SELECT_TIMER0_SYNC,
|
||||
.timer_val = 0,
|
||||
.count_direction = MCPWM_TIMER_DIRECTION_UP
|
||||
};
|
||||
mcpwm_sync_configure(mcpwm_unit, MCPWM_TIMER_0, &sync_conf);
|
||||
mcpwm_sync_configure(mcpwm_unit, MCPWM_TIMER_1, &sync_conf);
|
||||
mcpwm_sync_configure(mcpwm_unit, MCPWM_TIMER_2, &sync_conf);
|
||||
|
||||
// Enable sync event for all timers to be the TEZ of timer0
|
||||
mcpwm_set_timer_sync_output(mcpwm_unit, MCPWM_TIMER_0, MCPWM_SWSYNC_SOURCE_TEZ);
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
// supports Arudino/ATmega328, STM32 and ESP32
|
||||
void* _configure2PWM(long pwm_frequency, const int pinA, const int pinB) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25hz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 40kHz max
|
||||
|
||||
stepper_2pwm_motor_slots_t m_slot = {};
|
||||
|
||||
// determine which motor are we connecting
|
||||
// and set the appropriate configuration parameters
|
||||
int slot_num;
|
||||
for(slot_num = 0; slot_num < 4; slot_num++){
|
||||
if(esp32_stepper_2pwm_motor_slots[slot_num].pin1pwm == _EMPTY_SLOT){ // put the new motor in the first empty slot
|
||||
esp32_stepper_2pwm_motor_slots[slot_num].pin1pwm = pinA;
|
||||
m_slot = esp32_stepper_2pwm_motor_slots[slot_num];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// disable all the slots with the same MCPWM
|
||||
// disable 3pwm bldc motor which would go in the same slot
|
||||
esp32_bldc_3pwm_motor_slots[slot_num].pinA = _TAKEN_SLOT;
|
||||
if( slot_num < 2 ){
|
||||
// slot 0 of the 4pwm stepper
|
||||
esp32_stepper_4pwm_motor_slots[0].pin1A = _TAKEN_SLOT;
|
||||
// slot 0 of the 6pwm bldc
|
||||
esp32_bldc_6pwm_motor_slots[0].pinAH = _TAKEN_SLOT;
|
||||
}else{
|
||||
// slot 1 of the stepper
|
||||
esp32_stepper_4pwm_motor_slots[1].pin1A = _TAKEN_SLOT;
|
||||
// slot 1 of the 6pwm bldc
|
||||
esp32_bldc_6pwm_motor_slots[1].pinAH = _TAKEN_SLOT;
|
||||
}
|
||||
// configure pins
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_a, pinA);
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_b, pinB);
|
||||
|
||||
// configure the timer
|
||||
_configureTimerFrequency(pwm_frequency, m_slot.mcpwm_num, m_slot.mcpwm_unit);
|
||||
|
||||
ESP32MCPWMDriverParams* params = new ESP32MCPWMDriverParams {
|
||||
.pwm_frequency = pwm_frequency,
|
||||
.mcpwm_dev = m_slot.mcpwm_num,
|
||||
.mcpwm_unit = m_slot.mcpwm_unit,
|
||||
.mcpwm_operator1 = m_slot.mcpwm_operator
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
// supports Arudino/ATmega328, STM32 and ESP32
|
||||
void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25hz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 40kHz max
|
||||
|
||||
bldc_3pwm_motor_slots_t m_slot = {};
|
||||
|
||||
// determine which motor are we connecting
|
||||
// and set the appropriate configuration parameters
|
||||
int slot_num;
|
||||
for(slot_num = 0; slot_num < 4; slot_num++){
|
||||
if(esp32_bldc_3pwm_motor_slots[slot_num].pinA == _EMPTY_SLOT){ // put the new motor in the first empty slot
|
||||
esp32_bldc_3pwm_motor_slots[slot_num].pinA = pinA;
|
||||
m_slot = esp32_bldc_3pwm_motor_slots[slot_num];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// disable all the slots with the same MCPWM
|
||||
// disable 2pwm steppr motor which would go in the same slot
|
||||
esp32_stepper_2pwm_motor_slots[slot_num].pin1pwm = _TAKEN_SLOT;
|
||||
if( slot_num < 2 ){
|
||||
// slot 0 of the 4pwm stepper
|
||||
esp32_stepper_4pwm_motor_slots[0].pin1A = _TAKEN_SLOT;
|
||||
// slot 0 of the 6pwm bldc
|
||||
esp32_bldc_6pwm_motor_slots[0].pinAH = _TAKEN_SLOT;
|
||||
}else{
|
||||
// slot 1 of the stepper
|
||||
esp32_stepper_4pwm_motor_slots[1].pin1A = _TAKEN_SLOT;
|
||||
// slot 1 of the 6pwm bldc
|
||||
esp32_bldc_6pwm_motor_slots[1].pinAH = _TAKEN_SLOT;
|
||||
}
|
||||
// configure pins
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_a, pinA);
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_b, pinB);
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_c, pinC);
|
||||
|
||||
// configure the timer
|
||||
_configureTimerFrequency(pwm_frequency, m_slot.mcpwm_num, m_slot.mcpwm_unit);
|
||||
|
||||
ESP32MCPWMDriverParams* params = new ESP32MCPWMDriverParams {
|
||||
.pwm_frequency = pwm_frequency,
|
||||
.mcpwm_dev = m_slot.mcpwm_num,
|
||||
.mcpwm_unit = m_slot.mcpwm_unit,
|
||||
.mcpwm_operator1 = m_slot.mcpwm_operator
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure4PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC, const int pinD) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25hz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 40kHz max
|
||||
stepper_4pwm_motor_slots_t m_slot = {};
|
||||
// determine which motor are we connecting
|
||||
// and set the appropriate configuration parameters
|
||||
int slot_num;
|
||||
for(slot_num = 0; slot_num < 2; slot_num++){
|
||||
if(esp32_stepper_4pwm_motor_slots[slot_num].pin1A == _EMPTY_SLOT){ // put the new motor in the first empty slot
|
||||
esp32_stepper_4pwm_motor_slots[slot_num].pin1A = pinA;
|
||||
m_slot = esp32_stepper_4pwm_motor_slots[slot_num];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// disable all the slots with the same MCPWM
|
||||
if( slot_num == 0 ){
|
||||
// slots 0 and 1 of the 3pwm bldc
|
||||
esp32_bldc_3pwm_motor_slots[0].pinA = _TAKEN_SLOT;
|
||||
esp32_bldc_3pwm_motor_slots[1].pinA = _TAKEN_SLOT;
|
||||
// slots 0 and 1 of the 2pwm stepper taken
|
||||
esp32_stepper_2pwm_motor_slots[0].pin1pwm = _TAKEN_SLOT;
|
||||
esp32_stepper_2pwm_motor_slots[1].pin1pwm = _TAKEN_SLOT;
|
||||
// slot 0 of the 6pwm bldc
|
||||
esp32_bldc_6pwm_motor_slots[0].pinAH = _TAKEN_SLOT;
|
||||
}else{
|
||||
// slots 2 and 3 of the 3pwm bldc
|
||||
esp32_bldc_3pwm_motor_slots[2].pinA = _TAKEN_SLOT;
|
||||
esp32_bldc_3pwm_motor_slots[3].pinA = _TAKEN_SLOT;
|
||||
// slots 2 and 3 of the 2pwm stepper taken
|
||||
esp32_stepper_2pwm_motor_slots[2].pin1pwm = _TAKEN_SLOT;
|
||||
esp32_stepper_2pwm_motor_slots[3].pin1pwm = _TAKEN_SLOT;
|
||||
// slot 1 of the 6pwm bldc
|
||||
esp32_bldc_6pwm_motor_slots[1].pinAH = _TAKEN_SLOT;
|
||||
}
|
||||
|
||||
// configure pins
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_1a, pinA);
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_1b, pinB);
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_2a, pinC);
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_2b, pinD);
|
||||
|
||||
// configure the timer
|
||||
_configureTimerFrequency(pwm_frequency, m_slot.mcpwm_num, m_slot.mcpwm_unit);
|
||||
|
||||
ESP32MCPWMDriverParams* params = new ESP32MCPWMDriverParams {
|
||||
.pwm_frequency = pwm_frequency,
|
||||
.mcpwm_dev = m_slot.mcpwm_num,
|
||||
.mcpwm_unit = m_slot.mcpwm_unit,
|
||||
.mcpwm_operator1 = m_slot.mcpwm_operator1,
|
||||
.mcpwm_operator2 = m_slot.mcpwm_operator2
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
// ESP32 uses MCPWM
|
||||
void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params){
|
||||
// se the PWM on the slot timers
|
||||
// transform duty cycle from [0,1] to [0,100]
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_0, ((ESP32MCPWMDriverParams*)params)->mcpwm_operator1, dc_a*100.0);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_1, ((ESP32MCPWMDriverParams*)params)->mcpwm_operator1, dc_b*100.0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
// ESP32 uses MCPWM
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params){
|
||||
// se the PWM on the slot timers
|
||||
// transform duty cycle from [0,1] to [0,100]
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_0, ((ESP32MCPWMDriverParams*)params)->mcpwm_operator1, dc_a*100.0);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_1, ((ESP32MCPWMDriverParams*)params)->mcpwm_operator1, dc_b*100.0);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_2, ((ESP32MCPWMDriverParams*)params)->mcpwm_operator1, dc_c*100.0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
// ESP32 uses MCPWM
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params){
|
||||
// se the PWM on the slot timers
|
||||
// transform duty cycle from [0,1] to [0,100]
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_0, ((ESP32MCPWMDriverParams*)params)->mcpwm_operator1, dc_1a*100.0);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_1, ((ESP32MCPWMDriverParams*)params)->mcpwm_operator1, dc_1b*100.0);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_0, ((ESP32MCPWMDriverParams*)params)->mcpwm_operator2, dc_2a*100.0);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_1, ((ESP32MCPWMDriverParams*)params)->mcpwm_operator2, dc_2b*100.0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Configuring PWM frequency, resolution and alignment
|
||||
// - BLDC driver - 6PWM setting
|
||||
// - hardware specific
|
||||
void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l){
|
||||
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = 20000; // default frequency 20khz - centered pwm has twice lower frequency
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 40kHz max - centered pwm has twice lower frequency
|
||||
bldc_6pwm_motor_slots_t m_slot = {};
|
||||
// determine which motor are we connecting
|
||||
// and set the appropriate configuration parameters
|
||||
int slot_num;
|
||||
for(slot_num = 0; slot_num < 2; slot_num++){
|
||||
if(esp32_bldc_6pwm_motor_slots[slot_num].pinAH == _EMPTY_SLOT){ // put the new motor in the first empty slot
|
||||
esp32_bldc_6pwm_motor_slots[slot_num].pinAH = pinA_h;
|
||||
m_slot = esp32_bldc_6pwm_motor_slots[slot_num];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if no slots available
|
||||
if(slot_num >= 2) return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
// disable all the slots with the same MCPWM
|
||||
if( slot_num == 0 ){
|
||||
// slots 0 and 1 of the 3pwm bldc
|
||||
esp32_bldc_3pwm_motor_slots[0].pinA = _TAKEN_SLOT;
|
||||
esp32_bldc_3pwm_motor_slots[1].pinA = _TAKEN_SLOT;
|
||||
// slot 0 of the 6pwm bldc
|
||||
esp32_stepper_4pwm_motor_slots[0].pin1A = _TAKEN_SLOT;
|
||||
}else{
|
||||
// slots 2 and 3 of the 3pwm bldc
|
||||
esp32_bldc_3pwm_motor_slots[2].pinA = _TAKEN_SLOT;
|
||||
esp32_bldc_3pwm_motor_slots[3].pinA = _TAKEN_SLOT;
|
||||
// slot 1 of the 6pwm bldc
|
||||
esp32_stepper_4pwm_motor_slots[1].pin1A = _TAKEN_SLOT;
|
||||
}
|
||||
|
||||
// configure pins
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_ah, pinA_h);
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_al, pinA_l);
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_bh, pinB_h);
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_bl, pinB_l);
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_ch, pinC_h);
|
||||
mcpwm_gpio_init(m_slot.mcpwm_unit, m_slot.mcpwm_cl, pinC_l);
|
||||
|
||||
// configure the timer
|
||||
_configureTimerFrequency(pwm_frequency, m_slot.mcpwm_num, m_slot.mcpwm_unit, dead_zone);
|
||||
// return
|
||||
ESP32MCPWMDriverParams* params = new ESP32MCPWMDriverParams {
|
||||
.pwm_frequency = pwm_frequency,
|
||||
.mcpwm_dev = m_slot.mcpwm_num,
|
||||
.mcpwm_unit = m_slot.mcpwm_unit,
|
||||
.mcpwm_operator1 = m_slot.mcpwm_operator1,
|
||||
.mcpwm_operator2 = m_slot.mcpwm_operator2,
|
||||
.deadtime = _isset(dead_zone) ? dead_zone : 0
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
// - BLDC driver - 6PWM setting
|
||||
// - hardware specific
|
||||
void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void* params){
|
||||
// set the PWM on the slot timers
|
||||
// transform duty cycle from [0,1] to [0,100.0]
|
||||
#if SIMPLEFOC_ESP32_HW_DEADTIME == true
|
||||
// Hardware deadtime does deadtime insertion internally
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_0, MCPWM_OPR_A, dc_a*100.0f);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_0, MCPWM_OPR_B, dc_a*100.0f);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_1, MCPWM_OPR_A, dc_b*100.0f);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_1, MCPWM_OPR_B, dc_b*100.0f);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_2, MCPWM_OPR_A, dc_c*100.0f);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_2, MCPWM_OPR_B, dc_c*100.0f);
|
||||
_UNUSED(phase_state);
|
||||
#else
|
||||
float deadtime = 0.5f*((ESP32MCPWMDriverParams*)params)->deadtime;
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_0, MCPWM_OPR_A, (phase_state[0] == PHASE_ON || phase_state[0] == PHASE_HI) ? _constrain(dc_a-deadtime, 0.0f, 1.0f) * 100.0f : 0);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_0, MCPWM_OPR_B, (phase_state[0] == PHASE_ON || phase_state[0] == PHASE_LO) ? _constrain(dc_a+deadtime, 0.0f, 1.0f) * 100.0f : 100.0f);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_1, MCPWM_OPR_A, (phase_state[1] == PHASE_ON || phase_state[1] == PHASE_HI) ? _constrain(dc_b-deadtime, 0.0f, 1.0f) * 100.0f : 0);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_1, MCPWM_OPR_B, (phase_state[1] == PHASE_ON || phase_state[1] == PHASE_LO) ? _constrain(dc_b+deadtime, 0.0f, 1.0f) * 100.0f : 100.0f);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_2, MCPWM_OPR_A, (phase_state[2] == PHASE_ON || phase_state[2] == PHASE_HI) ? _constrain(dc_c-deadtime, 0.0f, 1.0f) * 100.0f : 0);
|
||||
mcpwm_set_duty(((ESP32MCPWMDriverParams*)params)->mcpwm_unit, MCPWM_TIMER_2, MCPWM_OPR_B, (phase_state[2] == PHASE_ON || phase_state[2] == PHASE_LO) ? _constrain(dc_c+deadtime, 0.0f, 1.0f) * 100.0f : 100.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,121 @@
|
||||
#include "../hardware_api.h"
|
||||
|
||||
#if defined(ESP_H) && defined(ARDUINO_ARCH_ESP8266)
|
||||
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for ESP8266")
|
||||
#pragma message("")
|
||||
|
||||
|
||||
#define _PWM_FREQUENCY 25000 // 25khz
|
||||
#define _PWM_FREQUENCY_MAX 50000 // 50khz
|
||||
|
||||
// configure High PWM frequency
|
||||
void _setHighFrequency(const long freq, const int pin){
|
||||
analogWrite(pin, 0);
|
||||
analogWriteFreq(freq);
|
||||
}
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure1PWM(long pwm_frequency, const int pinA) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
_setHighFrequency(pwm_frequency, pinA);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure2PWM(long pwm_frequency, const int pinA, const int pinB) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
_setHighFrequency(pwm_frequency, pinA);
|
||||
_setHighFrequency(pwm_frequency, pinB);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
_setHighFrequency(pwm_frequency, pinA);
|
||||
_setHighFrequency(pwm_frequency, pinB);
|
||||
_setHighFrequency(pwm_frequency, pinC);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB, pinC },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure4PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC, const int pinD) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
_setHighFrequency(pwm_frequency, pinA);
|
||||
_setHighFrequency(pwm_frequency, pinB);
|
||||
_setHighFrequency(pwm_frequency, pinC);
|
||||
_setHighFrequency(pwm_frequency, pinD);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB, pinC, pinD },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle1PWM(float dc_a, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
}
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_b);
|
||||
}
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_b);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[2], 255.0f*dc_c);
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_1a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_1b);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[2], 255.0f*dc_2a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[3], 255.0f*dc_2b);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,125 @@
|
||||
|
||||
#include "../hardware_api.h"
|
||||
|
||||
// if the mcu doen't have defiend analogWrite
|
||||
#if defined(ESP_H) && defined(ARDUINO_ARCH_ESP32) && !defined(analogWrite)
|
||||
__attribute__((weak)) void analogWrite(uint8_t pin, int value){ };
|
||||
#endif
|
||||
|
||||
// function setting the high pwm frequency to the supplied pin
|
||||
// - Stepper motor - 1PWM setting
|
||||
// - hardware speciffic
|
||||
// in generic case dont do anything
|
||||
__attribute__((weak)) void* _configure1PWM(long pwm_frequency, const int pinA) {
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
// in generic case dont do anything
|
||||
__attribute__((weak)) void* _configure2PWM(long pwm_frequency,const int pinA, const int pinB) {
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
// in generic case dont do anything
|
||||
__attribute__((weak)) void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB, pinC },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
// in generic case dont do anything
|
||||
__attribute__((weak)) void* _configure4PWM(long pwm_frequency, const int pin1A, const int pin1B, const int pin2A, const int pin2B) {
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pin1A, pin1B, pin2A, pin2B },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// Configuring PWM frequency, resolution and alignment
|
||||
// - BLDC driver - 6PWM setting
|
||||
// - hardware specific
|
||||
__attribute__((weak)) void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l){
|
||||
_UNUSED(pwm_frequency);
|
||||
_UNUSED(dead_zone);
|
||||
_UNUSED(pinA_h);
|
||||
_UNUSED(pinA_l);
|
||||
_UNUSED(pinB_h);
|
||||
_UNUSED(pinB_l);
|
||||
_UNUSED(pinC_h);
|
||||
_UNUSED(pinC_l);
|
||||
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 1PWM setting
|
||||
// - hardware speciffic
|
||||
__attribute__((weak)) void _writeDutyCycle1PWM(float dc_a, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
}
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
__attribute__((weak)) void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_b);
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
__attribute__((weak)) void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_b);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[2], 255.0f*dc_c);
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
__attribute__((weak)) void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_1a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_1b);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[2], 255.0f*dc_2a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[3], 255.0f*dc_2b);
|
||||
}
|
||||
|
||||
|
||||
// Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
// - BLDC driver - 6PWM setting
|
||||
// - hardware specific
|
||||
__attribute__((weak)) void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void* params){
|
||||
_UNUSED(dc_a);
|
||||
_UNUSED(dc_b);
|
||||
_UNUSED(dc_c);
|
||||
_UNUSED(phase_state);
|
||||
_UNUSED(params);
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
|
||||
#include "../hardware_api.h"
|
||||
|
||||
#if defined(NRF52_SERIES)
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for NRF52")
|
||||
#pragma message("")
|
||||
|
||||
|
||||
#define PWM_CLK (16000000)
|
||||
#define PWM_FREQ (40000)
|
||||
#define PWM_RESOLUTION (PWM_CLK/PWM_FREQ)
|
||||
#define PWM_MAX_FREQ (62500)
|
||||
#define DEAD_ZONE (250) // in ns
|
||||
#define DEAD_TIME (DEAD_ZONE / (PWM_RESOLUTION * 0.25 * 62.5)) // 62.5ns resolution of PWM
|
||||
|
||||
#ifdef NRF_PWM3
|
||||
#define PWM_COUNT 4
|
||||
#else
|
||||
#define PWM_COUNT 3
|
||||
#endif
|
||||
|
||||
// empty motor slot
|
||||
#define _EMPTY_SLOT (-0xAA)
|
||||
#define _TAKEN_SLOT (-0x55)
|
||||
|
||||
int pwm_range;
|
||||
|
||||
|
||||
static NRF_PWM_Type* pwms[PWM_COUNT] = {
|
||||
NRF_PWM0,
|
||||
NRF_PWM1,
|
||||
NRF_PWM2,
|
||||
#ifdef NRF_PWM3
|
||||
NRF_PWM3
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int pinA;
|
||||
NRF_PWM_Type* mcpwm;
|
||||
uint16_t mcpwm_channel_sequence[4];
|
||||
} bldc_3pwm_motor_slots_t;
|
||||
|
||||
typedef struct {
|
||||
int pin1A;
|
||||
NRF_PWM_Type* mcpwm;
|
||||
uint16_t mcpwm_channel_sequence[4];
|
||||
} stepper_motor_slots_t;
|
||||
|
||||
typedef struct {
|
||||
int pinAH;
|
||||
NRF_PWM_Type* mcpwm1;
|
||||
NRF_PWM_Type* mcpwm2;
|
||||
uint16_t mcpwm_channel_sequence[8];
|
||||
} bldc_6pwm_motor_slots_t;
|
||||
|
||||
// define bldc motor slots array
|
||||
bldc_3pwm_motor_slots_t nrf52_bldc_3pwm_motor_slots[4] = {
|
||||
{_EMPTY_SLOT, pwms[0], {0,0,0,0}},// 1st motor will be PWM0
|
||||
{_EMPTY_SLOT, pwms[1], {0,0,0,0}},// 2nd motor will be PWM1
|
||||
{_EMPTY_SLOT, pwms[2], {0,0,0,0}},// 3rd motor will be PWM2
|
||||
{_EMPTY_SLOT, pwms[3], {0,0,0,0}} // 4th motor will be PWM3
|
||||
};
|
||||
|
||||
// define stepper motor slots array
|
||||
stepper_motor_slots_t nrf52_stepper_motor_slots[4] = {
|
||||
{_EMPTY_SLOT, pwms[0], {0,0,0,0}},// 1st motor will be on PWM0
|
||||
{_EMPTY_SLOT, pwms[1], {0,0,0,0}},// 1st motor will be on PWM1
|
||||
{_EMPTY_SLOT, pwms[2], {0,0,0,0}},// 1st motor will be on PWM2
|
||||
{_EMPTY_SLOT, pwms[3], {0,0,0,0}} // 1st motor will be on PWM3
|
||||
};
|
||||
|
||||
// define BLDC motor slots array
|
||||
bldc_6pwm_motor_slots_t nrf52_bldc_6pwm_motor_slots[2] = {
|
||||
{_EMPTY_SLOT, pwms[0], pwms[1], {0,0,0,0,0,0,0,0}},// 1st motor will be on PWM0 & PWM1
|
||||
{_EMPTY_SLOT, pwms[2], pwms[3], {0,0,0,0,0,0,0,0}} // 2nd motor will be on PWM1 & PWM2
|
||||
};
|
||||
|
||||
|
||||
|
||||
typedef struct NRF52DriverParams {
|
||||
union {
|
||||
bldc_3pwm_motor_slots_t* slot3pwm;
|
||||
bldc_6pwm_motor_slots_t* slot6pwm;
|
||||
stepper_motor_slots_t* slotstep;
|
||||
} slot;
|
||||
long pwm_frequency;
|
||||
float dead_time;
|
||||
} NRF52DriverParams;
|
||||
|
||||
|
||||
|
||||
|
||||
// configuring high frequency pwm timer
|
||||
void _configureHwPwm(NRF_PWM_Type* mcpwm1, NRF_PWM_Type* mcpwm2){
|
||||
|
||||
mcpwm1->ENABLE = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
|
||||
mcpwm1->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos);
|
||||
mcpwm1->MODE = (PWM_MODE_UPDOWN_UpAndDown << PWM_MODE_UPDOWN_Pos);
|
||||
mcpwm1->COUNTERTOP = pwm_range; //pwm freq.
|
||||
mcpwm1->LOOP = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos);
|
||||
mcpwm1->DECODER = ((uint32_t)PWM_DECODER_LOAD_Individual << PWM_DECODER_LOAD_Pos) | ((uint32_t)PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
|
||||
mcpwm1->SEQ[0].REFRESH = 0;
|
||||
mcpwm1->SEQ[0].ENDDELAY = 0;
|
||||
|
||||
if(mcpwm1 != mcpwm2){
|
||||
mcpwm2->ENABLE = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);
|
||||
mcpwm2->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos);
|
||||
mcpwm2->MODE = (PWM_MODE_UPDOWN_UpAndDown << PWM_MODE_UPDOWN_Pos);
|
||||
mcpwm2->COUNTERTOP = pwm_range; //pwm freq.
|
||||
mcpwm2->LOOP = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos);
|
||||
mcpwm2->DECODER = ((uint32_t)PWM_DECODER_LOAD_Individual << PWM_DECODER_LOAD_Pos) | ((uint32_t)PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
|
||||
mcpwm2->SEQ[0].REFRESH = 0;
|
||||
mcpwm2->SEQ[0].ENDDELAY = 0;
|
||||
}else{
|
||||
mcpwm1->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// can we support it using the generic driver on this MCU? Commented out to fall back to generic driver for 2-pwm
|
||||
// void* _configure2PWM(long pwm_frequency, const int pinA, const int pinB) {
|
||||
// return SIMPLEFOC_DRIVER_INIT_FAILED; // not supported
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
|
||||
|
||||
if( !pwm_frequency || pwm_frequency == NOT_SET) pwm_frequency = PWM_FREQ; // default frequency 20khz for a resolution of 800
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, PWM_MAX_FREQ); // constrain to 62.5kHz max for a resolution of 256
|
||||
|
||||
pwm_range = (PWM_CLK / pwm_frequency);
|
||||
|
||||
int pA = digitalPinToPinName(pinA); //g_ADigitalPinMap[pinA];
|
||||
int pB = digitalPinToPinName(pinB); //g_ADigitalPinMap[pinB];
|
||||
int pC = digitalPinToPinName(pinC); //g_ADigitalPinMap[pinC];
|
||||
|
||||
// determine which motor are we connecting
|
||||
// and set the appropriate configuration parameters
|
||||
int slot_num;
|
||||
for(slot_num = 0; slot_num < 4; slot_num++){
|
||||
if(nrf52_bldc_3pwm_motor_slots[slot_num].pinA == _EMPTY_SLOT){ // put the new motor in the first empty slot
|
||||
nrf52_bldc_3pwm_motor_slots[slot_num].pinA = pinA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if no slots available
|
||||
if(slot_num >= 4) return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
// disable all the slots with the same MCPWM
|
||||
if(slot_num < 2){
|
||||
// slot 0 of the stepper
|
||||
nrf52_stepper_motor_slots[slot_num].pin1A = _TAKEN_SLOT;
|
||||
// slot 0 of the 6pwm bldc
|
||||
nrf52_bldc_6pwm_motor_slots[0].pinAH = _TAKEN_SLOT;
|
||||
//NRF_PPI->CHEN &= ~1UL;
|
||||
}else{
|
||||
// slot 1 of the stepper
|
||||
nrf52_stepper_motor_slots[slot_num].pin1A = _TAKEN_SLOT;
|
||||
// slot 0 of the 6pwm bldc
|
||||
nrf52_bldc_6pwm_motor_slots[1].pinAH = _TAKEN_SLOT;
|
||||
//NRF_PPI->CHEN &= ~2UL;
|
||||
}
|
||||
|
||||
// configure pwm outputs
|
||||
|
||||
nrf52_bldc_3pwm_motor_slots[slot_num].mcpwm->PSEL.OUT[0] = pA;
|
||||
nrf52_bldc_3pwm_motor_slots[slot_num].mcpwm->PSEL.OUT[1] = pB;
|
||||
nrf52_bldc_3pwm_motor_slots[slot_num].mcpwm->PSEL.OUT[2] = pC;
|
||||
|
||||
nrf52_bldc_3pwm_motor_slots[slot_num].mcpwm->SEQ[0].PTR = (uint32_t)&nrf52_bldc_3pwm_motor_slots[slot_num].mcpwm_channel_sequence[0];
|
||||
nrf52_bldc_3pwm_motor_slots[slot_num].mcpwm->SEQ[0].CNT = 4;
|
||||
|
||||
// configure the pwm
|
||||
_configureHwPwm(nrf52_bldc_3pwm_motor_slots[slot_num].mcpwm, nrf52_bldc_3pwm_motor_slots[slot_num].mcpwm);
|
||||
|
||||
NRF52DriverParams* params = new NRF52DriverParams();
|
||||
params->slot.slot3pwm = &(nrf52_bldc_3pwm_motor_slots[slot_num]);
|
||||
params->pwm_frequency = pwm_frequency;
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure4PWM(long pwm_frequency, const int pinA, const int pinB, const int pinC, const int pinD) {
|
||||
|
||||
if( !pwm_frequency || pwm_frequency == NOT_SET) pwm_frequency = PWM_FREQ; // default frequency 20khz for a resolution of 800
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, PWM_MAX_FREQ); // constrain to 62.5kHz max for a resolution of 256
|
||||
|
||||
pwm_range = (PWM_CLK / pwm_frequency);
|
||||
|
||||
int pA = digitalPinToPinName(pinA); //g_ADigitalPinMap[pinA];
|
||||
int pB = digitalPinToPinName(pinB); //g_ADigitalPinMap[pinB];
|
||||
int pC = digitalPinToPinName(pinC); //g_ADigitalPinMap[pinC];
|
||||
int pD = digitalPinToPinName(pinD); //g_ADigitalPinMap[pinD];
|
||||
|
||||
// determine which motor are we connecting
|
||||
// and set the appropriate configuration parameters
|
||||
int slot_num;
|
||||
for(slot_num = 0; slot_num < 4; slot_num++){
|
||||
if(nrf52_stepper_motor_slots[slot_num].pin1A == _EMPTY_SLOT){ // put the new motor in the first empty slot
|
||||
nrf52_stepper_motor_slots[slot_num].pin1A = pinA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if no slots available
|
||||
if (slot_num >= 4) return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
// disable all the slots with the same MCPWM
|
||||
if (slot_num < 2){
|
||||
// slots 0 and 1 of the 3pwm bldc
|
||||
nrf52_bldc_3pwm_motor_slots[slot_num].pinA = _TAKEN_SLOT;
|
||||
// slot 0 of the 6pwm bldc
|
||||
nrf52_bldc_6pwm_motor_slots[0].pinAH = _TAKEN_SLOT;
|
||||
//NRF_PPI->CHEN &= ~1UL;
|
||||
}else{
|
||||
// slots 2 and 3 of the 3pwm bldc
|
||||
nrf52_bldc_3pwm_motor_slots[slot_num].pinA = _TAKEN_SLOT;
|
||||
// slot 1 of the 6pwm bldc
|
||||
nrf52_bldc_6pwm_motor_slots[1].pinAH = _TAKEN_SLOT;
|
||||
//NRF_PPI->CHEN &= ~2UL;
|
||||
}
|
||||
|
||||
// configure pwm outputs
|
||||
|
||||
nrf52_stepper_motor_slots[slot_num].mcpwm->PSEL.OUT[0] = pA;
|
||||
nrf52_stepper_motor_slots[slot_num].mcpwm->PSEL.OUT[1] = pB;
|
||||
nrf52_stepper_motor_slots[slot_num].mcpwm->PSEL.OUT[2] = pC;
|
||||
nrf52_stepper_motor_slots[slot_num].mcpwm->PSEL.OUT[3] = pD;
|
||||
|
||||
nrf52_stepper_motor_slots[slot_num].mcpwm->SEQ[0].PTR = (uint32_t)&nrf52_stepper_motor_slots[slot_num].mcpwm_channel_sequence[0];
|
||||
nrf52_stepper_motor_slots[slot_num].mcpwm->SEQ[0].CNT = 4;
|
||||
|
||||
// configure the pwm
|
||||
_configureHwPwm(nrf52_stepper_motor_slots[slot_num].mcpwm, nrf52_stepper_motor_slots[slot_num].mcpwm);
|
||||
|
||||
NRF52DriverParams* params = new NRF52DriverParams();
|
||||
params->slot.slotstep = &(nrf52_stepper_motor_slots[slot_num]);
|
||||
params->pwm_frequency = pwm_frequency;
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params){
|
||||
// transform duty cycle from [0,1] to [0,range]
|
||||
bldc_3pwm_motor_slots_t* p = ((NRF52DriverParams*)params)->slot.slot3pwm;
|
||||
p->mcpwm_channel_sequence[0] = (int)(dc_a * pwm_range) | 0x8000;
|
||||
p->mcpwm_channel_sequence[1] = (int)(dc_b * pwm_range) | 0x8000;
|
||||
p->mcpwm_channel_sequence[2] = (int)(dc_c * pwm_range) | 0x8000;
|
||||
|
||||
p->mcpwm->TASKS_SEQSTART[0] = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params){
|
||||
|
||||
stepper_motor_slots_t* p = ((NRF52DriverParams*)params)->slot.slotstep;
|
||||
p->mcpwm_channel_sequence[0] = (int)(dc_1a * pwm_range) | 0x8000;
|
||||
p->mcpwm_channel_sequence[1] = (int)(dc_1b * pwm_range) | 0x8000;
|
||||
p->mcpwm_channel_sequence[2] = (int)(dc_2a * pwm_range) | 0x8000;
|
||||
p->mcpwm_channel_sequence[3] = (int)(dc_2b * pwm_range) | 0x8000;
|
||||
|
||||
p->mcpwm->TASKS_SEQSTART[0] = 1;
|
||||
}
|
||||
|
||||
/* Configuring PWM frequency, resolution and alignment
|
||||
// - BLDC driver - 6PWM setting
|
||||
// - hardware specific
|
||||
*/
|
||||
void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l){
|
||||
|
||||
if( !pwm_frequency || pwm_frequency == NOT_SET) pwm_frequency = PWM_FREQ; // default frequency 20khz - centered pwm has twice lower frequency for a resolution of 400
|
||||
else pwm_frequency = _constrain(pwm_frequency*2, 0, PWM_MAX_FREQ); // constrain to 62.5kHz max => 31.25kHz for a resolution of 256
|
||||
|
||||
pwm_range = (PWM_CLK / pwm_frequency);
|
||||
pwm_range /= 2; // scale the frequency (centered PWM)
|
||||
|
||||
float dead_time;
|
||||
if (dead_zone != NOT_SET){
|
||||
dead_time = dead_zone/2;
|
||||
}else{
|
||||
dead_time = DEAD_TIME/2;
|
||||
}
|
||||
|
||||
int pA_l = digitalPinToPinName(pinA_l); //g_ADigitalPinMap[pinA_l];
|
||||
int pA_h = digitalPinToPinName(pinA_h); //g_ADigitalPinMap[pinA_h];
|
||||
int pB_l = digitalPinToPinName(pinB_l); //g_ADigitalPinMap[pinB_l];
|
||||
int pB_h = digitalPinToPinName(pinB_h); //g_ADigitalPinMap[pinB_h];
|
||||
int pC_l = digitalPinToPinName(pinC_l); //g_ADigitalPinMap[pinC_l];
|
||||
int pC_h = digitalPinToPinName(pinC_h); //g_ADigitalPinMap[pinC_h];
|
||||
|
||||
|
||||
// determine which motor are we connecting
|
||||
// and set the appropriate configuration parameters
|
||||
int slot_num;
|
||||
for(slot_num = 0; slot_num < 2; slot_num++){
|
||||
if(nrf52_bldc_6pwm_motor_slots[slot_num].pinAH == _EMPTY_SLOT){ // put the new motor in the first empty slot
|
||||
nrf52_bldc_6pwm_motor_slots[slot_num].pinAH = pinA_h;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if no slots available
|
||||
if(slot_num >= 2) return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
// disable all the slots with the same MCPWM
|
||||
if( slot_num == 0 ){
|
||||
// slots 0 and 1 of the 3pwm bldc
|
||||
nrf52_bldc_3pwm_motor_slots[0].pinA = _TAKEN_SLOT;
|
||||
nrf52_bldc_3pwm_motor_slots[1].pinA = _TAKEN_SLOT;
|
||||
// slot 0 and 1 of the stepper
|
||||
nrf52_stepper_motor_slots[0].pin1A = _TAKEN_SLOT;
|
||||
nrf52_stepper_motor_slots[1].pin1A = _TAKEN_SLOT;
|
||||
}else{
|
||||
// slots 2 and 3 of the 3pwm bldc
|
||||
nrf52_bldc_3pwm_motor_slots[2].pinA = _TAKEN_SLOT;
|
||||
nrf52_bldc_3pwm_motor_slots[3].pinA = _TAKEN_SLOT;
|
||||
// slot 1 of the stepper
|
||||
nrf52_stepper_motor_slots[2].pin1A = _TAKEN_SLOT;
|
||||
nrf52_stepper_motor_slots[3].pin1A = _TAKEN_SLOT;
|
||||
}
|
||||
|
||||
// Configure pwm outputs
|
||||
|
||||
nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm1->PSEL.OUT[0] = pA_h;
|
||||
nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm1->PSEL.OUT[1] = pA_l;
|
||||
nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm1->PSEL.OUT[2] = pB_h;
|
||||
nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm1->PSEL.OUT[3] = pB_l;
|
||||
nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm1->SEQ[0].PTR = (uint32_t)&nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm_channel_sequence[0];
|
||||
nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm1->SEQ[0].CNT = 4;
|
||||
|
||||
nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm2->PSEL.OUT[0] = pC_h;
|
||||
nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm2->PSEL.OUT[1] = pC_l;
|
||||
nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm2->SEQ[0].PTR = (uint32_t)&nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm_channel_sequence[4];
|
||||
nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm2->SEQ[0].CNT = 4;
|
||||
|
||||
// Initializing the PPI peripheral for sync the pwm slots
|
||||
|
||||
NRF_PPI->CH[slot_num].EEP = (uint32_t)&NRF_EGU0->EVENTS_TRIGGERED[0];
|
||||
NRF_PPI->CH[slot_num].TEP = (uint32_t)&nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm1->TASKS_SEQSTART[0];
|
||||
NRF_PPI->FORK[slot_num].TEP = (uint32_t)&nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm2->TASKS_SEQSTART[0];
|
||||
NRF_PPI->CHEN = 1UL << slot_num;
|
||||
|
||||
// configure the pwm type
|
||||
_configureHwPwm(nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm1, nrf52_bldc_6pwm_motor_slots[slot_num].mcpwm2);
|
||||
|
||||
NRF52DriverParams* params = new NRF52DriverParams();
|
||||
params->slot.slot6pwm = &(nrf52_bldc_6pwm_motor_slots[slot_num]);
|
||||
params->pwm_frequency = pwm_frequency;
|
||||
params->dead_time = dead_time;
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Function setting the duty cycle to the pwm pin
|
||||
// - BLDC driver - 6PWM setting
|
||||
// - hardware specific
|
||||
*/
|
||||
void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void* params){
|
||||
bldc_6pwm_motor_slots_t* p = ((NRF52DriverParams*)params)->slot.slot6pwm;
|
||||
float dead_time = ((NRF52DriverParams*)params)->dead_time;
|
||||
p->mcpwm_channel_sequence[0] = (int)(_constrain(dc_a-dead_time,0,1)*pwm_range) | 0x8000;
|
||||
p->mcpwm_channel_sequence[1] = (int)(_constrain(dc_a+dead_time,0,1)*pwm_range);
|
||||
p->mcpwm_channel_sequence[2] = (int)(_constrain(dc_b-dead_time,0,1)*pwm_range) | 0x8000;
|
||||
p->mcpwm_channel_sequence[3] = (int)(_constrain(dc_b+dead_time,0,1)*pwm_range);
|
||||
p->mcpwm_channel_sequence[4] = (int)(_constrain(dc_c-dead_time,0,1)*pwm_range) | 0x8000;
|
||||
p->mcpwm_channel_sequence[5] = (int)(_constrain(dc_c+dead_time,0,1)*pwm_range);
|
||||
NRF_EGU0->TASKS_TRIGGER[0] = 1;
|
||||
|
||||
_UNUSED(phase_state);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,545 @@
|
||||
|
||||
#include "../hardware_api.h"
|
||||
|
||||
#if defined(TARGET_PORTENTA_H7)
|
||||
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for Arduino/Portenta_H7")
|
||||
#pragma message("")
|
||||
|
||||
|
||||
#include "pwmout_api.h"
|
||||
#include "pinDefinitions.h"
|
||||
#include "platform/mbed_critical.h"
|
||||
#include "platform/mbed_power_mgmt.h"
|
||||
#include "platform/mbed_assert.h"
|
||||
#include "PeripheralPins.h"
|
||||
#include "pwmout_device.h"
|
||||
|
||||
// default pwm parameters
|
||||
#define _PWM_FREQUENCY 25000 // 25khz
|
||||
#define _PWM_FREQUENCY_MAX 50000 // 50khz
|
||||
|
||||
|
||||
// // 6pwm parameters
|
||||
// #define _HARDWARE_6PWM 1
|
||||
// #define _SOFTWARE_6PWM 0
|
||||
// #define _ERROR_6PWM -1
|
||||
|
||||
|
||||
|
||||
typedef struct PortentaDriverParams {
|
||||
pwmout_t pins[4];
|
||||
long pwm_frequency;
|
||||
// float dead_zone;
|
||||
} PortentaDriverParams;
|
||||
|
||||
|
||||
|
||||
/* Convert STM32 Cube HAL channel to LL channel */
|
||||
uint32_t _TIM_ChannelConvert_HAL2LL(uint32_t channel, pwmout_t *obj)
|
||||
{
|
||||
#if !defined(PWMOUT_INVERTED_NOT_SUPPORTED)
|
||||
if (obj->inverted) {
|
||||
switch (channel) {
|
||||
case TIM_CHANNEL_1 :
|
||||
return LL_TIM_CHANNEL_CH1N;
|
||||
case TIM_CHANNEL_2 :
|
||||
return LL_TIM_CHANNEL_CH2N;
|
||||
case TIM_CHANNEL_3 :
|
||||
return LL_TIM_CHANNEL_CH3N;
|
||||
#if defined(LL_TIM_CHANNEL_CH4N)
|
||||
case TIM_CHANNEL_4 :
|
||||
return LL_TIM_CHANNEL_CH4N;
|
||||
#endif
|
||||
default : /* Optional */
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
switch (channel) {
|
||||
case TIM_CHANNEL_1 :
|
||||
return LL_TIM_CHANNEL_CH1;
|
||||
case TIM_CHANNEL_2 :
|
||||
return LL_TIM_CHANNEL_CH2;
|
||||
case TIM_CHANNEL_3 :
|
||||
return LL_TIM_CHANNEL_CH3;
|
||||
case TIM_CHANNEL_4 :
|
||||
return LL_TIM_CHANNEL_CH4;
|
||||
default : /* Optional */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// int _pwm_init(pwmout_t *obj, uint32_t pin, long frequency){
|
||||
// return _pwm_init(obj, digitalPinToPinName(pin), frequency);
|
||||
// }
|
||||
|
||||
int _pwm_init(pwmout_t *obj, uint32_t pin, long frequency){
|
||||
int peripheral = (int)pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM);
|
||||
int function = (int)pinmap_find_function(digitalPinToPinName(pin), PinMap_PWM);
|
||||
|
||||
const PinMap static_pinmap = {digitalPinToPinName(pin), peripheral, function};
|
||||
|
||||
pwmout_init_direct(obj, &static_pinmap);
|
||||
|
||||
TIM_HandleTypeDef TimHandle;
|
||||
TimHandle.Instance = (TIM_TypeDef *)(obj->pwm);
|
||||
RCC_ClkInitTypeDef RCC_ClkInitStruct;
|
||||
uint32_t PclkFreq = 0;
|
||||
uint32_t APBxCLKDivider = RCC_HCLK_DIV1;
|
||||
uint8_t i = 0;
|
||||
|
||||
|
||||
__HAL_TIM_DISABLE(&TimHandle);
|
||||
|
||||
// Get clock configuration
|
||||
// Note: PclkFreq contains here the Latency (not used after)
|
||||
HAL_RCC_GetClockConfig(&RCC_ClkInitStruct, &PclkFreq);
|
||||
|
||||
/* Parse the pwm / apb mapping table to find the right entry */
|
||||
while (pwm_apb_map_table[i].pwm != obj->pwm) i++;
|
||||
// sanity check
|
||||
if (pwm_apb_map_table[i].pwm == 0) return -1;
|
||||
|
||||
|
||||
if (pwm_apb_map_table[i].pwmoutApb == PWMOUT_ON_APB1) {
|
||||
PclkFreq = HAL_RCC_GetPCLK1Freq();
|
||||
APBxCLKDivider = RCC_ClkInitStruct.APB1CLKDivider;
|
||||
} else {
|
||||
#if !defined(PWMOUT_APB2_NOT_SUPPORTED)
|
||||
PclkFreq = HAL_RCC_GetPCLK2Freq();
|
||||
APBxCLKDivider = RCC_ClkInitStruct.APB2CLKDivider;
|
||||
#endif
|
||||
}
|
||||
|
||||
long period_us = 500000.0/((float)frequency);
|
||||
/* By default use, 1us as SW pre-scaler */
|
||||
obj->prescaler = 1;
|
||||
// TIMxCLK = PCLKx when the APB prescaler = 1 else TIMxCLK = 2 * PCLKx
|
||||
if (APBxCLKDivider == RCC_HCLK_DIV1) {
|
||||
TimHandle.Init.Prescaler = (((PclkFreq) / 1000000)) - 1; // 1 us tick
|
||||
} else {
|
||||
TimHandle.Init.Prescaler = (((PclkFreq * 2) / 1000000)) - 1; // 1 us tick
|
||||
}
|
||||
TimHandle.Init.Period = (period_us - 1);
|
||||
|
||||
/* In case period or pre-scalers are out of range, loop-in to get valid values */
|
||||
while ((TimHandle.Init.Period > 0xFFFF) || (TimHandle.Init.Prescaler > 0xFFFF)) {
|
||||
obj->prescaler = obj->prescaler * 2;
|
||||
if (APBxCLKDivider == RCC_HCLK_DIV1) {
|
||||
TimHandle.Init.Prescaler = (((PclkFreq) / 1000000) * obj->prescaler) - 1;
|
||||
} else {
|
||||
TimHandle.Init.Prescaler = (((PclkFreq * 2) / 1000000) * obj->prescaler) - 1;
|
||||
}
|
||||
TimHandle.Init.Period = (period_us - 1) / obj->prescaler;
|
||||
/* Period decreases and prescaler increases over loops, so check for
|
||||
* possible out of range cases */
|
||||
if ((TimHandle.Init.Period < 0xFFFF) && (TimHandle.Init.Prescaler > 0xFFFF)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TimHandle.Init.ClockDivision = 0;
|
||||
TimHandle.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED3; // center aligned
|
||||
|
||||
if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
TIM_OC_InitTypeDef sConfig;
|
||||
// Configure channels
|
||||
sConfig.OCMode = TIM_OCMODE_PWM1;
|
||||
sConfig.Pulse = obj->pulse / obj->prescaler;
|
||||
sConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
|
||||
sConfig.OCFastMode = TIM_OCFAST_DISABLE;
|
||||
#if defined(TIM_OCIDLESTATE_RESET)
|
||||
sConfig.OCIdleState = TIM_OCIDLESTATE_RESET;
|
||||
#endif
|
||||
#if defined(TIM_OCNIDLESTATE_RESET)
|
||||
sConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH;
|
||||
sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
|
||||
#endif
|
||||
|
||||
int channel = 0;
|
||||
switch (obj->channel) {
|
||||
case 1:
|
||||
channel = TIM_CHANNEL_1;
|
||||
break;
|
||||
case 2:
|
||||
channel = TIM_CHANNEL_2;
|
||||
break;
|
||||
case 3:
|
||||
channel = TIM_CHANNEL_3;
|
||||
break;
|
||||
case 4:
|
||||
channel = TIM_CHANNEL_4;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (LL_TIM_CC_IsEnabledChannel(TimHandle.Instance, _TIM_ChannelConvert_HAL2LL(channel, obj)) == 0) {
|
||||
// If channel is not enabled, proceed to channel configuration
|
||||
if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, channel) != HAL_OK) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Save for future use
|
||||
obj->period = period_us;
|
||||
#if !defined(PWMOUT_INVERTED_NOT_SUPPORTED)
|
||||
if (obj->inverted) {
|
||||
HAL_TIMEx_PWMN_Start(&TimHandle, channel);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
HAL_TIM_PWM_Start(&TimHandle, channel);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// setting pwm to hardware pin - instead analogWrite()
|
||||
void _pwm_write(pwmout_t *obj, float value){
|
||||
TIM_HandleTypeDef TimHandle;
|
||||
int channel = 0;
|
||||
|
||||
TimHandle.Instance = (TIM_TypeDef *)(obj->pwm);
|
||||
|
||||
if (value < (float)0.0) {
|
||||
value = 0.0;
|
||||
} else if (value > (float)1.0) {
|
||||
value = 1.0;
|
||||
}
|
||||
|
||||
obj->pulse = (uint32_t)((float)obj->period * value + 0.5);
|
||||
|
||||
switch (obj->channel) {
|
||||
case 1:
|
||||
channel = TIM_CHANNEL_1;
|
||||
break;
|
||||
case 2:
|
||||
channel = TIM_CHANNEL_2;
|
||||
break;
|
||||
case 3:
|
||||
channel = TIM_CHANNEL_3;
|
||||
break;
|
||||
case 4:
|
||||
channel = TIM_CHANNEL_4;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// If channel already enabled, only update compare value to avoid glitch
|
||||
__HAL_TIM_SET_COMPARE(&TimHandle, channel, obj->pulse / obj->prescaler);
|
||||
}
|
||||
|
||||
// init low side pin
|
||||
// HardwareTimer* _initPinPWMLow(uint32_t PWM_freq, int ulPin)
|
||||
// {
|
||||
// PinName pin = digitalPinToPinName(ulPin);
|
||||
// TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(pin, PinMap_PWM);
|
||||
// uint32_t index = get_timer_index(Instance);
|
||||
|
||||
// if (HardwareTimer_Handle[index] == NULL) {
|
||||
// HardwareTimer_Handle[index]->__this = new HardwareTimer((TIM_TypeDef *)pinmap_peripheral(pin, PinMap_PWM));
|
||||
// HardwareTimer_Handle[index]->handle.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED3;
|
||||
// HAL_TIM_Base_Init(&(HardwareTimer_Handle[index]->handle));
|
||||
// TIM_OC_InitTypeDef sConfigOC = TIM_OC_InitTypeDef();
|
||||
// sConfigOC.OCMode = TIM_OCMODE_PWM2;
|
||||
// sConfigOC.Pulse = 100;
|
||||
// sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
|
||||
// sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
|
||||
// sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
|
||||
// sConfigOC.OCIdleState = TIM_OCIDLESTATE_SET;
|
||||
// sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
|
||||
// uint32_t channel = STM_PIN_CHANNEL(pinmap_function(pin, PinMap_PWM));
|
||||
// HAL_TIM_PWM_ConfigChannel(&(HardwareTimer_Handle[index]->handle), &sConfigOC, channel);
|
||||
// }
|
||||
// HardwareTimer *HT = (HardwareTimer *)(HardwareTimer_Handle[index]->__this);
|
||||
// uint32_t channel = STM_PIN_CHANNEL(pinmap_function(pin, PinMap_PWM));
|
||||
// HT->setMode(channel, TIMER_OUTPUT_COMPARE_PWM2, pin);
|
||||
// HT->setOverflow(PWM_freq, HERTZ_FORMAT);
|
||||
// HT->pause();
|
||||
// HT->refresh();
|
||||
// return HT;
|
||||
// }
|
||||
|
||||
|
||||
// align the timers to end the init
|
||||
void _alignPWMTimers(pwmout_t *t1, pwmout_t *t2){
|
||||
TIM_HandleTypeDef TimHandle1, TimHandle2;
|
||||
TimHandle1.Instance = (TIM_TypeDef *)(t1->pwm);
|
||||
TimHandle2.Instance = (TIM_TypeDef *)(t2->pwm);
|
||||
__HAL_TIM_DISABLE(&TimHandle1);
|
||||
__HAL_TIM_DISABLE(&TimHandle2);
|
||||
__HAL_TIM_ENABLE(&TimHandle1);
|
||||
__HAL_TIM_ENABLE(&TimHandle2);
|
||||
}
|
||||
|
||||
// align the timers to end the init
|
||||
void _alignPWMTimers(pwmout_t *t1, pwmout_t *t2, pwmout_t *t3){
|
||||
TIM_HandleTypeDef TimHandle1, TimHandle2, TimHandle3;
|
||||
TimHandle1.Instance = (TIM_TypeDef *)(t1->pwm);
|
||||
TimHandle2.Instance = (TIM_TypeDef *)(t2->pwm);
|
||||
TimHandle3.Instance = (TIM_TypeDef *)(t3->pwm);
|
||||
__HAL_TIM_DISABLE(&TimHandle1);
|
||||
__HAL_TIM_DISABLE(&TimHandle2);
|
||||
__HAL_TIM_DISABLE(&TimHandle3);
|
||||
__HAL_TIM_ENABLE(&TimHandle1);
|
||||
__HAL_TIM_ENABLE(&TimHandle2);
|
||||
__HAL_TIM_ENABLE(&TimHandle3);
|
||||
}
|
||||
|
||||
// align the timers to end the init
|
||||
void _alignPWMTimers(pwmout_t *t1, pwmout_t *t2, pwmout_t *t3, pwmout_t *t4){
|
||||
TIM_HandleTypeDef TimHandle1, TimHandle2, TimHandle3, TimHandle4;
|
||||
TimHandle1.Instance = (TIM_TypeDef *)(t1->pwm);
|
||||
TimHandle2.Instance = (TIM_TypeDef *)(t2->pwm);
|
||||
TimHandle3.Instance = (TIM_TypeDef *)(t3->pwm);
|
||||
TimHandle4.Instance = (TIM_TypeDef *)(t4->pwm);
|
||||
__HAL_TIM_DISABLE(&TimHandle1);
|
||||
__HAL_TIM_DISABLE(&TimHandle2);
|
||||
__HAL_TIM_DISABLE(&TimHandle3);
|
||||
__HAL_TIM_DISABLE(&TimHandle4);
|
||||
__HAL_TIM_ENABLE(&TimHandle1);
|
||||
__HAL_TIM_ENABLE(&TimHandle2);
|
||||
__HAL_TIM_ENABLE(&TimHandle3);
|
||||
__HAL_TIM_ENABLE(&TimHandle4);
|
||||
}
|
||||
|
||||
// // configure hardware 6pwm interface only one timer with inverted channels
|
||||
// HardwareTimer* _initHardware6PWMInterface(uint32_t PWM_freq, float dead_zone, int pinA_h, int pinA_l, int pinB_h, int pinB_l, int pinC_h, int pinC_l)
|
||||
// {
|
||||
// PinName uhPinName = digitalPinToPinName(pinA_h);
|
||||
// PinName ulPinName = digitalPinToPinName(pinA_l);
|
||||
// PinName vhPinName = digitalPinToPinName(pinB_h);
|
||||
// PinName vlPinName = digitalPinToPinName(pinB_l);
|
||||
// PinName whPinName = digitalPinToPinName(pinC_h);
|
||||
// PinName wlPinName = digitalPinToPinName(pinC_l);
|
||||
|
||||
// TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(uhPinName, PinMap_PWM);
|
||||
|
||||
// uint32_t index = get_timer_index(Instance);
|
||||
|
||||
// if (HardwareTimer_Handle[index] == NULL) {
|
||||
// HardwareTimer_Handle[index]->__this = new HardwareTimer((TIM_TypeDef *)pinmap_peripheral(uhPinName, PinMap_PWM));
|
||||
// HardwareTimer_Handle[index]->handle.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED3;
|
||||
// HAL_TIM_Base_Init(&(HardwareTimer_Handle[index]->handle));
|
||||
// ((HardwareTimer *)HardwareTimer_Handle[index]->__this)->setOverflow(PWM_freq, HERTZ_FORMAT);
|
||||
// }
|
||||
// HardwareTimer *HT = (HardwareTimer *)(HardwareTimer_Handle[index]->__this);
|
||||
|
||||
// HT->setMode(STM_PIN_CHANNEL(pinmap_function(uhPinName, PinMap_PWM)), TIMER_OUTPUT_COMPARE_PWM1, uhPinName);
|
||||
// HT->setMode(STM_PIN_CHANNEL(pinmap_function(ulPinName, PinMap_PWM)), TIMER_OUTPUT_COMPARE_PWM1, ulPinName);
|
||||
// HT->setMode(STM_PIN_CHANNEL(pinmap_function(vhPinName, PinMap_PWM)), TIMER_OUTPUT_COMPARE_PWM1, vhPinName);
|
||||
// HT->setMode(STM_PIN_CHANNEL(pinmap_function(vlPinName, PinMap_PWM)), TIMER_OUTPUT_COMPARE_PWM1, vlPinName);
|
||||
// HT->setMode(STM_PIN_CHANNEL(pinmap_function(whPinName, PinMap_PWM)), TIMER_OUTPUT_COMPARE_PWM1, whPinName);
|
||||
// HT->setMode(STM_PIN_CHANNEL(pinmap_function(wlPinName, PinMap_PWM)), TIMER_OUTPUT_COMPARE_PWM1, wlPinName);
|
||||
|
||||
// // dead time is set in nanoseconds
|
||||
// uint32_t dead_time_ns = (float)(1e9f/PWM_freq)*dead_zone;
|
||||
// uint32_t dead_time = __LL_TIM_CALC_DEADTIME(SystemCoreClock, LL_TIM_GetClockDivision(HT->getHandle()->Instance), dead_time_ns);
|
||||
// LL_TIM_OC_SetDeadTime(HT->getHandle()->Instance, dead_time); // deadtime is non linear!
|
||||
// LL_TIM_CC_EnableChannel(HT->getHandle()->Instance, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH1N | LL_TIM_CHANNEL_CH2 | LL_TIM_CHANNEL_CH2N | LL_TIM_CHANNEL_CH3 | LL_TIM_CHANNEL_CH3N);
|
||||
|
||||
// HT->pause();
|
||||
// HT->refresh();
|
||||
// HT->resume();
|
||||
// return HT;
|
||||
// }
|
||||
|
||||
|
||||
// // returns 0 if each pair of pwm channels has same channel
|
||||
// // returns 1 all the channels belong to the same timer - hardware inverted channels
|
||||
// // returns -1 if neither - avoid configuring - error!!!
|
||||
// int _interfaceType(const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l){
|
||||
// PinName nameAH = digitalPinToPinName(pinA_h);
|
||||
// PinName nameAL = digitalPinToPinName(pinA_l);
|
||||
// PinName nameBH = digitalPinToPinName(pinB_h);
|
||||
// PinName nameBL = digitalPinToPinName(pinB_l);
|
||||
// PinName nameCH = digitalPinToPinName(pinC_h);
|
||||
// PinName nameCL = digitalPinToPinName(pinC_l);
|
||||
// int tim1 = get_timer_index((TIM_TypeDef *)pinmap_peripheral(nameAH, PinMap_PWM));
|
||||
// int tim2 = get_timer_index((TIM_TypeDef *)pinmap_peripheral(nameAL, PinMap_PWM));
|
||||
// int tim3 = get_timer_index((TIM_TypeDef *)pinmap_peripheral(nameBH, PinMap_PWM));
|
||||
// int tim4 = get_timer_index((TIM_TypeDef *)pinmap_peripheral(nameBL, PinMap_PWM));
|
||||
// int tim5 = get_timer_index((TIM_TypeDef *)pinmap_peripheral(nameCH, PinMap_PWM));
|
||||
// int tim6 = get_timer_index((TIM_TypeDef *)pinmap_peripheral(nameCL, PinMap_PWM));
|
||||
// if(tim1 == tim2 && tim2==tim3 && tim3==tim4 && tim4==tim5 && tim5==tim6)
|
||||
// return _HARDWARE_6PWM; // hardware 6pwm interface - only on timer
|
||||
// else if(tim1 == tim2 && tim3==tim4 && tim5==tim6)
|
||||
// return _SOFTWARE_6PWM; // software 6pwm interface - each pair of high-low side same timer
|
||||
// else
|
||||
// return _ERROR_6PWM; // might be error avoid configuration
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure2PWM(long pwm_frequency,const int pinA, const int pinB) {
|
||||
if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
|
||||
PortentaDriverParams* params = new PortentaDriverParams();
|
||||
params->pwm_frequency = pwm_frequency;
|
||||
|
||||
core_util_critical_section_enter();
|
||||
_pwm_init(&(params->pins[0]), pinA, (long)pwm_frequency);
|
||||
_pwm_init(&(params->pins[1]), pinB, (long)pwm_frequency);
|
||||
// allign the timers
|
||||
_alignPWMTimers(&(params->pins[0]), &(params->pins[1]));
|
||||
core_util_critical_section_exit();
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
|
||||
if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
|
||||
PortentaDriverParams* params = new PortentaDriverParams();
|
||||
params->pwm_frequency = pwm_frequency;
|
||||
|
||||
core_util_critical_section_enter();
|
||||
_pwm_init(&(params->pins[0]), pinA, (long)pwm_frequency);
|
||||
_pwm_init(&(params->pins[1]), pinB, (long)pwm_frequency);
|
||||
_pwm_init(&(params->pins[2]), pinC, (long)pwm_frequency);
|
||||
// allign the timers
|
||||
_alignPWMTimers(&(params->pins[0]), &(params->pins[1]), &(params->pins[2]));
|
||||
core_util_critical_section_exit();
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure4PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC, const int pinD) {
|
||||
if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
|
||||
PortentaDriverParams* params = new PortentaDriverParams();
|
||||
params->pwm_frequency = pwm_frequency;
|
||||
|
||||
core_util_critical_section_enter();
|
||||
_pwm_init(&(params->pins[0]), pinA, (long)pwm_frequency);
|
||||
_pwm_init(&(params->pins[1]), pinB, (long)pwm_frequency);
|
||||
_pwm_init(&(params->pins[2]), pinC, (long)pwm_frequency);
|
||||
_pwm_init(&(params->pins[3]), pinD, (long)pwm_frequency);
|
||||
// allign the timers
|
||||
_alignPWMTimers(&(params->pins[0]), &(params->pins[1]), &(params->pins[2]), &(params->pins[3]));
|
||||
core_util_critical_section_exit();
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 2PWM setting
|
||||
//- hardware speciffic
|
||||
void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params){
|
||||
core_util_critical_section_enter();
|
||||
_pwm_write(&(((PortentaDriverParams*)params)->pins[0]), (float)dc_a);
|
||||
_pwm_write(&(((PortentaDriverParams*)params)->pins[1]), (float)dc_b);
|
||||
core_util_critical_section_exit();
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - BLDC motor - 3PWM setting
|
||||
//- hardware speciffic
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params){
|
||||
core_util_critical_section_enter();
|
||||
_pwm_write(&(((PortentaDriverParams*)params)->pins[0]), (float)dc_a);
|
||||
_pwm_write(&(((PortentaDriverParams*)params)->pins[1]), (float)dc_b);
|
||||
_pwm_write(&(((PortentaDriverParams*)params)->pins[2]), (float)dc_c);
|
||||
core_util_critical_section_exit();
|
||||
}
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 4PWM setting
|
||||
//- hardware speciffic
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params){
|
||||
core_util_critical_section_enter();
|
||||
_pwm_write(&(((PortentaDriverParams*)params)->pins[0]), (float)dc_1a);
|
||||
_pwm_write(&(((PortentaDriverParams*)params)->pins[1]), (float)dc_1b);
|
||||
_pwm_write(&(((PortentaDriverParams*)params)->pins[2]), (float)dc_2a);
|
||||
_pwm_write(&(((PortentaDriverParams*)params)->pins[3]), (float)dc_2b);
|
||||
core_util_critical_section_exit();
|
||||
}
|
||||
|
||||
|
||||
// 6-PWM currently not supported, defer to generic, which also doesn't support it ;-)
|
||||
|
||||
// Configuring PWM frequency, resolution and alignment
|
||||
// - BLDC driver - 6PWM setting
|
||||
// - hardware specific
|
||||
//void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l){
|
||||
// if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
// else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to |%0kHz max
|
||||
// // center-aligned frequency is uses two periods
|
||||
// pwm_frequency *=2;
|
||||
|
||||
// // find configuration
|
||||
// int config = _interfaceType(pinA_h, pinA_l, pinB_h, pinB_l, pinC_h, pinC_l);
|
||||
// // configure accordingly
|
||||
// switch(config){
|
||||
// case _ERROR_6PWM:
|
||||
// return -1; // fail
|
||||
// break;
|
||||
// case _HARDWARE_6PWM:
|
||||
// _initHardware6PWMInterface(pwm_frequency, dead_zone, pinA_h, pinA_l, pinB_h, pinB_l, pinC_h, pinC_l);
|
||||
// break;
|
||||
// case _SOFTWARE_6PWM:
|
||||
// HardwareTimer* HT1 = _initPinPWMHigh(pwm_frequency, pinA_h);
|
||||
// _initPinPWMLow(pwm_frequency, pinA_l);
|
||||
// HardwareTimer* HT2 = _initPinPWMHigh(pwm_frequency,pinB_h);
|
||||
// _initPinPWMLow(pwm_frequency, pinB_l);
|
||||
// HardwareTimer* HT3 = _initPinPWMHigh(pwm_frequency,pinC_h);
|
||||
// _initPinPWMLow(pwm_frequency, pinC_l);
|
||||
// _alignPWMTimers(HT1, HT2, HT3);
|
||||
// break;
|
||||
// }
|
||||
// return -1; // success
|
||||
// }
|
||||
|
||||
// Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
// - BLDC driver - 6PWM setting
|
||||
// - hardware specific
|
||||
//void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, void* params){
|
||||
// // find configuration
|
||||
// int config = _interfaceType(pinA_h, pinA_l, pinB_h, pinB_l, pinC_h, pinC_l);
|
||||
// // set pwm accordingly
|
||||
// switch(config){
|
||||
// case _HARDWARE_6PWM:
|
||||
// _setPwm(pinA_h, _PWM_RANGE*dc_a, _PWM_RESOLUTION);
|
||||
// _setPwm(pinB_h, _PWM_RANGE*dc_b, _PWM_RESOLUTION);
|
||||
// _setPwm(pinC_h, _PWM_RANGE*dc_c, _PWM_RESOLUTION);
|
||||
// break;
|
||||
// case _SOFTWARE_6PWM:
|
||||
// _setPwm(pinA_l, _constrain(dc_a + dead_zone/2, 0, 1)*_PWM_RANGE, _PWM_RESOLUTION);
|
||||
// _setPwm(pinA_h, _constrain(dc_a - dead_zone/2, 0, 1)*_PWM_RANGE, _PWM_RESOLUTION);
|
||||
// _setPwm(pinB_l, _constrain(dc_b + dead_zone/2, 0, 1)*_PWM_RANGE, _PWM_RESOLUTION);
|
||||
// _setPwm(pinB_h, _constrain(dc_b - dead_zone/2, 0, 1)*_PWM_RANGE, _PWM_RESOLUTION);
|
||||
// _setPwm(pinC_l, _constrain(dc_c + dead_zone/2, 0, 1)*_PWM_RANGE, _PWM_RESOLUTION);
|
||||
// _setPwm(pinC_h, _constrain(dc_c - dead_zone/2, 0, 1)*_PWM_RANGE, _PWM_RESOLUTION);
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
#endif
|
||||
@@ -0,0 +1,609 @@
|
||||
|
||||
#include "./renesas.h"
|
||||
|
||||
#if defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)
|
||||
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for Arduino/Renesas (UNO R4)")
|
||||
#pragma message("")
|
||||
|
||||
|
||||
|
||||
#include "communication/SimpleFOCDebug.h"
|
||||
#include "FspTimer.h"
|
||||
|
||||
#define GPT_OPEN (0x00475054ULL)
|
||||
|
||||
/*
|
||||
We use the GPT timers, there are 2 channels (32 bit) + 6 channels (16 bit)
|
||||
Each channel has 2 outputs (GTIOCAx and GTIOCBx) which can be complimentary.
|
||||
|
||||
So each timer channel can handle one half-bridge, using either a single (3-PWM) or
|
||||
two complimentary PWM signals (6-PWM).
|
||||
|
||||
For 1-PWM through 4-PWM, we need as many channels as PWM signals, and we can use
|
||||
either output A or B of the timer (we can set the polarity) - but not both.
|
||||
|
||||
For 6-PWM we need 3 channels, and use both outputs A and B of each channel, then
|
||||
we can do hardware dead-time.
|
||||
Or we can use seperate channels for high and low side, with software dead-time.
|
||||
Each phase can be either hardware (1 channel) or software (2 channels) dead-time
|
||||
individually, they don't all have to be one or the other.
|
||||
|
||||
Selected channels can be started together, so we can keep the phases in sync for
|
||||
low-side current sensing and software 6-PWM.
|
||||
|
||||
The event system should permit low side current sensing without needing interrupts,
|
||||
but this will be handled by the current sense driver.
|
||||
|
||||
Supported:
|
||||
- arbitrary PWM frequencies between 1Hz (minimum we can set with our integer based API)
|
||||
and around 48kHz (more would be possible but the range will be low)
|
||||
- PWM range at 24kHz (default) is 1000
|
||||
- PWM range at 48kHz is 500
|
||||
- polarity setting is supported, in all modes
|
||||
- phase state setting is supported, in 3-PWM, 6-PWM hardware dead-time and 6-PWM software dead-time
|
||||
|
||||
TODOs:
|
||||
- change setDutyCycle to use register access for speed
|
||||
- add event system support for low-side current sensing
|
||||
- perhaps add support to reserve timers used also in
|
||||
Arduino Pwm.h code, for compatibility with analogWrite()
|
||||
- check if there is a better way for phase-state setting
|
||||
*/
|
||||
|
||||
|
||||
// track which channels are used
|
||||
bool channel_used[GPT_HOWMANY] = { false };
|
||||
|
||||
|
||||
struct RenesasTimerConfig {
|
||||
timer_cfg_t timer_cfg;
|
||||
gpt_instance_ctrl_t ctrl;
|
||||
gpt_extended_cfg_t ext_cfg;
|
||||
gpt_extended_pwm_cfg_t pwm_cfg;
|
||||
gpt_io_pin_t duty_pin;
|
||||
};
|
||||
|
||||
struct ClockDivAndRange {
|
||||
timer_source_div_t clk_div;
|
||||
uint32_t range;
|
||||
};
|
||||
|
||||
ClockDivAndRange getClockDivAndRange(uint32_t pwm_frequency, uint8_t timer_channel) {
|
||||
ClockDivAndRange result;
|
||||
uint32_t max_count = (timer_channel < GTP32_HOWMANY)? 4294967295 : 65535;
|
||||
uint32_t freq_hz = R_FSP_SystemClockHzGet(FSP_PRIV_CLOCK_PCLKD);
|
||||
float range = (float) freq_hz / ((float) pwm_frequency * 2.0f);
|
||||
if(range / 1.0 < max_count) {
|
||||
result.range = (uint32_t) (range / 1.0);
|
||||
result.clk_div = TIMER_SOURCE_DIV_1;
|
||||
}
|
||||
else if (range / 2.0 < max_count) {
|
||||
result.range = (uint32_t) (range / 2.0);
|
||||
result.clk_div = TIMER_SOURCE_DIV_2;
|
||||
}
|
||||
else if(range / 4.0 < max_count) {
|
||||
result.range = (uint32_t) (range / 4.0);
|
||||
result.clk_div = TIMER_SOURCE_DIV_4;
|
||||
}
|
||||
else if(range / 8.0 < max_count) {
|
||||
result.range = (uint32_t) (range / 8.0 );
|
||||
result.clk_div = TIMER_SOURCE_DIV_8;
|
||||
}
|
||||
else if(range / 16.0 < max_count) {
|
||||
result.range = (uint32_t) (range / 16.0 );
|
||||
result.clk_div = TIMER_SOURCE_DIV_16;
|
||||
}
|
||||
else if (range / 32.0 < max_count) {
|
||||
result.range = (uint32_t) (range / 32.0 );
|
||||
result.clk_div = TIMER_SOURCE_DIV_32;
|
||||
}
|
||||
else if(range / 64.0 < max_count) {
|
||||
result.range = (uint32_t) (range / 64.0 );
|
||||
result.clk_div = TIMER_SOURCE_DIV_64;
|
||||
}
|
||||
else if(range / 128.0 < max_count) {
|
||||
result.range = (uint32_t) (range / 128.0 );
|
||||
result.clk_div = TIMER_SOURCE_DIV_128;
|
||||
}
|
||||
else if(range / 256.0 < max_count) {
|
||||
result.range = (uint32_t) (range / 256.0 );
|
||||
result.clk_div = TIMER_SOURCE_DIV_256;
|
||||
}
|
||||
else if(range / 512.0 < max_count) {
|
||||
result.range = (uint32_t) (range / 512.0 );
|
||||
result.clk_div = TIMER_SOURCE_DIV_512;
|
||||
}
|
||||
else if(range / 1024.0 < max_count) {
|
||||
result.range = (uint32_t) (range / 1024.0 );
|
||||
result.clk_div = TIMER_SOURCE_DIV_1024;
|
||||
}
|
||||
else {
|
||||
SimpleFOCDebug::println("DRV: PWM frequency too low");
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
bool configureTimerPin(RenesasHardwareDriverParams* params, uint8_t index, bool active_high, bool complementary = false, bool complementary_active_high = true) {
|
||||
uint8_t pin = params->pins[index];
|
||||
uint8_t pin_C;
|
||||
std::array<uint16_t, 3> pinCfgs = getPinCfgs(pin, PIN_CFG_REQ_PWM);
|
||||
std::array<uint16_t, 3> pinCfgs_C;
|
||||
if(pinCfgs[0] == 0) {
|
||||
SIMPLEFOC_DEBUG("DRV: no PWM on pin ", pin);
|
||||
return false;
|
||||
}
|
||||
if (IS_PIN_AGT_PWM(pinCfgs[0])) {
|
||||
SIMPLEFOC_DEBUG("DRV: AGT timer not supported");
|
||||
return false;
|
||||
}
|
||||
// get the timer channel
|
||||
uint8_t timer_channel = GET_CHANNEL(pinCfgs[0]);
|
||||
// check its not used
|
||||
if (channel_used[timer_channel]) {
|
||||
SIMPLEFOC_DEBUG("DRV: channel in use");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (complementary) {
|
||||
pin_C = params->pins[index+1];
|
||||
pinCfgs_C = getPinCfgs(pin_C, PIN_CFG_REQ_PWM);
|
||||
if(pinCfgs_C[0] == 0) {
|
||||
SIMPLEFOC_DEBUG("DRV: no PWM on pin ", pin_C);
|
||||
return false;
|
||||
}
|
||||
if (IS_PIN_AGT_PWM(pinCfgs_C[0]) || GET_CHANNEL(pinCfgs_C[0])!=timer_channel) {
|
||||
SIMPLEFOC_DEBUG("DRV: comp. channel different");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TimerPWMChannel_t pwm_output = IS_PWM_ON_A(pinCfgs[0]) ? CHANNEL_A : CHANNEL_B;
|
||||
if (complementary) {
|
||||
TimerPWMChannel_t pwm_output_C = IS_PWM_ON_A(pinCfgs_C[0]) ? CHANNEL_A : CHANNEL_B;
|
||||
if (pwm_output_C != CHANNEL_A || pwm_output != CHANNEL_B) {
|
||||
SIMPLEFOC_DEBUG("DRV: output A/B mismatch");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// configure GPIO pin
|
||||
fsp_err_t err = R_IOPORT_PinCfg(&g_ioport_ctrl, g_pin_cfg[pin].pin, (uint32_t) (IOPORT_CFG_PERIPHERAL_PIN | IOPORT_PERIPHERAL_GPT1));
|
||||
if ((err == FSP_SUCCESS) && complementary)
|
||||
err = R_IOPORT_PinCfg(&g_ioport_ctrl, g_pin_cfg[pin_C].pin, (uint32_t) (IOPORT_CFG_PERIPHERAL_PIN | IOPORT_PERIPHERAL_GPT1));
|
||||
if (err != FSP_SUCCESS) {
|
||||
SIMPLEFOC_DEBUG("DRV: pin config failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// configure timer channel - frequency / top value
|
||||
ClockDivAndRange timings = getClockDivAndRange(params->pwm_frequency, timer_channel);
|
||||
#if defined(SIMPLEFOC_RENESAS_DEBUG)
|
||||
SimpleFOCDebug::println("---PWM Config---");
|
||||
SimpleFOCDebug::println("DRV: pwm pin: ", pin);
|
||||
if (complementary)
|
||||
SimpleFOCDebug::println("DRV: compl. pin: ", pin_C);
|
||||
SimpleFOCDebug::println("DRV: pwm channel: ", timer_channel);
|
||||
SimpleFOCDebug::print("DRV: pwm A/B: "); SimpleFOCDebug::println(complementary?"A+B":((pwm_output==CHANNEL_A)?"A":"B"));
|
||||
SimpleFOCDebug::println("DRV: pwm freq: ", (int)params->pwm_frequency);
|
||||
SimpleFOCDebug::println("DRV: pwm range: ", (int)timings.range);
|
||||
SimpleFOCDebug::println("DRV: pwm clkdiv: ", timings.clk_div);
|
||||
#endif
|
||||
|
||||
RenesasTimerConfig* t = new RenesasTimerConfig();
|
||||
// configure timer channel - count mode
|
||||
t->timer_cfg.channel = timer_channel;
|
||||
t->timer_cfg.mode = TIMER_MODE_TRIANGLE_WAVE_SYMMETRIC_PWM;
|
||||
t->timer_cfg.source_div = timings.clk_div;
|
||||
t->timer_cfg.period_counts = timings.range;
|
||||
t->timer_cfg.duty_cycle_counts = 0;
|
||||
t->timer_cfg.p_callback = nullptr;
|
||||
t->timer_cfg.p_context = nullptr;
|
||||
t->timer_cfg.p_extend = &(t->ext_cfg);
|
||||
t->timer_cfg.cycle_end_ipl = BSP_IRQ_DISABLED;
|
||||
t->timer_cfg.cycle_end_irq = FSP_INVALID_VECTOR;
|
||||
|
||||
t->ext_cfg.p_pwm_cfg = &(t->pwm_cfg);
|
||||
t->pwm_cfg.trough_ipl = BSP_IRQ_DISABLED;
|
||||
t->pwm_cfg.trough_irq = FSP_INVALID_VECTOR;
|
||||
t->pwm_cfg.poeg_link = GPT_POEG_LINK_POEG0;
|
||||
t->pwm_cfg.output_disable = GPT_OUTPUT_DISABLE_NONE;
|
||||
t->pwm_cfg.adc_trigger = GPT_ADC_TRIGGER_NONE;
|
||||
t->pwm_cfg.dead_time_count_up = 0;
|
||||
t->pwm_cfg.dead_time_count_down = 0;
|
||||
t->pwm_cfg.adc_a_compare_match = 0;
|
||||
t->pwm_cfg.adc_b_compare_match = 0;
|
||||
t->pwm_cfg.interrupt_skip_source = GPT_INTERRUPT_SKIP_SOURCE_NONE;
|
||||
t->pwm_cfg.interrupt_skip_count = GPT_INTERRUPT_SKIP_COUNT_0;
|
||||
t->pwm_cfg.interrupt_skip_adc = GPT_INTERRUPT_SKIP_ADC_NONE;
|
||||
t->pwm_cfg.gtioca_disable_setting = GPT_GTIOC_DISABLE_PROHIBITED;
|
||||
t->pwm_cfg.gtiocb_disable_setting = GPT_GTIOC_DISABLE_PROHIBITED;
|
||||
// configure dead-time if both outputs are used
|
||||
if (complementary) {
|
||||
uint32_t dt = params->dead_zone * timings.range;
|
||||
t->pwm_cfg.dead_time_count_up = dt;
|
||||
t->pwm_cfg.dead_time_count_down = dt;
|
||||
}
|
||||
|
||||
// configure timer channel - outputs and polarity
|
||||
t->ext_cfg.gtior_setting.gtior = 0L;
|
||||
if (!complementary) {
|
||||
if(pwm_output == CHANNEL_A) {
|
||||
t->duty_pin = GPT_IO_PIN_GTIOCA;
|
||||
t->ext_cfg.gtioca.output_enabled = true;
|
||||
t->ext_cfg.gtiocb.output_enabled = false;
|
||||
t->ext_cfg.gtior_setting.gtior_b.gtioa = 0x03 | (active_high ? 0x00 : 0x10);
|
||||
t->ext_cfg.gtior_setting.gtior_b.oadflt = active_high ? 0x00 : 0x01;
|
||||
// t->ext_cfg.gtior_setting.gtior_b.oahld = 0x0;
|
||||
// t->ext_cfg.gtior_setting.gtior_b.oadf = 0x0;
|
||||
// t->ext_cfg.gtior_setting.gtior_b.nfaen = 0x0;
|
||||
}
|
||||
else {
|
||||
t->duty_pin = GPT_IO_PIN_GTIOCB;
|
||||
t->ext_cfg.gtiocb.output_enabled = true;
|
||||
t->ext_cfg.gtioca.output_enabled = false;
|
||||
t->ext_cfg.gtior_setting.gtior_b.gtiob = 0x03 | (active_high ? 0x00 : 0x10);
|
||||
t->ext_cfg.gtior_setting.gtior_b.obdflt = active_high ? 0x00 : 0x01;
|
||||
}
|
||||
}
|
||||
else {
|
||||
t->duty_pin = GPT_IO_PIN_GTIOCA_AND_GTIOCB;
|
||||
t->ext_cfg.gtioca.output_enabled = true;
|
||||
t->ext_cfg.gtiocb.output_enabled = true;
|
||||
t->ext_cfg.gtior_setting.gtior_b.gtioa = 0x03 | (!complementary_active_high ? 0x00 : 0x10);
|
||||
t->ext_cfg.gtior_setting.gtior_b.oadflt = !complementary_active_high ? 0x00 : 0x01;
|
||||
t->ext_cfg.gtior_setting.gtior_b.gtiob = 0x03 | (active_high ? 0x00 : 0x10);
|
||||
t->ext_cfg.gtior_setting.gtior_b.obdflt = active_high ? 0x00 : 0x01;
|
||||
}
|
||||
|
||||
// lets stop the timer in case its running
|
||||
if (GPT_OPEN == t->ctrl.open) {
|
||||
if (R_GPT_Stop(&(t->ctrl)) != FSP_SUCCESS) {
|
||||
SIMPLEFOC_DEBUG("DRV: timer stop failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&(t->ctrl), 0, sizeof(gpt_instance_ctrl_t));
|
||||
err = R_GPT_Open(&(t->ctrl),&(t->timer_cfg));
|
||||
if ((err != FSP_ERR_ALREADY_OPEN) && (err != FSP_SUCCESS)) {
|
||||
SIMPLEFOC_DEBUG("DRV: open failed");
|
||||
return false;
|
||||
}
|
||||
#if defined(SIMPLEFOC_RESENSAS_DEBUG)
|
||||
if (err == FSP_ERR_ALREADY_OPEN) {
|
||||
SimpleFOCDebug::println("DRV: timer already open");
|
||||
}
|
||||
#endif
|
||||
err = R_GPT_PeriodSet(&(t->ctrl), t->timer_cfg.period_counts);
|
||||
if (err != FSP_SUCCESS) {
|
||||
SIMPLEFOC_DEBUG("DRV: period set failed");
|
||||
return false;
|
||||
}
|
||||
err = R_GPT_OutputEnable(&(t->ctrl), t->duty_pin);
|
||||
if (err != FSP_SUCCESS) {
|
||||
SIMPLEFOC_DEBUG("DRV: pin enable failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
channel_used[timer_channel] = true;
|
||||
params->timer_config[index] = t;
|
||||
params->channels[index] = timer_channel;
|
||||
if (complementary) {
|
||||
params->timer_config[index+1] = t;
|
||||
params->channels[index+1] = timer_channel;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// start the timer channels for the motor, synchronously
|
||||
bool startTimerChannels(RenesasHardwareDriverParams* params, int num_channels) {
|
||||
uint32_t mask = 0;
|
||||
for (int i = 0; i < num_channels; i++) {
|
||||
// RenesasTimerConfig* t = params->timer_config[i];
|
||||
// if (R_GPT_Start(&(t->ctrl)) != FSP_SUCCESS) {
|
||||
// SIMPLEFOC_DEBUG("DRV: timer start failed");
|
||||
// return false;
|
||||
// }
|
||||
mask |= (1 << params->channels[i]);
|
||||
#if defined(SIMPLEFOC_RENESAS_DEBUG)
|
||||
SimpleFOCDebug::println("DRV: starting timer: ", params->channels[i]);
|
||||
#endif
|
||||
}
|
||||
params->timer_config[0]->ctrl.p_reg->GTSTR |= mask;
|
||||
#if defined(SIMPLEFOC_RENESAS_DEBUG)
|
||||
SimpleFOCDebug::println("DRV: timers started");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// check if the given pins are on the same timer channel
|
||||
bool isHardware6Pwm(const int pin1, const int pin2) {
|
||||
std::array<uint16_t, 3> pinCfgs1 = getPinCfgs(pin1, PIN_CFG_REQ_PWM);
|
||||
std::array<uint16_t, 3> pinCfgs2 = getPinCfgs(pin2, PIN_CFG_REQ_PWM);
|
||||
if(pinCfgs1[0] == 0 || pinCfgs2[0] == 0)
|
||||
return false;
|
||||
if (IS_PIN_AGT_PWM(pinCfgs1[0]) || IS_PIN_AGT_PWM(pinCfgs2[0]))
|
||||
return false;
|
||||
uint8_t timer_channel1 = GET_CHANNEL(pinCfgs1[0]);
|
||||
uint8_t timer_channel2 = GET_CHANNEL(pinCfgs2[0]);
|
||||
return timer_channel1==timer_channel2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void* _configure1PWM(long pwm_frequency, const int pinA) {
|
||||
RenesasHardwareDriverParams* params = new RenesasHardwareDriverParams;
|
||||
params->pins[0] = pinA;
|
||||
params->pwm_frequency = (pwm_frequency==NOT_SET)?RENESAS_DEFAULT_PWM_FREQUENCY:pwm_frequency;
|
||||
bool success = true;
|
||||
success = configureTimerPin(params, 0, SIMPLEFOC_PWM_ACTIVE_HIGH);
|
||||
if (success)
|
||||
success = startTimerChannels(params, 1);
|
||||
if (!success)
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
void* _configure2PWM(long pwm_frequency,const int pinA, const int pinB) {
|
||||
RenesasHardwareDriverParams* params = new RenesasHardwareDriverParams;
|
||||
params->pins[0] = pinA; params->pins[1] = pinB;
|
||||
params->pwm_frequency = (pwm_frequency==NOT_SET)?RENESAS_DEFAULT_PWM_FREQUENCY:pwm_frequency;
|
||||
bool success = true;
|
||||
success = configureTimerPin(params, 0, SIMPLEFOC_PWM_ACTIVE_HIGH);
|
||||
success &= configureTimerPin(params, 1, SIMPLEFOC_PWM_ACTIVE_HIGH);
|
||||
if (!success)
|
||||
success &= startTimerChannels(params, 2);
|
||||
if (!success)
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
|
||||
RenesasHardwareDriverParams* params = new RenesasHardwareDriverParams;
|
||||
params->pins[0] = pinA; params->pins[1] = pinB; params->pins[2] = pinC;
|
||||
params->pwm_frequency = (pwm_frequency==NOT_SET)?RENESAS_DEFAULT_PWM_FREQUENCY:pwm_frequency;
|
||||
bool success = true;
|
||||
success = configureTimerPin(params, 0, SIMPLEFOC_PWM_ACTIVE_HIGH);
|
||||
success &= configureTimerPin(params, 1, SIMPLEFOC_PWM_ACTIVE_HIGH);
|
||||
success &= configureTimerPin(params, 2, SIMPLEFOC_PWM_ACTIVE_HIGH);
|
||||
if (success)
|
||||
success = startTimerChannels(params, 3);
|
||||
if (!success)
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
void* _configure4PWM(long pwm_frequency, const int pin1A, const int pin1B, const int pin2A, const int pin2B) {
|
||||
RenesasHardwareDriverParams* params = new RenesasHardwareDriverParams;
|
||||
params->pins[0] = pin1A; params->pins[1] = pin1B; params->pins[2] = pin2A; params->pins[3] = pin2B;
|
||||
params->pwm_frequency = (pwm_frequency==NOT_SET)?RENESAS_DEFAULT_PWM_FREQUENCY:pwm_frequency;
|
||||
bool success = true;
|
||||
success = configureTimerPin(params, 0, SIMPLEFOC_PWM_ACTIVE_HIGH);
|
||||
success &= configureTimerPin(params, 1, SIMPLEFOC_PWM_ACTIVE_HIGH);
|
||||
success &= configureTimerPin(params, 2, SIMPLEFOC_PWM_ACTIVE_HIGH);
|
||||
success &= configureTimerPin(params, 3, SIMPLEFOC_PWM_ACTIVE_HIGH);
|
||||
if (success)
|
||||
success = startTimerChannels(params, 4);
|
||||
if (!success)
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l){
|
||||
RenesasHardwareDriverParams* params = new RenesasHardwareDriverParams;
|
||||
params->pins[0] = pinA_h; params->pins[1] = pinA_l; params->pins[2] = pinB_h; params->pins[3] = pinB_l; params->pins[4] = pinC_h; params->pins[5] = pinC_l;
|
||||
params->pwm_frequency = (pwm_frequency==NOT_SET)?RENESAS_DEFAULT_PWM_FREQUENCY:pwm_frequency;
|
||||
params->dead_zone = (dead_zone==NOT_SET)?RENESAS_DEFAULT_DEAD_ZONE:dead_zone;
|
||||
|
||||
bool success = true;
|
||||
if (isHardware6Pwm(pinA_h, pinA_l))
|
||||
success &= configureTimerPin(params, 0, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, true, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH);
|
||||
else {
|
||||
success &= configureTimerPin(params, 0, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH);
|
||||
success &= configureTimerPin(params, 1, !SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH); // reverse polarity on low side gives desired active high/low behaviour
|
||||
}
|
||||
|
||||
if (isHardware6Pwm(pinB_h, pinB_l))
|
||||
success &= configureTimerPin(params, 2, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, true, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH);
|
||||
else {
|
||||
success &= configureTimerPin(params, 2, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH);
|
||||
success &= configureTimerPin(params, 3, !SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH);
|
||||
}
|
||||
|
||||
if (isHardware6Pwm(pinC_h, pinC_l))
|
||||
success &= configureTimerPin(params, 4, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, true, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH);
|
||||
else {
|
||||
success &= configureTimerPin(params, 4, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH);
|
||||
success &= configureTimerPin(params, 5, !SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH);
|
||||
}
|
||||
|
||||
if (success)
|
||||
success = startTimerChannels(params, 6);
|
||||
if (!success)
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void _writeDutyCycle1PWM(float dc_a, void* params){
|
||||
RenesasTimerConfig* t = ((RenesasHardwareDriverParams*)params)->timer_config[0];
|
||||
uint32_t duty_cycle_counts = (uint32_t)(dc_a * (float)(t->timer_cfg.period_counts));
|
||||
if (R_GPT_DutyCycleSet(&(t->ctrl), duty_cycle_counts, t->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params){
|
||||
RenesasTimerConfig* t = ((RenesasHardwareDriverParams*)params)->timer_config[0];
|
||||
uint32_t duty_cycle_counts = (uint32_t)(dc_a * (float)(t->timer_cfg.period_counts));
|
||||
if (R_GPT_DutyCycleSet(&(t->ctrl), duty_cycle_counts, t->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
t = ((RenesasHardwareDriverParams*)params)->timer_config[1];
|
||||
duty_cycle_counts = (uint32_t)(dc_b * (float)(t->timer_cfg.period_counts));
|
||||
if (R_GPT_DutyCycleSet(&(t->ctrl), duty_cycle_counts, t->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params){
|
||||
RenesasTimerConfig* t = ((RenesasHardwareDriverParams*)params)->timer_config[0];
|
||||
uint32_t duty_cycle_counts = (uint32_t)(dc_a * (float)(t->timer_cfg.period_counts));
|
||||
if (R_GPT_DutyCycleSet(&(t->ctrl), duty_cycle_counts, t->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
t = ((RenesasHardwareDriverParams*)params)->timer_config[1];
|
||||
duty_cycle_counts = (uint32_t)(dc_b * (float)(t->timer_cfg.period_counts));
|
||||
if (R_GPT_DutyCycleSet(&(t->ctrl), duty_cycle_counts, t->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
t = ((RenesasHardwareDriverParams*)params)->timer_config[2];
|
||||
duty_cycle_counts = (uint32_t)(dc_c * (float)(t->timer_cfg.period_counts));
|
||||
if (R_GPT_DutyCycleSet(&(t->ctrl), duty_cycle_counts, t->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params){
|
||||
RenesasTimerConfig* t = ((RenesasHardwareDriverParams*)params)->timer_config[0];
|
||||
uint32_t duty_cycle_counts = (uint32_t)(dc_1a * (float)(t->timer_cfg.period_counts));
|
||||
if (R_GPT_DutyCycleSet(&(t->ctrl), duty_cycle_counts, t->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
t = ((RenesasHardwareDriverParams*)params)->timer_config[1];
|
||||
duty_cycle_counts = (uint32_t)(dc_1b * (float)(t->timer_cfg.period_counts));
|
||||
if (R_GPT_DutyCycleSet(&(t->ctrl), duty_cycle_counts, t->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
t = ((RenesasHardwareDriverParams*)params)->timer_config[2];
|
||||
duty_cycle_counts = (uint32_t)(dc_2a * (float)(t->timer_cfg.period_counts));
|
||||
if (R_GPT_DutyCycleSet(&(t->ctrl), duty_cycle_counts, t->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
t = ((RenesasHardwareDriverParams*)params)->timer_config[3];
|
||||
duty_cycle_counts = (uint32_t)(dc_2b * (float)(t->timer_cfg.period_counts));
|
||||
if (R_GPT_DutyCycleSet(&(t->ctrl), duty_cycle_counts, t->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _setSinglePhaseState(RenesasTimerConfig* hi, RenesasTimerConfig* lo, PhaseState state) {
|
||||
gpt_gtior_setting_t gtior;
|
||||
gtior.gtior = hi->ctrl.p_reg->GTIOR;
|
||||
bool on = (state==PHASE_ON) || (state==PHASE_HI);
|
||||
|
||||
if (hi->duty_pin == GPT_IO_PIN_GTIOCA_AND_GTIOCB) {
|
||||
bool ch = false;
|
||||
if (gtior.gtior_b.obe != on) {
|
||||
gtior.gtior_b.obe = on;
|
||||
ch = true;
|
||||
} // B is high side
|
||||
on = (state==PHASE_ON) || (state==PHASE_LO);
|
||||
if (gtior.gtior_b.oae != on) {
|
||||
gtior.gtior_b.oae = on;
|
||||
ch = true;
|
||||
}
|
||||
if (ch)
|
||||
hi->ctrl.p_reg->GTIOR = gtior.gtior;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hi->duty_pin == GPT_IO_PIN_GTIOCA) {
|
||||
if (gtior.gtior_b.oae != on) {
|
||||
gtior.gtior_b.oae = on;
|
||||
hi->ctrl.p_reg->GTIOR = gtior.gtior;
|
||||
}
|
||||
}
|
||||
else if (hi->duty_pin == GPT_IO_PIN_GTIOCB) {
|
||||
if (gtior.gtior_b.obe != on) {
|
||||
gtior.gtior_b.obe = on;
|
||||
hi->ctrl.p_reg->GTIOR = gtior.gtior;
|
||||
}
|
||||
}
|
||||
|
||||
gtior.gtior = lo->ctrl.p_reg->GTIOR;
|
||||
on = (state==PHASE_ON) || (state==PHASE_LO);
|
||||
if (lo->duty_pin == GPT_IO_PIN_GTIOCA) {
|
||||
if (gtior.gtior_b.oae != on) {
|
||||
gtior.gtior_b.oae = on;
|
||||
lo->ctrl.p_reg->GTIOR = gtior.gtior;
|
||||
}
|
||||
}
|
||||
else if (lo->duty_pin == GPT_IO_PIN_GTIOCB) {
|
||||
if (gtior.gtior_b.obe != on) {
|
||||
gtior.gtior_b.obe = on;
|
||||
lo->ctrl.p_reg->GTIOR = gtior.gtior;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void* params){
|
||||
RenesasTimerConfig* t = ((RenesasHardwareDriverParams*)params)->timer_config[0];
|
||||
RenesasTimerConfig* t1 = ((RenesasHardwareDriverParams*)params)->timer_config[1];
|
||||
uint32_t dt = (uint32_t)(((RenesasHardwareDriverParams*)params)->dead_zone * (float)(t->timer_cfg.period_counts));
|
||||
uint32_t duty_cycle_counts = (uint32_t)(dc_a * (float)(t->timer_cfg.period_counts));
|
||||
bool hw_deadtime = ((RenesasHardwareDriverParams*)params)->channels[0] == ((RenesasHardwareDriverParams*)params)->channels[1];
|
||||
uint32_t dt_act = (duty_cycle_counts>0 && !hw_deadtime)?dt:0;
|
||||
_setSinglePhaseState(t, t1, phase_state[0]);
|
||||
if (R_GPT_DutyCycleSet(&(t->ctrl), duty_cycle_counts - dt_act, t->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
if (!hw_deadtime) {
|
||||
if (R_GPT_DutyCycleSet(&(t1->ctrl), duty_cycle_counts + dt_act, t1->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
}
|
||||
|
||||
t = ((RenesasHardwareDriverParams*)params)->timer_config[2];
|
||||
t1 = ((RenesasHardwareDriverParams*)params)->timer_config[3];
|
||||
duty_cycle_counts = (uint32_t)(dc_b * (float)(t->timer_cfg.period_counts));
|
||||
hw_deadtime = ((RenesasHardwareDriverParams*)params)->channels[2] == ((RenesasHardwareDriverParams*)params)->channels[3];
|
||||
dt_act = (duty_cycle_counts>0 && !hw_deadtime)?dt:0;
|
||||
_setSinglePhaseState(t, t1, phase_state[1]);
|
||||
if (R_GPT_DutyCycleSet(&(t->ctrl), duty_cycle_counts - dt_act, t->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
if (!hw_deadtime) {
|
||||
if (R_GPT_DutyCycleSet(&(t1->ctrl), duty_cycle_counts + dt_act, t1->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
}
|
||||
|
||||
t = ((RenesasHardwareDriverParams*)params)->timer_config[4];
|
||||
t1 = ((RenesasHardwareDriverParams*)params)->timer_config[5];
|
||||
duty_cycle_counts = (uint32_t)(dc_c * (float)(t->timer_cfg.period_counts));
|
||||
hw_deadtime = ((RenesasHardwareDriverParams*)params)->channels[4] == ((RenesasHardwareDriverParams*)params)->channels[5];
|
||||
dt_act = (duty_cycle_counts>0 && !hw_deadtime)?dt:0;
|
||||
_setSinglePhaseState(t, t1, phase_state[2]);
|
||||
if (R_GPT_DutyCycleSet(&(t->ctrl), duty_cycle_counts, t->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
if (!hw_deadtime) {
|
||||
if (R_GPT_DutyCycleSet(&(t1->ctrl), duty_cycle_counts + dt_act, t1->duty_pin) != FSP_SUCCESS) {
|
||||
// error
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../../hardware_api.h"
|
||||
|
||||
|
||||
#if defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_UNOR4_MINIMA)
|
||||
|
||||
// uncomment to enable debug output from Renesas driver
|
||||
// can set this as build-flag in Arduino IDE or PlatformIO
|
||||
#define SIMPLEFOC_RENESAS_DEBUG
|
||||
|
||||
#define RENESAS_DEFAULT_PWM_FREQUENCY 24000
|
||||
#define RENESAS_DEFAULT_DEAD_ZONE 0.05f
|
||||
|
||||
struct RenesasTimerConfig;
|
||||
|
||||
typedef struct RenesasHardwareDriverParams {
|
||||
uint8_t pins[6];
|
||||
uint8_t channels[6];
|
||||
RenesasTimerConfig* timer_config[6];
|
||||
long pwm_frequency;
|
||||
float dead_zone;
|
||||
} RenesasHardwareDriverParams;
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,237 @@
|
||||
|
||||
/**
|
||||
* Support for the RP2040 MCU, as found on the Raspberry Pi Pico.
|
||||
*/
|
||||
#if defined(TARGET_RP2040)
|
||||
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for RP2040")
|
||||
#pragma message("")
|
||||
|
||||
|
||||
#define SIMPLEFOC_DEBUG_RP2040
|
||||
|
||||
#include "../../hardware_api.h"
|
||||
#include "./rp2040_mcu.h"
|
||||
#include "hardware/pwm.h"
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
#define _PWM_FREQUENCY 24000
|
||||
#define _PWM_FREQUENCY_MAX 66000
|
||||
#define _PWM_FREQUENCY_MIN 1
|
||||
|
||||
|
||||
|
||||
// until I can figure out if this can be quickly read from some register, keep it here.
|
||||
// it also serves as a marker for what slices are already used.
|
||||
uint16_t wrapvalues[NUM_PWM_SLICES];
|
||||
|
||||
|
||||
// TODO add checks which channels are already used...
|
||||
|
||||
void setupPWM(int pin, long pwm_frequency, bool invert, RP2040DriverParams* params, uint8_t index) {
|
||||
gpio_set_function(pin, GPIO_FUNC_PWM);
|
||||
uint slice = pwm_gpio_to_slice_num(pin);
|
||||
uint chan = pwm_gpio_to_channel(pin);
|
||||
params->pins[index] = pin;
|
||||
params->slice[index] = slice;
|
||||
params->chan[index] = chan;
|
||||
uint32_t sysclock_hz = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS) * 1000;
|
||||
uint32_t factor = 4096 * 2 * pwm_frequency;
|
||||
uint32_t div = sysclock_hz / factor;
|
||||
if (sysclock_hz % factor !=0) div+=1;
|
||||
if (div < 16) div = 16;
|
||||
uint32_t wrapvalue = (sysclock_hz * 8) / div / pwm_frequency - 1;
|
||||
#ifdef SIMPLEFOC_DEBUG_RP2040
|
||||
SimpleFOCDebug::print("Configuring pin ");
|
||||
SimpleFOCDebug::print(pin);
|
||||
SimpleFOCDebug::print(" slice ");
|
||||
SimpleFOCDebug::print((int)slice);
|
||||
SimpleFOCDebug::print(" channel ");
|
||||
SimpleFOCDebug::print((int)chan);
|
||||
SimpleFOCDebug::print(" frequency ");
|
||||
SimpleFOCDebug::print((int)pwm_frequency);
|
||||
SimpleFOCDebug::print(" divisor ");
|
||||
SimpleFOCDebug::print((int)(div>>4));
|
||||
SimpleFOCDebug::print(".");
|
||||
SimpleFOCDebug::print((int)(div&0xF));
|
||||
SimpleFOCDebug::print(" top value ");
|
||||
SimpleFOCDebug::println((int)wrapvalue);
|
||||
#endif
|
||||
if (wrapvalue < 999)
|
||||
SimpleFOCDebug::println("Warning: PWM resolution is low.");
|
||||
pwm_set_clkdiv_int_frac(slice, div>>4, div&0xF);
|
||||
pwm_set_phase_correct(slice, true);
|
||||
pwm_set_wrap(slice, wrapvalue);
|
||||
wrapvalues[slice] = wrapvalue;
|
||||
if (invert) {
|
||||
if (chan==0)
|
||||
hw_write_masked(&pwm_hw->slice[slice].csr, 0x1 << PWM_CH0_CSR_A_INV_LSB, PWM_CH0_CSR_A_INV_BITS);
|
||||
else
|
||||
hw_write_masked(&pwm_hw->slice[slice].csr, 0x1 << PWM_CH0_CSR_B_INV_LSB, PWM_CH0_CSR_B_INV_BITS);
|
||||
}
|
||||
pwm_set_chan_level(slice, chan, 0); // switch off initially
|
||||
}
|
||||
|
||||
|
||||
void syncSlices() {
|
||||
for (uint i=0;i<NUM_PWM_SLICES;i++) {
|
||||
pwm_set_enabled(i, false);
|
||||
pwm_set_counter(i, 0);
|
||||
}
|
||||
// enable all slices
|
||||
pwm_set_mask_enabled(0xFF);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void* _configure1PWM(long pwm_frequency, const int pinA) {
|
||||
RP2040DriverParams* params = new RP2040DriverParams();
|
||||
if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY;
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX);
|
||||
params->pwm_frequency = pwm_frequency;
|
||||
setupPWM(pinA, pwm_frequency, !SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0);
|
||||
syncSlices();
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void* _configure2PWM(long pwm_frequency, const int pinA, const int pinB) {
|
||||
RP2040DriverParams* params = new RP2040DriverParams();
|
||||
if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY;
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX);
|
||||
params->pwm_frequency = pwm_frequency;
|
||||
setupPWM(pinA, pwm_frequency, !SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0);
|
||||
setupPWM(pinB, pwm_frequency, !SIMPLEFOC_PWM_ACTIVE_HIGH, params, 1);
|
||||
syncSlices();
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void* _configure3PWM(long pwm_frequency, const int pinA, const int pinB, const int pinC) {
|
||||
RP2040DriverParams* params = new RP2040DriverParams();
|
||||
if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY;
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX);
|
||||
params->pwm_frequency = pwm_frequency;
|
||||
setupPWM(pinA, pwm_frequency, !SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0);
|
||||
setupPWM(pinB, pwm_frequency, !SIMPLEFOC_PWM_ACTIVE_HIGH, params, 1);
|
||||
setupPWM(pinC, pwm_frequency, !SIMPLEFOC_PWM_ACTIVE_HIGH, params, 2);
|
||||
syncSlices();
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void* _configure4PWM(long pwm_frequency, const int pin1A, const int pin1B, const int pin2A, const int pin2B) {
|
||||
RP2040DriverParams* params = new RP2040DriverParams();
|
||||
if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY;
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX);
|
||||
params->pwm_frequency = pwm_frequency;
|
||||
setupPWM(pin1A, pwm_frequency, !SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0);
|
||||
setupPWM(pin1B, pwm_frequency, !SIMPLEFOC_PWM_ACTIVE_HIGH, params, 1);
|
||||
setupPWM(pin2A, pwm_frequency, !SIMPLEFOC_PWM_ACTIVE_HIGH, params, 2);
|
||||
setupPWM(pin2B, pwm_frequency, !SIMPLEFOC_PWM_ACTIVE_HIGH, params, 3);
|
||||
syncSlices();
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) {
|
||||
// non-PIO solution...
|
||||
RP2040DriverParams* params = new RP2040DriverParams();
|
||||
if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY;
|
||||
else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX);
|
||||
params->pwm_frequency = pwm_frequency;
|
||||
params->dead_zone = dead_zone;
|
||||
setupPWM(pinA_h, pwm_frequency, !SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 0);
|
||||
setupPWM(pinB_h, pwm_frequency, !SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 2);
|
||||
setupPWM(pinC_h, pwm_frequency, !SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 4);
|
||||
setupPWM(pinA_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 1);
|
||||
setupPWM(pinB_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 3);
|
||||
setupPWM(pinC_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 5);
|
||||
syncSlices();
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void writeDutyCycle(float val, uint slice, uint chan) {
|
||||
pwm_set_chan_level(slice, chan, (wrapvalues[slice]+1) * val);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void _writeDutyCycle1PWM(float dc_a, void* params) {
|
||||
writeDutyCycle(dc_a, ((RP2040DriverParams*)params)->slice[0], ((RP2040DriverParams*)params)->chan[0]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params) {
|
||||
writeDutyCycle(dc_a, ((RP2040DriverParams*)params)->slice[0], ((RP2040DriverParams*)params)->chan[0]);
|
||||
writeDutyCycle(dc_b, ((RP2040DriverParams*)params)->slice[1], ((RP2040DriverParams*)params)->chan[1]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params) {
|
||||
writeDutyCycle(dc_a, ((RP2040DriverParams*)params)->slice[0], ((RP2040DriverParams*)params)->chan[0]);
|
||||
writeDutyCycle(dc_b, ((RP2040DriverParams*)params)->slice[1], ((RP2040DriverParams*)params)->chan[1]);
|
||||
writeDutyCycle(dc_c, ((RP2040DriverParams*)params)->slice[2], ((RP2040DriverParams*)params)->chan[2]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params) {
|
||||
writeDutyCycle(dc_1a, ((RP2040DriverParams*)params)->slice[0], ((RP2040DriverParams*)params)->chan[0]);
|
||||
writeDutyCycle(dc_1b, ((RP2040DriverParams*)params)->slice[1], ((RP2040DriverParams*)params)->chan[1]);
|
||||
writeDutyCycle(dc_2a, ((RP2040DriverParams*)params)->slice[2], ((RP2040DriverParams*)params)->chan[2]);
|
||||
writeDutyCycle(dc_2b, ((RP2040DriverParams*)params)->slice[3], ((RP2040DriverParams*)params)->chan[3]);
|
||||
}
|
||||
|
||||
inline float swDti(float val, float dt) {
|
||||
float ret = dt+val;
|
||||
if (ret>1.0) ret = 1.0f;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void* params) {
|
||||
if (phase_state[0]==PhaseState::PHASE_ON || phase_state[0]==PhaseState::PHASE_HI)
|
||||
writeDutyCycle(dc_a, ((RP2040DriverParams*)params)->slice[0], ((RP2040DriverParams*)params)->chan[0]);
|
||||
else
|
||||
writeDutyCycle(0.0f, ((RP2040DriverParams*)params)->slice[0], ((RP2040DriverParams*)params)->chan[0]);
|
||||
if (phase_state[0]==PhaseState::PHASE_ON || phase_state[0]==PhaseState::PHASE_LO)
|
||||
writeDutyCycle(swDti(dc_a, ((RP2040DriverParams*)params)->dead_zone), ((RP2040DriverParams*)params)->slice[1], ((RP2040DriverParams*)params)->chan[1]);
|
||||
else
|
||||
writeDutyCycle(0.0f, ((RP2040DriverParams*)params)->slice[1], ((RP2040DriverParams*)params)->chan[1]);
|
||||
|
||||
if (phase_state[1]==PhaseState::PHASE_ON || phase_state[1]==PhaseState::PHASE_HI)
|
||||
writeDutyCycle(dc_b, ((RP2040DriverParams*)params)->slice[2], ((RP2040DriverParams*)params)->chan[2]);
|
||||
else
|
||||
writeDutyCycle(0.0f, ((RP2040DriverParams*)params)->slice[2], ((RP2040DriverParams*)params)->chan[2]);
|
||||
if (phase_state[1]==PhaseState::PHASE_ON || phase_state[1]==PhaseState::PHASE_LO)
|
||||
writeDutyCycle(swDti(dc_b, ((RP2040DriverParams*)params)->dead_zone), ((RP2040DriverParams*)params)->slice[3], ((RP2040DriverParams*)params)->chan[3]);
|
||||
else
|
||||
writeDutyCycle(0.0f, ((RP2040DriverParams*)params)->slice[3], ((RP2040DriverParams*)params)->chan[3]);
|
||||
|
||||
if (phase_state[2]==PhaseState::PHASE_ON || phase_state[2]==PhaseState::PHASE_HI)
|
||||
writeDutyCycle(dc_c, ((RP2040DriverParams*)params)->slice[4], ((RP2040DriverParams*)params)->chan[4]);
|
||||
else
|
||||
writeDutyCycle(0.0f, ((RP2040DriverParams*)params)->slice[4], ((RP2040DriverParams*)params)->chan[4]);
|
||||
if (phase_state[2]==PhaseState::PHASE_ON || phase_state[2]==PhaseState::PHASE_LO)
|
||||
writeDutyCycle(swDti(dc_c, ((RP2040DriverParams*)params)->dead_zone), ((RP2040DriverParams*)params)->slice[5], ((RP2040DriverParams*)params)->chan[5]);
|
||||
else
|
||||
writeDutyCycle(0.0f, ((RP2040DriverParams*)params)->slice[5], ((RP2040DriverParams*)params)->chan[5]);
|
||||
|
||||
_UNUSED(phase_state);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#if defined(TARGET_RP2040)
|
||||
|
||||
|
||||
|
||||
typedef struct RP2040DriverParams {
|
||||
int pins[6];
|
||||
uint slice[6];
|
||||
uint chan[6];
|
||||
long pwm_frequency;
|
||||
float dead_zone;
|
||||
} RP2040DriverParams;
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,353 @@
|
||||
|
||||
|
||||
|
||||
#include "./samd_mcu.h"
|
||||
|
||||
|
||||
#ifdef _SAMD21_
|
||||
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for SAMD21")
|
||||
#pragma message("")
|
||||
|
||||
|
||||
#ifndef TCC3_CH0
|
||||
#define TCC3_CH0 NOT_ON_TIMER
|
||||
#endif
|
||||
#ifndef TCC3_CH1
|
||||
#define TCC3_CH1 NOT_ON_TIMER
|
||||
#endif
|
||||
#ifndef TCC3_CH2
|
||||
#define TCC3_CH2 NOT_ON_TIMER
|
||||
#endif
|
||||
#ifndef TCC3_CH3
|
||||
#define TCC3_CH3 NOT_ON_TIMER
|
||||
#endif
|
||||
#ifndef TCC3_CH4
|
||||
#define TCC3_CH4 NOT_ON_TIMER
|
||||
#endif
|
||||
#ifndef TCC3_CH5
|
||||
#define TCC3_CH5 NOT_ON_TIMER
|
||||
#endif
|
||||
#ifndef TCC3_CH6
|
||||
#define TCC3_CH6 NOT_ON_TIMER
|
||||
#endif
|
||||
#ifndef TCC3_CH7
|
||||
#define TCC3_CH7 NOT_ON_TIMER
|
||||
#endif
|
||||
#ifndef TC6_CH0
|
||||
#define TC6_CH0 NOT_ON_TIMER
|
||||
#endif
|
||||
#ifndef TC6_CH1
|
||||
#define TC6_CH1 NOT_ON_TIMER
|
||||
#endif
|
||||
#ifndef TC7_CH0
|
||||
#define TC7_CH0 NOT_ON_TIMER
|
||||
#endif
|
||||
#ifndef TC7_CH1
|
||||
#define TC7_CH1 NOT_ON_TIMER
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#define NUM_WO_ASSOCIATIONS 48
|
||||
|
||||
/*
|
||||
* For SAM D21 A/B/C/D Variant Devices and SAM DA1 A/B Variant Devices
|
||||
* Good for SAMD2xE, SAMD2xG and SAMD2xJ devices. Other SAMD21s currently not supported in arduino anyway?
|
||||
*
|
||||
* Note: only the pins which have timers associated are listed in this table.
|
||||
* You can use the values from g_APinDescription.ulPort and g_APinDescription.ulPin to find the correct row in the table.
|
||||
*
|
||||
* See Microchip Technology datasheet DS40001882F-page 30
|
||||
*/
|
||||
struct wo_association WO_associations[] = {
|
||||
|
||||
{ PORTA, 0, TCC2_CH0, 0, NOT_ON_TIMER, 0},
|
||||
{ PORTA, 1, TCC2_CH1, 1, NOT_ON_TIMER, 0},
|
||||
{ PORTA, 2, NOT_ON_TIMER, 0, TCC3_CH0, 0},
|
||||
{ PORTA, 3, NOT_ON_TIMER, 0, TCC3_CH1, 1},
|
||||
// PB04, PB05, PB06, PB07 - no timers
|
||||
{ PORTB, 8, TC4_CH0, 0, TCC3_CH6, 6},
|
||||
{ PORTB, 9, TC4_CH1, 1, TCC3_CH7, 7},
|
||||
{ PORTA, 4, TCC0_CH0, 0, TCC3_CH2, 2},
|
||||
{ PORTA, 5, TCC0_CH1, 1, TCC3_CH3, 3},
|
||||
{ PORTA, 6, TCC1_CH0, 0, TCC3_CH4, 4},
|
||||
{ PORTA, 7, TCC1_CH1, 1, TCC3_CH5, 5},
|
||||
{ PORTA, 8, TCC0_CH0, 0, TCC1_CH2, 2},
|
||||
{ PORTA, 9, TCC0_CH1, 1, TCC1_CH3, 3},
|
||||
{ PORTA, 10, TCC1_CH0, 0, TCC0_CH2, 2},
|
||||
{ PORTA, 11, TCC1_CH1, 1, TCC0_CH3, 3},
|
||||
{ PORTB, 10, TC5_CH0, 0, TCC0_CH4, 4},
|
||||
{ PORTB, 11, TC5_CH1, 1, TCC0_CH5, 5},
|
||||
{ PORTB, 12, TC4_CH0, 0, TCC0_CH6, 6},
|
||||
{ PORTB, 13, TC4_CH1, 1, TCC0_CH7, 7},
|
||||
{ PORTB, 14, TC5_CH0, 0, NOT_ON_TIMER, 0},
|
||||
{ PORTB, 15, TC5_CH1, 1, NOT_ON_TIMER, 0},
|
||||
{ PORTA, 12, TCC2_CH0, 0, TCC0_CH6, 6},
|
||||
{ PORTA, 13, TCC2_CH1, 1, TCC0_CH7, 7},
|
||||
{ PORTA, 14, TC3_CH0, 0, TCC0_CH4, 4},
|
||||
{ PORTA, 15, TC3_CH1, 1, TCC0_CH5, 5},
|
||||
{ PORTA, 16, TCC2_CH0, 0, TCC0_CH6, 6},
|
||||
{ PORTA, 17, TCC2_CH1, 1, TCC0_CH7, 7},
|
||||
{ PORTA, 18, TC3_CH0, 0, TCC0_CH2, 2},
|
||||
{ PORTA, 19, TC3_CH1, 1, TCC0_CH3, 3},
|
||||
{ PORTB, 16, TC6_CH0, 0, TCC0_CH4, 4},
|
||||
{ PORTB, 17, TC6_CH1, 1, TCC0_CH5, 5},
|
||||
{ PORTA, 20, TC7_CH0, 0, TCC0_CH6, 6},
|
||||
{ PORTA, 21, TC7_CH1, 1, TCC0_CH7, 7},
|
||||
{ PORTA, 22, TC4_CH0, 0, TCC0_CH4, 4},
|
||||
{ PORTA, 23, TC4_CH1, 1, TCC0_CH5, 5},
|
||||
{ PORTA, 24, TC5_CH0, 0, TCC1_CH2, 2},
|
||||
{ PORTA, 25, TC5_CH1, 1, TCC1_CH3, 3},
|
||||
{ PORTB, 22, TC7_CH0, 0, TCC3_CH0, 0},
|
||||
{ PORTB, 23, TC7_CH1, 1, TCC3_CH1, 1},
|
||||
{ PORTA, 27, NOT_ON_TIMER, 0, TCC3_CH6, 6},
|
||||
{ PORTA, 28, NOT_ON_TIMER, 0, TCC3_CH7, 7},
|
||||
{ PORTA, 30, TCC1_CH0, 0, TCC3_CH4, 4},
|
||||
{ PORTA, 31, TCC1_CH1, 1, TCC3_CH5, 5},
|
||||
{ PORTB, 30, TCC0_CH0, 0, TCC1_CH2, 2},
|
||||
{ PORTB, 31, TCC0_CH1, 1, TCC1_CH3, 3},
|
||||
{ PORTB, 0, TC7_CH0, 0, NOT_ON_TIMER, 0},
|
||||
{ PORTB, 1, TC7_CH1, 1, NOT_ON_TIMER, 0},
|
||||
{ PORTB, 2, TC6_CH0, 0, TCC3_CH2, 2},
|
||||
{ PORTB, 3, TC6_CH1, 1, TCC3_CH3, 3}
|
||||
};
|
||||
wo_association ASSOCIATION_NOT_FOUND = { NOT_A_PORT, 0, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0};
|
||||
|
||||
|
||||
|
||||
struct wo_association& getWOAssociation(EPortType port, uint32_t pin) {
|
||||
for (int i=0;i<NUM_WO_ASSOCIATIONS;i++) {
|
||||
if (WO_associations[i].port==port && WO_associations[i].pin==pin)
|
||||
return WO_associations[i];
|
||||
}
|
||||
return ASSOCIATION_NOT_FOUND;
|
||||
};
|
||||
|
||||
|
||||
|
||||
EPioType getPeripheralOfPermutation(int permutation, int pin_position) {
|
||||
return ((permutation>>pin_position)&0x01)==0x1?PIO_TIMER_ALT:PIO_TIMER;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void syncTCC(Tcc* TCCx) {
|
||||
while (TCCx->SYNCBUSY.reg & TCC_SYNCBUSY_MASK); // Wait for synchronization of registers between the clock domains
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Configure Clock 4 - we want all simplefoc PWMs to use the same clock. This ensures that
|
||||
* any compatible pin combination can be used without having to worry about configuring different
|
||||
* clocks.
|
||||
*/
|
||||
void configureSAMDClock() {
|
||||
|
||||
// TODO investigate using the FDPLL96M clock to get 96MHz timer clocks... this
|
||||
// would enable 48KHz PWM clocks, and setting the frequency between 24Khz with resolution 2000, to 48KHz with resolution 1000
|
||||
|
||||
if (!SAMDClockConfigured) {
|
||||
SAMDClockConfigured = true; // mark clock as configured
|
||||
for (int i=0;i<TCC_INST_NUM;i++) // mark all the TCCs as not yet configured
|
||||
tccConfigured[i] = false;
|
||||
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) | // Divide the 48MHz clock source by divisor N=1: 48MHz/1=48MHz
|
||||
GCLK_GENDIV_ID(4); // Select Generic Clock (GCLK) 4
|
||||
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
|
||||
|
||||
REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
|
||||
GCLK_GENCTRL_GENEN | // Enable GCLK4
|
||||
GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source
|
||||
// GCLK_GENCTRL_SRC_FDPLL | // Set the 96MHz clock source
|
||||
GCLK_GENCTRL_ID(4); // Select GCLK4
|
||||
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
|
||||
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Configured clock...");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Configure a TCC unit
|
||||
* pwm_frequency is fixed at 24kHz for now. We could go slower, but going
|
||||
* faster won't be possible without sacrificing resolution.
|
||||
*/
|
||||
void configureTCC(tccConfiguration& tccConfig, long pwm_frequency, bool negate, float hw6pwm) {
|
||||
|
||||
long pwm_resolution = (24000000) / pwm_frequency;
|
||||
if (pwm_resolution>SIMPLEFOC_SAMD_MAX_PWM_RESOLUTION)
|
||||
pwm_resolution = SIMPLEFOC_SAMD_MAX_PWM_RESOLUTION;
|
||||
if (pwm_resolution<SIMPLEFOC_SAMD_MIN_PWM_RESOLUTION)
|
||||
pwm_resolution = SIMPLEFOC_SAMD_MIN_PWM_RESOLUTION;
|
||||
// store for later use
|
||||
tccConfig.pwm_res = pwm_resolution;
|
||||
|
||||
// TODO for the moment we ignore the frequency...
|
||||
if (!tccConfigured[tccConfig.tcc.tccn]) {
|
||||
uint32_t GCLK_CLKCTRL_ID_ofthistcc = -1;
|
||||
switch (tccConfig.tcc.tccn>>1) {
|
||||
case 0: GCLK_CLKCTRL_ID_ofthistcc = GCLK_CLKCTRL_ID(GCM_TCC0_TCC1); break; //GCLK_CLKCTRL_ID_TCC0_TCC1;
|
||||
case 1: GCLK_CLKCTRL_ID_ofthistcc = GCLK_CLKCTRL_ID(GCM_TCC2_TC3); break; //GCLK_CLKCTRL_ID_TCC2_TC3;
|
||||
case 2: GCLK_CLKCTRL_ID_ofthistcc = GCLK_CLKCTRL_ID(GCM_TC4_TC5); break; //GCLK_CLKCTRL_ID_TC4_TC5;
|
||||
case 3: GCLK_CLKCTRL_ID_ofthistcc = GCLK_CLKCTRL_ID(GCM_TC6_TC7); break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
// Feed GCLK4 to TCC
|
||||
REG_GCLK_CLKCTRL = (uint16_t) GCLK_CLKCTRL_CLKEN | // Enable GCLK4
|
||||
GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
|
||||
GCLK_CLKCTRL_ID_ofthistcc; // Feed GCLK4 to tcc
|
||||
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
|
||||
|
||||
tccConfigured[tccConfig.tcc.tccn] = true;
|
||||
|
||||
if (tccConfig.tcc.tccn>=TCC_INST_NUM) {
|
||||
Tc* tc = (Tc*)GetTC(tccConfig.tcc.chaninfo);
|
||||
// disable
|
||||
tc->COUNT8.CTRLA.bit.ENABLE = 0;
|
||||
while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
|
||||
// unfortunately we need the 8-bit counter mode to use the PER register...
|
||||
tc->COUNT8.CTRLA.reg |= TC_CTRLA_MODE_COUNT8 | TC_CTRLA_WAVEGEN_NPWM ;
|
||||
while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
|
||||
// meaning prescaler of 8, since TC-Unit has no up/down mode, and has resolution of 250 rather than 1000...
|
||||
tc->COUNT8.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_DIV8_Val ;
|
||||
while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
|
||||
// period is 250, period cannot be higher than 256!
|
||||
tc->COUNT8.PER.reg = SIMPLEFOC_SAMD_PWM_TC_RESOLUTION-1;
|
||||
while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
|
||||
// initial duty cycle is 0
|
||||
tc->COUNT8.CC[tccConfig.tcc.chan].reg = 0;
|
||||
while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
|
||||
// enable
|
||||
tc->COUNT8.CTRLA.bit.ENABLE = 1;
|
||||
while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Initialized TC ", tccConfig.tcc.tccn);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
Tcc* tcc = (Tcc*)GetTC(tccConfig.tcc.chaninfo);
|
||||
|
||||
uint8_t invenMask = ~(1<<tccConfig.tcc.chan); // negate (invert) the signal if needed
|
||||
uint8_t invenVal = negate?(1<<tccConfig.tcc.chan):0;
|
||||
tcc->DRVCTRL.vec.INVEN = (tcc->DRVCTRL.vec.INVEN&invenMask)|invenVal;
|
||||
syncTCC(tcc); // wait for sync
|
||||
|
||||
tcc->WAVE.reg |= TCC_WAVE_POL(0xF)|TCC_WAVEB_WAVEGENB_DSBOTH; // Set wave form configuration
|
||||
while ( tcc->SYNCBUSY.bit.WAVE == 1 ); // wait for sync
|
||||
|
||||
if (hw6pwm>0.0) {
|
||||
tcc->WEXCTRL.vec.DTIEN |= (1<<tccConfig.tcc.chan);
|
||||
tcc->WEXCTRL.bit.DTLS = hw6pwm*(pwm_resolution-1);
|
||||
tcc->WEXCTRL.bit.DTHS = hw6pwm*(pwm_resolution-1);
|
||||
syncTCC(tcc); // wait for sync
|
||||
}
|
||||
|
||||
tcc->PER.reg = pwm_resolution - 1; // Set counter Top using the PER register
|
||||
while ( tcc->SYNCBUSY.bit.PER == 1 ); // wait for sync
|
||||
|
||||
// set all channels to 0%
|
||||
uint8_t chanCount = (tccConfig.tcc.tccn==1||tccConfig.tcc.tccn==2)?2:4;
|
||||
for (int i=0;i<chanCount;i++) {
|
||||
tcc->CC[i].reg = 0; // start off at 0% duty cycle
|
||||
uint32_t chanbit = 0x1<<(TCC_SYNCBUSY_CC0_Pos+i);
|
||||
while ( (tcc->SYNCBUSY.reg & chanbit) > 0 );
|
||||
}
|
||||
|
||||
// Enable TC
|
||||
tcc->CTRLA.reg |= TCC_CTRLA_ENABLE | TCC_CTRLA_PRESCALER_DIV1; //48Mhz/1=48Mhz/2(up/down)=24MHz/1024=24KHz
|
||||
while ( tcc->SYNCBUSY.bit.ENABLE == 1 ); // wait for sync
|
||||
|
||||
#if defined(SIMPLEFOC_SAMD_DEBUG) && !defined(SIMPLEFOC_DISABLE_DEBUG)
|
||||
SimpleFOCDebug::print("SAMD: Initialized TCC ");
|
||||
SimpleFOCDebug::print(tccConfig.tcc.tccn);
|
||||
SimpleFOCDebug::print("-");
|
||||
SimpleFOCDebug::print(tccConfig.tcc.chan);
|
||||
SimpleFOCDebug::print("[");
|
||||
SimpleFOCDebug::print(tccConfig.wo);
|
||||
SimpleFOCDebug::print("] pwm res ");
|
||||
SimpleFOCDebug::print((int)pwm_resolution);
|
||||
SimpleFOCDebug::println();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (tccConfig.tcc.tccn<TCC_INST_NUM) {
|
||||
// set invert bit for TCC channels in SW 6-PWM...
|
||||
Tcc* tcc = (Tcc*)GetTC(tccConfig.tcc.chaninfo);
|
||||
|
||||
tcc->CTRLA.bit.ENABLE = 0;
|
||||
while ( tcc->SYNCBUSY.bit.ENABLE == 1 );
|
||||
|
||||
uint8_t invenMask = ~(1<<tccConfig.tcc.chan); // negate (invert) the signal if needed
|
||||
uint8_t invenVal = negate?(1<<tccConfig.tcc.chan):0;
|
||||
tcc->DRVCTRL.vec.INVEN = (tcc->DRVCTRL.vec.INVEN&invenMask)|invenVal;
|
||||
syncTCC(tcc); // wait for sync
|
||||
|
||||
if (hw6pwm>0.0) {
|
||||
tcc->WEXCTRL.vec.DTIEN |= (1<<tccConfig.tcc.chan);
|
||||
tcc->WEXCTRL.bit.DTLS = hw6pwm*(pwm_resolution-1);
|
||||
tcc->WEXCTRL.bit.DTHS = hw6pwm*(pwm_resolution-1);
|
||||
syncTCC(tcc); // wait for sync
|
||||
}
|
||||
|
||||
tcc->CTRLA.bit.ENABLE = 1;
|
||||
while ( tcc->SYNCBUSY.bit.ENABLE == 1 );
|
||||
|
||||
#if defined(SIMPLEFOC_SAMD_DEBUG) && !defined(SIMPLEFOC_DISABLE_DEBUG)
|
||||
SimpleFOCDebug::print("SAMD: (Re-)Initialized TCC ");
|
||||
SimpleFOCDebug::print(tccConfig.tcc.tccn);
|
||||
SimpleFOCDebug::print("-");
|
||||
SimpleFOCDebug::print(tccConfig.tcc.chan);
|
||||
SimpleFOCDebug::print("[");
|
||||
SimpleFOCDebug::print(tccConfig.wo);
|
||||
SimpleFOCDebug::print("] pwm res ");
|
||||
SimpleFOCDebug::print((int)pwm_resolution);
|
||||
SimpleFOCDebug::println();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void writeSAMDDutyCycle(tccConfiguration* info, float dc) {
|
||||
uint8_t tccn = GetTCNumber(info->tcc.chaninfo);
|
||||
uint8_t chan = GetTCChannelNumber(info->tcc.chaninfo);
|
||||
if (tccn<TCC_INST_NUM) {
|
||||
Tcc* tcc = (Tcc*)GetTC(info->tcc.chaninfo);
|
||||
// set via CC
|
||||
// tcc->CC[chan].reg = (uint32_t)((SIMPLEFOC_SAMD_PWM_RESOLUTION-1) * dc);
|
||||
// uint32_t chanbit = 0x1<<(TCC_SYNCBUSY_CC0_Pos+chan);
|
||||
// while ( (tcc->SYNCBUSY.reg & chanbit) > 0 );
|
||||
// set via CCB
|
||||
//while ( (tcc->SYNCBUSY.vec.CC & (0x1<<chan)) > 0 );
|
||||
tcc->CCB[chan].reg = (uint32_t)((info->pwm_res-1) * dc);
|
||||
// while ( (tcc->SYNCBUSY.vec.CCB & (0x1<<chan)) > 0 );
|
||||
// tcc->STATUS.vec.CCBV |= (0x1<<chan);
|
||||
// while ( tcc->SYNCBUSY.bit.STATUS > 0 );
|
||||
// tcc->CTRLBSET.reg |= TCC_CTRLBSET_CMD(TCC_CTRLBSET_CMD_UPDATE_Val);
|
||||
// while ( tcc->SYNCBUSY.bit.CTRLB > 0 );
|
||||
}
|
||||
else {
|
||||
Tc* tc = (Tc*)GetTC(info->tcc.chaninfo);
|
||||
tc->COUNT8.CC[chan].reg = (uint8_t)((SIMPLEFOC_SAMD_PWM_TC_RESOLUTION-1) * dc);
|
||||
while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,351 @@
|
||||
|
||||
|
||||
#include "./samd_mcu.h"
|
||||
|
||||
|
||||
#if defined(_SAMD51_)||defined(_SAME51_)
|
||||
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for SAMD51/SAME51")
|
||||
#pragma message("")
|
||||
|
||||
|
||||
|
||||
// expected frequency on DPLL, since we don't configure it ourselves. Typically this is the CPU frequency.
|
||||
// for custom boards or overclockers you can override it using this define.
|
||||
#ifndef SIMPLEFOC_SAMD51_DPLL_FREQ
|
||||
#define SIMPLEFOC_SAMD51_DPLL_FREQ 120000000
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef TCC3_CH0
|
||||
#define TCC3_CH0 NOT_ON_TIMER
|
||||
#define TCC3_CH1 NOT_ON_TIMER
|
||||
#endif
|
||||
|
||||
#ifndef TCC4_CH0
|
||||
#define TCC4_CH0 NOT_ON_TIMER
|
||||
#define TCC4_CH1 NOT_ON_TIMER
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef TC4_CH0
|
||||
#define TC4_CH0 NOT_ON_TIMER
|
||||
#define TC4_CH1 NOT_ON_TIMER
|
||||
#endif
|
||||
|
||||
#ifndef TC5_CH0
|
||||
#define TC5_CH0 NOT_ON_TIMER
|
||||
#define TC5_CH1 NOT_ON_TIMER
|
||||
#endif
|
||||
|
||||
#ifndef TC6_CH0
|
||||
#define TC6_CH0 NOT_ON_TIMER
|
||||
#define TC6_CH1 NOT_ON_TIMER
|
||||
#endif
|
||||
|
||||
#ifndef TC7_CH0
|
||||
#define TC7_CH0 NOT_ON_TIMER
|
||||
#define TC7_CH1 NOT_ON_TIMER
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// TCC# Channels WO_NUM Counter size Fault Dithering Output matrix DTI SWAP Pattern generation
|
||||
// 0 6 8 24-bit Yes Yes Yes Yes Yes Yes
|
||||
// 1 4 8 24-bit Yes Yes Yes Yes Yes Yes
|
||||
// 2 3 3 16-bit Yes - Yes - - -
|
||||
// 3 2 2 16-bit Yes - - - - -
|
||||
// 4 2 2 16-bit Yes - - - - -
|
||||
|
||||
|
||||
#define NUM_WO_ASSOCIATIONS 72
|
||||
|
||||
struct wo_association WO_associations[] = {
|
||||
|
||||
{ PORTB, 9, TC4_CH1, 1, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0},
|
||||
{ PORTA, 4, TC0_CH0, 0, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0},
|
||||
{ PORTA, 5, TC0_CH1, 1, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0},
|
||||
{ PORTA, 6, TC1_CH0, 0, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0},
|
||||
{ PORTA, 7, TC1_CH1, 1, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0},
|
||||
{ PORTC, 4, NOT_ON_TIMER, 0, TCC0_CH0, 0, NOT_ON_TIMER, 0},
|
||||
// PC05, PC06, PC07 -> no timers
|
||||
{ PORTA, 8, TC0_CH0, 0, TCC0_CH0, 0, TCC1_CH0, 4},
|
||||
{ PORTA, 9, TC0_CH1, 1, TCC0_CH1, 1, TCC1_CH1, 5},
|
||||
{ PORTA, 10, TC1_CH0, 0, TCC0_CH2, 2, TCC1_CH2, 6},
|
||||
{ PORTA, 11, TC1_CH1, 1, TCC0_CH3, 3, TCC1_CH3, 7},
|
||||
{ PORTB, 10, TC5_CH0, 0, TCC0_CH4, 4, TCC1_CH0, 0},
|
||||
{ PORTB, 11, TC5_CH1, 1, TCC0_CH5, 5, TCC1_CH1, 1},
|
||||
{ PORTB, 12, TC4_CH0, 0, TCC3_CH0, 0, TCC0_CH0, 0},
|
||||
{ PORTB, 13, TC4_CH1, 1, TCC3_CH1, 1, TCC0_CH1, 1},
|
||||
{ PORTB, 14, TC5_CH0, 0, TCC4_CH0, 0, TCC0_CH2, 2},
|
||||
{ PORTB, 15, TC5_CH1, 1, TCC4_CH1, 1, TCC0_CH3, 3},
|
||||
{ PORTD, 8, NOT_ON_TIMER, 0, TCC0_CH1, 1, NOT_ON_TIMER, 0},
|
||||
{ PORTD, 9, NOT_ON_TIMER, 0, TCC0_CH2, 2, NOT_ON_TIMER, 0},
|
||||
{ PORTD, 10, NOT_ON_TIMER, 0, TCC0_CH3, 3, NOT_ON_TIMER, 0},
|
||||
{ PORTD, 11, NOT_ON_TIMER, 0, TCC0_CH4, 4, NOT_ON_TIMER, 0},
|
||||
{ PORTD, 12, NOT_ON_TIMER, 0, TCC0_CH5, 5, NOT_ON_TIMER, 0},
|
||||
{ PORTC, 10, NOT_ON_TIMER, 0, TCC0_CH0, 0, TCC1_CH0, 4},
|
||||
{ PORTC, 11, NOT_ON_TIMER, 0, TCC0_CH1, 1, TCC1_CH1, 5},
|
||||
{ PORTC, 12, NOT_ON_TIMER, 0, TCC0_CH2, 2, TCC1_CH2, 6},
|
||||
{ PORTC, 13, NOT_ON_TIMER, 0, TCC0_CH3, 3, TCC1_CH3, 7},
|
||||
{ PORTC, 14, NOT_ON_TIMER, 0, TCC0_CH4, 4, TCC1_CH0, 0},
|
||||
{ PORTC, 15, NOT_ON_TIMER, 0, TCC0_CH5, 5, TCC1_CH1, 1},
|
||||
{ PORTA, 12, TC2_CH0, 0, TCC0_CH0, 6, TCC1_CH2, 2},
|
||||
{ PORTA, 13, TC2_CH1, 1, TCC0_CH1, 7, TCC1_CH3, 3},
|
||||
{ PORTA, 14, TC3_CH0, 0, TCC2_CH0, 0, TCC1_CH2, 2},
|
||||
{ PORTA, 15, TC3_CH1, 1, TCC2_CH1, 1, TCC1_CH3, 3},
|
||||
{ PORTA, 16, TC2_CH0, 0, TCC1_CH0, 0, TCC0_CH4, 4},
|
||||
{ PORTA, 17, TC2_CH1, 1, TCC1_CH1, 1, TCC0_CH5, 5},
|
||||
{ PORTA, 18, TC3_CH0, 0, TCC1_CH2, 2, TCC0_CH0, 6},
|
||||
{ PORTA, 19, TC3_CH1, 1, TCC1_CH3, 3, TCC0_CH1, 7},
|
||||
{ PORTC, 16, NOT_ON_TIMER, 0, TCC0_CH0, 0, NOT_ON_TIMER, 0}, // PDEC0
|
||||
{ PORTC, 17, NOT_ON_TIMER, 0, TCC0_CH1, 1, NOT_ON_TIMER, 0}, // PDEC1
|
||||
{ PORTC, 18, NOT_ON_TIMER, 0, TCC0_CH2, 2, NOT_ON_TIMER, 0}, // PDEC2
|
||||
{ PORTC, 19, NOT_ON_TIMER, 0, TCC0_CH3, 3, NOT_ON_TIMER, 0},
|
||||
{ PORTC, 20, NOT_ON_TIMER, 0, TCC0_CH4, 4, NOT_ON_TIMER, 0},
|
||||
{ PORTC, 21, NOT_ON_TIMER, 0, TCC0_CH5, 5, NOT_ON_TIMER, 0},
|
||||
{ PORTC, 22, NOT_ON_TIMER, 0, TCC0_CH0, 6, NOT_ON_TIMER, 0},
|
||||
{ PORTC, 23, NOT_ON_TIMER, 0, TCC0_CH1, 7, NOT_ON_TIMER, 0},
|
||||
{ PORTD, 20, NOT_ON_TIMER, 0, TCC1_CH0, 0, NOT_ON_TIMER, 0},
|
||||
{ PORTD, 21, NOT_ON_TIMER, 0, TCC1_CH1, 1, NOT_ON_TIMER, 0},
|
||||
{ PORTB, 16, TC6_CH0, 0, TCC3_CH0, 0, TCC0_CH4, 4},
|
||||
{ PORTB, 17, TC6_CH1, 1, TCC3_CH1, 1, TCC0_CH5, 5},
|
||||
{ PORTB, 18, NOT_ON_TIMER, 0, TCC1_CH0, 0, NOT_ON_TIMER, 0}, // PDEC0
|
||||
{ PORTB, 19, NOT_ON_TIMER, 0, TCC1_CH1, 1, NOT_ON_TIMER, 0}, // PDEC1
|
||||
{ PORTB, 20, NOT_ON_TIMER, 0, TCC1_CH2, 2, NOT_ON_TIMER, 0}, // PDEC2
|
||||
{ PORTB, 21, NOT_ON_TIMER, 0, TCC1_CH3, 3, NOT_ON_TIMER, 0},
|
||||
{ PORTA, 20, TC7_CH0, 0, TCC1_CH0, 4, TCC0_CH0, 0},
|
||||
{ PORTA, 21, TC7_CH1, 1, TCC1_CH1, 5, TCC0_CH1, 1},
|
||||
{ PORTA, 22, TC4_CH0, 0, TCC1_CH2, 6, TCC0_CH2, 2},
|
||||
{ PORTA, 23, TC4_CH1, 1, TCC1_CH3, 7, TCC0_CH3, 3},
|
||||
{ PORTA, 24, TC5_CH0, 0, TCC2_CH2, 2, NOT_ON_TIMER, 0}, // PDEC0
|
||||
{ PORTA, 25, TC5_CH1, 1, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0}, // PDEC1
|
||||
{ PORTB, 22, TC7_CH0, 0, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0}, // PDEC2
|
||||
{ PORTB, 23, TC7_CH1, 1, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0}, // PDEC0
|
||||
{ PORTB, 24, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0}, // PDEC1
|
||||
{ PORTB, 25, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0}, // PDEC2
|
||||
{ PORTB, 26, NOT_ON_TIMER, 0, TCC1_CH2, 2, NOT_ON_TIMER, 0},
|
||||
{ PORTB, 27, NOT_ON_TIMER, 0, TCC1_CH3, 3, NOT_ON_TIMER, 0},
|
||||
{ PORTB, 28, NOT_ON_TIMER, 0, TCC1_CH0, 4, NOT_ON_TIMER, 0},
|
||||
{ PORTB, 29, NOT_ON_TIMER, 0, TCC1_CH1, 5, NOT_ON_TIMER, 0},
|
||||
// PC24-PC28, PA27, RESET -> no TC/TCC peripherals
|
||||
{ PORTA, 30, TC6_CH0, 0, TCC2_CH0, 0, NOT_ON_TIMER, 0},
|
||||
{ PORTA, 31, TC6_CH1, 1, TCC2_CH1, 1, NOT_ON_TIMER, 0},
|
||||
{ PORTB, 30, TC0_CH0, 0, TCC4_CH0, 0, TCC0_CH0, 6},
|
||||
{ PORTB, 31, TC0_CH1, 1, TCC4_CH1, 1, TCC0_CH1, 7},
|
||||
// PC30, PC31 -> no TC/TCC peripherals
|
||||
{ PORTB, 0, TC7_CH0, 0, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0},
|
||||
{ PORTB, 1, TC7_CH1, 1, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0},
|
||||
{ PORTB, 2, TC6_CH0, 0, TCC2_CH2, 2, NOT_ON_TIMER, 0},
|
||||
|
||||
};
|
||||
|
||||
wo_association ASSOCIATION_NOT_FOUND = { NOT_A_PORT, 0, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0};
|
||||
|
||||
#ifndef TCC3_CC_NUM
|
||||
uint8_t TCC_CHANNEL_COUNT[] = { TCC0_CC_NUM, TCC1_CC_NUM, TCC2_CC_NUM };
|
||||
#else
|
||||
uint8_t TCC_CHANNEL_COUNT[] = { TCC0_CC_NUM, TCC1_CC_NUM, TCC2_CC_NUM, TCC3_CC_NUM, TCC4_CC_NUM };
|
||||
#endif
|
||||
|
||||
|
||||
struct wo_association& getWOAssociation(EPortType port, uint32_t pin) {
|
||||
for (int i=0;i<NUM_WO_ASSOCIATIONS;i++) {
|
||||
if (WO_associations[i].port==port && WO_associations[i].pin==pin)
|
||||
return WO_associations[i];
|
||||
}
|
||||
return ASSOCIATION_NOT_FOUND;
|
||||
};
|
||||
|
||||
|
||||
EPioType getPeripheralOfPermutation(int permutation, int pin_position) {
|
||||
return ((permutation>>pin_position)&0x01)==0x1?PIO_TCC_PDEC:PIO_TIMER_ALT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void syncTCC(Tcc* TCCx) {
|
||||
while (TCCx->SYNCBUSY.reg & TCC_SYNCBUSY_MASK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void writeSAMDDutyCycle(tccConfiguration* info, float dc) {
|
||||
uint8_t tccn = GetTCNumber(info->tcc.chaninfo);
|
||||
uint8_t chan = GetTCChannelNumber(info->tcc.chaninfo);
|
||||
if (tccn<TCC_INST_NUM) {
|
||||
Tcc* tcc = (Tcc*)GetTC(info->tcc.chaninfo);
|
||||
// set via CCBUF
|
||||
// while ( (tcc->SYNCBUSY.vec.CC & (0x1<<chan)) > 0 );
|
||||
tcc->CCBUF[chan].reg = (uint32_t)((info->pwm_res-1) * dc); // TODO pwm frequency!
|
||||
}
|
||||
else {
|
||||
// we don't support the TC channels on SAMD51, isn't worth it.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define DPLL_CLOCK_NUM 2 // use GCLK2
|
||||
#define PWM_CLOCK_NUM 3 // use GCLK3
|
||||
|
||||
|
||||
/**
|
||||
* Configure Clock 4 - we want all simplefoc PWMs to use the same clock. This ensures that
|
||||
* any compatible pin combination can be used without having to worry about configuring different
|
||||
* clocks.
|
||||
*/
|
||||
void configureSAMDClock() {
|
||||
if (!SAMDClockConfigured) {
|
||||
SAMDClockConfigured = true; // mark clock as configured
|
||||
for (int i=0;i<TCC_INST_NUM;i++) // mark all the TCCs as not yet configured
|
||||
tccConfigured[i] = false;
|
||||
|
||||
// GCLK->GENCTRL[DPLL_CLOCK_NUM].reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_DIV(1)
|
||||
// | GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_DFLL_Val);
|
||||
// while (GCLK->SYNCBUSY.vec.GENCTRL&(0x1<<DPLL_CLOCK_NUM));
|
||||
|
||||
// GCLK->PCHCTRL[1].reg = GCLK_PCHCTRL_GEN(DPLL_CLOCK_NUM)|GCLK_PCHCTRL_CHEN;
|
||||
// while (GCLK->SYNCBUSY.vec.GENCTRL&(0x1<<DPLL_CLOCK_NUM));
|
||||
|
||||
// OSCCTRL->Dpll[0].DPLLCTRLA.bit.ENABLE = 0;
|
||||
// while (OSCCTRL->Dpll[0].DPLLSYNCBUSY.reg!=0x0);
|
||||
|
||||
// OSCCTRL->Dpll[0].DPLLCTRLB.bit.REFCLK = OSCCTRL_DPLLCTRLB_REFCLK_GCLK_Val;
|
||||
// while (OSCCTRL->Dpll[0].DPLLSYNCBUSY.reg!=0x0);
|
||||
// OSCCTRL->Dpll[0].DPLLRATIO.reg = 3;
|
||||
// while (OSCCTRL->Dpll[0].DPLLSYNCBUSY.reg!=0x0);
|
||||
|
||||
// OSCCTRL->Dpll[0].DPLLCTRLA.bit.ENABLE = 1;
|
||||
// while (OSCCTRL->Dpll[0].DPLLSYNCBUSY.reg!=0x0);
|
||||
|
||||
GCLK->GENCTRL[PWM_CLOCK_NUM].bit.GENEN = 0;
|
||||
while (GCLK->SYNCBUSY.vec.GENCTRL&(0x1<<PWM_CLOCK_NUM));
|
||||
|
||||
GCLK->GENCTRL[PWM_CLOCK_NUM].reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_DIV(1) | GCLK_GENCTRL_IDC
|
||||
//| GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_DFLL_Val);
|
||||
| GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_DPLL0_Val);
|
||||
while (GCLK->SYNCBUSY.vec.GENCTRL&(0x1<<PWM_CLOCK_NUM));
|
||||
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Configured clock...");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Configure a TCC unit
|
||||
* pwm_frequency is fixed at 24kHz for now. We could go slower, but going
|
||||
* faster won't be possible without sacrificing resolution.
|
||||
*/
|
||||
void configureTCC(tccConfiguration& tccConfig, long pwm_frequency, bool negate, float hw6pwm) {
|
||||
// TODO for the moment we ignore the frequency...
|
||||
if (!tccConfigured[tccConfig.tcc.tccn]) {
|
||||
uint32_t GCLK_CLKCTRL_ID_ofthistcc = GCLK_CLKCTRL_IDs[tccConfig.tcc.tccn];
|
||||
GCLK->PCHCTRL[GCLK_CLKCTRL_ID_ofthistcc].reg = GCLK_PCHCTRL_GEN(PWM_CLOCK_NUM)|GCLK_PCHCTRL_CHEN;
|
||||
while (GCLK->SYNCBUSY.vec.GENCTRL&(0x1<<PWM_CLOCK_NUM));
|
||||
}
|
||||
|
||||
if (tccConfig.tcc.tccn<TCC_INST_NUM) {
|
||||
Tcc* tcc = (Tcc*)GetTC(tccConfig.tcc.chaninfo);
|
||||
|
||||
tcc->CTRLA.bit.ENABLE = 0; //switch off tcc
|
||||
while ( tcc->SYNCBUSY.bit.ENABLE == 1 ); // wait for sync
|
||||
|
||||
uint8_t invenMask = ~(1<<tccConfig.tcc.chan); // negate (invert) the signal if needed
|
||||
uint8_t invenVal = negate?(1<<tccConfig.tcc.chan):0;
|
||||
tcc->DRVCTRL.vec.INVEN = (tcc->DRVCTRL.vec.INVEN&invenMask)|invenVal;
|
||||
syncTCC(tcc); // wait for sync
|
||||
|
||||
// work out pwm resolution for desired frequency and constrain to max/min values
|
||||
long pwm_resolution = (SIMPLEFOC_SAMD51_DPLL_FREQ/2) / pwm_frequency;
|
||||
if (pwm_resolution>SIMPLEFOC_SAMD_MAX_PWM_RESOLUTION)
|
||||
pwm_resolution = SIMPLEFOC_SAMD_MAX_PWM_RESOLUTION;
|
||||
if (pwm_resolution<SIMPLEFOC_SAMD_MIN_PWM_RESOLUTION)
|
||||
pwm_resolution = SIMPLEFOC_SAMD_MIN_PWM_RESOLUTION;
|
||||
// store for later use
|
||||
tccConfig.pwm_res = pwm_resolution;
|
||||
|
||||
if (hw6pwm>0.0) {
|
||||
tcc->WEXCTRL.vec.DTIEN |= (1<<tccConfig.tcc.chan);
|
||||
tcc->WEXCTRL.bit.DTLS = hw6pwm*(pwm_resolution-1);
|
||||
tcc->WEXCTRL.bit.DTHS = hw6pwm*(pwm_resolution-1);
|
||||
syncTCC(tcc); // wait for sync
|
||||
}
|
||||
|
||||
if (!tccConfigured[tccConfig.tcc.tccn]) {
|
||||
tcc->WAVE.reg |= TCC_WAVE_POL(0xF)|TCC_WAVE_WAVEGEN_DSTOP; // Set wave form configuration - TODO check this... why set like this?
|
||||
while ( tcc->SYNCBUSY.bit.WAVE == 1 ); // wait for sync
|
||||
|
||||
tcc->PER.reg = pwm_resolution - 1; // Set counter Top using the PER register
|
||||
while ( tcc->SYNCBUSY.bit.PER == 1 ); // wait for sync
|
||||
|
||||
// set all channels to 0%
|
||||
for (int i=0;i<TCC_CHANNEL_COUNT[tccConfig.tcc.tccn];i++) {
|
||||
tcc->CC[i].reg = 0; // start off at 0% duty cycle
|
||||
uint32_t chanbit = 0x1<<(TCC_SYNCBUSY_CC0_Pos+i);
|
||||
while ( (tcc->SYNCBUSY.reg & chanbit) > 0 );
|
||||
}
|
||||
}
|
||||
|
||||
// Enable TCC
|
||||
tcc->CTRLA.reg |= TCC_CTRLA_ENABLE | TCC_CTRLA_PRESCALER_DIV1; //48Mhz/1=48Mhz/2(up/down)=24MHz/1024=24KHz
|
||||
while ( tcc->SYNCBUSY.bit.ENABLE == 1 ); // wait for sync
|
||||
|
||||
#if defined(SIMPLEFOC_SAMD_DEBUG) && !defined(SIMPLEFOC_DISABLE_DEBUG)
|
||||
SimpleFOCDebug::print("SAMD: (Re-)Initialized TCC ");
|
||||
SimpleFOCDebug::print(tccConfig.tcc.tccn);
|
||||
SimpleFOCDebug::print("-");
|
||||
SimpleFOCDebug::print(tccConfig.tcc.chan);
|
||||
SimpleFOCDebug::print("[");
|
||||
SimpleFOCDebug::print(tccConfig.wo);
|
||||
SimpleFOCDebug::print("] pwm res ");
|
||||
SimpleFOCDebug::print((int)pwm_resolution);
|
||||
SimpleFOCDebug::println();
|
||||
#endif
|
||||
}
|
||||
else if (tccConfig.tcc.tccn>=TCC_INST_NUM) {
|
||||
//Tc* tc = (Tc*)GetTC(tccConfig.tcc.chaninfo);
|
||||
|
||||
// disable
|
||||
// tc->COUNT8.CTRLA.bit.ENABLE = 0;
|
||||
// while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
|
||||
// // unfortunately we need the 8-bit counter mode to use the PER register...
|
||||
// tc->COUNT8.CTRLA.reg |= TC_CTRLA_MODE_COUNT8 | TC_CTRLA_WAVEGEN_NPWM ;
|
||||
// while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
|
||||
// // meaning prescaler of 8, since TC-Unit has no up/down mode, and has resolution of 250 rather than 1000...
|
||||
// tc->COUNT8.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_DIV8_Val ;
|
||||
// while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
|
||||
// // period is 250, period cannot be higher than 256!
|
||||
// tc->COUNT8.PER.reg = SIMPLEFOC_SAMD_PWM_TC_RESOLUTION-1;
|
||||
// while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
|
||||
// // initial duty cycle is 0
|
||||
// tc->COUNT8.CC[tccConfig.tcc.chan].reg = 0;
|
||||
// while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
|
||||
// // enable
|
||||
// tc->COUNT8.CTRLA.bit.ENABLE = 1;
|
||||
// while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
|
||||
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Not initialized: TC ", tccConfig.tcc.tccn);
|
||||
SIMPLEFOC_DEBUG("SAMD: TC units not supported on SAMD51");
|
||||
#endif
|
||||
}
|
||||
|
||||
// set as configured
|
||||
tccConfigured[tccConfig.tcc.tccn] = true;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,914 @@
|
||||
|
||||
|
||||
|
||||
#include "./samd_mcu.h"
|
||||
|
||||
#if defined(_SAMD21_)||defined(_SAMD51_)||defined(_SAME51_)
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Global state
|
||||
*/
|
||||
tccConfiguration tccPinConfigurations[SIMPLEFOC_SAMD_MAX_TCC_PINCONFIGURATIONS];
|
||||
uint8_t numTccPinConfigurations = 0;
|
||||
bool SAMDClockConfigured = false;
|
||||
bool tccConfigured[TCC_INST_NUM+TC_INST_NUM];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Attach the TCC to the pin
|
||||
*/
|
||||
bool attachTCC(tccConfiguration& tccConfig) {
|
||||
if (numTccPinConfigurations>=SIMPLEFOC_SAMD_MAX_TCC_PINCONFIGURATIONS)
|
||||
return false;
|
||||
pinMode(tccConfig.pin, OUTPUT);
|
||||
|
||||
pinPeripheral(tccConfig.pin, tccConfig.peripheral);
|
||||
tccPinConfigurations[numTccPinConfigurations++] = tccConfig;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int getPermutationNumber(int pins) {
|
||||
int num = 1;
|
||||
for (int i=0;i<pins;i++)
|
||||
num *= NUM_PIO_TIMER_PERIPHERALS;
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check if the configuration is in use already.
|
||||
*/
|
||||
bool inUse(tccConfiguration& tccConfig) {
|
||||
for (int i=0;i<numTccPinConfigurations;i++) {
|
||||
if (tccPinConfigurations[i].tcc.chaninfo==tccConfig.tcc.chaninfo)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
tccConfiguration* getTccPinConfiguration(uint8_t pin) {
|
||||
for (int i=0;i<numTccPinConfigurations;i++)
|
||||
if (tccPinConfigurations[i].pin==pin)
|
||||
return &tccPinConfigurations[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the TCC channel associated with that pin
|
||||
*/
|
||||
tccConfiguration getTCCChannelNr(int pin, EPioType peripheral) {
|
||||
tccConfiguration result;
|
||||
result.pin = pin;
|
||||
result.peripheral = peripheral;
|
||||
result.tcc.tccn = -2;
|
||||
result.tcc.chan = -2;
|
||||
const PinDescription& pinDesc = g_APinDescription[pin];
|
||||
struct wo_association& association = getWOAssociation(pinDesc.ulPort, pinDesc.ulPin);
|
||||
if (association.port==NOT_A_PORT)
|
||||
return result; // could not find the port/pin
|
||||
if (peripheral==PIO_TIMER) {
|
||||
result.tcc.chaninfo = association.tccE;
|
||||
result.wo = association.woE;
|
||||
}
|
||||
else if (peripheral==PIO_TIMER_ALT) {
|
||||
result.tcc.chaninfo = association.tccF;
|
||||
result.wo = association.woF;
|
||||
}
|
||||
#if defined(_SAMD51_)||defined(_SAME51_)
|
||||
else if (peripheral==PIO_TCC_PDEC) {
|
||||
result.tcc.chaninfo = association.tccG;
|
||||
result.wo = association.woG;
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool checkPeripheralPermutationSameTCC6(tccConfiguration& pinAh, tccConfiguration& pinAl, tccConfiguration& pinBh, tccConfiguration& pinBl, tccConfiguration& pinCh, tccConfiguration& pinCl) {
|
||||
if (inUse(pinAh) || inUse(pinAl) || inUse(pinBh) || inUse(pinBl) || inUse(pinCh) || inUse(pinCl))
|
||||
return false;
|
||||
|
||||
if (pinAh.tcc.tccn<0 || pinAh.tcc.tccn!=pinAl.tcc.tccn || pinAh.tcc.tccn!=pinBh.tcc.tccn || pinAh.tcc.tccn!=pinBl.tcc.tccn
|
||||
|| pinAh.tcc.tccn!=pinCh.tcc.tccn || pinAh.tcc.tccn!=pinCl.tcc.tccn || pinAh.tcc.tccn>=TCC_INST_NUM)
|
||||
return false;
|
||||
|
||||
if (pinAh.tcc.chan==pinBh.tcc.chan || pinAh.tcc.chan==pinBl.tcc.chan || pinAh.tcc.chan==pinCh.tcc.chan || pinAh.tcc.chan==pinCl.tcc.chan)
|
||||
return false;
|
||||
if (pinBh.tcc.chan==pinCh.tcc.chan || pinBh.tcc.chan==pinCl.tcc.chan)
|
||||
return false;
|
||||
if (pinAl.tcc.chan==pinBh.tcc.chan || pinAl.tcc.chan==pinBl.tcc.chan || pinAl.tcc.chan==pinCh.tcc.chan || pinAl.tcc.chan==pinCl.tcc.chan)
|
||||
return false;
|
||||
if (pinBl.tcc.chan==pinCh.tcc.chan || pinBl.tcc.chan==pinCl.tcc.chan)
|
||||
return false;
|
||||
|
||||
if (pinAh.tcc.chan!=pinAl.tcc.chan || pinBh.tcc.chan!=pinBl.tcc.chan || pinCh.tcc.chan!=pinCl.tcc.chan)
|
||||
return false;
|
||||
if (pinAh.wo==pinAl.wo || pinBh.wo==pinBl.wo || pinCh.wo!=pinCl.wo)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool checkPeripheralPermutationCompatible(tccConfiguration pins[], uint8_t num) {
|
||||
for (int i=0;i<num;i++)
|
||||
if (pins[i].tcc.tccn<0)
|
||||
return false;
|
||||
for (int i=0;i<num-1;i++)
|
||||
for (int j=i+1;j<num;j++)
|
||||
if (pins[i].tcc.chaninfo==pins[j].tcc.chaninfo)
|
||||
return false;
|
||||
for (int i=0;i<num;i++)
|
||||
if (inUse(pins[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool checkPeripheralPermutationCompatible6(tccConfiguration& pinAh, tccConfiguration& pinAl, tccConfiguration& pinBh, tccConfiguration& pinBl, tccConfiguration& pinCh, tccConfiguration& pinCl) {
|
||||
// check we're valid PWM pins
|
||||
if (pinAh.tcc.tccn<0 || pinAl.tcc.tccn<0 || pinBh.tcc.tccn<0 || pinBl.tcc.tccn<0 || pinCh.tcc.tccn<0 || pinCl.tcc.tccn<0)
|
||||
return false;
|
||||
// only TCC units for 6-PWM
|
||||
if (pinAh.tcc.tccn>=TCC_INST_NUM || pinAl.tcc.tccn>=TCC_INST_NUM || pinBh.tcc.tccn>=TCC_INST_NUM
|
||||
|| pinBl.tcc.tccn>=TCC_INST_NUM || pinCh.tcc.tccn>=TCC_INST_NUM || pinCl.tcc.tccn>=TCC_INST_NUM)
|
||||
return false;
|
||||
|
||||
// check we're not in use
|
||||
if (inUse(pinAh) || inUse(pinAl) || inUse(pinBh) || inUse(pinBl) || inUse(pinCh) || inUse(pinCl))
|
||||
return false;
|
||||
|
||||
// check pins are all different tccs/channels
|
||||
if (pinAh.tcc.chaninfo==pinBh.tcc.chaninfo || pinAh.tcc.chaninfo==pinBl.tcc.chaninfo || pinAh.tcc.chaninfo==pinCh.tcc.chaninfo || pinAh.tcc.chaninfo==pinCl.tcc.chaninfo)
|
||||
return false;
|
||||
if (pinAl.tcc.chaninfo==pinBh.tcc.chaninfo || pinAl.tcc.chaninfo==pinBl.tcc.chaninfo || pinAl.tcc.chaninfo==pinCh.tcc.chaninfo || pinAl.tcc.chaninfo==pinCl.tcc.chaninfo)
|
||||
return false;
|
||||
if (pinBh.tcc.chaninfo==pinCh.tcc.chaninfo || pinBh.tcc.chaninfo==pinCl.tcc.chaninfo)
|
||||
return false;
|
||||
if (pinBl.tcc.chaninfo==pinCh.tcc.chaninfo || pinBl.tcc.chaninfo==pinCl.tcc.chaninfo)
|
||||
return false;
|
||||
|
||||
// check H/L pins are on same timer
|
||||
if (pinAh.tcc.tccn!=pinAl.tcc.tccn || pinBh.tcc.tccn!=pinBl.tcc.tccn || pinCh.tcc.tccn!=pinCl.tcc.tccn)
|
||||
return false;
|
||||
|
||||
// check H/L pins aren't on both the same timer and wo
|
||||
if (pinAh.wo==pinAl.wo || pinBh.wo==pinBl.wo || pinCh.wo==pinCl.wo)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int checkHardware6PWM(const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) {
|
||||
for (int i=0;i<64;i++) {
|
||||
tccConfiguration pinAh = getTCCChannelNr(pinA_h, getPeripheralOfPermutation(i, 0));
|
||||
tccConfiguration pinAl = getTCCChannelNr(pinA_l, getPeripheralOfPermutation(i, 1));
|
||||
tccConfiguration pinBh = getTCCChannelNr(pinB_h, getPeripheralOfPermutation(i, 2));
|
||||
tccConfiguration pinBl = getTCCChannelNr(pinB_l, getPeripheralOfPermutation(i, 3));
|
||||
tccConfiguration pinCh = getTCCChannelNr(pinC_h, getPeripheralOfPermutation(i, 4));
|
||||
tccConfiguration pinCl = getTCCChannelNr(pinC_l, getPeripheralOfPermutation(i, 5));
|
||||
if (checkPeripheralPermutationSameTCC6(pinAh, pinAl, pinBh, pinBl, pinCh, pinCl))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int checkSoftware6PWM(const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) {
|
||||
for (int i=0;i<64;i++) {
|
||||
tccConfiguration pinAh = getTCCChannelNr(pinA_h, getPeripheralOfPermutation(i, 0));
|
||||
tccConfiguration pinAl = getTCCChannelNr(pinA_l, getPeripheralOfPermutation(i, 1));
|
||||
tccConfiguration pinBh = getTCCChannelNr(pinB_h, getPeripheralOfPermutation(i, 2));
|
||||
tccConfiguration pinBl = getTCCChannelNr(pinB_l, getPeripheralOfPermutation(i, 3));
|
||||
tccConfiguration pinCh = getTCCChannelNr(pinC_h, getPeripheralOfPermutation(i, 4));
|
||||
tccConfiguration pinCl = getTCCChannelNr(pinC_l, getPeripheralOfPermutation(i, 5));
|
||||
if (checkPeripheralPermutationCompatible6(pinAh, pinAl, pinBh, pinBl, pinCh, pinCl))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int scorePermutation(tccConfiguration pins[], uint8_t num) {
|
||||
uint32_t usedtccs = 0;
|
||||
for (int i=0;i<num;i++)
|
||||
usedtccs |= (1<<pins[i].tcc.tccn);
|
||||
int score = 0;
|
||||
for (int i=0;i<TCC_INST_NUM;i++){
|
||||
if (usedtccs&0x1)
|
||||
score+=1;
|
||||
usedtccs = usedtccs>>1;
|
||||
}
|
||||
for (int i=0;i<TC_INST_NUM;i++){
|
||||
if (usedtccs&0x1)
|
||||
score+=(num+1);
|
||||
usedtccs = usedtccs>>1;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int checkPermutations(uint8_t num, int pins[], bool (*checkFunc)(tccConfiguration[], uint8_t) ) {
|
||||
tccConfiguration tccConfs[num];
|
||||
int best = -1;
|
||||
int bestscore = 1000000;
|
||||
for (int i=0;i<(0x1<<num);i++) {
|
||||
for (int j=0;j<num;j++)
|
||||
tccConfs[j] = getTCCChannelNr(pins[j], getPeripheralOfPermutation(i, j));
|
||||
if (checkFunc(tccConfs, num)) {
|
||||
int score = scorePermutation(tccConfs, num);
|
||||
if (score<bestscore) {
|
||||
bestscore = score;
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Configuring PWM frequency, resolution and alignment
|
||||
* - Stepper driver - 2PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param pwm_frequency - frequency in hertz - if applicable
|
||||
* @param pinA pinA bldc driver
|
||||
* @param pinB pinB bldc driver
|
||||
*/
|
||||
void* _configure2PWM(long pwm_frequency, const int pinA, const int pinB) {
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
printAllPinInfos();
|
||||
#endif
|
||||
int pins[2] = { pinA, pinB };
|
||||
int compatibility = checkPermutations(2, pins, checkPeripheralPermutationCompatible);
|
||||
if (compatibility<0) {
|
||||
// no result!
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Bad pin combination!");
|
||||
#endif
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
|
||||
tccConfiguration tccConfs[2] = { getTCCChannelNr(pinA, getPeripheralOfPermutation(compatibility, 0)),
|
||||
getTCCChannelNr(pinB, getPeripheralOfPermutation(compatibility, 1)) };
|
||||
|
||||
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Found configuration: score=", scorePermutation(tccConfs, 2));
|
||||
printTCCConfiguration(tccConfs[0]);
|
||||
printTCCConfiguration(tccConfs[1]);
|
||||
#endif
|
||||
|
||||
// attach pins to timer peripherals
|
||||
attachTCC(tccConfs[0]); // in theory this can fail, but there is no way to signal it...
|
||||
attachTCC(tccConfs[1]);
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Attached pins...");
|
||||
#endif
|
||||
|
||||
// set up clock - Note: if we did this right it should be possible to get all TCC units synchronized?
|
||||
// e.g. attach all the timers, start them, and then start the clock... but this would require API-changes in SimpleFOC...
|
||||
configureSAMDClock();
|
||||
|
||||
if (pwm_frequency==NOT_SET) {
|
||||
// use default frequency
|
||||
pwm_frequency = SIMPLEFOC_SAMD_DEFAULT_PWM_FREQUENCY_HZ;
|
||||
}
|
||||
|
||||
// configure the TCC (waveform, top-value, pre-scaler = frequency)
|
||||
configureTCC(tccConfs[0], pwm_frequency);
|
||||
configureTCC(tccConfs[1], pwm_frequency);
|
||||
getTccPinConfiguration(pinA)->pwm_res = tccConfs[0].pwm_res;
|
||||
getTccPinConfiguration(pinB)->pwm_res = tccConfs[1].pwm_res;
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Configured TCCs...");
|
||||
#endif
|
||||
|
||||
SAMDHardwareDriverParams* params = new SAMDHardwareDriverParams {
|
||||
.tccPinConfigurations = { getTccPinConfiguration(pinA), getTccPinConfiguration(pinB) },
|
||||
.pwm_frequency = (uint32_t)pwm_frequency
|
||||
}; // Someone with a stepper-setup who can test it?
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Configuring PWM frequency, resolution and alignment
|
||||
* - BLDC driver - 3PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* SAMD21 will support up to 2 BLDC motors in 3-PWM:
|
||||
* one on TCC0 using 3 of the channels 0,1,2 or 3
|
||||
* one on TCC3 using 3 of the channels 0,1,2 or 3
|
||||
* i.e. 8 different pins can be used, but only 4 different signals (WO[x]) on those 8 pins
|
||||
* WO[0] and WO[4] are the same
|
||||
* WO[1] and WO[5] are the same
|
||||
* WO[2] and WO[6] are the same
|
||||
* WO[3] and WO[7] are the same
|
||||
*
|
||||
* If you're on the Arduino Nano33 IoT, please see the Nano33 IoT pinout diagram to see which TCC0/WO[x]
|
||||
* signal is on which pin of the Nano. You can drive one motor on TCC0. For other boards, consult their documentation.
|
||||
*
|
||||
* Note:
|
||||
* That's if we want to keep the signals strictly in sync.
|
||||
*
|
||||
* If we can accept out-of-sync PWMs on the different phases, we could drive up to 4 BLDCs in 3-PWM mode,
|
||||
* using all the TCC channels. (TCC0 & TCC3 - 4 channels each, TCC1 & TCC2 - 2 channels each)
|
||||
*
|
||||
* All channels will use the same resolution, prescaler and clock, but they will have different start-times leading
|
||||
* to misaligned signals.
|
||||
*
|
||||
*
|
||||
* @param pwm_frequency - frequency in hertz - if applicable
|
||||
* @param pinA pinA bldc driver
|
||||
* @param pinB pinB bldc driver
|
||||
* @param pinC pinC bldc driver
|
||||
*/
|
||||
void* _configure3PWM(long pwm_frequency, const int pinA, const int pinB, const int pinC) {
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
printAllPinInfos();
|
||||
#endif
|
||||
int pins[3] = { pinA, pinB, pinC };
|
||||
int compatibility = checkPermutations(3, pins, checkPeripheralPermutationCompatible);
|
||||
if (compatibility<0) {
|
||||
// no result!
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Bad pin combination!");
|
||||
#endif
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
|
||||
tccConfiguration tccConfs[3] = { getTCCChannelNr(pinA, getPeripheralOfPermutation(compatibility, 0)),
|
||||
getTCCChannelNr(pinB, getPeripheralOfPermutation(compatibility, 1)),
|
||||
getTCCChannelNr(pinC, getPeripheralOfPermutation(compatibility, 2)) };
|
||||
|
||||
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Found configuration: score=", scorePermutation(tccConfs, 3));
|
||||
printTCCConfiguration(tccConfs[0]);
|
||||
printTCCConfiguration(tccConfs[1]);
|
||||
printTCCConfiguration(tccConfs[2]);
|
||||
#endif
|
||||
|
||||
// attach pins to timer peripherals
|
||||
attachTCC(tccConfs[0]); // in theory this can fail, but there is no way to signal it...
|
||||
attachTCC(tccConfs[1]);
|
||||
attachTCC(tccConfs[2]);
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Attached pins...");
|
||||
#endif
|
||||
|
||||
// set up clock - Note: if we did this right it should be possible to get all TCC units synchronized?
|
||||
// e.g. attach all the timers, start them, and then start the clock... but this would require API-changes in SimpleFOC...
|
||||
configureSAMDClock();
|
||||
|
||||
if (pwm_frequency==NOT_SET) {
|
||||
// use default frequency
|
||||
pwm_frequency = SIMPLEFOC_SAMD_DEFAULT_PWM_FREQUENCY_HZ;
|
||||
}
|
||||
|
||||
// configure the TCC (waveform, top-value, pre-scaler = frequency)
|
||||
configureTCC(tccConfs[0], pwm_frequency);
|
||||
configureTCC(tccConfs[1], pwm_frequency);
|
||||
configureTCC(tccConfs[2], pwm_frequency);
|
||||
getTccPinConfiguration(pinA)->pwm_res = tccConfs[0].pwm_res;
|
||||
getTccPinConfiguration(pinB)->pwm_res = tccConfs[1].pwm_res;
|
||||
getTccPinConfiguration(pinC)->pwm_res = tccConfs[2].pwm_res;
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Configured TCCs...");
|
||||
#endif
|
||||
|
||||
return new SAMDHardwareDriverParams {
|
||||
.tccPinConfigurations = { getTccPinConfiguration(pinA), getTccPinConfiguration(pinB), getTccPinConfiguration(pinC) },
|
||||
.pwm_frequency = (uint32_t)pwm_frequency
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Configuring PWM frequency, resolution and alignment
|
||||
* - Stepper driver - 4PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param pwm_frequency - frequency in hertz - if applicable
|
||||
* @param pin1A pin1A stepper driver
|
||||
* @param pin1B pin1B stepper driver
|
||||
* @param pin2A pin2A stepper driver
|
||||
* @param pin2B pin2B stepper driver
|
||||
*/
|
||||
void* _configure4PWM(long pwm_frequency, const int pin1A, const int pin1B, const int pin2A, const int pin2B) {
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
printAllPinInfos();
|
||||
#endif
|
||||
int pins[4] = { pin1A, pin1B, pin2A, pin2B };
|
||||
int compatibility = checkPermutations(4, pins, checkPeripheralPermutationCompatible);
|
||||
if (compatibility<0) {
|
||||
// no result!
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Bad pin combination!");
|
||||
#endif
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
|
||||
tccConfiguration tccConfs[4] = { getTCCChannelNr(pin1A, getPeripheralOfPermutation(compatibility, 0)),
|
||||
getTCCChannelNr(pin1B, getPeripheralOfPermutation(compatibility, 1)),
|
||||
getTCCChannelNr(pin2A, getPeripheralOfPermutation(compatibility, 2)),
|
||||
getTCCChannelNr(pin2B, getPeripheralOfPermutation(compatibility, 3)) };
|
||||
|
||||
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Found configuration: score=", scorePermutation(tccConfs, 4));
|
||||
printTCCConfiguration(tccConfs[0]);
|
||||
printTCCConfiguration(tccConfs[1]);
|
||||
printTCCConfiguration(tccConfs[2]);
|
||||
printTCCConfiguration(tccConfs[3]);
|
||||
#endif
|
||||
|
||||
// attach pins to timer peripherals
|
||||
attachTCC(tccConfs[0]); // in theory this can fail, but there is no way to signal it...
|
||||
attachTCC(tccConfs[1]);
|
||||
attachTCC(tccConfs[2]);
|
||||
attachTCC(tccConfs[3]);
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Attached pins...");
|
||||
#endif
|
||||
|
||||
// set up clock - Note: if we did this right it should be possible to get all TCC units synchronized?
|
||||
// e.g. attach all the timers, start them, and then start the clock... but this would require API-changes in SimpleFOC...
|
||||
configureSAMDClock();
|
||||
|
||||
if (pwm_frequency==NOT_SET) {
|
||||
// use default frequency
|
||||
pwm_frequency = SIMPLEFOC_SAMD_DEFAULT_PWM_FREQUENCY_HZ;
|
||||
}
|
||||
|
||||
// configure the TCC (waveform, top-value, pre-scaler = frequency)
|
||||
configureTCC(tccConfs[0], pwm_frequency);
|
||||
configureTCC(tccConfs[1], pwm_frequency);
|
||||
configureTCC(tccConfs[2], pwm_frequency);
|
||||
configureTCC(tccConfs[3], pwm_frequency);
|
||||
getTccPinConfiguration(pin1A)->pwm_res = tccConfs[0].pwm_res;
|
||||
getTccPinConfiguration(pin2A)->pwm_res = tccConfs[1].pwm_res;
|
||||
getTccPinConfiguration(pin1B)->pwm_res = tccConfs[2].pwm_res;
|
||||
getTccPinConfiguration(pin2B)->pwm_res = tccConfs[3].pwm_res;
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Configured TCCs...");
|
||||
#endif
|
||||
|
||||
return new SAMDHardwareDriverParams {
|
||||
.tccPinConfigurations = { getTccPinConfiguration(pin1A), getTccPinConfiguration(pin1B), getTccPinConfiguration(pin2A), getTccPinConfiguration(pin2B) },
|
||||
.pwm_frequency = (uint32_t)pwm_frequency
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Configuring PWM frequency, resolution and alignment
|
||||
* - BLDC driver - 6PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* SAMD21 will support up to 2 BLDC motors in 6-PWM:
|
||||
* one on TCC0 using 3 of the channels 0,1,2 or 3
|
||||
* one on TCC3 using 3 of the channels 0,1,2 or 3
|
||||
* i.e. 6 out of 8 pins must be used, in the following high/low side pairs:
|
||||
* WO[0] & WO[4] (high side & low side)
|
||||
* WO[1] & WO[5]
|
||||
* WO[2] & WO[6]
|
||||
* WO[3] & WO[7]
|
||||
*
|
||||
* If you're on the Arduino Nano33 IoT, please see the Nano33 IoT pinout diagram to see which TCC0/WO[x]
|
||||
* signal is on which pin of the Nano. You can drive 1 BLDC on TCC0. For other boards, consult their documentation.
|
||||
*
|
||||
*
|
||||
* @param pwm_frequency - frequency in hertz - if applicable
|
||||
* @param dead_zone duty cycle protection zone [0, 1] - both low and high side low - if applicable
|
||||
* @param pinA_h pinA high-side bldc driver
|
||||
* @param pinA_l pinA low-side bldc driver
|
||||
* @param pinB_h pinA high-side bldc driver
|
||||
* @param pinB_l pinA low-side bldc driver
|
||||
* @param pinC_h pinA high-side bldc driver
|
||||
* @param pinC_l pinA low-side bldc driver
|
||||
*
|
||||
* @return 0 if config good, -1 if failed
|
||||
*/
|
||||
void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) {
|
||||
// we want to use a TCC channel with 1 non-inverted and 1 inverted output for each phase, with dead-time insertion
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
printAllPinInfos();
|
||||
#endif
|
||||
int compatibility = checkHardware6PWM(pinA_h, pinA_l, pinB_h, pinB_l, pinC_h, pinC_l);
|
||||
if (compatibility<0) {
|
||||
compatibility = checkSoftware6PWM(pinA_h, pinA_l, pinB_h, pinB_l, pinC_h, pinC_l);
|
||||
if (compatibility<0) {
|
||||
// no result!
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Bad pin combination!");
|
||||
#endif
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
tccConfiguration pinAh = getTCCChannelNr(pinA_h, getPeripheralOfPermutation(compatibility, 0));
|
||||
tccConfiguration pinAl = getTCCChannelNr(pinA_l, getPeripheralOfPermutation(compatibility, 1));
|
||||
tccConfiguration pinBh = getTCCChannelNr(pinB_h, getPeripheralOfPermutation(compatibility, 2));
|
||||
tccConfiguration pinBl = getTCCChannelNr(pinB_l, getPeripheralOfPermutation(compatibility, 3));
|
||||
tccConfiguration pinCh = getTCCChannelNr(pinC_h, getPeripheralOfPermutation(compatibility, 4));
|
||||
tccConfiguration pinCl = getTCCChannelNr(pinC_l, getPeripheralOfPermutation(compatibility, 5));
|
||||
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Found configuration: ");
|
||||
printTCCConfiguration(pinAh);
|
||||
printTCCConfiguration(pinAl);
|
||||
printTCCConfiguration(pinBh);
|
||||
printTCCConfiguration(pinBl);
|
||||
printTCCConfiguration(pinCh);
|
||||
printTCCConfiguration(pinCl);
|
||||
#endif
|
||||
|
||||
// attach pins to timer peripherals
|
||||
bool allAttached = true;
|
||||
allAttached |= attachTCC(pinAh); // in theory this can fail, but there is no way to signal it...
|
||||
allAttached |= attachTCC(pinAl);
|
||||
allAttached |= attachTCC(pinBh);
|
||||
allAttached |= attachTCC(pinBl);
|
||||
allAttached |= attachTCC(pinCh);
|
||||
allAttached |= attachTCC(pinCl);
|
||||
if (!allAttached)
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Attached pins...");
|
||||
#endif
|
||||
// set up clock - if we did this right it should be possible to get all TCC units synchronized?
|
||||
// e.g. attach all the timers, start them, and then start the clock... but this would require API changes in SimpleFOC driver API
|
||||
configureSAMDClock();
|
||||
|
||||
if (pwm_frequency==NOT_SET) {
|
||||
// use default frequency
|
||||
pwm_frequency = SIMPLEFOC_SAMD_DEFAULT_PWM_FREQUENCY_HZ;
|
||||
}
|
||||
|
||||
// configure the TCC(s)
|
||||
configureTCC(pinAh, pwm_frequency, false, (pinAh.tcc.chaninfo==pinAl.tcc.chaninfo)?dead_zone:-1);
|
||||
if ((pinAh.tcc.chaninfo!=pinAl.tcc.chaninfo))
|
||||
configureTCC(pinAl, pwm_frequency, true, -1.0);
|
||||
configureTCC(pinBh, pwm_frequency, false, (pinBh.tcc.chaninfo==pinBl.tcc.chaninfo)?dead_zone:-1);
|
||||
if ((pinBh.tcc.chaninfo!=pinBl.tcc.chaninfo))
|
||||
configureTCC(pinBl, pwm_frequency, true, -1.0);
|
||||
configureTCC(pinCh, pwm_frequency, false, (pinCh.tcc.chaninfo==pinCl.tcc.chaninfo)?dead_zone:-1);
|
||||
if ((pinCh.tcc.chaninfo!=pinCl.tcc.chaninfo))
|
||||
configureTCC(pinCl, pwm_frequency, true, -1.0);
|
||||
getTccPinConfiguration(pinA_h)->pwm_res = pinAh.pwm_res;
|
||||
getTccPinConfiguration(pinA_l)->pwm_res = pinAh.pwm_res; // use the high phase resolution, in case we didn't set it
|
||||
getTccPinConfiguration(pinB_h)->pwm_res = pinBh.pwm_res;
|
||||
getTccPinConfiguration(pinB_l)->pwm_res = pinBh.pwm_res;
|
||||
getTccPinConfiguration(pinC_h)->pwm_res = pinCh.pwm_res;
|
||||
getTccPinConfiguration(pinC_l)->pwm_res = pinCh.pwm_res;
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
SIMPLEFOC_DEBUG("SAMD: Configured TCCs...");
|
||||
#endif
|
||||
|
||||
return new SAMDHardwareDriverParams {
|
||||
.tccPinConfigurations = { getTccPinConfiguration(pinA_h), getTccPinConfiguration(pinA_l), getTccPinConfiguration(pinB_h), getTccPinConfiguration(pinB_l), getTccPinConfiguration(pinC_h), getTccPinConfiguration(pinC_l) },
|
||||
.pwm_frequency = (uint32_t)pwm_frequency,
|
||||
.dead_zone = dead_zone,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
* - Stepper driver - 2PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param dc_a duty cycle phase A [0, 1]
|
||||
* @param dc_b duty cycle phase B [0, 1]
|
||||
* @param pinA phase A hardware pin number
|
||||
* @param pinB phase B hardware pin number
|
||||
*/
|
||||
void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params) {
|
||||
writeSAMDDutyCycle(((SAMDHardwareDriverParams*)params)->tccPinConfigurations[0], dc_a);
|
||||
writeSAMDDutyCycle(((SAMDHardwareDriverParams*)params)->tccPinConfigurations[1], dc_b);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
* - BLDC driver - 3PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param dc_a duty cycle phase A [0, 1]
|
||||
* @param dc_b duty cycle phase B [0, 1]
|
||||
* @param dc_c duty cycle phase C [0, 1]
|
||||
* @param pinA phase A hardware pin number
|
||||
* @param pinB phase B hardware pin number
|
||||
* @param pinC phase C hardware pin number
|
||||
*/
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params) {
|
||||
writeSAMDDutyCycle(((SAMDHardwareDriverParams*)params)->tccPinConfigurations[0], dc_a);
|
||||
writeSAMDDutyCycle(((SAMDHardwareDriverParams*)params)->tccPinConfigurations[1], dc_b);
|
||||
writeSAMDDutyCycle(((SAMDHardwareDriverParams*)params)->tccPinConfigurations[2], dc_c);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
* - Stepper driver - 4PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* @param dc_1a duty cycle phase 1A [0, 1]
|
||||
* @param dc_1b duty cycle phase 1B [0, 1]
|
||||
* @param dc_2a duty cycle phase 2A [0, 1]
|
||||
* @param dc_2b duty cycle phase 2B [0, 1]
|
||||
* @param pin1A phase 1A hardware pin number
|
||||
* @param pin1B phase 1B hardware pin number
|
||||
* @param pin2A phase 2A hardware pin number
|
||||
* @param pin2B phase 2B hardware pin number
|
||||
*/
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params){
|
||||
writeSAMDDutyCycle(((SAMDHardwareDriverParams*)params)->tccPinConfigurations[0], dc_1a);
|
||||
writeSAMDDutyCycle(((SAMDHardwareDriverParams*)params)->tccPinConfigurations[1], dc_1b);
|
||||
writeSAMDDutyCycle(((SAMDHardwareDriverParams*)params)->tccPinConfigurations[2], dc_2a);
|
||||
writeSAMDDutyCycle(((SAMDHardwareDriverParams*)params)->tccPinConfigurations[3], dc_2b);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
* - BLDC driver - 6PWM setting
|
||||
* - hardware specific
|
||||
*
|
||||
* Note: dead-time must be setup in advance, so parameter "dead_zone" is ignored
|
||||
* the low side pins are automatically driven by the SAMD DTI module, so it is enough to set the high-side
|
||||
* duty cycle.
|
||||
* No sanity checks are perfomed to ensure the pinA, pinB, pinC are the same pins you used in configure method...
|
||||
* so use appropriately.
|
||||
*
|
||||
* @param dc_a duty cycle phase A [0, 1]
|
||||
* @param dc_b duty cycle phase B [0, 1]
|
||||
* @param dc_c duty cycle phase C [0, 1]
|
||||
* @param dead_zone duty cycle protection zone [0, 1] - both low and high side low
|
||||
* @param pinA_h phase A high-side hardware pin number
|
||||
* @param pinA_l phase A low-side hardware pin number
|
||||
* @param pinB_h phase B high-side hardware pin number
|
||||
* @param pinB_l phase B low-side hardware pin number
|
||||
* @param pinC_h phase C high-side hardware pin number
|
||||
* @param pinC_l phase C low-side hardware pin number
|
||||
*
|
||||
*/
|
||||
void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void* params){
|
||||
SAMDHardwareDriverParams* p = (SAMDHardwareDriverParams*)params;
|
||||
tccConfiguration* tcc1 = p->tccPinConfigurations[0];
|
||||
tccConfiguration* tcc2 = p->tccPinConfigurations[1];
|
||||
uint32_t pwm_res =p->tccPinConfigurations[0]->pwm_res;
|
||||
if (tcc1->tcc.chaninfo!=tcc2->tcc.chaninfo) {
|
||||
// low-side on a different pin of same TCC - do dead-time in software...
|
||||
float ls = dc_a+(p->dead_zone * (pwm_res-1)); // TODO resolution!!!
|
||||
if (ls>1.0) ls = 1.0f; // no off-time is better than too-short dead-time
|
||||
writeSAMDDutyCycle(tcc1, dc_a);
|
||||
writeSAMDDutyCycle(tcc2, ls);
|
||||
}
|
||||
else
|
||||
writeSAMDDutyCycle(tcc1, dc_a); // dead-time is done is hardware, no need to set low side pin explicitly
|
||||
|
||||
tcc1 = p->tccPinConfigurations[2];
|
||||
tcc2 = p->tccPinConfigurations[3];
|
||||
if (tcc1->tcc.chaninfo!=tcc2->tcc.chaninfo) {
|
||||
float ls = dc_b+(p->dead_zone * (pwm_res-1));
|
||||
if (ls>1.0) ls = 1.0f; // no off-time is better than too-short dead-time
|
||||
writeSAMDDutyCycle(tcc1, dc_b);
|
||||
writeSAMDDutyCycle(tcc2, ls);
|
||||
}
|
||||
else
|
||||
writeSAMDDutyCycle(tcc1, dc_b);
|
||||
|
||||
tcc1 = p->tccPinConfigurations[4];
|
||||
tcc2 = p->tccPinConfigurations[5];
|
||||
if (tcc1->tcc.chaninfo!=tcc2->tcc.chaninfo) {
|
||||
float ls = dc_c+(p->dead_zone * (pwm_res-1));
|
||||
if (ls>1.0) ls = 1.0f; // no off-time is better than too-short dead-time
|
||||
writeSAMDDutyCycle(tcc1, dc_c);
|
||||
writeSAMDDutyCycle(tcc2, ls);
|
||||
}
|
||||
else
|
||||
writeSAMDDutyCycle(tcc1, dc_c);
|
||||
return;
|
||||
|
||||
_UNUSED(phase_state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
|
||||
/**
|
||||
* Prints a table of pin assignments for your SAMD MCU. Very useful since the
|
||||
* board pinout descriptions and variant.cpp are usually quite wrong, and this
|
||||
* saves you hours of cross-referencing with the datasheet.
|
||||
*/
|
||||
void printAllPinInfos() {
|
||||
SimpleFOCDebug::println();
|
||||
for (uint8_t pin=0;pin<PINS_COUNT;pin++) {
|
||||
const PinDescription& pinDesc = g_APinDescription[pin];
|
||||
wo_association& association = getWOAssociation(pinDesc.ulPort, pinDesc.ulPin);
|
||||
SimpleFOCDebug::print("Pin ");
|
||||
if (pin<10) SimpleFOCDebug::print("0");
|
||||
SimpleFOCDebug::print(pin);
|
||||
switch (pinDesc.ulPort) {
|
||||
case NOT_A_PORT: SimpleFOCDebug::print(" "); break;
|
||||
case PORTA: SimpleFOCDebug::print(" PA"); break;
|
||||
case PORTB: SimpleFOCDebug::print(" PB"); break;
|
||||
case PORTC: SimpleFOCDebug::print(" PC"); break;
|
||||
#if defined(_SAMD51_)||defined(_SAME51_)
|
||||
case PORTD: SimpleFOCDebug::print(" PD"); break;
|
||||
#endif
|
||||
}
|
||||
if (pinDesc.ulPin <10) SimpleFOCDebug::print("0");
|
||||
SimpleFOCDebug::print((int)pinDesc.ulPin);
|
||||
|
||||
SimpleFOCDebug::print(" E=");
|
||||
if (association.tccE>=0) {
|
||||
int tcn = GetTCNumber(association.tccE);
|
||||
if (tcn>=TCC_INST_NUM){
|
||||
SimpleFOCDebug::print(" TC");
|
||||
SimpleFOCDebug::print(tcn-TCC_INST_NUM);
|
||||
}
|
||||
else {
|
||||
SimpleFOCDebug::print("TCC");
|
||||
SimpleFOCDebug::print(tcn);
|
||||
}
|
||||
SimpleFOCDebug::print("-");
|
||||
SimpleFOCDebug::print(GetTCChannelNumber(association.tccE));
|
||||
SimpleFOCDebug::print("[");
|
||||
SimpleFOCDebug::print(GetTCChannelNumber(association.woE));
|
||||
SimpleFOCDebug::print("]");
|
||||
if (tcn<10)
|
||||
SimpleFOCDebug::print(" ");
|
||||
}
|
||||
else
|
||||
SimpleFOCDebug::print(" None ");
|
||||
|
||||
SimpleFOCDebug::print(" F=");
|
||||
if (association.tccF>=0) {
|
||||
int tcn = GetTCNumber(association.tccF);
|
||||
if (tcn>=TCC_INST_NUM){
|
||||
SimpleFOCDebug::print(" TC");
|
||||
SimpleFOCDebug::print(tcn-TCC_INST_NUM);
|
||||
}
|
||||
else {
|
||||
SimpleFOCDebug::print("TCC");
|
||||
SimpleFOCDebug::print(tcn);
|
||||
}
|
||||
SimpleFOCDebug::print("-");
|
||||
SimpleFOCDebug::print(GetTCChannelNumber(association.tccF));
|
||||
SimpleFOCDebug::print("[");
|
||||
SimpleFOCDebug::print(GetTCChannelNumber(association.woF));
|
||||
SimpleFOCDebug::print("]");
|
||||
if (tcn<10)
|
||||
SimpleFOCDebug::print(" ");
|
||||
}
|
||||
else
|
||||
SimpleFOCDebug::print(" None ");
|
||||
|
||||
#if defined(_SAMD51_)||defined(_SAME51_)
|
||||
SimpleFOCDebug::print(" G=");
|
||||
if (association.tccG>=0) {
|
||||
int tcn = GetTCNumber(association.tccG);
|
||||
SimpleFOCDebug::print("TCC");
|
||||
if (tcn<10)
|
||||
SimpleFOCDebug::print(" ");
|
||||
SimpleFOCDebug::print(tcn);
|
||||
SimpleFOCDebug::print("-");
|
||||
SimpleFOCDebug::print(GetTCChannelNumber(association.tccG));
|
||||
SimpleFOCDebug::print("[");
|
||||
SimpleFOCDebug::print(GetTCChannelNumber(association.woG));
|
||||
SimpleFOCDebug::println("]");
|
||||
}
|
||||
else
|
||||
SimpleFOCDebug::println(" None ");
|
||||
#else
|
||||
SimpleFOCDebug::println();
|
||||
#endif
|
||||
|
||||
}
|
||||
SimpleFOCDebug::println();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void printTCCConfiguration(tccConfiguration& info) {
|
||||
SimpleFOCDebug::print(info.pin);
|
||||
if (info.tcc.tccn>=TCC_INST_NUM)
|
||||
SimpleFOCDebug::print(": TC Peripheral");
|
||||
else
|
||||
SimpleFOCDebug::print(": TCC Peripheral");
|
||||
switch (info.peripheral) {
|
||||
case PIO_TIMER:
|
||||
SimpleFOCDebug::print(" E "); break;
|
||||
case PIO_TIMER_ALT:
|
||||
SimpleFOCDebug::print(" F "); break;
|
||||
#if defined(_SAMD51_)||defined(_SAME51_)
|
||||
case PIO_TCC_PDEC:
|
||||
SimpleFOCDebug::print(" G "); break;
|
||||
#endif
|
||||
default:
|
||||
SimpleFOCDebug::print(" ? "); break;
|
||||
}
|
||||
if (info.tcc.tccn>=0) {
|
||||
SimpleFOCDebug::print(info.tcc.tccn);
|
||||
SimpleFOCDebug::print("-");
|
||||
SimpleFOCDebug::print(info.tcc.chan);
|
||||
SimpleFOCDebug::print("[");
|
||||
SimpleFOCDebug::print(info.wo);
|
||||
SimpleFOCDebug::println("]");
|
||||
}
|
||||
else
|
||||
SimpleFOCDebug::println(" None");
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,127 @@
|
||||
|
||||
#ifndef SAMD_MCU_H
|
||||
#define SAMD_MCU_H
|
||||
|
||||
|
||||
// uncomment to enable debug output from SAMD driver
|
||||
// can set this as build-flag in Arduino IDE or PlatformIO
|
||||
// #define SIMPLEFOC_SAMD_DEBUG
|
||||
|
||||
#include "../../hardware_api.h"
|
||||
|
||||
|
||||
#if defined(__SAME51J19A__) || defined(__ATSAME51J19A__)
|
||||
#ifndef _SAME51_
|
||||
#define _SAME51_
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_SAMD21_)||defined(_SAMD51_)||defined(_SAME51_)
|
||||
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "variant.h"
|
||||
#include "wiring_private.h"
|
||||
|
||||
|
||||
#ifndef SIMPLEFOC_SAMD_PWM_RESOLUTION
|
||||
#define SIMPLEFOC_SAMD_PWM_RESOLUTION 1000
|
||||
#endif
|
||||
|
||||
#define SIMPLEFOC_SAMD_DEFAULT_PWM_FREQUENCY_HZ 24000
|
||||
// arbitrary maximum. On SAMD51 with 120MHz clock this means 2kHz minimum pwm frequency
|
||||
#define SIMPLEFOC_SAMD_MAX_PWM_RESOLUTION 30000
|
||||
// lets not go too low - 400 with clock speed of 120MHz on SAMD51 means 150kHz maximum PWM frequency...
|
||||
// 400 with 48MHz clock on SAMD21 means 60kHz maximum PWM frequency...
|
||||
#define SIMPLEFOC_SAMD_MIN_PWM_RESOLUTION 400
|
||||
// this is the most we can support on the TC units
|
||||
#define SIMPLEFOC_SAMD_PWM_TC_RESOLUTION 250
|
||||
|
||||
#ifndef SIMPLEFOC_SAMD_MAX_TCC_PINCONFIGURATIONS
|
||||
#define SIMPLEFOC_SAMD_MAX_TCC_PINCONFIGURATIONS 24
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
struct tccConfiguration {
|
||||
uint8_t pin;
|
||||
EPioType peripheral; // 1=true, 0=false
|
||||
uint8_t wo;
|
||||
union tccChanInfo {
|
||||
struct {
|
||||
int8_t chan;
|
||||
int8_t tccn;
|
||||
};
|
||||
uint16_t chaninfo;
|
||||
} tcc;
|
||||
uint16_t pwm_res;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct wo_association {
|
||||
EPortType port;
|
||||
uint32_t pin;
|
||||
ETCChannel tccE;
|
||||
uint8_t woE;
|
||||
ETCChannel tccF;
|
||||
uint8_t woF;
|
||||
#if defined(_SAMD51_)||defined(_SAME51_)
|
||||
ETCChannel tccG;
|
||||
uint8_t woG;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
typedef struct SAMDHardwareDriverParams {
|
||||
tccConfiguration* tccPinConfigurations[6];
|
||||
uint32_t pwm_frequency;
|
||||
float dead_zone;
|
||||
} SAMDHardwareDriverParams;
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(_SAMD21_)
|
||||
#define NUM_PIO_TIMER_PERIPHERALS 2
|
||||
#elif defined(_SAMD51_)||defined(_SAME51_)
|
||||
#define NUM_PIO_TIMER_PERIPHERALS 3
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Global state
|
||||
*/
|
||||
extern struct wo_association WO_associations[];
|
||||
extern uint8_t TCC_CHANNEL_COUNT[];
|
||||
extern tccConfiguration tccPinConfigurations[SIMPLEFOC_SAMD_MAX_TCC_PINCONFIGURATIONS];
|
||||
extern uint8_t numTccPinConfigurations;
|
||||
extern bool SAMDClockConfigured;
|
||||
extern bool tccConfigured[TCC_INST_NUM+TC_INST_NUM];
|
||||
|
||||
|
||||
|
||||
struct wo_association& getWOAssociation(EPortType port, uint32_t pin);
|
||||
void writeSAMDDutyCycle(tccConfiguration* info, float dc);
|
||||
void configureSAMDClock();
|
||||
void configureTCC(tccConfiguration& tccConfig, long pwm_frequency, bool negate=false, float hw6pwm=-1);
|
||||
__inline__ void syncTCC(Tcc* TCCx) __attribute__((always_inline, unused));
|
||||
EPioType getPeripheralOfPermutation(int permutation, int pin_position);
|
||||
|
||||
#ifdef SIMPLEFOC_SAMD_DEBUG
|
||||
void printTCCConfiguration(tccConfiguration& info);
|
||||
void printAllPinInfos();
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,952 @@
|
||||
|
||||
#include "../../hardware_api.h"
|
||||
#include "stm32_mcu.h"
|
||||
|
||||
#if defined(_STM32_DEF_)
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for STM32")
|
||||
#pragma message("")
|
||||
|
||||
|
||||
//#define SIMPLEFOC_STM32_DEBUG
|
||||
|
||||
#ifdef SIMPLEFOC_STM32_DEBUG
|
||||
void printTimerCombination(int numPins, PinMap* timers[], int score);
|
||||
int getTimerNumber(int timerIndex);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef SIMPLEFOC_STM32_MAX_PINTIMERSUSED
|
||||
#define SIMPLEFOC_STM32_MAX_PINTIMERSUSED 12
|
||||
#endif
|
||||
int numTimerPinsUsed;
|
||||
PinMap* timerPinsUsed[SIMPLEFOC_STM32_MAX_PINTIMERSUSED];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// setting pwm to hardware pin - instead analogWrite()
|
||||
void _setPwm(HardwareTimer *HT, uint32_t channel, uint32_t value, int resolution)
|
||||
{
|
||||
// TODO - remove commented code
|
||||
// PinName pin = digitalPinToPinName(ulPin);
|
||||
// TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(pin, PinMap_PWM);
|
||||
// uint32_t index = get_timer_index(Instance);
|
||||
// HardwareTimer *HT = (HardwareTimer *)(HardwareTimer_Handle[index]->__this);
|
||||
|
||||
HT->setCaptureCompare(channel, value, (TimerCompareFormat_t)resolution);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int getLLChannel(PinMap* timer) {
|
||||
#if defined(TIM_CCER_CC1NE)
|
||||
if (STM_PIN_INVERTED(timer->function)) {
|
||||
switch (STM_PIN_CHANNEL(timer->function)) {
|
||||
case 1: return LL_TIM_CHANNEL_CH1N;
|
||||
case 2: return LL_TIM_CHANNEL_CH2N;
|
||||
case 3: return LL_TIM_CHANNEL_CH3N;
|
||||
#if defined(LL_TIM_CHANNEL_CH4N)
|
||||
case 4: return LL_TIM_CHANNEL_CH4N;
|
||||
#endif
|
||||
default: return -1;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
switch (STM_PIN_CHANNEL(timer->function)) {
|
||||
case 1: return LL_TIM_CHANNEL_CH1;
|
||||
case 2: return LL_TIM_CHANNEL_CH2;
|
||||
case 3: return LL_TIM_CHANNEL_CH3;
|
||||
case 4: return LL_TIM_CHANNEL_CH4;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// init pin pwm
|
||||
HardwareTimer* _initPinPWM(uint32_t PWM_freq, PinMap* timer) {
|
||||
// sanity check
|
||||
if (timer==NP)
|
||||
return NP;
|
||||
uint32_t index = get_timer_index((TIM_TypeDef*)timer->peripheral);
|
||||
bool init = false;
|
||||
if (HardwareTimer_Handle[index] == NULL) {
|
||||
HardwareTimer_Handle[index]->__this = new HardwareTimer((TIM_TypeDef*)timer->peripheral);
|
||||
HardwareTimer_Handle[index]->handle.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED3;
|
||||
HardwareTimer_Handle[index]->handle.Init.RepetitionCounter = 1;
|
||||
HAL_TIM_Base_Init(&(HardwareTimer_Handle[index]->handle));
|
||||
init = true;
|
||||
}
|
||||
HardwareTimer *HT = (HardwareTimer *)(HardwareTimer_Handle[index]->__this);
|
||||
uint32_t channel = STM_PIN_CHANNEL(timer->function);
|
||||
HT->pause();
|
||||
if (init)
|
||||
HT->setOverflow(PWM_freq, HERTZ_FORMAT);
|
||||
HT->setMode(channel, TIMER_OUTPUT_COMPARE_PWM1, timer->pin);
|
||||
#if SIMPLEFOC_PWM_ACTIVE_HIGH==false
|
||||
LL_TIM_OC_SetPolarity(HT->getHandle()->Instance, getLLChannel(timer), LL_TIM_OCPOLARITY_LOW);
|
||||
#endif
|
||||
#ifdef SIMPLEFOC_STM32_DEBUG
|
||||
SIMPLEFOC_DEBUG("STM32-DRV: Configuring high timer ", (int)getTimerNumber(get_timer_index(HardwareTimer_Handle[index]->handle.Instance)));
|
||||
SIMPLEFOC_DEBUG("STM32-DRV: Configuring high channel ", (int)channel);
|
||||
#endif
|
||||
return HT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// init high side pin
|
||||
HardwareTimer* _initPinPWMHigh(uint32_t PWM_freq, PinMap* timer) {
|
||||
HardwareTimer* HT = _initPinPWM(PWM_freq, timer);
|
||||
#if SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH==false && SIMPLEFOC_PWM_ACTIVE_HIGH==true
|
||||
LL_TIM_OC_SetPolarity(HT->getHandle()->Instance, getLLChannel(timer), LL_TIM_OCPOLARITY_LOW);
|
||||
#endif
|
||||
return HT;
|
||||
}
|
||||
|
||||
// init low side pin
|
||||
HardwareTimer* _initPinPWMLow(uint32_t PWM_freq, PinMap* timer)
|
||||
{
|
||||
uint32_t index = get_timer_index((TIM_TypeDef*)timer->peripheral);
|
||||
|
||||
bool init = false;
|
||||
if (HardwareTimer_Handle[index] == NULL) {
|
||||
HardwareTimer_Handle[index]->__this = new HardwareTimer((TIM_TypeDef*)timer->peripheral);
|
||||
HardwareTimer_Handle[index]->handle.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED3;
|
||||
HardwareTimer_Handle[index]->handle.Init.RepetitionCounter = 1;
|
||||
HAL_TIM_Base_Init(&(HardwareTimer_Handle[index]->handle));
|
||||
init = true;
|
||||
}
|
||||
HardwareTimer *HT = (HardwareTimer *)(HardwareTimer_Handle[index]->__this);
|
||||
uint32_t channel = STM_PIN_CHANNEL(timer->function);
|
||||
|
||||
#ifdef SIMPLEFOC_STM32_DEBUG
|
||||
SIMPLEFOC_DEBUG("STM32-DRV: Configuring low timer ", (int)getTimerNumber(get_timer_index(HardwareTimer_Handle[index]->handle.Instance)));
|
||||
SIMPLEFOC_DEBUG("STM32-DRV: Configuring low channel ", (int)channel);
|
||||
#endif
|
||||
|
||||
HT->pause();
|
||||
if (init)
|
||||
HT->setOverflow(PWM_freq, HERTZ_FORMAT);
|
||||
// sets internal fields of HT, but we can't set polarity here
|
||||
HT->setMode(channel, TIMER_OUTPUT_COMPARE_PWM2, timer->pin);
|
||||
#if SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH==false
|
||||
LL_TIM_OC_SetPolarity(HT->getHandle()->Instance, getLLChannel(timer), LL_TIM_OCPOLARITY_LOW);
|
||||
#endif
|
||||
return HT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// align the timers to end the init
|
||||
void _alignPWMTimers(HardwareTimer *HT1, HardwareTimer *HT2, HardwareTimer *HT3)
|
||||
{
|
||||
HT1->pause();
|
||||
HT1->refresh();
|
||||
HT2->pause();
|
||||
HT2->refresh();
|
||||
HT3->pause();
|
||||
HT3->refresh();
|
||||
HT1->resume();
|
||||
HT2->resume();
|
||||
HT3->resume();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// align the timers to end the init
|
||||
void _alignPWMTimers(HardwareTimer *HT1, HardwareTimer *HT2, HardwareTimer *HT3, HardwareTimer *HT4)
|
||||
{
|
||||
HT1->pause();
|
||||
HT1->refresh();
|
||||
HT2->pause();
|
||||
HT2->refresh();
|
||||
HT3->pause();
|
||||
HT3->refresh();
|
||||
HT4->pause();
|
||||
HT4->refresh();
|
||||
HT1->resume();
|
||||
HT2->resume();
|
||||
HT3->resume();
|
||||
HT4->resume();
|
||||
}
|
||||
|
||||
|
||||
// align the timers to end the init
|
||||
void _stopTimers(HardwareTimer **timers_to_stop, int timer_num)
|
||||
{
|
||||
// TODO - stop each timer only once
|
||||
// stop timers
|
||||
for (int i=0; i < timer_num; i++) {
|
||||
if(timers_to_stop[i] == NP) return;
|
||||
timers_to_stop[i]->pause();
|
||||
timers_to_stop[i]->refresh();
|
||||
#ifdef SIMPLEFOC_STM32_DEBUG
|
||||
SIMPLEFOC_DEBUG("STM32-DRV: Stopping timer ", getTimerNumber(get_timer_index(timers_to_stop[i]->getHandle()->Instance)));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// align the timers to end the init
|
||||
void _startTimers(HardwareTimer **timers_to_start, int timer_num)
|
||||
{
|
||||
// TODO - sart each timer only once
|
||||
// sart timers
|
||||
for (int i=0; i < timer_num; i++) {
|
||||
if(timers_to_start[i] == NP) return;
|
||||
timers_to_start[i]->resume();
|
||||
#ifdef SIMPLEFOC_STM32_DEBUG
|
||||
SIMPLEFOC_DEBUG("STM32-DRV: Starting timer ", getTimerNumber(get_timer_index(timers_to_start[i]->getHandle()->Instance)));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void _alignTimersNew() {
|
||||
int numTimers = 0;
|
||||
HardwareTimer *timers[numTimerPinsUsed];
|
||||
|
||||
// reset timer counters
|
||||
for (int i=0; i<numTimerPinsUsed; i++) {
|
||||
uint32_t index = get_timer_index((TIM_TypeDef*)timerPinsUsed[i]->peripheral);
|
||||
HardwareTimer *timer = (HardwareTimer *)(HardwareTimer_Handle[index]->__this);
|
||||
bool found = false;
|
||||
for (int j=0; j<numTimers; j++) {
|
||||
if (timers[j] == timer) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
timers[numTimers++] = timer;
|
||||
}
|
||||
|
||||
// enable timer clock
|
||||
for (int i=0; i<numTimers; i++) {
|
||||
timers[i]->pause();
|
||||
timers[i]->refresh();
|
||||
#ifdef SIMPLEFOC_STM32_DEBUG
|
||||
SIMPLEFOC_DEBUG("STM32-DRV: Restarting timer ", getTimerNumber(get_timer_index(timers[i]->getHandle()->Instance)));
|
||||
#endif
|
||||
}
|
||||
|
||||
for (int i=0; i<numTimers; i++) {
|
||||
timers[i]->resume();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// configure hardware 6pwm for a complementary pair of channels
|
||||
STM32DriverParams* _initHardware6PWMPair(long PWM_freq, float dead_zone, PinMap* pinH, PinMap* pinL, STM32DriverParams* params, int paramsPos) {
|
||||
// sanity check
|
||||
if (pinH==NP || pinL==NP)
|
||||
return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
#if defined(STM32L0xx) // L0 boards dont have hardware 6pwm interface
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED; // return nothing
|
||||
#endif
|
||||
|
||||
uint32_t channel1 = STM_PIN_CHANNEL(pinH->function);
|
||||
uint32_t channel2 = STM_PIN_CHANNEL(pinL->function);
|
||||
|
||||
// more sanity check
|
||||
if (channel1!=channel2 || pinH->peripheral!=pinL->peripheral)
|
||||
return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
uint32_t index = get_timer_index((TIM_TypeDef*)pinH->peripheral);
|
||||
|
||||
if (HardwareTimer_Handle[index] == NULL) {
|
||||
HardwareTimer_Handle[index]->__this = new HardwareTimer((TIM_TypeDef*)pinH->peripheral);
|
||||
HardwareTimer_Handle[index]->handle.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED3;
|
||||
HardwareTimer_Handle[index]->handle.Init.RepetitionCounter = 1;
|
||||
// HardwareTimer_Handle[index]->handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
|
||||
HAL_TIM_Base_Init(&(HardwareTimer_Handle[index]->handle));
|
||||
((HardwareTimer *)HardwareTimer_Handle[index]->__this)->setOverflow((uint32_t)PWM_freq, HERTZ_FORMAT);
|
||||
}
|
||||
HardwareTimer *HT = (HardwareTimer *)(HardwareTimer_Handle[index]->__this);
|
||||
|
||||
HT->setMode(channel1, TIMER_OUTPUT_COMPARE_PWM1, pinH->pin);
|
||||
HT->setMode(channel2, TIMER_OUTPUT_COMPARE_PWM1, pinL->pin);
|
||||
|
||||
// dead time is set in nanoseconds
|
||||
uint32_t dead_time_ns = (float)(1e9f/PWM_freq)*dead_zone;
|
||||
uint32_t dead_time = __LL_TIM_CALC_DEADTIME(SystemCoreClock, LL_TIM_GetClockDivision(HT->getHandle()->Instance), dead_time_ns);
|
||||
if (dead_time>255) dead_time = 255;
|
||||
if (dead_time==0 && dead_zone>0) {
|
||||
dead_time = 255; // LL_TIM_CALC_DEADTIME returns 0 if dead_time_ns is too large
|
||||
SIMPLEFOC_DEBUG("STM32-DRV: WARN: dead time too large, setting to max");
|
||||
}
|
||||
LL_TIM_OC_SetDeadTime(HT->getHandle()->Instance, dead_time); // deadtime is non linear!
|
||||
#if SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH==false
|
||||
LL_TIM_OC_SetPolarity(HT->getHandle()->Instance, getLLChannel(pinH), LL_TIM_OCPOLARITY_LOW);
|
||||
#endif
|
||||
#if SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH==false
|
||||
LL_TIM_OC_SetPolarity(HT->getHandle()->Instance, getLLChannel(pinL), LL_TIM_OCPOLARITY_LOW);
|
||||
#endif
|
||||
LL_TIM_CC_EnableChannel(HT->getHandle()->Instance, getLLChannel(pinH) | getLLChannel(pinL));
|
||||
HT->pause();
|
||||
|
||||
// make sure timer output goes to LOW when timer channels are temporarily disabled
|
||||
LL_TIM_SetOffStates(HT->getHandle()->Instance, LL_TIM_OSSI_DISABLE, LL_TIM_OSSR_ENABLE);
|
||||
|
||||
params->timers[paramsPos] = HT;
|
||||
params->timers[paramsPos+1] = HT;
|
||||
params->channels[paramsPos] = channel1;
|
||||
params->channels[paramsPos+1] = channel2;
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
STM32DriverParams* _initHardware6PWMInterface(long PWM_freq, float dead_zone, PinMap* pinA_h, PinMap* pinA_l, PinMap* pinB_h, PinMap* pinB_l, PinMap* pinC_h, PinMap* pinC_l) {
|
||||
STM32DriverParams* params = new STM32DriverParams {
|
||||
.timers = { NP, NP, NP, NP, NP, NP },
|
||||
.channels = { 0, 0, 0, 0, 0, 0 },
|
||||
.pwm_frequency = PWM_freq,
|
||||
.dead_zone = dead_zone,
|
||||
.interface_type = _HARDWARE_6PWM
|
||||
};
|
||||
|
||||
if (_initHardware6PWMPair(PWM_freq, dead_zone, pinA_h, pinA_l, params, 0) == SIMPLEFOC_DRIVER_INIT_FAILED)
|
||||
return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
if(_initHardware6PWMPair(PWM_freq, dead_zone, pinB_h, pinB_l, params, 2) == SIMPLEFOC_DRIVER_INIT_FAILED)
|
||||
return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
if (_initHardware6PWMPair(PWM_freq, dead_zone, pinC_h, pinC_l, params, 4) == SIMPLEFOC_DRIVER_INIT_FAILED)
|
||||
return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
timer combination scoring function
|
||||
assigns a score, and also checks the combination is valid
|
||||
returns <0 if combination is invalid, >=0 if combination is valid. lower (but positive) score is better
|
||||
for 6 pwm, hardware 6-pwm is preferred over software 6-pwm
|
||||
hardware 6-pwm is possible if each low channel is the inverted counterpart of its high channel
|
||||
inverted channels are not allowed except when using hardware 6-pwm (in theory they could be but lets not complicate things)
|
||||
*/
|
||||
int scoreCombination(int numPins, PinMap* pinTimers[]) {
|
||||
// check already used - TODO move this to outer loop also...
|
||||
for (int i=0; i<numTimerPinsUsed; i++) {
|
||||
if (pinTimers[i]->peripheral == timerPinsUsed[i]->peripheral
|
||||
&& STM_PIN_CHANNEL(pinTimers[i]->function) == STM_PIN_CHANNEL(timerPinsUsed[i]->function))
|
||||
return -2; // bad combination - timer channel already used
|
||||
}
|
||||
|
||||
// TODO LPTIM and HRTIM should be ignored for now
|
||||
|
||||
// check for inverted channels
|
||||
if (numPins < 6) {
|
||||
for (int i=0; i<numPins; i++) {
|
||||
if (STM_PIN_INVERTED(pinTimers[i]->function))
|
||||
return -3; // bad combination - inverted channel used in non-hardware 6pwm
|
||||
}
|
||||
}
|
||||
// check for duplicated channels
|
||||
for (int i=0; i<numPins-1; i++) {
|
||||
for (int j=i+1; j<numPins; j++) {
|
||||
if (pinTimers[i]->peripheral == pinTimers[j]->peripheral
|
||||
&& STM_PIN_CHANNEL(pinTimers[i]->function) == STM_PIN_CHANNEL(pinTimers[j]->function)
|
||||
&& STM_PIN_INVERTED(pinTimers[i]->function) == STM_PIN_INVERTED(pinTimers[j]->function))
|
||||
return -4; // bad combination - duplicated channel
|
||||
}
|
||||
}
|
||||
int score = 0;
|
||||
for (int i=0; i<numPins; i++) {
|
||||
// count distinct timers
|
||||
bool found = false;
|
||||
for (int j=i+1; j<numPins; j++) {
|
||||
if (pinTimers[i]->peripheral == pinTimers[j]->peripheral)
|
||||
found = true;
|
||||
}
|
||||
if (!found) score++;
|
||||
}
|
||||
if (numPins==6) {
|
||||
// check for inverted channels - best: 1 timer, 3 channels, 3 matching inverted channels
|
||||
// >1 timer, 3 channels, 3 matching inverted channels
|
||||
// 1 timer, 6 channels (no inverted channels)
|
||||
// >1 timer, 6 channels (no inverted channels)
|
||||
// check for inverted high-side channels - TODO is this a configuration we should allow? what if all 3 high side channels are inverted and the low-side non-inverted?
|
||||
if (STM_PIN_INVERTED(pinTimers[0]->function) || STM_PIN_INVERTED(pinTimers[2]->function) || STM_PIN_INVERTED(pinTimers[4]->function))
|
||||
return -5; // bad combination - inverted channel used on high-side channel
|
||||
if (pinTimers[0]->peripheral == pinTimers[1]->peripheral
|
||||
&& pinTimers[2]->peripheral == pinTimers[3]->peripheral
|
||||
&& pinTimers[4]->peripheral == pinTimers[5]->peripheral
|
||||
&& STM_PIN_CHANNEL(pinTimers[0]->function) == STM_PIN_CHANNEL(pinTimers[1]->function)
|
||||
&& STM_PIN_CHANNEL(pinTimers[2]->function) == STM_PIN_CHANNEL(pinTimers[3]->function)
|
||||
&& STM_PIN_CHANNEL(pinTimers[4]->function) == STM_PIN_CHANNEL(pinTimers[5]->function)
|
||||
&& STM_PIN_INVERTED(pinTimers[1]->function) && STM_PIN_INVERTED(pinTimers[3]->function) && STM_PIN_INVERTED(pinTimers[5]->function)) {
|
||||
// hardware 6pwm, score <10
|
||||
|
||||
// TODO F37xxx doesn't support dead-time insertion, it has no TIM1/TIM8
|
||||
// F301, F302 --> 6 channels, but only 1-3 have dead-time insertion
|
||||
// TIM2/TIM3/TIM4/TIM5 don't do dead-time insertion
|
||||
// TIM15/TIM16/TIM17 do dead-time insertion only on channel 1
|
||||
|
||||
// TODO check these defines
|
||||
#if defined(STM32F4xx_HAL_TIM_H) || defined(STM32F3xx_HAL_TIM_H) || defined(STM32F2xx_HAL_TIM_H) || defined(STM32F1xx_HAL_TIM_H) || defined(STM32F100_HAL_TIM_H) || defined(STM32FG0x1_HAL_TIM_H) || defined(STM32G0x0_HAL_TIM_H)
|
||||
if (STM_PIN_CHANNEL(pinTimers[0]->function)>3 || STM_PIN_CHANNEL(pinTimers[2]->function)>3 || STM_PIN_CHANNEL(pinTimers[4]->function)>3 )
|
||||
return -8; // channel 4 does not have dead-time insertion
|
||||
#endif
|
||||
#ifdef STM32G4xx_HAL_TIM_H
|
||||
if (STM_PIN_CHANNEL(pinTimers[0]->function)>4 || STM_PIN_CHANNEL(pinTimers[2]->function)>4 || STM_PIN_CHANNEL(pinTimers[4]->function)>4 )
|
||||
return -8; // channels 5 & 6 do not have dead-time insertion
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
// check for inverted low-side channels
|
||||
if (STM_PIN_INVERTED(pinTimers[1]->function) || STM_PIN_INVERTED(pinTimers[3]->function) || STM_PIN_INVERTED(pinTimers[5]->function))
|
||||
return -6; // bad combination - inverted channel used on low-side channel in software 6-pwm
|
||||
if (pinTimers[0]->peripheral != pinTimers[1]->peripheral
|
||||
|| pinTimers[2]->peripheral != pinTimers[3]->peripheral
|
||||
|| pinTimers[4]->peripheral != pinTimers[5]->peripheral)
|
||||
return -7; // bad combination - non-matching timers for H/L side in software 6-pwm
|
||||
score += 10; // software 6pwm, score >10
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int findIndexOfFirstPinMapEntry(int pin) {
|
||||
PinName pinName = digitalPinToPinName(pin);
|
||||
int i = 0;
|
||||
while (PinMap_TIM[i].pin!=NC) {
|
||||
if (pinName == PinMap_TIM[i].pin)
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int findIndexOfLastPinMapEntry(int pin) {
|
||||
PinName pinName = digitalPinToPinName(pin);
|
||||
int i = 0;
|
||||
while (PinMap_TIM[i].pin!=NC) {
|
||||
if ( pinName == (PinMap_TIM[i].pin & ~ALTX_MASK)
|
||||
&& pinName != (PinMap_TIM[i+1].pin & ~ALTX_MASK))
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define NOT_FOUND 1000
|
||||
|
||||
int findBestTimerCombination(int numPins, int index, int pins[], PinMap* pinTimers[]) {
|
||||
PinMap* searchArray[numPins];
|
||||
for (int j=0;j<numPins;j++)
|
||||
searchArray[j] = pinTimers[j];
|
||||
int bestScore = NOT_FOUND;
|
||||
int startIndex = findIndexOfFirstPinMapEntry(pins[index]);
|
||||
int endIndex = findIndexOfLastPinMapEntry(pins[index]);
|
||||
if (startIndex == -1 || endIndex == -1) {
|
||||
SIMPLEFOC_DEBUG("STM32-DRV: ERR: no timer on pin ", pins[index]);
|
||||
return -1; // pin is not connected to any timer
|
||||
}
|
||||
for (int i=startIndex;i<=endIndex;i++) {
|
||||
searchArray[index] = (PinMap*)&PinMap_TIM[i];
|
||||
int score = NOT_FOUND;
|
||||
if (index<numPins-1)
|
||||
score = findBestTimerCombination(numPins, index+1, pins, searchArray);
|
||||
else {
|
||||
score = scoreCombination(numPins, searchArray);
|
||||
#ifdef SIMPLEFOC_STM32_DEBUG
|
||||
printTimerCombination(numPins, searchArray, score);
|
||||
#endif
|
||||
}
|
||||
if (score==-1)
|
||||
return -1; // pin not connected to any timer, propagate driectly
|
||||
if (score>=0 && score<bestScore) {
|
||||
bestScore = score;
|
||||
for (int j=index;j<numPins;j++)
|
||||
pinTimers[j] = searchArray[j];
|
||||
}
|
||||
}
|
||||
return bestScore;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int findBestTimerCombination(int numPins, int pins[], PinMap* pinTimers[]) {
|
||||
int bestScore = findBestTimerCombination(numPins, 0, pins, pinTimers);
|
||||
if (bestScore == NOT_FOUND) {
|
||||
#ifdef SIMPLEFOC_STM32_DEBUG
|
||||
SimpleFOCDebug::println("STM32-DRV: no workable combination found on these pins");
|
||||
#endif
|
||||
return -10; // no workable combination found
|
||||
}
|
||||
else if (bestScore >= 0) {
|
||||
#ifdef SIMPLEFOC_STM32_DEBUG
|
||||
SimpleFOCDebug::print("STM32-DRV: best: ");
|
||||
printTimerCombination(numPins, pinTimers, bestScore);
|
||||
#endif
|
||||
}
|
||||
return bestScore;
|
||||
};
|
||||
|
||||
|
||||
|
||||
void* _configure1PWM(long pwm_frequency, const int pinA) {
|
||||
if (numTimerPinsUsed+1 > SIMPLEFOC_STM32_MAX_PINTIMERSUSED) {
|
||||
SIMPLEFOC_DEBUG("STM32-DRV: ERR: too many pins used");
|
||||
return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
|
||||
if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
// center-aligned frequency is uses two periods
|
||||
pwm_frequency *=2;
|
||||
|
||||
int pins[1] = { pinA };
|
||||
PinMap* pinTimers[1] = { NP };
|
||||
if (findBestTimerCombination(1, pins, pinTimers)<0)
|
||||
return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
HardwareTimer* HT1 = _initPinPWM(pwm_frequency, pinTimers[0]);\
|
||||
// allign the timers
|
||||
_alignTimersNew();
|
||||
|
||||
uint32_t channel1 = STM_PIN_CHANNEL(pinTimers[0]->function);
|
||||
|
||||
STM32DriverParams* params = new STM32DriverParams {
|
||||
.timers = { HT1 },
|
||||
.channels = { channel1 },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
timerPinsUsed[numTimerPinsUsed++] = pinTimers[0];
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure2PWM(long pwm_frequency, const int pinA, const int pinB) {
|
||||
if (numTimerPinsUsed+2 > SIMPLEFOC_STM32_MAX_PINTIMERSUSED) {
|
||||
SIMPLEFOC_DEBUG("STM32-DRV: ERR: too many pins used");
|
||||
return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
|
||||
if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
// center-aligned frequency is uses two periods
|
||||
pwm_frequency *=2;
|
||||
|
||||
int pins[2] = { pinA, pinB };
|
||||
PinMap* pinTimers[2] = { NP, NP };
|
||||
if (findBestTimerCombination(2, pins, pinTimers)<0)
|
||||
return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
HardwareTimer* HT1 = _initPinPWM(pwm_frequency, pinTimers[0]);
|
||||
HardwareTimer* HT2 = _initPinPWM(pwm_frequency, pinTimers[1]);
|
||||
// allign the timers
|
||||
_alignPWMTimers(HT1, HT2, HT2);
|
||||
|
||||
uint32_t channel1 = STM_PIN_CHANNEL(pinTimers[0]->function);
|
||||
uint32_t channel2 = STM_PIN_CHANNEL(pinTimers[1]->function);
|
||||
|
||||
STM32DriverParams* params = new STM32DriverParams {
|
||||
.timers = { HT1, HT2 },
|
||||
.channels = { channel1, channel2 },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
timerPinsUsed[numTimerPinsUsed++] = pinTimers[0];
|
||||
timerPinsUsed[numTimerPinsUsed++] = pinTimers[1];
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
|
||||
if (numTimerPinsUsed+3 > SIMPLEFOC_STM32_MAX_PINTIMERSUSED) {
|
||||
SIMPLEFOC_DEBUG("STM32-DRV: ERR: too many pins used");
|
||||
return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
// center-aligned frequency is uses two periods
|
||||
pwm_frequency *=2;
|
||||
|
||||
int pins[3] = { pinA, pinB, pinC };
|
||||
PinMap* pinTimers[3] = { NP, NP, NP };
|
||||
if (findBestTimerCombination(3, pins, pinTimers)<0)
|
||||
return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
HardwareTimer* HT1 = _initPinPWM(pwm_frequency, pinTimers[0]);
|
||||
HardwareTimer* HT2 = _initPinPWM(pwm_frequency, pinTimers[1]);
|
||||
HardwareTimer* HT3 = _initPinPWM(pwm_frequency, pinTimers[2]);
|
||||
|
||||
uint32_t channel1 = STM_PIN_CHANNEL(pinTimers[0]->function);
|
||||
uint32_t channel2 = STM_PIN_CHANNEL(pinTimers[1]->function);
|
||||
uint32_t channel3 = STM_PIN_CHANNEL(pinTimers[2]->function);
|
||||
|
||||
STM32DriverParams* params = new STM32DriverParams {
|
||||
.timers = { HT1, HT2, HT3 },
|
||||
.channels = { channel1, channel2, channel3 },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
timerPinsUsed[numTimerPinsUsed++] = pinTimers[0];
|
||||
timerPinsUsed[numTimerPinsUsed++] = pinTimers[1];
|
||||
timerPinsUsed[numTimerPinsUsed++] = pinTimers[2];
|
||||
|
||||
_alignTimersNew();
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure4PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC, const int pinD) {
|
||||
if (numTimerPinsUsed+4 > SIMPLEFOC_STM32_MAX_PINTIMERSUSED) {
|
||||
SIMPLEFOC_DEBUG("STM32-DRV: ERR: too many pins used");
|
||||
return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
// center-aligned frequency is uses two periods
|
||||
pwm_frequency *=2;
|
||||
|
||||
int pins[4] = { pinA, pinB, pinC, pinD };
|
||||
PinMap* pinTimers[4] = { NP, NP, NP, NP };
|
||||
if (findBestTimerCombination(4, pins, pinTimers)<0)
|
||||
return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
HardwareTimer* HT1 = _initPinPWM(pwm_frequency, pinTimers[0]);
|
||||
HardwareTimer* HT2 = _initPinPWM(pwm_frequency, pinTimers[1]);
|
||||
HardwareTimer* HT3 = _initPinPWM(pwm_frequency, pinTimers[2]);
|
||||
HardwareTimer* HT4 = _initPinPWM(pwm_frequency, pinTimers[3]);
|
||||
// allign the timers
|
||||
_alignPWMTimers(HT1, HT2, HT3, HT4);
|
||||
|
||||
uint32_t channel1 = STM_PIN_CHANNEL(pinTimers[0]->function);
|
||||
uint32_t channel2 = STM_PIN_CHANNEL(pinTimers[1]->function);
|
||||
uint32_t channel3 = STM_PIN_CHANNEL(pinTimers[2]->function);
|
||||
uint32_t channel4 = STM_PIN_CHANNEL(pinTimers[3]->function);
|
||||
|
||||
STM32DriverParams* params = new STM32DriverParams {
|
||||
.timers = { HT1, HT2, HT3, HT4 },
|
||||
.channels = { channel1, channel2, channel3, channel4 },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
timerPinsUsed[numTimerPinsUsed++] = pinTimers[0];
|
||||
timerPinsUsed[numTimerPinsUsed++] = pinTimers[1];
|
||||
timerPinsUsed[numTimerPinsUsed++] = pinTimers[2];
|
||||
timerPinsUsed[numTimerPinsUsed++] = pinTimers[3];
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - DC motor - 1PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle1PWM(float dc_a, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
_setPwm(((STM32DriverParams*)params)->timers[0], ((STM32DriverParams*)params)->channels[0], _PWM_RANGE*dc_a, _PWM_RESOLUTION);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 2PWM setting
|
||||
//- hardware speciffic
|
||||
void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params){
|
||||
// transform duty cycle from [0,1] to [0,4095]
|
||||
_setPwm(((STM32DriverParams*)params)->timers[0], ((STM32DriverParams*)params)->channels[0], _PWM_RANGE*dc_a, _PWM_RESOLUTION);
|
||||
_setPwm(((STM32DriverParams*)params)->timers[1], ((STM32DriverParams*)params)->channels[1], _PWM_RANGE*dc_b, _PWM_RESOLUTION);
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - BLDC motor - 3PWM setting
|
||||
//- hardware speciffic
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params){
|
||||
// transform duty cycle from [0,1] to [0,4095]
|
||||
_setPwm(((STM32DriverParams*)params)->timers[0], ((STM32DriverParams*)params)->channels[0], _PWM_RANGE*dc_a, _PWM_RESOLUTION);
|
||||
_setPwm(((STM32DriverParams*)params)->timers[1], ((STM32DriverParams*)params)->channels[1], _PWM_RANGE*dc_b, _PWM_RESOLUTION);
|
||||
_setPwm(((STM32DriverParams*)params)->timers[2], ((STM32DriverParams*)params)->channels[2], _PWM_RANGE*dc_c, _PWM_RESOLUTION);
|
||||
}
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 4PWM setting
|
||||
//- hardware speciffic
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params){
|
||||
// transform duty cycle from [0,1] to [0,4095]
|
||||
_setPwm(((STM32DriverParams*)params)->timers[0], ((STM32DriverParams*)params)->channels[0], _PWM_RANGE*dc_1a, _PWM_RESOLUTION);
|
||||
_setPwm(((STM32DriverParams*)params)->timers[1], ((STM32DriverParams*)params)->channels[1], _PWM_RANGE*dc_1b, _PWM_RESOLUTION);
|
||||
_setPwm(((STM32DriverParams*)params)->timers[2], ((STM32DriverParams*)params)->channels[2], _PWM_RANGE*dc_2a, _PWM_RESOLUTION);
|
||||
_setPwm(((STM32DriverParams*)params)->timers[3], ((STM32DriverParams*)params)->channels[3], _PWM_RANGE*dc_2b, _PWM_RESOLUTION);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Configuring PWM frequency, resolution and alignment
|
||||
// - BLDC driver - 6PWM setting
|
||||
// - hardware specific
|
||||
void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l){
|
||||
if (numTimerPinsUsed+6 > SIMPLEFOC_STM32_MAX_PINTIMERSUSED) {
|
||||
SIMPLEFOC_DEBUG("STM32-DRV: ERR: too many pins used");
|
||||
return (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to |%0kHz max
|
||||
// center-aligned frequency is uses two periods
|
||||
pwm_frequency *=2;
|
||||
|
||||
// find configuration
|
||||
int pins[6] = { pinA_h, pinA_l, pinB_h, pinB_l, pinC_h, pinC_l };
|
||||
PinMap* pinTimers[6] = { NP, NP, NP, NP, NP, NP };
|
||||
int score = findBestTimerCombination(6, pins, pinTimers);
|
||||
|
||||
STM32DriverParams* params;
|
||||
// configure accordingly
|
||||
if (score<0)
|
||||
params = (STM32DriverParams*)SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
else if (score<10) // hardware pwm
|
||||
params = _initHardware6PWMInterface(pwm_frequency, dead_zone, pinTimers[0], pinTimers[1], pinTimers[2], pinTimers[3], pinTimers[4], pinTimers[5]);
|
||||
else { // software pwm
|
||||
HardwareTimer* HT1 = _initPinPWMHigh(pwm_frequency, pinTimers[0]);
|
||||
HardwareTimer* HT2 = _initPinPWMLow(pwm_frequency, pinTimers[1]);
|
||||
HardwareTimer* HT3 = _initPinPWMHigh(pwm_frequency, pinTimers[2]);
|
||||
HardwareTimer* HT4 = _initPinPWMLow(pwm_frequency, pinTimers[3]);
|
||||
HardwareTimer* HT5 = _initPinPWMHigh(pwm_frequency, pinTimers[4]);
|
||||
HardwareTimer* HT6 = _initPinPWMLow(pwm_frequency, pinTimers[5]);
|
||||
uint32_t channel1 = STM_PIN_CHANNEL(pinTimers[0]->function);
|
||||
uint32_t channel2 = STM_PIN_CHANNEL(pinTimers[1]->function);
|
||||
uint32_t channel3 = STM_PIN_CHANNEL(pinTimers[2]->function);
|
||||
uint32_t channel4 = STM_PIN_CHANNEL(pinTimers[3]->function);
|
||||
uint32_t channel5 = STM_PIN_CHANNEL(pinTimers[4]->function);
|
||||
uint32_t channel6 = STM_PIN_CHANNEL(pinTimers[5]->function);
|
||||
params = new STM32DriverParams {
|
||||
.timers = { HT1, HT2, HT3, HT4, HT5, HT6 },
|
||||
.channels = { channel1, channel2, channel3, channel4, channel5, channel6 },
|
||||
.pwm_frequency = pwm_frequency,
|
||||
.dead_zone = dead_zone,
|
||||
.interface_type = _SOFTWARE_6PWM
|
||||
};
|
||||
}
|
||||
if (score>=0) {
|
||||
for (int i=0; i<6; i++)
|
||||
timerPinsUsed[numTimerPinsUsed++] = pinTimers[i];
|
||||
_alignTimersNew();
|
||||
}
|
||||
return params; // success
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _setSinglePhaseState(PhaseState state, HardwareTimer *HT, int channel1,int channel2) {
|
||||
_UNUSED(channel2);
|
||||
switch (state) {
|
||||
case PhaseState::PHASE_OFF:
|
||||
// Due to a weird quirk in the arduino timer API, pauseChannel only disables the complementary channel (e.g. CC1NE).
|
||||
// To actually make the phase floating, we must also set pwm to 0.
|
||||
HT->pauseChannel(channel1);
|
||||
break;
|
||||
default:
|
||||
HT->resumeChannel(channel1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function setting the duty cycle to the pwm pin (ex. analogWrite())
|
||||
// - BLDC driver - 6PWM setting
|
||||
// - hardware specific
|
||||
void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState* phase_state, void* params){
|
||||
switch(((STM32DriverParams*)params)->interface_type){
|
||||
case _HARDWARE_6PWM:
|
||||
// phase a
|
||||
_setSinglePhaseState(phase_state[0], ((STM32DriverParams*)params)->timers[0], ((STM32DriverParams*)params)->channels[0], ((STM32DriverParams*)params)->channels[1]);
|
||||
if(phase_state[0] == PhaseState::PHASE_OFF) dc_a = 0.0f;
|
||||
_setPwm(((STM32DriverParams*)params)->timers[0], ((STM32DriverParams*)params)->channels[0], _PWM_RANGE*dc_a, _PWM_RESOLUTION);
|
||||
// phase b
|
||||
_setSinglePhaseState(phase_state[1], ((STM32DriverParams*)params)->timers[2], ((STM32DriverParams*)params)->channels[2], ((STM32DriverParams*)params)->channels[3]);
|
||||
if(phase_state[1] == PhaseState::PHASE_OFF) dc_b = 0.0f;
|
||||
_setPwm(((STM32DriverParams*)params)->timers[2], ((STM32DriverParams*)params)->channels[2], _PWM_RANGE*dc_b, _PWM_RESOLUTION);
|
||||
// phase c
|
||||
_setSinglePhaseState(phase_state[2], ((STM32DriverParams*)params)->timers[4], ((STM32DriverParams*)params)->channels[4], ((STM32DriverParams*)params)->channels[5]);
|
||||
if(phase_state[2] == PhaseState::PHASE_OFF) dc_c = 0.0f;
|
||||
_setPwm(((STM32DriverParams*)params)->timers[4], ((STM32DriverParams*)params)->channels[4], _PWM_RANGE*dc_c, _PWM_RESOLUTION);
|
||||
break;
|
||||
case _SOFTWARE_6PWM:
|
||||
float dead_zone = ((STM32DriverParams*)params)->dead_zone / 2.0f;
|
||||
if (phase_state[0] == PhaseState::PHASE_ON || phase_state[0] == PhaseState::PHASE_HI)
|
||||
_setPwm(((STM32DriverParams*)params)->timers[0], ((STM32DriverParams*)params)->channels[0], _constrain(dc_a - dead_zone, 0.0f, 1.0f)*_PWM_RANGE, _PWM_RESOLUTION);
|
||||
else
|
||||
_setPwm(((STM32DriverParams*)params)->timers[0], ((STM32DriverParams*)params)->channels[0], 0.0f, _PWM_RESOLUTION);
|
||||
if (phase_state[0] == PhaseState::PHASE_ON || phase_state[0] == PhaseState::PHASE_LO)
|
||||
_setPwm(((STM32DriverParams*)params)->timers[1], ((STM32DriverParams*)params)->channels[1], _constrain(dc_a + dead_zone, 0.0f, 1.0f)*_PWM_RANGE, _PWM_RESOLUTION);
|
||||
else
|
||||
_setPwm(((STM32DriverParams*)params)->timers[1], ((STM32DriverParams*)params)->channels[1], 0.0f, _PWM_RESOLUTION);
|
||||
|
||||
if (phase_state[1] == PhaseState::PHASE_ON || phase_state[1] == PhaseState::PHASE_HI)
|
||||
_setPwm(((STM32DriverParams*)params)->timers[2], ((STM32DriverParams*)params)->channels[2], _constrain(dc_b - dead_zone, 0.0f, 1.0f)*_PWM_RANGE, _PWM_RESOLUTION);
|
||||
else
|
||||
_setPwm(((STM32DriverParams*)params)->timers[2], ((STM32DriverParams*)params)->channels[2], 0.0f, _PWM_RESOLUTION);
|
||||
if (phase_state[1] == PhaseState::PHASE_ON || phase_state[1] == PhaseState::PHASE_LO)
|
||||
_setPwm(((STM32DriverParams*)params)->timers[3], ((STM32DriverParams*)params)->channels[3], _constrain(dc_b + dead_zone, 0.0f, 1.0f)*_PWM_RANGE, _PWM_RESOLUTION);
|
||||
else
|
||||
_setPwm(((STM32DriverParams*)params)->timers[3], ((STM32DriverParams*)params)->channels[3], 0.0f, _PWM_RESOLUTION);
|
||||
|
||||
if (phase_state[2] == PhaseState::PHASE_ON || phase_state[2] == PhaseState::PHASE_HI)
|
||||
_setPwm(((STM32DriverParams*)params)->timers[4], ((STM32DriverParams*)params)->channels[4], _constrain(dc_c - dead_zone, 0.0f, 1.0f)*_PWM_RANGE, _PWM_RESOLUTION);
|
||||
else
|
||||
_setPwm(((STM32DriverParams*)params)->timers[4], ((STM32DriverParams*)params)->channels[4], 0.0f, _PWM_RESOLUTION);
|
||||
if (phase_state[2] == PhaseState::PHASE_ON || phase_state[2] == PhaseState::PHASE_LO)
|
||||
_setPwm(((STM32DriverParams*)params)->timers[5], ((STM32DriverParams*)params)->channels[5], _constrain(dc_c + dead_zone, 0.0f, 1.0f)*_PWM_RANGE, _PWM_RESOLUTION);
|
||||
else
|
||||
_setPwm(((STM32DriverParams*)params)->timers[5], ((STM32DriverParams*)params)->channels[5], 0.0f, _PWM_RESOLUTION);
|
||||
break;
|
||||
}
|
||||
_UNUSED(phase_state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef SIMPLEFOC_STM32_DEBUG
|
||||
|
||||
int getTimerNumber(int timerIndex) {
|
||||
#if defined(TIM1_BASE)
|
||||
if (timerIndex==TIMER1_INDEX) return 1;
|
||||
#endif
|
||||
#if defined(TIM2_BASE)
|
||||
if (timerIndex==TIMER2_INDEX) return 2;
|
||||
#endif
|
||||
#if defined(TIM3_BASE)
|
||||
if (timerIndex==TIMER3_INDEX) return 3;
|
||||
#endif
|
||||
#if defined(TIM4_BASE)
|
||||
if (timerIndex==TIMER4_INDEX) return 4;
|
||||
#endif
|
||||
#if defined(TIM5_BASE)
|
||||
if (timerIndex==TIMER5_INDEX) return 5;
|
||||
#endif
|
||||
#if defined(TIM6_BASE)
|
||||
if (timerIndex==TIMER6_INDEX) return 6;
|
||||
#endif
|
||||
#if defined(TIM7_BASE)
|
||||
if (timerIndex==TIMER7_INDEX) return 7;
|
||||
#endif
|
||||
#if defined(TIM8_BASE)
|
||||
if (timerIndex==TIMER8_INDEX) return 8;
|
||||
#endif
|
||||
#if defined(TIM9_BASE)
|
||||
if (timerIndex==TIMER9_INDEX) return 9;
|
||||
#endif
|
||||
#if defined(TIM10_BASE)
|
||||
if (timerIndex==TIMER10_INDEX) return 10;
|
||||
#endif
|
||||
#if defined(TIM11_BASE)
|
||||
if (timerIndex==TIMER11_INDEX) return 11;
|
||||
#endif
|
||||
#if defined(TIM12_BASE)
|
||||
if (timerIndex==TIMER12_INDEX) return 12;
|
||||
#endif
|
||||
#if defined(TIM13_BASE)
|
||||
if (timerIndex==TIMER13_INDEX) return 13;
|
||||
#endif
|
||||
#if defined(TIM14_BASE)
|
||||
if (timerIndex==TIMER14_INDEX) return 14;
|
||||
#endif
|
||||
#if defined(TIM15_BASE)
|
||||
if (timerIndex==TIMER15_INDEX) return 15;
|
||||
#endif
|
||||
#if defined(TIM16_BASE)
|
||||
if (timerIndex==TIMER16_INDEX) return 16;
|
||||
#endif
|
||||
#if defined(TIM17_BASE)
|
||||
if (timerIndex==TIMER17_INDEX) return 17;
|
||||
#endif
|
||||
#if defined(TIM18_BASE)
|
||||
if (timerIndex==TIMER18_INDEX) return 18;
|
||||
#endif
|
||||
#if defined(TIM19_BASE)
|
||||
if (timerIndex==TIMER19_INDEX) return 19;
|
||||
#endif
|
||||
#if defined(TIM20_BASE)
|
||||
if (timerIndex==TIMER20_INDEX) return 20;
|
||||
#endif
|
||||
#if defined(TIM21_BASE)
|
||||
if (timerIndex==TIMER21_INDEX) return 21;
|
||||
#endif
|
||||
#if defined(TIM22_BASE)
|
||||
if (timerIndex==TIMER22_INDEX) return 22;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void printTimerCombination(int numPins, PinMap* timers[], int score) {
|
||||
for (int i=0; i<numPins; i++) {
|
||||
if (timers[i] == NP)
|
||||
SimpleFOCDebug::print("NP");
|
||||
else {
|
||||
SimpleFOCDebug::print("TIM");
|
||||
SimpleFOCDebug::print(getTimerNumber(get_timer_index((TIM_TypeDef*)timers[i]->peripheral)));
|
||||
SimpleFOCDebug::print("-CH");
|
||||
SimpleFOCDebug::print(STM_PIN_CHANNEL(timers[i]->function));
|
||||
if (STM_PIN_INVERTED(timers[i]->function))
|
||||
SimpleFOCDebug::print("N");
|
||||
}
|
||||
SimpleFOCDebug::print(" ");
|
||||
}
|
||||
SimpleFOCDebug::println("score: ", score);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef STM32_DRIVER_MCU_DEF
|
||||
#define STM32_DRIVER_MCU_DEF
|
||||
#include "../../hardware_api.h"
|
||||
|
||||
#if defined(_STM32_DEF_)
|
||||
|
||||
// default pwm parameters
|
||||
#define _PWM_RESOLUTION 12 // 12bit
|
||||
#define _PWM_RANGE 4095.0f // 2^12 -1 = 4095
|
||||
#define _PWM_FREQUENCY 25000 // 25khz
|
||||
#define _PWM_FREQUENCY_MAX 50000 // 50khz
|
||||
|
||||
// 6pwm parameters
|
||||
#define _HARDWARE_6PWM 1
|
||||
#define _SOFTWARE_6PWM 0
|
||||
#define _ERROR_6PWM -1
|
||||
|
||||
|
||||
typedef struct STM32DriverParams {
|
||||
HardwareTimer* timers[6] = {NULL};
|
||||
uint32_t channels[6];
|
||||
long pwm_frequency;
|
||||
float dead_zone;
|
||||
uint8_t interface_type;
|
||||
} STM32DriverParams;
|
||||
|
||||
// timer synchornisation functions
|
||||
void _stopTimers(HardwareTimer **timers_to_stop, int timer_num=6);
|
||||
void _startTimers(HardwareTimer **timers_to_start, int timer_num=6);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,228 @@
|
||||
#include "teensy_mcu.h"
|
||||
|
||||
// if defined
|
||||
// - Teensy 3.0 MK20DX128
|
||||
// - Teensy 3.1/3.2 MK20DX256
|
||||
// - Teensy 3.5 MK20DX128
|
||||
// - Teensy LC MKL26Z64
|
||||
// - Teensy 3.5 MK64FX512
|
||||
// - Teensy 3.6 MK66FX1M0
|
||||
#if defined(__arm__) && defined(CORE_TEENSY) && (defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) || defined(__MK64FX512__) || defined(__MK66FX1M0__))
|
||||
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for Teensy 3.x")
|
||||
#pragma message("")
|
||||
|
||||
// pin definition from https://github.com/PaulStoffregen/cores/blob/286511f3ec849a6c9e0ec8b73ad6a2fada52e44c/teensy3/pins_teensy.c
|
||||
#if defined(__MK20DX128__)
|
||||
#define FTM0_CH0_PIN 22
|
||||
#define FTM0_CH1_PIN 23
|
||||
#define FTM0_CH2_PIN 9
|
||||
#define FTM0_CH3_PIN 10
|
||||
#define FTM0_CH4_PIN 6
|
||||
#define FTM0_CH5_PIN 20
|
||||
#define FTM0_CH6_PIN 21
|
||||
#define FTM0_CH7_PIN 5
|
||||
#define FTM1_CH0_PIN 3
|
||||
#define FTM1_CH1_PIN 4
|
||||
#elif defined(__MK20DX256__)
|
||||
#define FTM0_CH0_PIN 22
|
||||
#define FTM0_CH1_PIN 23
|
||||
#define FTM0_CH2_PIN 9
|
||||
#define FTM0_CH3_PIN 10
|
||||
#define FTM0_CH4_PIN 6
|
||||
#define FTM0_CH5_PIN 20
|
||||
#define FTM0_CH6_PIN 21
|
||||
#define FTM0_CH7_PIN 5
|
||||
#define FTM1_CH0_PIN 3
|
||||
#define FTM1_CH1_PIN 4
|
||||
#define FTM2_CH0_PIN 32
|
||||
#define FTM2_CH1_PIN 25
|
||||
#elif defined(__MKL26Z64__)
|
||||
#define FTM0_CH0_PIN 22
|
||||
#define FTM0_CH1_PIN 23
|
||||
#define FTM0_CH2_PIN 9
|
||||
#define FTM0_CH3_PIN 10
|
||||
#define FTM0_CH4_PIN 6
|
||||
#define FTM0_CH5_PIN 20
|
||||
#define FTM1_CH0_PIN 16
|
||||
#define FTM1_CH1_PIN 17
|
||||
#define FTM2_CH0_PIN 3
|
||||
#define FTM2_CH1_PIN 4
|
||||
#elif defined(__MK64FX512__)
|
||||
#define FTM0_CH0_PIN 22
|
||||
#define FTM0_CH1_PIN 23
|
||||
#define FTM0_CH2_PIN 9
|
||||
#define FTM0_CH3_PIN 10
|
||||
#define FTM0_CH4_PIN 6
|
||||
#define FTM0_CH5_PIN 20
|
||||
#define FTM0_CH6_PIN 21
|
||||
#define FTM0_CH7_PIN 5
|
||||
#define FTM1_CH0_PIN 3
|
||||
#define FTM1_CH1_PIN 4
|
||||
#define FTM2_CH0_PIN 29
|
||||
#define FTM2_CH1_PIN 30
|
||||
#define FTM3_CH0_PIN 2
|
||||
#define FTM3_CH1_PIN 14
|
||||
#define FTM3_CH2_PIN 7
|
||||
#define FTM3_CH3_PIN 8
|
||||
#define FTM3_CH4_PIN 35
|
||||
#define FTM3_CH5_PIN 36
|
||||
#define FTM3_CH6_PIN 37
|
||||
#define FTM3_CH7_PIN 38
|
||||
#elif defined(__MK66FX1M0__)
|
||||
#define FTM0_CH0_PIN 22
|
||||
#define FTM0_CH1_PIN 23
|
||||
#define FTM0_CH2_PIN 9
|
||||
#define FTM0_CH3_PIN 10
|
||||
#define FTM0_CH4_PIN 6
|
||||
#define FTM0_CH5_PIN 20
|
||||
#define FTM0_CH6_PIN 21
|
||||
#define FTM0_CH7_PIN 5
|
||||
#define FTM1_CH0_PIN 3
|
||||
#define FTM1_CH1_PIN 4
|
||||
#define FTM2_CH0_PIN 29
|
||||
#define FTM2_CH1_PIN 30
|
||||
#define FTM3_CH0_PIN 2
|
||||
#define FTM3_CH1_PIN 14
|
||||
#define FTM3_CH2_PIN 7
|
||||
#define FTM3_CH3_PIN 8
|
||||
#define FTM3_CH4_PIN 35
|
||||
#define FTM3_CH5_PIN 36
|
||||
#define FTM3_CH6_PIN 37
|
||||
#define FTM3_CH7_PIN 38
|
||||
#define TPM1_CH0_PIN 16
|
||||
#define TPM1_CH1_PIN 17
|
||||
#endif
|
||||
|
||||
int _findTimer( const int Ah, const int Al, const int Bh, const int Bl, const int Ch, const int Cl){
|
||||
|
||||
if((Ah == FTM0_CH0_PIN && Al == FTM0_CH1_PIN) ||
|
||||
(Ah == FTM0_CH2_PIN && Al == FTM0_CH3_PIN) ||
|
||||
(Ah == FTM0_CH4_PIN && Al == FTM0_CH5_PIN) ){
|
||||
if((Bh == FTM0_CH0_PIN && Bl == FTM0_CH1_PIN) ||
|
||||
(Bh == FTM0_CH2_PIN && Bl == FTM0_CH3_PIN) ||
|
||||
(Bh == FTM0_CH4_PIN && Bl == FTM0_CH5_PIN) ){
|
||||
if((Ch == FTM0_CH0_PIN && Cl == FTM0_CH1_PIN) ||
|
||||
(Ch == FTM0_CH2_PIN && Cl == FTM0_CH3_PIN) ||
|
||||
(Ch == FTM0_CH4_PIN && Cl == FTM0_CH5_PIN) ){
|
||||
#ifdef SIMPLEFOC_TEENSY_DEBUG
|
||||
SIMPLEFOC_DEBUG("TEENSY-DRV: Using timer FTM0.");
|
||||
#endif
|
||||
// timer FTM0
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FTM3_SC // if the board has FTM3 timer
|
||||
if((Ah == FTM3_CH0_PIN && Al == FTM3_CH1_PIN) ||
|
||||
(Ah == FTM3_CH2_PIN && Al == FTM3_CH3_PIN) ||
|
||||
(Ah == FTM3_CH4_PIN && Al == FTM3_CH5_PIN) ){
|
||||
if((Bh == FTM3_CH0_PIN && Bl == FTM3_CH1_PIN) ||
|
||||
(Bh == FTM3_CH2_PIN && Bl == FTM3_CH3_PIN) ||
|
||||
(Bh == FTM3_CH4_PIN && Bl == FTM3_CH5_PIN) ){
|
||||
if((Ch == FTM3_CH0_PIN && Cl == FTM3_CH1_PIN) ||
|
||||
(Ch == FTM3_CH2_PIN && Cl == FTM3_CH3_PIN) ||
|
||||
(Ch == FTM3_CH4_PIN && Cl == FTM3_CH5_PIN) ){
|
||||
// timer FTM3
|
||||
#ifdef SIMPLEFOC_TEENSY_DEBUG
|
||||
SIMPLEFOC_DEBUG("TEENSY-DRV: Using timer FTM3.");
|
||||
#endif
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SIMPLEFOC_TEENSY_DEBUG
|
||||
SIMPLEFOC_DEBUG("TEENSY-DRV: ERR: Pins not on timers FTM0 or FTM3!");
|
||||
#endif
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 6PWM setting
|
||||
// - hardware specific
|
||||
void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
unsigned long pwm_freq = 2*pwm_frequency; // center-aligned pwm has 4 times lower freq
|
||||
_setHighFrequency(pwm_freq, pinA_h);
|
||||
_setHighFrequency(pwm_freq, pinA_l);
|
||||
_setHighFrequency(pwm_freq, pinB_h);
|
||||
_setHighFrequency(pwm_freq, pinB_l);
|
||||
_setHighFrequency(pwm_freq, pinC_h);
|
||||
_setHighFrequency(pwm_freq, pinC_l);
|
||||
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA_h,pinA_l, pinB_h,pinB_l, pinC_h, pinC_l },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
|
||||
|
||||
int timer = _findTimer(pinA_h,pinA_l,pinB_h,pinB_l,pinC_h,pinC_l);
|
||||
if(timer<0) return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
|
||||
// find the best combination of prescalers and counter value
|
||||
double dead_time = dead_zone/pwm_freq;
|
||||
int prescaler = 1; // initial prescaler (1,4 or 16)
|
||||
double count = 1; // inital count (1 - 63)
|
||||
for (; prescaler<=16; prescaler*=4){
|
||||
count = dead_time*((double)F_CPU)/((double)prescaler);
|
||||
if(count < 64) break; // found the solution
|
||||
}
|
||||
count = _constrain(count, 1, 63);
|
||||
|
||||
// configure the timer
|
||||
if(timer==0){
|
||||
// Configure FTM0
|
||||
// // inverting and deadtime insertion for FTM1
|
||||
FTM0_COMBINE = 0x00121212; // 0x2 - complemetary mode, 0x1 - dead timer insertion enabled
|
||||
|
||||
// Deadtime config
|
||||
FTM0_DEADTIME = (int)count; // set counter - 1-63
|
||||
FTM0_DEADTIME |= ((prescaler>1) << 7) | ((prescaler>4) << 6); // set prescaler (0b01 - 1, 0b10 - 4, 0b11 - 16)
|
||||
|
||||
// configure center aligned PWM
|
||||
FTM0_SC = 0x00000028; // 0x2 - center-alignment, 0x8 - fixed clock freq
|
||||
}else if(timer==3){
|
||||
// Configure FTM3
|
||||
// inverting and deadtime insertion for FTM1
|
||||
FTM3_COMBINE = 0x00121212; // 0x2 - complemetary mode, 0x1 - dead timer insertion enabled
|
||||
|
||||
// Deadtime config
|
||||
FTM3_DEADTIME = (int)count; // set counter - 1-63
|
||||
FTM3_DEADTIME |= ((prescaler>1) << 7) | ((prescaler>4) << 6); // set prescaler (0b01 - 1, 0b10 - 4, 0b11 - 16)
|
||||
|
||||
// configure center aligned PWM
|
||||
FTM3_SC = 0x00000028; // 0x2 - center-alignment, 0x8 - fixed clock freq
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 6PWM setting
|
||||
// - hardware specific
|
||||
void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void* params){
|
||||
_UNUSED(phase_state);
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
// phase A
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_a);
|
||||
|
||||
// phase B
|
||||
analogWrite(((GenericDriverParams*)params)->pins[2], 255.0f*dc_b);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[3], 255.0f*dc_b);
|
||||
|
||||
// phase C
|
||||
analogWrite(((GenericDriverParams*)params)->pins[4], 255.0f*dc_c);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[5], 255.0f*dc_c);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,306 @@
|
||||
#include "teensy_mcu.h"
|
||||
#include "teensy4_mcu.h"
|
||||
#include "../../../communication/SimpleFOCDebug.h"
|
||||
|
||||
// if defined
|
||||
// - Teensy 4.0
|
||||
// - Teensy 4.1
|
||||
#if defined(__arm__) && defined(CORE_TEENSY) && ( defined(__IMXRT1062__) || defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) || defined(ARDUINO_TEENSY_MICROMOD) )
|
||||
|
||||
|
||||
#pragma message("")
|
||||
#pragma message("SimpleFOC: compiling for Teensy 4.x")
|
||||
#pragma message("")
|
||||
|
||||
|
||||
// half_cycle of the PWM variable
|
||||
int half_cycle = 0;
|
||||
|
||||
|
||||
// function which finds the flexpwm instance for a pair of pins
|
||||
// if they do not belong to the same timer it returns a nullpointer
|
||||
IMXRT_FLEXPWM_t* get_flexpwm(uint8_t pin, uint8_t pin1){
|
||||
|
||||
const struct pwm_pin_info_struct *info;
|
||||
if (pin >= CORE_NUM_DIGITAL || pin1 >= CORE_NUM_DIGITAL) {
|
||||
#ifdef SIMPLEFOC_TEENSY_DEBUG
|
||||
char s[60];
|
||||
sprintf (s, "TEENSY-DRV: ERR: Pins: %d, %d not Flextimer pins!", pin, pin1);
|
||||
SIMPLEFOC_DEBUG(s);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
info = pwm_pin_info + pin;
|
||||
// FlexPWM pin
|
||||
IMXRT_FLEXPWM_t *flexpwm1,*flexpwm2;
|
||||
switch ((info->module >> 4) & 3) {
|
||||
case 0: flexpwm1 = &IMXRT_FLEXPWM1; break;
|
||||
case 1: flexpwm1 = &IMXRT_FLEXPWM2; break;
|
||||
case 2: flexpwm1 = &IMXRT_FLEXPWM3; break;
|
||||
default: flexpwm1 = &IMXRT_FLEXPWM4;
|
||||
}
|
||||
|
||||
info = pwm_pin_info + pin1;
|
||||
switch ((info->module >> 4) & 3) {
|
||||
case 0: flexpwm2 = &IMXRT_FLEXPWM1; break;
|
||||
case 1: flexpwm2 = &IMXRT_FLEXPWM2; break;
|
||||
case 2: flexpwm2 = &IMXRT_FLEXPWM3; break;
|
||||
default: flexpwm2 = &IMXRT_FLEXPWM4;
|
||||
}
|
||||
if(flexpwm1 == flexpwm2){
|
||||
char s[60];
|
||||
sprintf (s, "TEENSY-DRV: Pins: %d, %d on Flextimer %d.", pin, pin1, ((info->module >> 4) & 3) + 1);
|
||||
SIMPLEFOC_DEBUG(s);
|
||||
return flexpwm1;
|
||||
} else {
|
||||
#ifdef SIMPLEFOC_TEENSY_DEBUG
|
||||
char s[60];
|
||||
sprintf (s, "TEENSY-DRV: ERR: Pins: %d, %d not in same Flextimer!", pin, pin1);
|
||||
SIMPLEFOC_DEBUG(s);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// function which finds the timer submodule for a pair of pins
|
||||
// if they do not belong to the same submodule it returns a -1
|
||||
int get_submodule(uint8_t pin, uint8_t pin1){
|
||||
|
||||
const struct pwm_pin_info_struct *info;
|
||||
if (pin >= CORE_NUM_DIGITAL || pin1 >= CORE_NUM_DIGITAL){
|
||||
#ifdef SIMPLEFOC_TEENSY_DEBUG
|
||||
char s[60];
|
||||
sprintf (s, "TEENSY-DRV: ERR: Pins: %d, %d not Flextimer pins!", pin, pin1);
|
||||
SIMPLEFOC_DEBUG(s);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
info = pwm_pin_info + pin;
|
||||
int sm1 = info->module&0x3;
|
||||
info = pwm_pin_info + pin1;
|
||||
int sm2 = info->module&0x3;
|
||||
|
||||
if (sm1 == sm2) {
|
||||
char s[60];
|
||||
sprintf (s, "TEENSY-DRV: Pins: %d, %d on submodule %d.", pin, pin1, sm1);
|
||||
SIMPLEFOC_DEBUG(s);
|
||||
return sm1;
|
||||
} else {
|
||||
#ifdef SIMPLEFOC_TEENSY_DEBUG
|
||||
char s[50];
|
||||
sprintf (s, "TEENSY-DRV: ERR: Pins: %d, %d not in same submodule!", pin, pin1);
|
||||
SIMPLEFOC_DEBUG(s);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// function which finds the timer submodule for a pair of pins
|
||||
// if they do not belong to the same submodule it returns a -1
|
||||
int get_inverted_channel(uint8_t pin, uint8_t pin1){
|
||||
|
||||
const struct pwm_pin_info_struct *info;
|
||||
if (pin >= CORE_NUM_DIGITAL || pin1 >= CORE_NUM_DIGITAL){
|
||||
#ifdef SIMPLEFOC_TEENSY_DEBUG
|
||||
char s[60];
|
||||
sprintf (s, "TEENSY-DRV: ERR: Pins: %d, %d not Flextimer pins!", pin, pin1);
|
||||
SIMPLEFOC_DEBUG(s);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
info = pwm_pin_info + pin;
|
||||
int ch1 = info->channel;
|
||||
info = pwm_pin_info + pin1;
|
||||
int ch2 = info->channel;
|
||||
|
||||
if (ch1 != 1) {
|
||||
#ifdef SIMPLEFOC_TEENSY_DEBUG
|
||||
char s[60];
|
||||
sprintf (s, "TEENSY-DRV: ERR: Pin: %d on channel %s - only A supported", pin1, ch1==2 ? "B" : "X");
|
||||
SIMPLEFOC_DEBUG(s);
|
||||
#endif
|
||||
return -1;
|
||||
} else if (ch2 != 2) {
|
||||
#ifdef SIMPLEFOC_TEENSY_DEBUG
|
||||
char s[60];
|
||||
sprintf (s, "TEENSY-DRV: ERR: Inverted pin: %d on channel %s - only B supported", pin1, ch2==1 ? "A" : "X");
|
||||
SIMPLEFOC_DEBUG(s);
|
||||
#endif
|
||||
return -1;
|
||||
} else {
|
||||
#ifdef SIMPLEFOC_TEENSY_DEBUG
|
||||
char s[60];
|
||||
sprintf (s, "TEENSY-DRV: Pin: %d on channel B inverted.", pin1);
|
||||
SIMPLEFOC_DEBUG(s);
|
||||
#endif
|
||||
return ch2;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to set up A/B pair on a FlexPWM submodule.
|
||||
// can configure sync, prescale and B inversion.
|
||||
// sets the desired frequency of the PWM
|
||||
// sets the center-aligned pwm
|
||||
void setup_pwm_pair (IMXRT_FLEXPWM_t * flexpwm, int submodule, const long frequency, float dead_zone )
|
||||
{
|
||||
int submodule_mask = 1 << submodule ;
|
||||
flexpwm->MCTRL &= ~ FLEXPWM_MCTRL_RUN (submodule_mask) ; // stop it if its already running
|
||||
flexpwm->MCTRL |= FLEXPWM_MCTRL_CLDOK (submodule_mask) ; // clear load OK
|
||||
|
||||
|
||||
// calculate the counter and prescaler for the desired pwm frequency
|
||||
uint32_t newdiv = (uint32_t)((float)F_BUS_ACTUAL / frequency + 0.5f);
|
||||
uint32_t prescale = 0;
|
||||
//printf(" div=%lu\n", newdiv);
|
||||
while (newdiv > 65535 && prescale < 7) {
|
||||
newdiv = newdiv >> 1;
|
||||
prescale = prescale + 1;
|
||||
}
|
||||
if (newdiv > 65535) {
|
||||
newdiv = 65535;
|
||||
} else if (newdiv < 2) {
|
||||
newdiv = 2;
|
||||
}
|
||||
|
||||
// the halfcycle of the PWM
|
||||
half_cycle = int(newdiv/2.0f);
|
||||
int dead_time = int(dead_zone*half_cycle); //default dead-time - 2%
|
||||
int mid_pwm = int((half_cycle)/2.0f);
|
||||
|
||||
// setup the timer
|
||||
// https://github.com/PaulStoffregen/cores/blob/master/teensy4/imxrt.h
|
||||
flexpwm->SM[submodule].CTRL2 = FLEXPWM_SMCTRL2_WAITEN | FLEXPWM_SMCTRL2_DBGEN |
|
||||
FLEXPWM_SMCTRL2_FRCEN | FLEXPWM_SMCTRL2_INIT_SEL(0) | FLEXPWM_SMCTRL2_FORCE_SEL(6);
|
||||
flexpwm->SM[submodule].CTRL = FLEXPWM_SMCTRL_FULL | FLEXPWM_SMCTRL_HALF | FLEXPWM_SMCTRL_PRSC(prescale) ;
|
||||
// https://github.com/PaulStoffregen/cores/blob/70ba01accd728abe75ebfc8dcd8b3d3a8f3e3f25/teensy4/imxrt.h#L4948
|
||||
flexpwm->SM[submodule].OCTRL = 0;//channel_to_invert==2 ? 0 : FLEXPWM_SMOCTRL_POLA | FLEXPWM_SMOCTRL_POLB ;
|
||||
flexpwm->SM[submodule].DTCNT0 = dead_time ; // should try this out (deadtime control)
|
||||
flexpwm->SM[submodule].DTCNT1 = dead_time ; // should try this out (deadtime control)
|
||||
// flexpwm->SM[submodule].TCTRL = FLEXPWM_SMTCTRL_OUT_TRIG_EN (0b000010) ; // sync trig out on VAL1 match.
|
||||
flexpwm->SM[submodule].INIT = -half_cycle; // count from -HALFCYCLE to +HALFCYCLE
|
||||
flexpwm->SM[submodule].VAL0 = 0 ;
|
||||
flexpwm->SM[submodule].VAL1 = half_cycle ;
|
||||
flexpwm->SM[submodule].VAL2 = -mid_pwm ;
|
||||
flexpwm->SM[submodule].VAL3 = +mid_pwm ;
|
||||
// flexpwm->SM[submodule].VAL4 = -mid_pwm ;
|
||||
// flexpwm->SM[submodule].VAL5 = +mid_pwm ;
|
||||
|
||||
flexpwm->MCTRL |= FLEXPWM_MCTRL_LDOK (submodule_mask) ; // loading reenabled
|
||||
flexpwm->MCTRL |= FLEXPWM_MCTRL_RUN (submodule_mask) ; // start it running
|
||||
}
|
||||
|
||||
|
||||
// staring the PWM on A and B channels of the submodule
|
||||
void startup_pwm_pair (IMXRT_FLEXPWM_t * flexpwm, int submodule)
|
||||
{
|
||||
int submodule_mask = 1 << submodule ;
|
||||
|
||||
flexpwm->OUTEN |= FLEXPWM_OUTEN_PWMA_EN (submodule_mask); // enable A output
|
||||
flexpwm->OUTEN |= FLEXPWM_OUTEN_PWMB_EN (submodule_mask); // enable B output
|
||||
}
|
||||
|
||||
|
||||
|
||||
// PWM setting on the high and low pair of the PWM channels
|
||||
void write_pwm_pair(IMXRT_FLEXPWM_t * flexpwm, int submodule, float duty){
|
||||
|
||||
int mid_pwm = int((half_cycle)/2.0f);
|
||||
int count_pwm = int(mid_pwm*(duty*2-1)) + mid_pwm;
|
||||
|
||||
flexpwm->SM[submodule].VAL2 = count_pwm; // A on
|
||||
flexpwm->SM[submodule].VAL3 = -count_pwm ; // A off
|
||||
// flexpwm->SM[submodule].VAL4 = - count_pwm ; // B off (assuming B inverted)
|
||||
// flexpwm->SM[submodule].VAL5 = + count_pwm ; // B on
|
||||
flexpwm->MCTRL |= FLEXPWM_MCTRL_LDOK (1<<submodule) ; // signal new values
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 6PWM setting
|
||||
// - hardware specific
|
||||
void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
|
||||
IMXRT_FLEXPWM_t *flexpwmA,*flexpwmB,*flexpwmC;
|
||||
int submoduleA,submoduleB,submoduleC;
|
||||
int inverted_channelA,inverted_channelB,inverted_channelC;
|
||||
flexpwmA = get_flexpwm(pinA_h,pinA_l);
|
||||
submoduleA = get_submodule(pinA_h,pinA_l);
|
||||
inverted_channelA = get_inverted_channel(pinA_h,pinA_l);
|
||||
flexpwmB = get_flexpwm(pinB_h,pinB_l);
|
||||
submoduleB = get_submodule(pinB_h,pinB_l);
|
||||
inverted_channelB = get_inverted_channel(pinB_h,pinB_l);
|
||||
flexpwmC = get_flexpwm(pinC_h,pinC_l);
|
||||
submoduleC = get_submodule(pinC_h,pinC_l);
|
||||
inverted_channelC = get_inverted_channel(pinC_h,pinC_l);
|
||||
|
||||
if((flexpwmA == nullptr) || (flexpwmB == nullptr) || (flexpwmC == nullptr) ){
|
||||
#ifdef SIMPLEFOC_TEENSY_DEBUG
|
||||
SIMPLEFOC_DEBUG("TEENSY-DRV: ERR: Flextimer problem - failed driver config!");
|
||||
#endif
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
if((submoduleA < 0) || (submoduleB < 0) || (submoduleC < 0) ){
|
||||
#ifdef SIMPLEFOC_TEENSY_DEBUG
|
||||
SIMPLEFOC_DEBUG("TEENSY-DRV: ERR: Flextimer submodule problem - failed driver config!");
|
||||
#endif
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
if((inverted_channelA < 0) || (inverted_channelB < 0) || (inverted_channelC < 0) ){
|
||||
#ifdef SIMPLEFOC_TEENSY_DEBUG
|
||||
SIMPLEFOC_DEBUG("TEENSY-DRV: ERR: Flextimer channel problem - failed driver config!");
|
||||
#endif
|
||||
return SIMPLEFOC_DRIVER_INIT_FAILED;
|
||||
}
|
||||
|
||||
|
||||
Teensy4DriverParams* params = new Teensy4DriverParams {
|
||||
.flextimers = { flexpwmA,flexpwmB, flexpwmC},
|
||||
.submodules = { submoduleA, submoduleB, submoduleC},
|
||||
.pwm_frequency = pwm_frequency,
|
||||
.dead_zone = dead_zone
|
||||
};
|
||||
|
||||
// Configure FlexPWM units, each driving A/B pair, B inverted.
|
||||
// full speed about 80kHz, prescale 2 (div by 4) gives 20kHz
|
||||
setup_pwm_pair (flexpwmA, submoduleA, pwm_frequency, dead_zone) ; // this is the master, internally synced
|
||||
setup_pwm_pair (flexpwmB, submoduleB, pwm_frequency, dead_zone) ; // others externally synced
|
||||
setup_pwm_pair (flexpwmC, submoduleC, pwm_frequency, dead_zone) ;
|
||||
delayMicroseconds (100) ;
|
||||
|
||||
startup_pwm_pair (flexpwmA, submoduleA) ;
|
||||
startup_pwm_pair (flexpwmB, submoduleB) ;
|
||||
startup_pwm_pair (flexpwmC, submoduleC) ;
|
||||
|
||||
delayMicroseconds(50) ;
|
||||
// config the pins 2/3/6/9/8/7 as their FLEXPWM alternates.
|
||||
*portConfigRegister(pinA_h) = pwm_pin_info[pinA_h].muxval ;
|
||||
*portConfigRegister(pinA_l) = pwm_pin_info[pinA_l].muxval ;
|
||||
*portConfigRegister(pinB_h) = pwm_pin_info[pinB_h].muxval ;
|
||||
*portConfigRegister(pinB_l) = pwm_pin_info[pinB_l].muxval ;
|
||||
*portConfigRegister(pinC_h) = pwm_pin_info[pinC_h].muxval ;
|
||||
*portConfigRegister(pinC_l) = pwm_pin_info[pinC_l].muxval ;
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 6PWM setting
|
||||
// - hardware specific
|
||||
void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void* params){
|
||||
_UNUSED(phase_state);
|
||||
write_pwm_pair (((Teensy4DriverParams*)params)->flextimers[0], ((Teensy4DriverParams*)params)->submodules[0], dc_a);
|
||||
write_pwm_pair (((Teensy4DriverParams*)params)->flextimers[1], ((Teensy4DriverParams*)params)->submodules[1], dc_b);
|
||||
write_pwm_pair (((Teensy4DriverParams*)params)->flextimers[2], ((Teensy4DriverParams*)params)->submodules[2], dc_c);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
#include "teensy_mcu.h"
|
||||
|
||||
// if defined
|
||||
// - Teensy 4.0
|
||||
// - Teensy 4.1
|
||||
#if defined(__arm__) && defined(CORE_TEENSY) && ( defined(__IMXRT1062__) || defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) || defined(ARDUINO_TEENSY_MICROMOD) )
|
||||
|
||||
// teensy 4 driver configuration parameters
|
||||
typedef struct Teensy4DriverParams {
|
||||
IMXRT_FLEXPWM_t* flextimers[3] = {NULL};
|
||||
int submodules[3];
|
||||
long pwm_frequency;
|
||||
float dead_zone;
|
||||
} Teensy4DriverParams;
|
||||
|
||||
|
||||
// pin definition from https://github.com/PaulStoffregen/cores/blob/master/teensy4/pwm.c
|
||||
struct pwm_pin_info_struct {
|
||||
uint8_t type; // 0=no pwm, 1=flexpwm, 2=quad
|
||||
uint8_t module; // 0-3, 0-3
|
||||
uint8_t channel; // 0=X, 1=A, 2=B
|
||||
uint8_t muxval; //
|
||||
};
|
||||
#define M(a, b) ((((a) - 1) << 4) | (b))
|
||||
#if defined(__IMXRT1062__)
|
||||
const struct pwm_pin_info_struct pwm_pin_info[] = {
|
||||
{1, M(1, 1), 0, 4}, // FlexPWM1_1_X 0 // AD_B0_03
|
||||
{1, M(1, 0), 0, 4}, // FlexPWM1_0_X 1 // AD_B0_02
|
||||
{1, M(4, 2), 1, 1}, // FlexPWM4_2_A 2 // EMC_04
|
||||
{1, M(4, 2), 2, 1}, // FlexPWM4_2_B 3 // EMC_05
|
||||
{1, M(2, 0), 1, 1}, // FlexPWM2_0_A 4 // EMC_06
|
||||
{1, M(2, 1), 1, 1}, // FlexPWM2_1_A 5 // EMC_08
|
||||
{1, M(2, 2), 1, 2}, // FlexPWM2_2_A 6 // B0_10
|
||||
{1, M(1, 3), 2, 6}, // FlexPWM1_3_B 7 // B1_01
|
||||
{1, M(1, 3), 1, 6}, // FlexPWM1_3_A 8 // B1_00
|
||||
{1, M(2, 2), 2, 2}, // FlexPWM2_2_B 9 // B0_11
|
||||
{2, M(1, 0), 0, 1}, // QuadTimer1_0 10 // B0_00
|
||||
{2, M(1, 2), 0, 1}, // QuadTimer1_2 11 // B0_02
|
||||
{2, M(1, 1), 0, 1}, // QuadTimer1_1 12 // B0_01
|
||||
{2, M(2, 0), 0, 1}, // QuadTimer2_0 13 // B0_03
|
||||
{2, M(3, 2), 0, 1}, // QuadTimer3_2 14 // AD_B1_02
|
||||
{2, M(3, 3), 0, 1}, // QuadTimer3_3 15 // AD_B1_03
|
||||
{0, M(1, 0), 0, 0},
|
||||
{0, M(1, 0), 0, 0},
|
||||
{2, M(3, 1), 0, 1}, // QuadTimer3_1 18 // AD_B1_01
|
||||
{2, M(3, 0), 0, 1}, // QuadTimer3_0 19 // AD_B1_00
|
||||
{0, M(1, 0), 0, 0},
|
||||
{0, M(1, 0), 0, 0},
|
||||
{1, M(4, 0), 1, 1}, // FlexPWM4_0_A 22 // AD_B1_08
|
||||
{1, M(4, 1), 1, 1}, // FlexPWM4_1_A 23 // AD_B1_09
|
||||
{1, M(1, 2), 0, 4}, // FlexPWM1_2_X 24 // AD_B0_12
|
||||
{1, M(1, 3), 0, 4}, // FlexPWM1_3_X 25 // AD_B0_13
|
||||
{0, M(1, 0), 0, 0},
|
||||
{0, M(1, 0), 0, 0},
|
||||
{1, M(3, 1), 2, 1}, // FlexPWM3_1_B 28 // EMC_32
|
||||
{1, M(3, 1), 1, 1}, // FlexPWM3_1_A 29 // EMC_31
|
||||
{0, M(1, 0), 0, 0},
|
||||
{0, M(1, 0), 0, 0},
|
||||
{0, M(1, 0), 0, 0},
|
||||
{1, M(2, 0), 2, 1}, // FlexPWM2_0_B 33 // EMC_07
|
||||
#ifdef ARDUINO_TEENSY40
|
||||
{1, M(1, 1), 2, 1}, // FlexPWM1_1_B 34 // SD_B0_03
|
||||
{1, M(1, 1), 1, 1}, // FlexPWM1_1_A 35 // SD_B0_02
|
||||
{1, M(1, 0), 2, 1}, // FlexPWM1_0_B 36 // SD_B0_01
|
||||
{1, M(1, 0), 1, 1}, // FlexPWM1_0_A 37 // SD_B0_00
|
||||
{1, M(1, 2), 2, 1}, // FlexPWM1_2_B 38 // SD_B0_05
|
||||
{1, M(1, 2), 1, 1}, // FlexPWM1_2_A 39 // SD_B0_04
|
||||
#endif
|
||||
#ifdef ARDUINO_TEENSY41
|
||||
{0, M(1, 0), 0, 0},
|
||||
{0, M(1, 0), 0, 0},
|
||||
{1, M(2, 3), 1, 6}, // FlexPWM2_3_A 36 // B1_00
|
||||
{1, M(2, 3), 2, 6}, // FlexPWM2_3_B 37 // B1_01
|
||||
{0, M(1, 0), 0, 0},
|
||||
{0, M(1, 0), 0, 0},
|
||||
{0, M(1, 0), 0, 0},
|
||||
{0, M(1, 0), 0, 0},
|
||||
{1, M(1, 1), 2, 1}, // FlexPWM1_1_B 42 // SD_B0_03
|
||||
{1, M(1, 1), 1, 1}, // FlexPWM1_1_A 43 // SD_B0_02
|
||||
{1, M(1, 0), 2, 1}, // FlexPWM1_0_B 44 // SD_B0_01
|
||||
{1, M(1, 0), 1, 1}, // FlexPWM1_0_A 45 // SD_B0_00
|
||||
{1, M(1, 2), 2, 1}, // FlexPWM1_2_B 46 // SD_B0_05
|
||||
{1, M(1, 2), 1, 1}, // FlexPWM1_2_A 47 // SD_B0_04
|
||||
{0, M(1, 0), 0, 0}, // duplicate FlexPWM1_0_B
|
||||
{0, M(1, 0), 0, 0}, // duplicate FlexPWM1_2_A
|
||||
{0, M(1, 0), 0, 0}, // duplicate FlexPWM1_2_B
|
||||
{1, M(3, 3), 2, 1}, // FlexPWM3_3_B 51 // EMC_22
|
||||
{0, M(1, 0), 0, 0}, // duplicate FlexPWM1_1_B
|
||||
{0, M(1, 0), 0, 0}, // duplicate FlexPWM1_1_A
|
||||
{1, M(3, 0), 1, 1}, // FlexPWM3_0_A 54 // EMC_29
|
||||
#endif
|
||||
#ifdef ARDUINO_TEENSY_MICROMOD
|
||||
{1, M(1, 1), 2, 1}, // FlexPWM1_1_B 34 // SD_B0_03
|
||||
{1, M(1, 1), 1, 1}, // FlexPWM1_1_A 35 // SD_B0_02
|
||||
{1, M(1, 0), 2, 1}, // FlexPWM1_0_B 36 // SD_B0_01
|
||||
{1, M(1, 0), 1, 1}, // FlexPWM1_0_A 37 // SD_B0_00
|
||||
{1, M(1, 2), 1, 1}, // FlexPWM1_2_A 38 // SD_B0_04
|
||||
{1, M(1, 2), 2, 1}, // FlexPWM1_2_B 39 // SD_B0_05
|
||||
{2, M(2, 1), 0, 1}, // QuadTimer2_1 40 // B0_04
|
||||
{2, M(2, 2), 0, 1}, // QuadTimer2_2 41 // B0_05
|
||||
{0, M(1, 0), 0, 0}, // duplicate QuadTimer3_0
|
||||
{0, M(1, 0), 0, 0}, // duplicate QuadTimer3_1
|
||||
{0, M(1, 0), 0, 0}, // duplicate QuadTimer3_2
|
||||
{2, M(4, 0), 0, 1}, // QuadTimer4_0 45 // B0_09
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,116 @@
|
||||
#include "teensy_mcu.h"
|
||||
|
||||
#if defined(__arm__) && defined(CORE_TEENSY)
|
||||
|
||||
// configure High PWM frequency
|
||||
void _setHighFrequency(const long freq, const int pin){
|
||||
analogWrite(pin, 0);
|
||||
analogWriteFrequency(pin, freq);
|
||||
}
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure1PWM(long pwm_frequency, const int pinA) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
_setHighFrequency(pwm_frequency, pinA);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure2PWM(long pwm_frequency, const int pinA, const int pinB) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
_setHighFrequency(pwm_frequency, pinA);
|
||||
_setHighFrequency(pwm_frequency, pinB);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
_setHighFrequency(pwm_frequency, pinA);
|
||||
_setHighFrequency(pwm_frequency, pinB);
|
||||
_setHighFrequency(pwm_frequency, pinC);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB, pinC },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
// function setting the high pwm frequency to the supplied pins
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
void* _configure4PWM(long pwm_frequency, const int pinA, const int pinB, const int pinC, const int pinD) {
|
||||
if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
|
||||
else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
|
||||
_setHighFrequency(pwm_frequency, pinA);
|
||||
_setHighFrequency(pwm_frequency, pinB);
|
||||
_setHighFrequency(pwm_frequency, pinC);
|
||||
_setHighFrequency(pwm_frequency, pinD);
|
||||
GenericDriverParams* params = new GenericDriverParams {
|
||||
.pins = { pinA, pinB, pinC, pinD },
|
||||
.pwm_frequency = pwm_frequency
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle1PWM(float dc_a, void* params) {
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
}
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 2PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params) {
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_b);
|
||||
}
|
||||
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - BLDC motor - 3PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_b);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[2], 255.0f*dc_c);
|
||||
}
|
||||
|
||||
// function setting the pwm duty cycle to the hardware
|
||||
// - Stepper motor - 4PWM setting
|
||||
// - hardware speciffic
|
||||
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params){
|
||||
// transform duty cycle from [0,1] to [0,255]
|
||||
analogWrite(((GenericDriverParams*)params)->pins[0], 255.0f*dc_1a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[1], 255.0f*dc_1b);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[2], 255.0f*dc_2a);
|
||||
analogWrite(((GenericDriverParams*)params)->pins[3], 255.0f*dc_2b);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "../../hardware_api.h"
|
||||
|
||||
#if defined(__arm__) && defined(CORE_TEENSY)
|
||||
|
||||
#define _PWM_FREQUENCY 25000 // 25khz
|
||||
#define _PWM_FREQUENCY_MAX 50000 // 50khz
|
||||
|
||||
// debugging output
|
||||
#define SIMPLEFOC_TEENSY_DEBUG
|
||||
|
||||
// configure High PWM frequency
|
||||
void _setHighFrequency(const long freq, const int pin);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user