0.3.0 AS5600

This commit is contained in:
rob tillaart 2022-07-07 12:53:02 +02:00
parent 23dd2659d6
commit 243fe8f47d
6 changed files with 283 additions and 83 deletions

View File

@ -1,7 +1,7 @@
//
// FILE: AS56000.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// VERSION: 0.3.0
// PURPOSE: Arduino library for AS5600 magnetic rotation meter
// DATE: 2022-05-28
// URL: https://github.com/RobTillaart/AS5600
@ -25,9 +25,13 @@
// define constants for configuration functions.
// fix conversion constants (4096 based)
// add get- setOffset(degrees) functions. (no radians yet)
// 0.2.1 notreleased add bool return to set() functions.
// update Readme (analog / PWM out)
// 0.3.0 2022-07-07 fix #18 invalid mask setConfigure().
// TODO
// Power-up time 1 minute
// Power-up time 1 minute (need HW)
// check Timing Characteristics
@ -151,9 +155,11 @@ uint8_t AS5600::getZMCO()
}
void AS5600::setZPosition(uint16_t value)
bool AS5600::setZPosition(uint16_t value)
{
writeReg2(AS5600_ZPOS, value & 0x0FFF);
if (value > 0x0FFF) return false;
writeReg2(AS5600_ZPOS, value);
return true;
}
@ -164,9 +170,11 @@ uint16_t AS5600::getZPosition()
}
void AS5600::setMPosition(uint16_t value)
bool AS5600::setMPosition(uint16_t value)
{
writeReg2(AS5600_MPOS, value & 0x0FFF);
if (value > 0x0FFF) return false;
writeReg2(AS5600_MPOS, value);
return true;
}
@ -177,9 +185,11 @@ uint16_t AS5600::getMPosition()
}
void AS5600::setMaxAngle(uint16_t value)
bool AS5600::setMaxAngle(uint16_t value)
{
writeReg2(AS5600_MANG, value & 0x0FFF);
if (value > 0x0FFF) return false;
writeReg2(AS5600_MANG, value);
return true;
}
@ -190,27 +200,30 @@ uint16_t AS5600::getMaxAngle()
}
void AS5600::setConfigure(uint16_t value)
bool AS5600::setConfigure(uint16_t value)
{
writeReg2(AS5600_CONF, value & 0x2FFF);
if (value > 0x3FFF) return false;
writeReg2(AS5600_CONF, value);
return true;
}
uint16_t AS5600::getConfigure()
{
uint16_t value = readReg2(AS5600_CONF) & 0x2FFF;
uint16_t value = readReg2(AS5600_CONF) & 0x3FFF;
return value;
}
// details configure
void AS5600::setPowerMode(uint8_t powerMode)
bool AS5600::setPowerMode(uint8_t powerMode)
{
if (powerMode > 3) return;
if (powerMode > 3) return false;
uint8_t value = readReg(AS5600_CONF + 1);
value &= ~AS5600_CONF_POWER_MODE;
value |= powerMode;
writeReg(AS5600_CONF + 1, value);
return true;
}
uint8_t AS5600::getPowerMode()
@ -218,13 +231,14 @@ uint8_t AS5600::getPowerMode()
return readReg(AS5600_CONF + 1) & 0x03;
}
void AS5600::setHysteresis(uint8_t hysteresis)
bool AS5600::setHysteresis(uint8_t hysteresis)
{
if (hysteresis > 3) return;
if (hysteresis > 3) return false;
uint8_t value = readReg(AS5600_CONF + 1);
value &= ~AS5600_CONF_HYSTERESIS;
value |= (hysteresis << 2);
writeReg(AS5600_CONF + 1, value);
return true;
}
uint8_t AS5600::getHysteresis()
@ -232,13 +246,14 @@ uint8_t AS5600::getHysteresis()
return (readReg(AS5600_CONF + 1) >> 2) & 0x03;
}
void AS5600::setOutputMode(uint8_t outputMode)
bool AS5600::setOutputMode(uint8_t outputMode)
{
if (outputMode > 2) return;
if (outputMode > 2) return false;
uint8_t value = readReg(AS5600_CONF + 1);
value &= ~AS5600_CONF_OUTPUT_MODE;
value |= (outputMode << 4);
writeReg(AS5600_CONF + 1, value);
return true;
}
uint8_t AS5600::getOutputMode()
@ -246,13 +261,14 @@ uint8_t AS5600::getOutputMode()
return (readReg(AS5600_CONF + 1) >> 4) & 0x03;
}
void AS5600::setPWMFrequency(uint8_t pwmFreq)
bool AS5600::setPWMFrequency(uint8_t pwmFreq)
{
if (pwmFreq > 3) return;
if (pwmFreq > 3) return false;
uint8_t value = readReg(AS5600_CONF + 1);
value &= ~AS5600_CONF_PWM_FREQUENCY;
value |= (pwmFreq << 6);
writeReg(AS5600_CONF + 1, value);
return true;
}
uint8_t AS5600::getPWMFrequency()
@ -260,13 +276,14 @@ uint8_t AS5600::getPWMFrequency()
return (readReg(AS5600_CONF + 1) >> 6) & 0x03;
}
void AS5600::setSlowFilter(uint8_t mask)
bool AS5600::setSlowFilter(uint8_t mask)
{
if (mask > 3) return;
if (mask > 3) return false;
uint8_t value = readReg(AS5600_CONF);
value &= ~AS5600_CONF_SLOW_FILTER;
value |= mask;
writeReg(AS5600_CONF, value);
return true;
}
uint8_t AS5600::getSlowFilter()
@ -274,13 +291,14 @@ uint8_t AS5600::getSlowFilter()
return readReg(AS5600_CONF) & 0x03;
}
void AS5600::setFastFilter(uint8_t mask)
bool AS5600::setFastFilter(uint8_t mask)
{
if (mask > 7) return;
if (mask > 7) return false;
uint8_t value = readReg(AS5600_CONF);
value &= ~AS5600_CONF_FAST_FILTER;
value |= (mask << 2);
writeReg(AS5600_CONF, value);
return true;
}
uint8_t AS5600::getFastFilter()
@ -288,13 +306,14 @@ uint8_t AS5600::getFastFilter()
return (readReg(AS5600_CONF) >> 2) & 0x07;
}
void AS5600::setWatchDog(uint8_t mask)
bool AS5600::setWatchDog(uint8_t mask)
{
if (mask > 1) return;
if (mask > 1) return false;
uint8_t value = readReg(AS5600_CONF);
value &= ~AS5600_CONF_WATCH_DOG;
value |= (mask << 5);
writeReg(AS5600_CONF, value);
return true;
}
uint8_t AS5600::getWatchDog()
@ -324,7 +343,7 @@ uint16_t AS5600::readAngle()
{
uint16_t value = readReg2(AS5600_ANGLE) & 0x0FFF;
if (_offset > 0) value = (value + _offset) & 0x0FFF;
if ((_directionPin == 255) && (_direction == AS5600_COUNTERCLOCK_WISE))
{
value = (4096 - value) & 4095;
@ -333,18 +352,18 @@ uint16_t AS5600::readAngle()
}
void AS5600::setOffset(float degrees)
bool AS5600::setOffset(float degrees)
{
bool neg = false;
if (degrees < 0)
{
neg = true;
degrees = -degrees;
}
// expect loss of precision.
if (abs(degrees) > 36000) return false;
bool neg = (degrees < 0);
if (neg) degrees = -degrees;
uint16_t offset = round(degrees * (4096 / 360.0));
offset &= 4095;
if (neg) offset = 4096 - offset;
_offset = offset;
_offset = offset;
return true;
}

View File

@ -2,7 +2,7 @@
//
// FILE: AS5600.h
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// VERSION: 0.3.0
// PURPOSE: Arduino library for AS5600 magnetic rotation meter
// DATE: 2022-05-28
// URL: https://github.com/RobTillaart/AS5600
@ -12,7 +12,7 @@
#include "Wire.h"
#define AS5600_LIB_VERSION (F("0.2.0"))
#define AS5600_LIB_VERSION (F("0.3.0"))
// setDirection
const uint8_t AS5600_CLOCK_WISE = 0; // LOW
@ -93,57 +93,76 @@ public:
// SET CONFIGURE REGISTERS
// read datasheet first
// 0 = AS5600_CLOCK_WISE
// 1 = AS5600_COUNTERCLOCK_WISE
// all other = AS5600_COUNTERCLOCK_WISE
void setDirection(uint8_t direction = AS5600_CLOCK_WISE);
uint8_t getDirection();
uint8_t getZMCO();
void setZPosition(uint16_t value);
// 0 .. 4095
// returns false if parameter out of range
bool setZPosition(uint16_t value);
uint16_t getZPosition();
void setMPosition(uint16_t value);
// 0 .. 4095
// returns false if parameter out of range
bool setMPosition(uint16_t value);
uint16_t getMPosition();
void setMaxAngle(uint16_t value);
// 0 .. 4095
// returns false if parameter out of range
bool setMaxAngle(uint16_t value);
uint16_t getMaxAngle();
// access the whole configuration register
// check datasheet for bit fields
void setConfigure(uint16_t value);
// returns false if parameter out of range
bool setConfigure(uint16_t value);
uint16_t getConfigure();
// access details of the configuration register
// 0 = Normal
// 1,2,3 are low power mode - check datasheet
void setPowerMode(uint8_t powerMode);
// returns false if parameter out of range
bool setPowerMode(uint8_t powerMode);
uint8_t getPowerMode();
// 0 = off 1 = lsb1 2 = lsb2 3 = lsb3
void setHysteresis(uint8_t hysteresis);
// returns false if parameter out of range
// suppresses noise when the magnet is not moving.
bool setHysteresis(uint8_t hysteresis);
uint8_t getHysteresis();
// 0 = analog 0-100%
// 1 = analog 10-90%
// 2 = PWM
void setOutputMode(uint8_t outputMode);
// returns false if parameter out of range
bool setOutputMode(uint8_t outputMode);
uint8_t getOutputMode();
// 0 = 115 1 = 230 2 = 460 3 = 920 (Hz)
void setPWMFrequency(uint8_t pwmFreq);
// returns false if parameter out of range
bool setPWMFrequency(uint8_t pwmFreq);
uint8_t getPWMFrequency();
// 0 = 16x 1 = 8x 2 = 4x 3 = 2x
void setSlowFilter(uint8_t mask);
// returns false if parameter out of range
bool setSlowFilter(uint8_t mask);
uint8_t getSlowFilter();
// 0 = none 1 = LSB6 2 = LSB7 3 = LSB9
// 4 = LSB18 5 = LSB21 6 = LSB24 7 = LSB10
void setFastFilter(uint8_t mask);
// returns false if parameter out of range
bool setFastFilter(uint8_t mask);
uint8_t getFastFilter();
// 0 = OFF
// 1 = ON (auto low power mode)
void setWatchDog(uint8_t mask);
// returns false if parameter out of range
bool setWatchDog(uint8_t mask);
uint8_t getWatchDog();
@ -152,7 +171,10 @@ public:
uint16_t readAngle();
// software based offset.
void setOffset(float degrees);
// degrees = -359.99 .. 359.99 (preferred)
// returns false if abs(parameter) > 36000
// => expect loss of precision
bool setOffset(float degrees);
float getOffset();

View File

@ -17,27 +17,30 @@ Arduino library for AS5600 magnetic rotation meter.
**AS5600** is a library for an AS5600 based magnetic rotation meter.
**Warning: experimental - not tested**
**Warning: experimental**
The sensor can measure a full rotation in 4096 steps.
The precision is therefore limited to 0.1°.
Noise levels unknown, but one might expect it to be effected by electric
and or magnetic signals in the environment.
Also unknown is the influence of metals near the sensor or an unstable or fluctuating power supply.
TODO: buy hardware to test the library and get hands on experience with the sensor.
Also unknown is the influence of metals near the sensor or an unstable
or fluctuating power supply.
### I2C Address
The I2C address of the **AS5600** is always 0x36.
To use more than one **AS5600** on one I2C bus, see Multiplexing below.
### OUT pin
The sensor has an output pin named **OUT**.
This pin can be used for an analogue or PWM output signal.
This pin can be used for an analogue or PWM output signal.
See **Analogue Pin** and **PWM Pin** below.
Examples are added to show how to use this pin with **setOutputMode()**.
@ -45,35 +48,45 @@ Examples are added to show how to use this pin with **setOutputMode()**.
Not tested.
PGO stand for Programming Option, it is used to calibrate / program the sensor.
PGO stands for Programming Option, it is used to calibrate / program the sensor.
As the sensor can be programmed only a few times one should
use this functionality with extreme care.
See datasheet for a detailed list of steps to be done.
See also **Make configuration persistent** below.
See **Make configuration persistent** below.
## Hardware connection
The I2C address of the **AS5600** is always 0x36.
The AS5600 datasheet states it supports Fast-Mode == 400 KHz
and Fast-Mode-Plus == 1000 KHz.
The sensor should connect the I2C lines SDA and SCL and the
VCC and GND to communicate with the processor.
The DIR (direction) pin of the sensor should be connected to:
- **GND** = fixed clockwise(\*)
- **VCC** = fixed counter clock wise
- a free IO pin of the processor = library control.
- a free IO pin of the processor = under library control.
In the latter setup the library can control the direction of
counting by initializing this pin in **begin(directionPin)**,
followed by **setDirection(direction)**. For the direction the
library defines two constants named:
In the latter setup the library can control the direction of counting by initializing this pin in **begin(directionPin)**, followed by **setDirection(direction)**. For the direction the library defines two constants named:
- **AS5600_CLOCK_WISE (0)**
- **AS5600_COUNTERCLOCK_WISE (1)**
(\*) if **begin()** is called without **directionPin** or with this parameter set to **255**, software direction control is enabled.
See below for more information.
(\*) if **begin()** is called without **directionPin** or with this
parameter set to **255**, software direction control is enabled.
See Software Direction Control below for more information.
## Interface
The I2C address of the **AS5600** is always 0x36.
### Constants
@ -100,12 +113,17 @@ Also Configuration bits below for configuration related ones.
### Constructor + I2C
- **AS5600(TwoWire \*wire = &Wire)** Constructor with optional Wire interface as parameter.
- **bool begin(uint8_t directionPin = 255)** set the value for the directionPin.
If the pin is set to 255, the default value, there will be software direction control instead of hardware control.
- **AS5600(TwoWire \*wire = &Wire)** Constructor with optional Wire
interface as parameter.
- **bool begin(uint8_t directionPin = 255)** set the value for the
directionPin.
If the pin is set to 255, the default value, there will be software
direction control instead of hardware control.
See below.
- **bool begin(int sda, int scl, uint8_t directionPin = 255)** idem, for the ESP32 where one can choose the I2C pins.
If the pin is set to 255, the default value, there will be software direction control instead of hardware control.
- **bool begin(int sda, int scl, uint8_t directionPin = 255)** idem,
for the ESP32 where one can choose the I2C pins.
If the pin is set to 255, the default value, there will be software
direction control instead of hardware control.
See below.
- **bool isConnected()** checks if the address 0x36 is on the I2C bus.
- **uint8_t getAddress()** returns the fixed device address 0x36.
@ -124,16 +142,22 @@ AS5600_COUNTERCLOCK_WISE (1).
Please read the datasheet for details.
- **void setZPosition(uint16_t value)** set start position for limited range.
- **bool setZPosition(uint16_t value)** set start position for limited range. Value = 0..4095.
Returns false if parameter is out of range.
- **uint16_t getZPosition()** get current start position.
- **void setMPosition(uint16_t value)** set stop position for limited range.
- **bool setMPosition(uint16_t value)** set stop position for limited range. Value = 0..4095.
Returns false if parameter is out of range.
- **uint16_t getMPosition()** get current stop position.
- **void setMaxAngle(uint16_t value)** set limited range.
- **bool setMaxAngle(uint16_t value)** set limited range.
Value = 0..4095.
Returns false if parameter is out of range.
See datasheet **Angle Programming**
- **uint16_t getMaxAngle()** get limited range.
- **void setConfigure(uint16_t value)**
- **bool setConfigure(uint16_t value)**
Value = 0..0x4000
Returns false if parameter is out of range.
- **uint16_t getConfigure()**
@ -152,6 +176,21 @@ Please read datasheet for details.
| 13 | WD | Watch Dog | 0 = OFF, 1 = ON |
| 15-14 | | not used |
The library has functions to address these fields directly.
- **bool setPowerMode(uint8_t powerMode)**
returns false if parameter is out of range.
- **uint8_t getPowerMode()** returns the mode set.
See the .h file for the other get/set functions.
#### Hysteresis
- **bool setHysteresis(uint8_t hysteresis)** Suppresses "noise" on the output when the magnet is not moving.
In a way one is trading precision for stability.
returns false if parameter is out of range.
### Read Angle
@ -249,21 +288,115 @@ You can write this only **ONE** time to the AS5600.
Experimental 0.2.0
Normally one controls the direction of the sensor by connecting the DIR pin to one of the available IO pins of the processor. This IO pin is set in the library as parameter of the **begin(directionPin)** function.
Normally one controls the direction of the sensor by connecting the DIR pin
to one of the available IO pins of the processor.
This IO pin is set in the library as parameter of the **begin(directionPin)** function.
The directionPin is default set to 255, which defines a software direction control.
To have this working one has to connect the DIR pin of the sensor to GND.
This puts the sensor in a hardware clock wise mode, so it is up to the library to do the additional math so the **readAngle()** and **rawAngle()** behave as if the DIR pin was connected to the processor IO pin.
This puts the sensor in a hardware clock wise mode, so it is up to the library
to do the additional math so the **readAngle()** and **rawAngle()** behave as
if the DIR pin was connected to the processor IO pin.
The gain is that the user does not need an IO pin for this, which makes connecting the sensor a bit easier.
The gain is that the user does not need an IO pin for this,
which makes connecting the sensor a bit easier.
In terms of the interface, the user call **setDirection()** as before to change the direction.
The user still calls **setDirection()** as before to change the direction.
TODO: measure performance impact.
TODO: investigate impact on functionality of other registers.
## Analogue OUT
(details datasheet - page 25)
The OUT pin can be configured with the function:
- **bool setOutputMode(uint8_t outputMode)**
When the analog OUT mode is set the OUT pin provides a voltage
which is linear with the angle.
| VDD | mode | percentage | output | 1° in V |
|:---:|:------:|:----------:|:---------:|:----------:|
| 5V0 | 0 | 0 - 100% | 0.0 - 5.0 | 0.01388889 |
| 5V0 | 1 | 10 - 90% | 0.5 - 4.5 | 0.01111111 |
| 3V3 | 0 | 0 - 100% | 0.0 - 3.3 | 0.00916667 |
| 3V3 | 1 | 10 - 90% | 0.3 - 3.0 | 0.00750000 |
To measure these angles a 10 bit ADC or higher is needed.
When analog OUT is selected **readAngle()** will still return valid values.
## PWM OUT
(details datasheet - page 27)
The OUT pin can be configured with the function:
- **bool setOutputMode(uint8_t outputMode)** outputMode = 2 = PWM
When the PWM OUT mode is set the OUT pin provides a duty cycle which is linear
with the angle. However they PWM has a lead in (HIGH) and a lead out (LOW).
The pulse width is 4351 units, 128 high, 4095 angle, 128 low.
| Angle | HIGH | LOW | HIGH % | LOW % | Notes |
|:-------:|:-------:|:------:|:--------:|:--------:|:--------|
| 0 | 128 | 4223 | 2,94% | 97,06% |
| 10 | 242 | 4109 | 5,56% | 94,44% |
| 20 | 356 | 3996 | 8,17% | 91,83% |
| 45 | 640 | 3711 | 14,71% | 85,29% |
| 90 | 1152 | 3199 | 26,47% | 73,53% |
| 135 | 1664 | 2687 | 38,24% | 61,76% |
| 180 | 2176 | 2176 | 50,00% | 50,00% |
| 225 | 2687 | 1664 | 61,76% | 38,24% |
| 270 | 3199 | 1152 | 73,53% | 26,47% |
| 315 | 3711 | 640 | 85,29% | 14,71% |
| 360 | 4223 | 128 | 97,06% | 2,94% | in fact 359.9 something as 360 == 0
#### Formula:
based upon the table above ```angle = map(dutyCycle, 2.94, 97.06, 0.0, 359.9);```
or in code ..
```cpp
t0 = micros(); // rising;
t1 = micros(); // falling;
t2 = micros(); // rising; new t0
// note that t2 - t0 should be a constant depending on frequency set.
// however as there might be up to 5% variation it cannot be hard coded.
float dutyCycle = (1.0 * (t1 - t0)) / (t2 - t0);
float angle = (dutyCycle - 0.0294) * (359.9 / (0.9706 - 0.0294));
```
#### PWM frequency
The AS5600 allows one to set the PWM base frequency (~5%)
- **bool setPWMFrequency(uint8_t pwmFreq)**
| mode | pwmFreq | step in us | 1° in time |
|:----:|:-------:|:----------:|:----------:|
| 0 | 115 Hz | 2.123 | 24.15 |
| 1 | 230 Hz | 1.062 | 12.77 |
| 2 | 460 Hz | 0.531 | 6.39 |
| 3 | 920 Hz | 0.216 | 3.20 |
Note that at the higher frequencies the step size becomes smaller
and smaller and it becomes harder to measure.
You need a sub-micro second hardware timer to measure the pulse width
with enough precision to get the max resolution.
When PWM OUT is selected **readAngle()** will still return valid values.
## Multiplexing
The I2C address of the **AS5600** is always 0x36.
@ -276,7 +409,7 @@ the sensor from listening to signals on the I2C bus.
Finally the sensor has an analogue output **OUT**.
This output could be used to connect multiple sensors to different analogue ports of the processor.
**Warning**: If and how well this analog option works is not verified or tested. (TODO)
**Warning**: If and how well this analog option works is not verified or tested.
## Operational
@ -317,17 +450,19 @@ priority is relative
- get hardware to test.
- improve documentation.
- investigate OUT output pin.
- PWM, analog_90 and analog_100
#### med prio
- limit the **setOffset()** to -359.99 .. 359.99
- investigate **readMagnitude()**
- combination of AGC and MD, ML and MH flags?
- investigate performance
- basic performance per function
- I2C improvements
- software direction
- investigate OUT behavior in practice
- analogue
- PWM
- write examples:
- as5600_calibration.ino (needs HW and lots of time)
- different configuration options

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/AS5600.git"
},
"version": "0.2.0",
"version": "0.3.0",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",

View File

@ -1,5 +1,5 @@
name=AS5600
version=0.2.0
version=0.3.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for AS5600 magnetic rotation meter

View File

@ -154,21 +154,45 @@ unittest(test_offset)
AS5600 as5600;
as5600.begin();
for (int of = 0; of < 360; of += 40)
{
as5600.setOffset(of);
assertEqualFloat(of, as5600.getOffset(), 0.05);
}
as5600.setOffset(-40.25);
assertTrue(as5600.setOffset(-40.25));
assertEqualFloat(319.75, as5600.getOffset(), 0.05);
as5600.setOffset(-400.25);
assertTrue(as5600.setOffset(-400.25));
assertEqualFloat(319.75, as5600.getOffset(), 0.05);
as5600.setOffset(753.15);
assertTrue(as5600.setOffset(753.15));
assertEqualFloat(33.15, as5600.getOffset(), 0.05);
assertFalse(as5600.setOffset(36000.1));
assertFalse(as5600.setOffset(-36000.1));
}
unittest(test_failing_set_commands)
{
AS5600 as5600;
as5600.begin();
assertFalse(as5600.setZPosition(4096));
assertFalse(as5600.setMPosition(4096));
assertFalse(as5600.setMaxAngle(4096));
assertFalse(as5600.setConfigure(0x4000));
assertFalse(as5600.setPowerMode(4));
assertFalse(as5600.setHysteresis(4));
assertFalse(as5600.setOutputMode(3));
assertFalse(as5600.setPWMFrequency(4));
assertFalse(as5600.setSlowFilter(4));
assertFalse(as5600.setFastFilter(8));
assertFalse(as5600.setWatchDog(2));
}