Skip to content

Commit e5e607c

Browse files
committed
adding MagneticSensorAnalog implementation
1 parent fe7dfd9 commit e5e607c

File tree

4 files changed

+298
-0
lines changed

4 files changed

+298
-0
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
*
3+
* Torque control example using voltage control loop.
4+
*
5+
* Most of the low-end BLDC driver boards doesn't have current measurement therefore SimpleFOC offers
6+
* you a way to control motor torque by setting the voltage to the motor instead of the current.
7+
*
8+
* This makes the BLDC motor effectively a DC motor, and you can use it in a same way.
9+
*/
10+
#include <SimpleFOC.h>
11+
12+
// motor instance
13+
BLDCMotor motor = BLDCMotor(9, 10, 11, 11, 7);
14+
15+
/**
16+
* magnetic sensor reading analog voltage on pin A1. This voltage is proportional to rotation position.
17+
* Tested on AS5600 magnetic sensor running in 'analog mode'. Note AS5600 works better in 'i2C mode' (less noise) but only supports one sensor per i2c bus.
18+
*/
19+
MagneticSensorAnalog sensor = MagneticSensorAnalog(A1, 14, 1020);
20+
21+
22+
23+
void setup() {
24+
25+
// initialize encoder sensor hardware
26+
sensor.init();
27+
// link the motor to the sensor
28+
motor.linkSensor(&sensor);
29+
30+
// power supply voltage
31+
// default 12V
32+
motor.voltage_power_supply = 12;
33+
// aligning voltage
34+
motor.voltage_sensor_align = 3;
35+
36+
// choose FOC modulation (optional)
37+
motor.foc_modulation = FOCModulationType::SpaceVectorPWM;
38+
39+
// set motion control loop to be used
40+
motor.controller = ControlType::voltage;
41+
42+
// use monitoring with serial for motor init
43+
// comment out if not needed
44+
motor.useMonitoring(Serial);
45+
46+
// use monitoring with serial
47+
Serial.begin(115200);
48+
// comment out if not needed
49+
motor.useMonitoring(Serial);
50+
51+
// initialize motor
52+
motor.init();
53+
// align sensor and start FOC
54+
motor.initFOC();
55+
56+
Serial.println("Motor ready.");
57+
Serial.println("Set the target voltage using serial terminal:");
58+
_delay(1000);
59+
}
60+
61+
// target voltage to be set to the motor
62+
float target_voltage = 2;
63+
64+
void loop() {
65+
66+
// main FOC algorithm function
67+
// the faster you run this function the better
68+
// Arduino UNO loop ~1kHz
69+
// Bluepill loop ~10kHz
70+
motor.loopFOC();
71+
72+
// Motion control function
73+
// velocity, position or voltage (defined in motor.controller)
74+
// this function can be run at much lower frequency than loopFOC() function
75+
// You can also use motor.move() and set the motor.target in the code
76+
motor.move(target_voltage);
77+
78+
// communicate with the user
79+
serialReceiveUserCommand();
80+
}
81+
82+
83+
// utility function enabling serial communication with the user to set the target values
84+
// this function can be implemented in serialEvent function as well
85+
void serialReceiveUserCommand() {
86+
87+
// a string to hold incoming data
88+
static String received_chars;
89+
90+
while (Serial.available()) {
91+
// get the new byte:
92+
char inChar = (char)Serial.read();
93+
// add it to the string buffer:
94+
received_chars += inChar;
95+
// end of user input
96+
if (inChar == '\n') {
97+
98+
// change the motor target
99+
target_voltage = received_chars.toFloat();
100+
Serial.print("Target voltage: ");
101+
Serial.println(target_voltage);
102+
103+
// reset the command buffer
104+
received_chars = "";
105+
}
106+
}
107+
}

src/MagneticSensorAnalog.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#include "MagneticSensorAnalog.h"
2+
3+
/** MagneticSensorAnalog(uint8_t _pinAnalog, int _min, int _max)
4+
* @param _pinAnalog the pin that is reading the pwm from magnetic sensor
5+
* @param _min_raw_count the smallest expected reading. Whilst you might expect it to be 0 it is often ~15. Getting this wrong results in a small click once per revolution
6+
* @param _max_raw_count the largest value read. whilst you might expect it to be 2^10 = 1023 it is often ~ 1020
7+
*/
8+
MagneticSensorAnalog::MagneticSensorAnalog(uint8_t _pinAnalog, int _min_raw_count, int _max_raw_count){
9+
10+
pinAnalog = _pinAnalog;
11+
12+
cpr = _max_raw_count - _min_raw_count;
13+
min_raw_count = _min_raw_count;
14+
max_raw_count = _max_raw_count;
15+
16+
if(pullup == Pullup::INTERN){
17+
pinMode(pinAnalog, INPUT_PULLUP);
18+
}else{
19+
pinMode(pinAnalog, INPUT);
20+
}
21+
22+
}
23+
24+
25+
void MagneticSensorAnalog::init(){
26+
27+
// velocity calculation init
28+
angle_prev = 0;
29+
velocity_calc_timestamp = _micros();
30+
31+
// full rotations tracking number
32+
full_rotation_offset = 0;
33+
raw_count_prev = getRawCount();
34+
zero_offset = 0;
35+
}
36+
37+
// Shaft angle calculation
38+
// angle is in radians [rad]
39+
float MagneticSensorAnalog::getAngle(){
40+
// raw data from the sensor
41+
raw_count = getRawCount();
42+
43+
int delta = raw_count - raw_count_prev;
44+
// if overflow happened track it as full rotation
45+
if(abs(delta) > (0.8*cpr) ) full_rotation_offset += delta > 0 ? -_2PI : _2PI;
46+
47+
float angle = natural_direction * (full_rotation_offset + ( (float) (raw_count - min_raw_count) / (float)cpr) * _2PI);
48+
49+
// calculate velocity here
50+
long now = _micros();
51+
float Ts = ( now - velocity_calc_timestamp)*1e-6;
52+
// quick fix for strange cases (micros overflow)
53+
if(Ts <= 0 || Ts > 0.5) Ts = 1e-3;
54+
velocity = (angle - angle_prev)/Ts;
55+
56+
// save variables for future pass
57+
raw_count_prev = raw_count;
58+
angle_prev = angle;
59+
velocity_calc_timestamp = now;
60+
61+
return angle;
62+
}
63+
64+
// Shaft velocity calculation
65+
float MagneticSensorAnalog::getVelocity(){
66+
// TODO: Refactor?: to avoid angle being called twice, velocity is pre-calculted during getAngle
67+
return velocity;
68+
}
69+
70+
// set current angle as zero angle
71+
// return the angle [rad] difference
72+
float MagneticSensorAnalog::initRelativeZero(){
73+
float angle_offset = -getAngle();
74+
zero_offset = getRawCount();
75+
76+
// angle tracking variables
77+
full_rotation_offset = 0;
78+
return angle_offset;
79+
}
80+
// set absolute zero angle as zero angle
81+
// return the angle [rad] difference
82+
float MagneticSensorAnalog::initAbsoluteZero(){
83+
float rotation = -(int)zero_offset;
84+
// init absolute zero
85+
zero_offset = 0;
86+
87+
// angle tracking variables
88+
full_rotation_offset = 0;
89+
// return offset in radians
90+
return rotation / (float)cpr * _2PI;
91+
}
92+
// returns 0 if it has no absolute 0 measurement
93+
// 0 - incremental encoder without index
94+
// 1 - encoder with index & magnetic sensors
95+
int MagneticSensorAnalog::hasAbsoluteZero(){
96+
return 1;
97+
}
98+
// returns 0 if it does need search for absolute zero
99+
// 0 - magnetic sensor
100+
// 1 - ecoder with index
101+
int MagneticSensorAnalog::needsAbsoluteZeroSearch(){
102+
return 0;
103+
}
104+
105+
// function reading the raw counter of the magnetic sensor
106+
int MagneticSensorAnalog::getRawCount(){
107+
return analogRead(pinAnalog);
108+
}

src/MagneticSensorAnalog.h

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#ifndef MAGNETICSENSORANALOG_LIB_H
2+
#define MAGNETICSENSORANALOG_LIB_H
3+
4+
#include "Arduino.h"
5+
#include <Wire.h>
6+
#include "FOCutils.h"
7+
#include "Sensor.h"
8+
9+
/**
10+
* This sensor has been tested with AS5600 running in 'analog mode'. This is where output pin of AS6000 is connected to an analog pin on your microcontroller.
11+
* This approach is very simple but you may more accurate results with MagneticSensorI2C if that is also supported as its skips the DAC -> ADC conversion (ADC supports MagneticSensorI2C)
12+
*/
13+
class MagneticSensorAnalog: public Sensor{
14+
public:
15+
/**
16+
* MagneticSensorAnalog class constructor
17+
* @param _pinAnalog the pin to read the PWM signal
18+
* @param _pinAnalog the pin to read the PWM signal
19+
*/
20+
MagneticSensorAnalog(uint8_t _pinAnalog, int _min = 0, int _max = 0);
21+
22+
23+
/** sensor initialise pins */
24+
void init();
25+
26+
int pinAnalog; //!< encoder hardware pin A
27+
28+
// Encoder configuration
29+
Pullup pullup;
30+
31+
// implementation of abstract functions of the Sensor class
32+
/** get current angle (rad) */
33+
float getAngle();
34+
/** get current angular velocity (rad/s) **/
35+
float getVelocity();
36+
/**
37+
* set current angle as zero angle
38+
* return the angle [rad] difference
39+
*/
40+
float initRelativeZero();
41+
/**
42+
* set absolute zero angle as zero angle
43+
* return the angle [rad] difference
44+
*/
45+
float initAbsoluteZero();
46+
/** returns 1 because it is the absolute sensor */
47+
int hasAbsoluteZero();
48+
/** returns 0 maning it doesn't need search for absolute zero */
49+
int needsAbsoluteZeroSearch();
50+
/** raw count (typically in range of 0-1023), useful for debugging resolution issues */
51+
int raw_count;
52+
int min_raw_count;
53+
int max_raw_count;
54+
int cpr;
55+
56+
private:
57+
// float cpr; //!< Maximum range of the magnetic sensor
58+
59+
60+
int read();
61+
62+
word zero_offset; //!< user defined zero offset
63+
/**
64+
* Function getting current angle register value
65+
* it uses angle_register variable
66+
*/
67+
int getRawCount();
68+
69+
// total angle tracking variables
70+
float full_rotation_offset; //!<number of full rotations made
71+
int raw_count_prev; //!< angle in previous position calculation step
72+
73+
// velocity calculation variables
74+
float angle_prev; //!< angle in previous velocity calculation step
75+
long velocity_calc_timestamp; //!< last velocity calculation timestamp
76+
float velocity;
77+
78+
79+
};
80+
81+
82+
#endif

src/SimpleFOC.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ void loop() {
9191
#include "Encoder.h"
9292
#include "MagneticSensorSPI.h"
9393
#include "MagneticSensorI2C.h"
94+
#include "MagneticSensorAnalog.h"
9495
#include "HallSensor.h"
9596
#include "BLDCMotor.h"
9697

0 commit comments

Comments
 (0)