Skip to content

Commit 878283d

Browse files
committed
Merge branch 'dev' of github.com:askuric/Arduino-FOC into dev
2 parents e6b8f5b + e38a194 commit 878283d

File tree

4 files changed

+145
-103
lines changed

4 files changed

+145
-103
lines changed

src/BLDCMotor.cpp

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,52 @@ void BLDCMotor::move(float new_target) {
224224
// regular sin + cos ~300us (no memory usaage)
225225
// approx _sin + _cos ~110us (400Byte ~ 20% of memory)
226226
void BLDCMotor::setPhaseVoltage(float Uq, float angle_el) {
227+
228+
const bool centered = true;
229+
int sector;
230+
227231
switch (foc_modulation)
228232
{
233+
case FOCModulationType::Trapezoid_120 :
234+
// see https://www.youtube.com/watch?v=InzXA7mWBWE Slide 5
235+
static int trap_120_map[6][3] = {
236+
{0,1,-1},{-1,1,0},{-1,0,1},{0,-1,1},{1,-1,0},{1,0,-1} // each is 60 degrees with values for 3 phases of 1=positive -1=negative 0=high-z
237+
};
238+
// static int trap_120_state = 0;
239+
sector = 6 * (_normalizeAngle(angle_el + PI/6.0 + zero_electric_angle) / _2PI); // adding PI/6 to align with other modes
240+
241+
Ua = Uq + trap_120_map[sector][0] * Uq;
242+
Ub = Uq + trap_120_map[sector][1] * Uq;
243+
Uc = Uq + trap_120_map[sector][2] * Uq;
244+
245+
if (centered) {
246+
Ua += (voltage_power_supply)/2 -Uq;
247+
Ub += (voltage_power_supply)/2 -Uq;
248+
Uc += (voltage_power_supply)/2 -Uq;
249+
}
250+
break;
251+
252+
case FOCModulationType::Trapezoid_150 :
253+
// see https://www.youtube.com/watch?v=InzXA7mWBWE Slide 8
254+
static int trap_150_map[12][3] = {
255+
{0,1,-1},{-1,1,-1},{-1,1,0},{-1,1,1},{-1,0,1},{-1,-1,1},{0,-1,1},{1,-1,1},{1,-1,0},{1,-1,-1},{1,0,-1},{1,1,-1} // each is 30 degrees with values for 3 phases of 1=positive -1=negative 0=high-z
256+
};
257+
// static int trap_150_state = 0;
258+
sector = 12 * (_normalizeAngle(angle_el + PI/6.0 + zero_electric_angle) / _2PI); // adding PI/6 to align with other modes
259+
260+
Ua = Uq + trap_150_map[sector][0] * Uq;
261+
Ub = Uq + trap_150_map[sector][1] * Uq;
262+
Uc = Uq + trap_150_map[sector][2] * Uq;
263+
264+
//center
265+
if (centered) {
266+
Ua += (voltage_power_supply)/2 -Uq;
267+
Ub += (voltage_power_supply)/2 -Uq;
268+
Uc += (voltage_power_supply)/2 -Uq;
269+
}
270+
271+
break;
272+
229273
case FOCModulationType::SinePWM :
230274
// Sinusoidal PWM modulation
231275
// Inverse Park + Clarke transformation
@@ -241,6 +285,14 @@ void BLDCMotor::setPhaseVoltage(float Uq, float angle_el) {
241285
Ua = Ualpha + driver->voltage_power_supply/2;
242286
Ub = -0.5 * Ualpha + _SQRT3_2 * Ubeta + driver->voltage_power_supply/2;
243287
Uc = -0.5 * Ualpha - _SQRT3_2 * Ubeta + driver->voltage_power_supply/2;
288+
289+
if (!centered) {
290+
float Umin = min(Ua, min(Ub, Uc));
291+
Ua -=Umin;
292+
Ub -=Umin;
293+
Uc -=Umin;
294+
}
295+
244296
break;
245297

246298
case FOCModulationType::SpaceVectorPWM :
@@ -257,15 +309,15 @@ void BLDCMotor::setPhaseVoltage(float Uq, float angle_el) {
257309
angle_el = _normalizeAngle(angle_el + zero_electric_angle + _PI_2);
258310

259311
// find the sector we are in currently
260-
int sector = floor(angle_el / _PI_3) + 1;
312+
sector = floor(angle_el / _PI_3) + 1;
261313
// calculate the duty cycles
262314
float T1 = _SQRT3*_sin(sector*_PI_3 - angle_el) * Uq/driver->voltage_power_supply;
263315
float T2 = _SQRT3*_sin(angle_el - (sector-1.0)*_PI_3) * Uq/driver->voltage_power_supply;
264316
// two versions possible
265-
// centered around driver->voltage_power_supply/2
266-
float T0 = 1 - T1 - T2;
267-
// pulled to 0 - better for low power supply voltage
268-
//float T0 = 0;
317+
float T0 = 0; // pulled to 0 - better for low power supply voltage
318+
if (centered) {
319+
T0 = 1 - T1 - T2; //centered around driver->voltage_power_supply/2
320+
}
269321

270322
// calculate the duty cycles(times)
271323
float Ta,Tb,Tc;
@@ -312,6 +364,7 @@ void BLDCMotor::setPhaseVoltage(float Uq, float angle_el) {
312364
Ub = Tb*driver->voltage_power_supply;
313365
Uc = Tc*driver->voltage_power_supply;
314366
break;
367+
315368
}
316369

317370
// set the voltages in driver

src/common/base_classes/FOCMotor.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ enum ControlType{
2828
*/
2929
enum FOCModulationType{
3030
SinePWM, //!< Sinusoidal PWM modulation
31-
SpaceVectorPWM //!< Space vector modulation method
31+
SpaceVectorPWM, //!< Space vector modulation method
32+
Trapezoid_120,
33+
Trapezoid_150
3234
};
3335

3436
/**

src/sensors/HallSensor.cpp

Lines changed: 64 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,15 @@
66
- hallA, hallB, hallC - HallSensor A, B and C pins
77
- pp - pole pairs
88
*/
9-
109
HallSensor::HallSensor(int _hallA, int _hallB, int _hallC, int _pp){
1110

12-
// HallSensor measurement structure init
1311
// hardware pins
1412
pinA = _hallA;
1513
pinB = _hallB;
1614
pinC = _hallC;
17-
// counter setup
18-
pulse_counter = 0;
19-
pulse_timestamp = 0;
20-
21-
cpr = _pp * 6; // hall has 6 segments per electrical revolution
22-
A_active = 0;
23-
B_active = 0;
24-
C_active = 0;
25-
26-
// velocity calculation variables
2715

28-
prev_pulse_counter = 0;
29-
prev_timestamp_us = _micros();
16+
// hall has 6 segments per electrical revolution
17+
cpr = _pp * 6;
3018

3119
// extern pullup as default
3220
pullup = Pullup::EXTERN;
@@ -50,104 +38,89 @@ void HallSensor::handleC() {
5038
updateState();
5139
}
5240

41+
/**
42+
* Updates the state and sector following an interrupt
43+
*/
5344
void HallSensor::updateState() {
54-
int newState = C_active + (B_active << 1) + (A_active << 2);
55-
Direction direction = decodeDirection(state, newState);
56-
state = newState;
57-
58-
pulse_counter += direction;
59-
pulse_timestamp = _micros();
60-
}
61-
62-
// determines whether the hallsensr state transition means that it has moved one step CW (+1), CCW (-1) or state transition is invalid (0)
63-
// states are 3bits, one for each hall sensor
64-
Direction HallSensor::decodeDirection(int oldState, int newState)
65-
{
66-
// here are expected state transitions (oldState > newState).
67-
// CW state transitions are: ( 6 > 2 > 3 > 1 > 5 > 4 > 6 )
68-
// CCW state transitions are: ( 6 > 4 > 5 > 1 > 3 > 2 > 6 )
69-
// invalid state transitions are oldState == newState or if newState or oldState == 0 | 7 as hallsensors can't be all on or all off
70-
71-
int rawDirection;
72-
73-
if (
74-
(oldState == 6 && newState == 2) || \
75-
(oldState == 2 && newState == 3) || \
76-
(oldState == 3 && newState == 1) || \
77-
(oldState == 1 && newState == 5) || \
78-
(oldState == 5 && newState == 4) || \
79-
(oldState == 4 && newState == 6)
80-
) {
81-
rawDirection = Direction::CW;
82-
} else if(
83-
(oldState == 6 && newState == 4) || \
84-
(oldState == 4 && newState == 5) || \
85-
(oldState == 5 && newState == 1) || \
86-
(oldState == 1 && newState == 3) || \
87-
(oldState == 3 && newState == 2) || \
88-
(oldState == 2 && newState == 6)
89-
) {
90-
rawDirection = Direction::CCW;
45+
long new_pulse_timestamp = _micros();
46+
hall_state = C_active + (B_active << 1) + (A_active << 2);
47+
int8_t new_electric_sector = ELECTRIC_SECTORS[hall_state];
48+
if (new_electric_sector - electric_sector > 3) {
49+
//underflow
50+
direction = static_cast<Direction>(natural_direction * -1);
51+
electric_rotations += direction;
52+
} else if (new_electric_sector - electric_sector < (-3)) {
53+
//overflow
54+
direction = static_cast<Direction>(natural_direction);
55+
electric_rotations += direction;
9156
} else {
92-
rawDirection = Direction::UNKNOWN;
57+
direction = (new_electric_sector > electric_sector)? static_cast<Direction>(natural_direction) : static_cast<Direction>(natural_direction * (-1));
9358
}
59+
electric_sector = new_electric_sector;
60+
pulse_diff = new_pulse_timestamp - pulse_timestamp;
61+
pulse_timestamp = new_pulse_timestamp;
62+
total_interrupts++;
63+
if (onSectorChange != nullptr) onSectorChange(electric_sector);
64+
}
9465

95-
direction = static_cast<Direction>(rawDirection * natural_direction);
96-
return direction; // * goofy;
66+
/**
67+
* Optionally set a function callback to be fired when sector changes
68+
* void onSectorChange(int sector) {
69+
* ... // for debug or call driver directly?
70+
* }
71+
* sensor.attachSectorCallback(onSectorChange);
72+
*/
73+
void HallSensor::attachSectorCallback(void (*_onSectorChange)(int sector)) {
74+
onSectorChange = _onSectorChange;
9775
}
9876

9977
/*
10078
Shaft angle calculation
10179
*/
102-
float HallSensor::getAngle(){
103-
104-
long dN = pulse_counter - prev_pulse_counter;
105-
106-
if (dN != 0)
107-
{
108-
109-
// time from last impulse
110-
float Th = (pulse_timestamp - prev_timestamp_us) * 1e-6;
111-
if (Th <= 0 || Th > 0.5)
112-
Th = 1e-3;
113-
// save variables for next pass
114-
prev_timestamp_us = pulse_timestamp;
115-
prev_pulse_counter = pulse_counter;
116-
velocity = (float) _2PI * dN / (cpr * Th);
117-
}
118-
angle = (float) _2PI * pulse_counter / cpr;
119-
120-
return angle;
80+
float HallSensor::getAngle() {
81+
return natural_direction * ((electric_rotations * 6 + electric_sector) / cpr) * _2PI;
12182
}
83+
12284
/*
12385
Shaft velocity calculation
12486
function using mixed time and frequency measurement technique
12587
*/
12688
float HallSensor::getVelocity(){
127-
// this is calculated during the last call to getAngle();
128-
return velocity;
89+
90+
if (pulse_diff == 0 || ((_micros() - pulse_timestamp) > STALE_HALL_DATA_MICROS) ) { // last velocity isn't accurate if too old
91+
return 0;
92+
} else {
93+
return direction * (_2PI / cpr) / (pulse_diff / 1000000.0);
94+
}
95+
12996
}
13097

13198
// getter for index pin
13299
// return -1 if no index
133100
int HallSensor::needsAbsoluteZeroSearch(){
134101
return 0;
135102
}
136-
// getter for index pin
103+
137104
int HallSensor::hasAbsoluteZero(){
138-
return 0;
105+
return 1;
139106
}
140-
// initialize counter to zero
107+
108+
// set current angle as zero angle
109+
// return the angle [rad] difference
141110
float HallSensor::initRelativeZero(){
142-
143-
pulse_counter = 0;
144-
pulse_timestamp = _micros();
145-
velocity = 0;
146-
return 0.0;
111+
112+
// nothing to do. The interrupts should have changed sector.
113+
electric_rotations = 0;
114+
return 0;
115+
147116
}
148-
// initialize index to zero
117+
118+
// set absolute zero angle as zero angle
119+
// return the angle [rad] difference
149120
float HallSensor::initAbsoluteZero(){
150-
return 0.0;
121+
122+
return -getAngle();
123+
151124
}
152125

153126
// HallSensor initialisation of the hardware pins
@@ -164,12 +137,14 @@ void HallSensor::init(){
164137
pinMode(pinB, INPUT);
165138
pinMode(pinC, INPUT);
166139
}
140+
141+
// init hall_state
142+
A_active= digitalRead(pinA);
143+
B_active = digitalRead(pinB);
144+
C_active = digitalRead(pinC);
145+
updateState();
167146

168-
// counter setup
169-
pulse_counter = 0;
170147
pulse_timestamp = _micros();
171-
prev_pulse_counter = 0;
172-
prev_timestamp_us = _micros();
173148

174149
}
175150

src/sensors/HallSensor.h

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
#include "../common/foc_utils.h"
77
#include "../common/time_utils.h"
88

9+
#ifndef STALE_HALL_DATA_MICROS
10+
#define STALE_HALL_DATA_MICROS 500000
11+
#endif
12+
13+
// seq 1 > 5 > 4 > 6 > 2 > 3 > 1 000 001 010 011 100 101 110 111
14+
const int8_t ELECTRIC_SECTORS[8] = { -1, 0, 4, 5, 2, 1, 3 , -1 };
915

1016
class HallSensor: public Sensor{
1117
public:
@@ -81,26 +87,32 @@ class HallSensor: public Sensor{
8187
// whether last step was CW (+1) or CCW (-1) direction
8288
Direction direction;
8389

84-
// the current 3bit state of the hall sensors
85-
int state;
90+
void attachSectorCallback(void (*onSectorChange)(int a) = nullptr);
8691

87-
volatile float angle; // rad/s
88-
volatile float velocity; // rad/s
92+
// the current 3bit state of the hall sensors
93+
volatile int8_t hall_state;
94+
// the current sector of the sensor. Each sector is 60deg electrical
95+
volatile int8_t electric_sector;
96+
// the number of electric rotations
97+
volatile long electric_rotations;
98+
// this is sometimes useful to identify interrupt issues (e.g. weak or no pullup resulting in 1000s of interrupts)
99+
volatile long total_interrupts;
89100

90101
private:
91102

92103
Direction decodeDirection(int oldState, int newState);
93104
void updateState();
94105

95-
volatile long pulse_counter;//!< current pulse counter
106+
int zero_offset;
96107
volatile long pulse_timestamp;//!< last impulse timestamp in us
97108
volatile int A_active; //!< current active states of A channel
98109
volatile int B_active; //!< current active states of B channel
99110
volatile int C_active; //!< current active states of C channel
100111

101-
// velocity calculation variables
102-
// float prev_Th, pulse_per_second;
103-
volatile long prev_pulse_counter, prev_timestamp_us;
112+
// function pointer for on sector change call back
113+
void (*onSectorChange)(int sector) = nullptr;
114+
115+
volatile long pulse_diff;
104116

105117
};
106118

0 commit comments

Comments
 (0)