Skip to content

Commit

Permalink
Improvements to LinearHall
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dekutree64 committed Feb 14, 2024
1 parent a0c668d commit d4c0ce5
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 0 deletions.
110 changes: 110 additions & 0 deletions src/encoders/linearhall/LinearHall.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#include "LinearHall.h"

// This function can be overridden with custom ADC code on platforms with poor analogRead performance.
__attribute__((weak)) void ReadLinearHalls(int hallA, int hallB, int *a, int *b)
{
*a = analogRead(hallA);
*b = analogRead(hallB);
}

LinearHall::LinearHall(int _hallA, int _hallB, int _pp){
centerA = 512;
centerB = 512;
pinA = _hallA;
pinB = _hallB;
pp = _pp;
}

float LinearHall::getSensorAngle() {
ReadLinearHalls(pinA, pinB, &lastA, &lastB);
//offset readings using center values, then compute angle
float reading = _atan2(lastA - centerA, lastB - centerB);

//handle rollover logic between each electrical revolution of the motor
if (reading > prev_reading) {
if (reading - prev_reading >= PI) {
if (electrical_rev - 1 < 0) {
electrical_rev = pp - 1;
} else {
electrical_rev = electrical_rev - 1;
}
}
} else if (reading < prev_reading) {
if (prev_reading - reading >= PI) {
if (electrical_rev + 1 >= pp) {
electrical_rev = 0;
} else {
electrical_rev = electrical_rev + 1;
}
}
}

//convert result from electrical angle and electrical revolution count to shaft angle in radians
float result = (reading + PI) / _2PI;
result = _2PI * (result + electrical_rev) / pp;

//update previous reading for rollover handling
prev_reading = reading;
return result;
}

void LinearHall::init(int _centerA, int _centerB) {
// Skip configuring the pins here because they normally default to input anyway, and
// this makes it possible to use ADC channel numbers instead of pin numbers when using
// custom implementation of ReadLinearHalls, to avoid having to remap them every update.
// If pins do need to be configured, it can be done by user code before calling init.
//pinMode(pinA, INPUT);
//pinMode(pinB, INPUT);

centerA = _centerA;
centerB = _centerB;

//establish initial reading for rollover handling
electrical_rev = 0;
ReadLinearHalls(pinA, pinB, &lastA, &lastB);
prev_reading = _atan2(lastA - centerA, lastB - centerB);
}

void LinearHall::init(FOCMotor *motor) {
if (!motor->enabled) {
SIMPLEFOC_DEBUG("LinearHall::init failed. Call after motor.init, but before motor.initFOC.");
return;
}

// See comment in other version of init for why these are commented out
//pinMode(pinA, INPUT);
//pinMode(pinB, INPUT);

int minA, maxA, minB, maxB;

ReadLinearHalls(pinA, pinB, &lastA, &lastB);
minA = maxA = centerA = lastA;
minB = maxB = centerB = lastB;

// move one mechanical revolution forward
for (int i = 0; i <= 2000; i++)
{
float angle = _3PI_2 + _2PI * i * pp / 2000.0f;
motor->setPhaseVoltage(motor->voltage_sensor_align, 0, angle);

ReadLinearHalls(pinA, pinB, &lastA, &lastB);

if (lastA < minA)
minA = lastA;
if (lastA > maxA)
maxA = lastA;
centerA = (minA + maxA) / 2;

if (lastB < minB)
minB = lastB;
if (lastB > maxB)
maxB = lastB;
centerB = (minB + maxB) / 2;

_delay(2);
}

//establish initial reading for rollover handling
electrical_rev = 0;
prev_reading = _atan2(lastA - centerA, lastB - centerB);
}
45 changes: 45 additions & 0 deletions src/encoders/linearhall/LinearHall.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#ifndef LINEAR_HALL_SENSOR_LIB_H
#define LINEAR_HALL_SENSOR_LIB_H

#include <SimpleFOC.h>

// This function can be overridden with custom ADC code on platforms with poor analogRead performance.
void ReadLinearHalls(int hallA, int hallB, int *a, int *b);

/**
* This sensor class is for two linear hall effect sensors such as 49E, which are
* positioned 90 electrical degrees apart (if one is centered on a rotor magnet,
* the other is half way between rotor magnets).
* It can also be used for a single magnet mounted to the motor shaft (set pp to 1).
*
* For more information, see this forum thread and PDF
* https://community.simplefoc.com/t/40-cent-magnetic-angle-sensing-technique/1959
* https://gist.github.com/nanoparticle/00030ea27c59649edbed84f0a957ebe1
*/
class LinearHall: public Sensor{
public:
LinearHall(int hallA, int hallB, int pp);

void init(int centerA, int centerB); // Initialize without moving motor
void init(class FOCMotor *motor); // Move motor to find center values

// Get current shaft angle from the sensor hardware, and
// return it as a float in radians, in the range 0 to 2PI.
// - This method is pure virtual and must be implemented in subclasses.
// Calling this method directly does not update the base-class internal fields.
// Use update() when calling from outside code.
float getSensorAngle() override;

int centerA;
int centerB;
int lastA, lastB;

private:
int pinA;
int pinB;
int pp;
int electrical_rev;
float prev_reading;
};

#endif

0 comments on commit d4c0ce5

Please sign in to comment.