Skip to content

Commit d4c0ce5

Browse files
committed
Improvements to LinearHall
Changes compared to the original pull request simplefoc#12 1. Added a version of init which turns the motor one revolution to find the center values of the sensors. 2. Moved the calls to analogRead into a weakly bound function ReadLinearHalls so it can be overridden with custom ADC code on platforms with poor analogRead performance. 3. Commented out the pinMode calls in init, which makes it possible to pass in ADC channel numbers for custom ReadLinearHalls to use without having to remap them every update. 4. Changed to use the much faster _atan2 function that was added to foc_utils recently.
1 parent a0c668d commit d4c0ce5

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#include "LinearHall.h"
2+
3+
// This function can be overridden with custom ADC code on platforms with poor analogRead performance.
4+
__attribute__((weak)) void ReadLinearHalls(int hallA, int hallB, int *a, int *b)
5+
{
6+
*a = analogRead(hallA);
7+
*b = analogRead(hallB);
8+
}
9+
10+
LinearHall::LinearHall(int _hallA, int _hallB, int _pp){
11+
centerA = 512;
12+
centerB = 512;
13+
pinA = _hallA;
14+
pinB = _hallB;
15+
pp = _pp;
16+
}
17+
18+
float LinearHall::getSensorAngle() {
19+
ReadLinearHalls(pinA, pinB, &lastA, &lastB);
20+
//offset readings using center values, then compute angle
21+
float reading = _atan2(lastA - centerA, lastB - centerB);
22+
23+
//handle rollover logic between each electrical revolution of the motor
24+
if (reading > prev_reading) {
25+
if (reading - prev_reading >= PI) {
26+
if (electrical_rev - 1 < 0) {
27+
electrical_rev = pp - 1;
28+
} else {
29+
electrical_rev = electrical_rev - 1;
30+
}
31+
}
32+
} else if (reading < prev_reading) {
33+
if (prev_reading - reading >= PI) {
34+
if (electrical_rev + 1 >= pp) {
35+
electrical_rev = 0;
36+
} else {
37+
electrical_rev = electrical_rev + 1;
38+
}
39+
}
40+
}
41+
42+
//convert result from electrical angle and electrical revolution count to shaft angle in radians
43+
float result = (reading + PI) / _2PI;
44+
result = _2PI * (result + electrical_rev) / pp;
45+
46+
//update previous reading for rollover handling
47+
prev_reading = reading;
48+
return result;
49+
}
50+
51+
void LinearHall::init(int _centerA, int _centerB) {
52+
// Skip configuring the pins here because they normally default to input anyway, and
53+
// this makes it possible to use ADC channel numbers instead of pin numbers when using
54+
// custom implementation of ReadLinearHalls, to avoid having to remap them every update.
55+
// If pins do need to be configured, it can be done by user code before calling init.
56+
//pinMode(pinA, INPUT);
57+
//pinMode(pinB, INPUT);
58+
59+
centerA = _centerA;
60+
centerB = _centerB;
61+
62+
//establish initial reading for rollover handling
63+
electrical_rev = 0;
64+
ReadLinearHalls(pinA, pinB, &lastA, &lastB);
65+
prev_reading = _atan2(lastA - centerA, lastB - centerB);
66+
}
67+
68+
void LinearHall::init(FOCMotor *motor) {
69+
if (!motor->enabled) {
70+
SIMPLEFOC_DEBUG("LinearHall::init failed. Call after motor.init, but before motor.initFOC.");
71+
return;
72+
}
73+
74+
// See comment in other version of init for why these are commented out
75+
//pinMode(pinA, INPUT);
76+
//pinMode(pinB, INPUT);
77+
78+
int minA, maxA, minB, maxB;
79+
80+
ReadLinearHalls(pinA, pinB, &lastA, &lastB);
81+
minA = maxA = centerA = lastA;
82+
minB = maxB = centerB = lastB;
83+
84+
// move one mechanical revolution forward
85+
for (int i = 0; i <= 2000; i++)
86+
{
87+
float angle = _3PI_2 + _2PI * i * pp / 2000.0f;
88+
motor->setPhaseVoltage(motor->voltage_sensor_align, 0, angle);
89+
90+
ReadLinearHalls(pinA, pinB, &lastA, &lastB);
91+
92+
if (lastA < minA)
93+
minA = lastA;
94+
if (lastA > maxA)
95+
maxA = lastA;
96+
centerA = (minA + maxA) / 2;
97+
98+
if (lastB < minB)
99+
minB = lastB;
100+
if (lastB > maxB)
101+
maxB = lastB;
102+
centerB = (minB + maxB) / 2;
103+
104+
_delay(2);
105+
}
106+
107+
//establish initial reading for rollover handling
108+
electrical_rev = 0;
109+
prev_reading = _atan2(lastA - centerA, lastB - centerB);
110+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#ifndef LINEAR_HALL_SENSOR_LIB_H
2+
#define LINEAR_HALL_SENSOR_LIB_H
3+
4+
#include <SimpleFOC.h>
5+
6+
// This function can be overridden with custom ADC code on platforms with poor analogRead performance.
7+
void ReadLinearHalls(int hallA, int hallB, int *a, int *b);
8+
9+
/**
10+
* This sensor class is for two linear hall effect sensors such as 49E, which are
11+
* positioned 90 electrical degrees apart (if one is centered on a rotor magnet,
12+
* the other is half way between rotor magnets).
13+
* It can also be used for a single magnet mounted to the motor shaft (set pp to 1).
14+
*
15+
* For more information, see this forum thread and PDF
16+
* https://community.simplefoc.com/t/40-cent-magnetic-angle-sensing-technique/1959
17+
* https://gist.github.com/nanoparticle/00030ea27c59649edbed84f0a957ebe1
18+
*/
19+
class LinearHall: public Sensor{
20+
public:
21+
LinearHall(int hallA, int hallB, int pp);
22+
23+
void init(int centerA, int centerB); // Initialize without moving motor
24+
void init(class FOCMotor *motor); // Move motor to find center values
25+
26+
// Get current shaft angle from the sensor hardware, and
27+
// return it as a float in radians, in the range 0 to 2PI.
28+
// - This method is pure virtual and must be implemented in subclasses.
29+
// Calling this method directly does not update the base-class internal fields.
30+
// Use update() when calling from outside code.
31+
float getSensorAngle() override;
32+
33+
int centerA;
34+
int centerB;
35+
int lastA, lastB;
36+
37+
private:
38+
int pinA;
39+
int pinB;
40+
int pp;
41+
int electrical_rev;
42+
float prev_reading;
43+
};
44+
45+
#endif

0 commit comments

Comments
 (0)