[P142] Add AS5600 Magnetic angle sensor

This commit is contained in:
Ton Huisman
2024-06-18 23:16:17 +02:00
parent 8fb409cd6f
commit 8c46c773ad
44 changed files with 4793 additions and 3 deletions
+640
View File
@@ -0,0 +1,640 @@
//
// FILE: AS56000.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.6.1
// PURPOSE: Arduino library for AS5600 magnetic rotation meter
// DATE: 2022-05-28
// URL: https://github.com/RobTillaart/AS5600
#include "AS5600.h"
// CONFIGURATION REGISTERS
const uint8_t AS5600_ZMCO = 0x00;
const uint8_t AS5600_ZPOS = 0x01; // + 0x02
const uint8_t AS5600_MPOS = 0x03; // + 0x04
const uint8_t AS5600_MANG = 0x05; // + 0x06
const uint8_t AS5600_CONF = 0x07; // + 0x08
// CONFIGURATION BIT MASKS - byte level
const uint8_t AS5600_CONF_POWER_MODE = 0x03;
const uint8_t AS5600_CONF_HYSTERESIS = 0x0C;
const uint8_t AS5600_CONF_OUTPUT_MODE = 0x30;
const uint8_t AS5600_CONF_PWM_FREQUENCY = 0xC0;
const uint8_t AS5600_CONF_SLOW_FILTER = 0x03;
const uint8_t AS5600_CONF_FAST_FILTER = 0x1C;
const uint8_t AS5600_CONF_WATCH_DOG = 0x20;
// UNKNOWN REGISTERS 0x09-0x0A
// OUTPUT REGISTERS
const uint8_t AS5600_RAW_ANGLE = 0x0C; // + 0x0D
const uint8_t AS5600_ANGLE = 0x0E; // + 0x0F
// I2C_ADDRESS REGISTERS (AS5600L)
const uint8_t AS5600_I2CADDR = 0x20;
const uint8_t AS5600_I2CUPDT = 0x21;
// STATUS REGISTERS
const uint8_t AS5600_STATUS = 0x0B;
const uint8_t AS5600_AGC = 0x1A;
const uint8_t AS5600_MAGNITUDE = 0x1B; // + 0x1C
const uint8_t AS5600_BURN = 0xFF;
// STATUS BITS
const uint8_t AS5600_MAGNET_HIGH = 0x08;
const uint8_t AS5600_MAGNET_LOW = 0x10;
const uint8_t AS5600_MAGNET_DETECT = 0x20;
AS5600::AS5600(TwoWire *wire)
{
_wire = wire;
}
bool AS5600::begin(uint8_t directionPin)
{
_directionPin = directionPin;
if (_directionPin != AS5600_SW_DIRECTION_PIN)
{
pinMode(_directionPin, OUTPUT);
}
setDirection(AS5600_CLOCK_WISE);
if (! isConnected()) return false;
return true;
}
bool AS5600::isConnected()
{
_wire->beginTransmission(_address);
return ( _wire->endTransmission() == 0);
}
uint8_t AS5600::getAddress()
{
return _address;
}
/////////////////////////////////////////////////////////
//
// CONFIGURATION REGISTERS + direction pin
//
void AS5600::setDirection(uint8_t direction)
{
_direction = direction;
if (_directionPin != AS5600_SW_DIRECTION_PIN)
{
digitalWrite(_directionPin, _direction);
}
}
uint8_t AS5600::getDirection()
{
if (_directionPin != AS5600_SW_DIRECTION_PIN)
{
_direction = digitalRead(_directionPin);
}
return _direction;
}
uint8_t AS5600::getZMCO()
{
uint8_t value = readReg(AS5600_ZMCO);
return value;
}
bool AS5600::setZPosition(uint16_t value)
{
if (value > 0x0FFF) return false;
writeReg2(AS5600_ZPOS, value);
return true;
}
uint16_t AS5600::getZPosition()
{
uint16_t value = readReg2(AS5600_ZPOS) & 0x0FFF;
return value;
}
bool AS5600::setMPosition(uint16_t value)
{
if (value > 0x0FFF) return false;
writeReg2(AS5600_MPOS, value);
return true;
}
uint16_t AS5600::getMPosition()
{
uint16_t value = readReg2(AS5600_MPOS) & 0x0FFF;
return value;
}
bool AS5600::setMaxAngle(uint16_t value)
{
if (value > 0x0FFF) return false;
writeReg2(AS5600_MANG, value);
return true;
}
uint16_t AS5600::getMaxAngle()
{
uint16_t value = readReg2(AS5600_MANG) & 0x0FFF;
return value;
}
/////////////////////////////////////////////////////////
//
// CONFIGURATION
//
bool AS5600::setConfigure(uint16_t value)
{
if (value > 0x3FFF) return false;
writeReg2(AS5600_CONF, value);
return true;
}
uint16_t AS5600::getConfigure()
{
uint16_t value = readReg2(AS5600_CONF) & 0x3FFF;
return value;
}
// details configure
bool AS5600::setPowerMode(uint8_t powerMode)
{
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()
{
return readReg(AS5600_CONF + 1) & 0x03;
}
bool AS5600::setHysteresis(uint8_t hysteresis)
{
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()
{
return (readReg(AS5600_CONF + 1) >> 2) & 0x03;
}
bool AS5600::setOutputMode(uint8_t outputMode)
{
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()
{
return (readReg(AS5600_CONF + 1) >> 4) & 0x03;
}
bool AS5600::setPWMFrequency(uint8_t pwmFreq)
{
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()
{
return (readReg(AS5600_CONF + 1) >> 6) & 0x03;
}
bool AS5600::setSlowFilter(uint8_t mask)
{
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()
{
return readReg(AS5600_CONF) & 0x03;
}
bool AS5600::setFastFilter(uint8_t mask)
{
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()
{
return (readReg(AS5600_CONF) >> 2) & 0x07;
}
bool AS5600::setWatchDog(uint8_t mask)
{
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()
{
return (readReg(AS5600_CONF) >> 5) & 0x01;
}
/////////////////////////////////////////////////////////
//
// OUTPUT REGISTERS
//
uint16_t AS5600::rawAngle()
{
int16_t value = readReg2(AS5600_RAW_ANGLE) & 0x0FFF;
if (_offset > 0) value = (value + _offset) & 0x0FFF;
if ((_directionPin == AS5600_SW_DIRECTION_PIN) &&
(_direction == AS5600_COUNTERCLOCK_WISE))
{
value = (4096 - value) & 0x0FFF;
}
return value;
}
uint16_t AS5600::readAngle()
{
uint16_t value = readReg2(AS5600_ANGLE) & 0x0FFF;
if (_offset > 0) value = (value + _offset) & 0x0FFF;
if ((_directionPin == AS5600_SW_DIRECTION_PIN) &&
(_direction == AS5600_COUNTERCLOCK_WISE))
{
value = (4096 - value) & 0x0FFF;
}
return value;
}
bool AS5600::setOffset(float degrees)
{
// expect loss of precision.
if (abs(degrees) > 36000) return false;
bool neg = (degrees < 0);
if (neg) degrees = -degrees;
uint16_t offset = round(degrees * AS5600_DEGREES_TO_RAW);
offset &= 4095;
if (neg) offset = 4096 - offset;
_offset = offset;
return true;
}
float AS5600::getOffset()
{
return _offset * AS5600_RAW_TO_DEGREES;
}
bool AS5600::increaseOffset(float degrees)
{
// add offset to existing offset in degrees.
return setOffset((_offset * AS5600_RAW_TO_DEGREES) + degrees);
}
/////////////////////////////////////////////////////////
//
// STATUS REGISTERS
//
uint8_t AS5600::readStatus()
{
uint8_t value = readReg(AS5600_STATUS);
return value;
}
uint8_t AS5600::readAGC()
{
uint8_t value = readReg(AS5600_AGC);
return value;
}
uint16_t AS5600::readMagnitude()
{
uint16_t value = readReg2(AS5600_MAGNITUDE) & 0x0FFF;
return value;
}
bool AS5600::detectMagnet()
{
return (readStatus() & AS5600_MAGNET_DETECT) > 0;
}
bool AS5600::magnetTooStrong()
{
return (readStatus() & AS5600_MAGNET_HIGH) > 0;
}
bool AS5600::magnetTooWeak()
{
return (readStatus() & AS5600_MAGNET_LOW) > 0;
}
/////////////////////////////////////////////////////////
//
// BURN COMMANDS
//
// DO NOT UNCOMMENT - USE AT OWN RISK - READ DATASHEET
//
// void AS5600::burnAngle()
// {
// writeReg(AS5600_BURN, x0x80);
// }
//
//
// See https://github.com/RobTillaart/AS5600/issues/38
// void AS5600::burnSetting()
// {
// writeReg(AS5600_BURN, 0x40);
// delay(5);
// writeReg(AS5600_BURN, 0x01);
// writeReg(AS5600_BURN, 0x11);
// writeReg(AS5600_BURN, 0x10);
// delay(5);
// }
float AS5600::getAngularSpeed(uint8_t mode)
{
uint32_t now = micros();
int angle = readAngle();
uint32_t deltaT = now - _lastMeasurement;
int deltaA = angle - _lastAngle;
// assumption is that there is no more than 180° rotation
// between two consecutive measurements.
// => at least two measurements per rotation (preferred 4).
if (deltaA > 2048) deltaA -= 4096;
if (deltaA < -2048) deltaA += 4096;
float speed = (deltaA * 1e6) / deltaT;
// remember last time & angle
_lastMeasurement = now;
_lastAngle = angle;
// return radians, RPM or degrees.
if (mode == AS5600_MODE_RADIANS)
{
return speed * AS5600_RAW_TO_RADIANS;
}
if (mode == AS5600_MODE_RPM)
{
return speed * AS5600_RAW_TO_RPM;
}
// default return degrees
return speed * AS5600_RAW_TO_DEGREES;
}
/////////////////////////////////////////////////////////
//
// POSITION cumulative
//
int32_t AS5600::getCumulativePosition()
{
int16_t value = readReg2(AS5600_ANGLE) & 0x0FFF;
if (_error != AS5600_OK) return _position;
// whole rotation CW?
// less than half a circle
if ((_lastPosition > 2048) && ( value < (_lastPosition - 2048)))
{
_position = _position + 4096 - _lastPosition + value;
}
// whole rotation CCW?
// less than half a circle
else if ((value > 2048) && ( _lastPosition < (value - 2048)))
{
_position = _position - 4096 - _lastPosition + value;
}
else _position = _position - _lastPosition + value;
_lastPosition = value;
return _position;
}
int32_t AS5600::getRevolutions()
{
int32_t p = _position >> 12; // divide by 4096
return p;
// if (p < 0) p++;
// return p;
}
int32_t AS5600::resetPosition(int32_t position)
{
int32_t old = _position;
_position = position;
return old;
}
int32_t AS5600::resetCumulativePosition(int32_t position)
{
_lastPosition = readReg2(AS5600_RAW_ANGLE) & 0x0FFF;
int32_t old = _position;
_position = position;
return old;
}
int AS5600::lastError()
{
int value = _error;
_error = AS5600_OK;
return value;
}
/////////////////////////////////////////////////////////
//
// PROTECTED AS5600
//
uint8_t AS5600::readReg(uint8_t reg)
{
_error = AS5600_OK;
_wire->beginTransmission(_address);
_wire->write(reg);
if (_wire->endTransmission() != 0)
{
_error = AS5600_ERROR_I2C_READ_0;
return 0;
}
uint8_t n = _wire->requestFrom(_address, (uint8_t)1);
if (n != 1)
{
_error = AS5600_ERROR_I2C_READ_1;
return 0;
}
uint8_t _data = _wire->read();
return _data;
}
uint16_t AS5600::readReg2(uint8_t reg)
{
_error = AS5600_OK;
_wire->beginTransmission(_address);
_wire->write(reg);
if (_wire->endTransmission() != 0)
{
_error = AS5600_ERROR_I2C_READ_2;
return 0;
}
uint8_t n = _wire->requestFrom(_address, (uint8_t)2);
if (n != 2)
{
_error = AS5600_ERROR_I2C_READ_3;
return 0;
}
uint16_t _data = _wire->read();
_data <<= 8;
_data += _wire->read();
return _data;
}
uint8_t AS5600::writeReg(uint8_t reg, uint8_t value)
{
_error = AS5600_OK;
_wire->beginTransmission(_address);
_wire->write(reg);
_wire->write(value);
if (_wire->endTransmission() != 0)
{
_error = AS5600_ERROR_I2C_WRITE_0;
}
return _error;
}
uint8_t AS5600::writeReg2(uint8_t reg, uint16_t value)
{
_error = AS5600_OK;
_wire->beginTransmission(_address);
_wire->write(reg);
_wire->write(value >> 8);
_wire->write(value & 0xFF);
if (_wire->endTransmission() != 0)
{
_error = AS5600_ERROR_I2C_WRITE_0;
}
return _error;
}
/////////////////////////////////////////////////////////////////////////////
//
// AS5600L
//
AS5600L::AS5600L(uint8_t address, TwoWire *wire) : AS5600(wire)
{
_address = address;; // 0x40 = default address AS5600L.
}
bool AS5600L::setAddress(uint8_t address)
{
// skip reserved I2C addresses
if ((address < 8) || (address > 119)) return false;
// note address need to be shifted 1 bit.
writeReg(AS5600_I2CADDR, address << 1);
writeReg(AS5600_I2CUPDT, address << 1);
// remember new address.
_address = address;
return true;
}
bool AS5600L::setI2CUPDT(uint8_t address)
{
// skip reserved I2C addresses
if ((address < 8) || (address > 119)) return false;
writeReg(AS5600_I2CUPDT, address << 1);
return true;
}
uint8_t AS5600L::getI2CUPDT()
{
return (readReg(AS5600_I2CUPDT) >> 1);
}
// -- END OF FILE --
+288
View File
@@ -0,0 +1,288 @@
#pragma once
//
// FILE: AS5600.h
// AUTHOR: Rob Tillaart
// VERSION: 0.6.1
// PURPOSE: Arduino library for AS5600 magnetic rotation meter
// DATE: 2022-05-28
// URL: https://github.com/RobTillaart/AS5600
#include "Arduino.h"
#include "Wire.h"
#define AS5600_LIB_VERSION (F("0.6.1"))
// default addresses
const uint8_t AS5600_DEFAULT_ADDRESS = 0x36;
const uint8_t AS5600L_DEFAULT_ADDRESS = 0x40;
const uint8_t AS5600_SW_DIRECTION_PIN = 255;
// setDirection
const uint8_t AS5600_CLOCK_WISE = 0; // LOW
const uint8_t AS5600_COUNTERCLOCK_WISE = 1; // HIGH
// 0.087890625;
const float AS5600_RAW_TO_DEGREES = 360.0 / 4096;
const float AS5600_DEGREES_TO_RAW = 4096 / 360.0;
// 0.00153398078788564122971808758949;
const float AS5600_RAW_TO_RADIANS = PI * 2.0 / 4096;
// 4.06901041666666e-6
const float AS5600_RAW_TO_RPM = 60.0 / 4096;
// getAngularSpeed
const uint8_t AS5600_MODE_DEGREES = 0;
const uint8_t AS5600_MODE_RADIANS = 1;
const uint8_t AS5600_MODE_RPM = 2;
// ERROR CODES
const int AS5600_OK = 0;
const int AS5600_ERROR_I2C_READ_0 = -100;
const int AS5600_ERROR_I2C_READ_1 = -101;
const int AS5600_ERROR_I2C_READ_2 = -102;
const int AS5600_ERROR_I2C_READ_3 = -103;
const int AS5600_ERROR_I2C_WRITE_0 = -200;
const int AS5600_ERROR_I2C_WRITE_1 = -201;
// CONFIGURE CONSTANTS
// check datasheet for details
// setOutputMode
const uint8_t AS5600_OUTMODE_ANALOG_100 = 0;
const uint8_t AS5600_OUTMODE_ANALOG_90 = 1;
const uint8_t AS5600_OUTMODE_PWM = 2;
// setPowerMode
const uint8_t AS5600_POWERMODE_NOMINAL = 0;
const uint8_t AS5600_POWERMODE_LOW1 = 1;
const uint8_t AS5600_POWERMODE_LOW2 = 2;
const uint8_t AS5600_POWERMODE_LOW3 = 3;
// setPWMFrequency
const uint8_t AS5600_PWM_115 = 0;
const uint8_t AS5600_PWM_230 = 1;
const uint8_t AS5600_PWM_460 = 2;
const uint8_t AS5600_PWM_920 = 3;
// setHysteresis
const uint8_t AS5600_HYST_OFF = 0;
const uint8_t AS5600_HYST_LSB1 = 1;
const uint8_t AS5600_HYST_LSB2 = 2;
const uint8_t AS5600_HYST_LSB3 = 3;
// setSlowFilter
const uint8_t AS5600_SLOW_FILT_16X = 0;
const uint8_t AS5600_SLOW_FILT_8X = 1;
const uint8_t AS5600_SLOW_FILT_4X = 2;
const uint8_t AS5600_SLOW_FILT_2X = 3;
// setFastFilter
const uint8_t AS5600_FAST_FILT_NONE = 0;
const uint8_t AS5600_FAST_FILT_LSB6 = 1;
const uint8_t AS5600_FAST_FILT_LSB7 = 2;
const uint8_t AS5600_FAST_FILT_LSB9 = 3;
const uint8_t AS5600_FAST_FILT_LSB18 = 4;
const uint8_t AS5600_FAST_FILT_LSB21 = 5;
const uint8_t AS5600_FAST_FILT_LSB24 = 6;
const uint8_t AS5600_FAST_FILT_LSB10 = 7;
// setWatchDog
const uint8_t AS5600_WATCHDOG_OFF = 0;
const uint8_t AS5600_WATCHDOG_ON = 1;
class AS5600
{
public:
AS5600(TwoWire *wire = &Wire);
bool begin(uint8_t directionPin = AS5600_SW_DIRECTION_PIN);
bool isConnected();
// address = fixed 0x36 for AS5600,
// = default 0x40 for AS5600L
uint8_t getAddress();
// 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();
// 0 .. 4095
// returns false if parameter out of range
bool setZPosition(uint16_t value);
uint16_t getZPosition();
// 0 .. 4095
// returns false if parameter out of range
bool setMPosition(uint16_t value);
uint16_t getMPosition();
// 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
// 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
// returns false if parameter out of range
bool setPowerMode(uint8_t powerMode);
uint8_t getPowerMode();
// 0 = off 1 = lsb1 2 = lsb2 3 = lsb3
// 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
// returns false if parameter out of range
bool setOutputMode(uint8_t outputMode);
uint8_t getOutputMode();
// 0 = 115 1 = 230 2 = 460 3 = 920 (Hz)
// returns false if parameter out of range
bool setPWMFrequency(uint8_t pwmFreq);
uint8_t getPWMFrequency();
// 0 = 16x 1 = 8x 2 = 4x 3 = 2x
// 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
// returns false if parameter out of range
bool setFastFilter(uint8_t mask);
uint8_t getFastFilter();
// 0 = OFF
// 1 = ON (auto low power mode)
// returns false if parameter out of range
bool setWatchDog(uint8_t mask);
uint8_t getWatchDog();
// READ OUTPUT REGISTERS
uint16_t rawAngle();
uint16_t readAngle();
// software based offset.
// degrees = -359.99 .. 359.99 (preferred)
// returns false if abs(parameter) > 36000
// => expect loss of precision
bool setOffset(float degrees); // sets an absolute offset
float getOffset();
bool increaseOffset(float degrees); // adds to existing offset.
// READ STATUS REGISTERS
uint8_t readStatus();
uint8_t readAGC();
uint16_t readMagnitude();
// access detail status register
bool detectMagnet();
bool magnetTooStrong();
bool magnetTooWeak();
// BURN COMMANDS
// DO NOT UNCOMMENT - USE AT OWN RISK - READ DATASHEET
// void burnAngle();
// void burnSetting();
// EXPERIMENTAL 0.1.2 - to be tested.
// approximation of the angular speed in rotations per second.
// mode == 1: radians /second
// mode == 0: degrees /second (default)
float getAngularSpeed(uint8_t mode = AS5600_MODE_DEGREES);
// EXPERIMENTAL CUMULATIVE POSITION
// reads sensor and updates cumulative position
int32_t getCumulativePosition();
// converts last position to whole revolutions.
int32_t getRevolutions();
// resets position only (not the i)
// returns last position but not internal lastPosition.
int32_t resetPosition(int32_t position = 0);
// resets position and internal lastPosition
// returns last position.
int32_t resetCumulativePosition(int32_t position = 0);
// EXPERIMENTAL 0.5.2
int lastError();
protected:
uint8_t readReg(uint8_t reg);
uint16_t readReg2(uint8_t reg);
uint8_t writeReg(uint8_t reg, uint8_t value);
uint8_t writeReg2(uint8_t reg, uint16_t value);
uint8_t _address = AS5600_DEFAULT_ADDRESS;
uint8_t _directionPin = 255;
uint8_t _direction = AS5600_CLOCK_WISE;
int _error = AS5600_OK;
TwoWire* _wire;
// for getAngularSpeed()
uint32_t _lastMeasurement = 0;
int16_t _lastAngle = 0;
// for readAngle() and rawAngle()
uint16_t _offset = 0;
// EXPERIMENTAL
// cumulative position counter
// works only if the sensor is read often enough.
int32_t _position = 0;
int16_t _lastPosition = 0;
};
/////////////////////////////////////////////////////////////////////////////
//
// AS5600L
//
class AS5600L : public AS5600
{
public:
AS5600L(uint8_t address = AS5600L_DEFAULT_ADDRESS, TwoWire *wire = &Wire);
bool setAddress(uint8_t address);
// UPDT = UPDATE
// are these two needed?
bool setI2CUPDT(uint8_t value);
uint8_t getI2CUPDT();
};
// -- END OF FILE --
+144
View File
@@ -0,0 +1,144 @@
# Change Log AS5600
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.6.1] - 2024-03-31
- improve **getCumulativePosition()**, catch I2C error, see #62
- update readme.md (incl reorder future work).
- update GitHub actions
- minor edits
## [0.6.0] - 2024-01-25
- add experimental error handling
- add **int lastError()** so user can check the status of last I2C actions.
- update readme.md
- update examples
- minor edits
----
## [0.5.1] - 2023-12-31
- fix #51, add **increaseOffset(float degrees)**
- update keywords.txt
- update readme.md (several cleanups)
## [0.5.0] - 2023-12-07
- refactor API, begin()
- update readme.md
- update examples
- add examples
- patch library.properties => category=Sensors
----
## [0.4.1] - 2023-09-16
- fix #45 support STM32 set I2C pins ARDUINO_ARCH_STM32
- update readme badges
- minor edits
## [0.4.0] - 2023-06-27
- fix #39 support for Wire2 on ESP32
- update readme.md
----
## [0.3.8] - 2023-06-18
- add **void burnSetting()** improvements from #38
- use with care
- add sketches to burn settings (use with care!)
- minor edits.
## [0.3.7] - 2023-05-09
- change **getCumulativePosition()** to use **AS5600_ANGLE**
so filters can be applied.
- add **AS5600_DEGREES_TO_RAW** to constants.
- add **AS5600_SW_DIRECTION_PIN** to constants.
- minor edits.
## [0.3.6] - 2023-02-20
- add **resetCumulativePosition(int32_t position)** to completely reset the cumulative counter.
This includes the delta since last call to **getCumulativePosition()**.
- add parameter position to **resetPosition(int32_t position)** so a new position can be set.
This does not reset the delta since last call to **getCumulativePosition()**.
- update readme.md
## [0.3.5] - 2023-02-01
- update GitHub actions
- update license 2023
- update readme.md
## [0.3.4] - 2022-12-22
- fix #26 edges problem of the experimental cumulative position (CP).
- decoupled CP from **rawAngle()**
- now one needs to call **getCumulativePosition()** to update the CP.
- updated the readme.md section about CP.
## [0.3.3] - 2022-12-19
- add experimental continuous position.
- add **getCumulativePosition()**
- add **resetPosition()**
- add **getRevolutions()**
- move code from .h to .cpp
- add AS5600_MODE_RPM to **getAngularSpeed()**
- add AS5600_RAW_TO_RPM
- add example for AS5600_MODE_RPM.
- add AS5600_DEFAULT_ADDRESS
- add AS5600L_DEFAULT_ADDRESS
- update readme.md
## [0.3.2] - 2022-10-16
- add CHANGELOG.md
- update readme.md
- update build-CI to support RP2040
## [0.3.1] - 2022-08-11
- add support for AS5600L (I2C address)
- add magnetTooStrong() + magnetTooWeak();
- add / update examples
- update documentation
## [0.3.0] - 2022-07-07
- fix #18 invalid mask setConfigure().
----
## [0.2.1] - not released
- add bool return to set() functions.
- update Readme (analog / PWM out)
## [0.2.0] - 2022-06-28
- add software based direction control.
- add examples
- define constants for configuration functions.
- fix conversion constants (4096 based)
- add get- setOffset(degrees) functions. (no radians yet)
----
## [0.1.4] - 2022-06-27
- fix #7 use readReg2() to improve I2C performance.
- define constants for configuration functions.
- add examples - especially OUT pin related.
- fix default parameter of the begin function.
## [0.1.3] - 2022-06-26
- add AS5600_RAW_TO_RADIANS.
- add getAngularSpeed() mode parameter.
- fix #8 bug in configure.
## [0.1.2] - 2022-06-02
- add getAngularSpeed().
## [0.1.1] - 2022-05-31
- add readReg2() to speed up reading 2 byte values.
- fix clock wise and counter clock wise.
- fix shift-direction @ getZPosition, getMPosition,
getMaxAngle and getConfigure.
## [0.1.0] - 2022-05-28
- initial version
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022-2024 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+786
View File
@@ -0,0 +1,786 @@
[![Arduino CI](https://github.com/RobTillaart/AS5600/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![Arduino-lint](https://github.com/RobTillaart/AS5600/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/AS5600/actions/workflows/arduino-lint.yml)
[![JSON check](https://github.com/RobTillaart/AS5600/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/AS5600/actions/workflows/jsoncheck.yml)
[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/AS5600.svg)](https://github.com/RobTillaart/AS5600/issues)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/AS5600/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/AS5600.svg?maxAge=3600)](https://github.com/RobTillaart/AS5600/releases)
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/AS5600.svg)](https://registry.platformio.org/libraries/robtillaart/AS5600)
# AS5600
Arduino library for AS5600 and AS5600L magnetic rotation meter.
## Description
#### AS5600
**AS5600** is a library for an AS5600 / AS5600L based magnetic **rotation** meter.
More exact, it measures the angle (rotation w.r.t. reference) and not RPM.
Multiple angle measurements allows one to calculate or estimate the RPM.
The AS5600 and AS5600L sensors are pin compatible (always check your model's datasheet).
**Warning: experimental**
The sensor can measure a full rotation in 4096 steps.
The precision of the position is therefore limited to approx 0.1°.
Noise levels are unknown, but one might expect that the sensor is affected 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.
Please share your experiences.
#### 0.5.0 Breaking change
Version 0.5.0 introduced a breaking change.
You cannot set the SDA and SCL pins in **begin()** any more.
This reduces the dependency of processor dependent Wire implementations.
The user has to call **Wire.begin()** and can optionally set the I2C pins
before calling **AS5600.begin()**.
#### Related libraries
- https://github.com/RobTillaart/Angle
- https://github.com/RobTillaart/AngleConvertor
- https://github.com/RobTillaart/AverageAngle
- https://github.com/RobTillaart/runningAngle
## Hardware connection
The I2C address of the **AS5600** is always 0x36.
The sensor should connect the I2C lines SDA and SCL and the
VCC and GND to communicate with the processor.
Do not forget to add the pull up resistors to improve the I2C signals.
The AS5600 datasheet states it supports Fast-Mode == 400 KHz
and Fast-Mode-Plus == 1000 KHz.
#### Pull ups
I2C performance tests with an AS5600L with an UNO failed at 400 KHz.
After investigation it became clear that pull ups are mandatory.
The UNO expects 5 Volt I2C signals from the AS5600.
However the device only provides 3V3 pulses on the bus.
So the signal was not stable fast enough (not "square enough").
After applying pull ups the AS5600L worked up to 1000 KHz.
#### DIR pin
From datasheet, page 30 - Direction (clockwise vs. counter-clockwise)
_**The AS5600 allows controlling the direction of the magnet
rotation with the DIR pin. If DIR is connected to GND (DIR = 0)
a clockwise rotation viewed from the top will generate an
increment of the calculated angle. If the DIR pin is connected
to VDD (DIR = 1) an increment of the calculated angle will
happen with counter-clockwise rotation.**_
This AS5600 library offers a 3rd option for the DIR (direction) pin of the sensor:
1. Connect to **GND** ==> fixed clockwise(\*). This is the default.
1. Connect to **VCC** ==> fixed counter-clockwise.
1. Connect to an IO pin of the processor == Hardware Direction Control by library.
In the 3rd configuration the library controls the direction of counting by initializing
this pin in **begin(directionPin)**, followed by **setDirection(direction)**.
For the parameter 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 Software Direction Control below for more information.
#### OUT pin
The sensor has an output pin named **OUT**.
This pin can be used for an analogue or PWM output signal (AS5600),
and only for PWM by the AS5600L.
See **Analogue Pin** and **PWM Pin** below.
Examples are added to show how to use this pin with **setOutputMode()**.
See more in the sections Analog OUT and PWM OUT below.
##### Note: (From Zipdox2 - See issue #36)
Some AS5600 modules seem to have a resistor between **PGO** and **GND**.
This causes the AS5600 to disable the output (to use it for programming, see datasheet).
This resistor needs to be removed to use the **OUT** pin.
#### PGO pin
Not tested. ==> Read the datasheet!
PGO stands for ProGramming Option, it is used to calibrate and 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 **Make configuration persistent** below.
## I2C
The I2C address of the **AS5600** is always 0x36.
#### Address
| sensor | address | changeable |
|:--------:|:---------:|:-------------|
| AS5600 | 0x36 | NO |
| AS5600L | 0x40 | YES, check setAddress() |
To use more than one **AS5600** on one I2C bus, see Multiplexing below.
The **AS5600L** supports the change of I2C address, optionally permanent.
Check the **setAddress()** function for non-permanent change.
#### I2C multiplexing
Sometimes you need to control more devices than possible with the default
address range the device provides.
This is possible with an I2C multiplexer e.g. TCA9548 which creates up
to eight channels (think of it as I2C subnets) which can use the complete
address range of the device.
Drawback of using a multiplexer is that it takes more administration in
your code e.g. which device is on which channel.
This will slow down the access, which must be taken into account when
deciding which devices are on which channel.
Also note that switching between channels will slow down other devices
too if they are behind the multiplexer.
- https://github.com/RobTillaart/TCA9548
Alternative could be the use of a AND port for the I2C clock line to prevent
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.
#### Performance
| board | sensor | results | notes |
|:-------------:|:---------:|:-----------------|:--------|
| Arduino UNO | AS5600 | up to 900 KHz. | https://github.com/RobTillaart/AS5600/issues/22
| Arduino UNO | AS5600L | up to 300 KHz. |
| ESP32 | AS5600 | no data |
| ESP32 | AS5600L | up to 800 KHz |
No other boards tested yet.
#### ESP32 and I2C
When polling the AS5600 with an ESP32 to measure RPM an issue has been reported.
See https://github.com/RobTillaart/AS5600/issues/28
The problem is that the ESP32 can be blocking for up to one second if there is a
problem in the connection with the sensor.
Using **setWireTimeout()** does not seem to solve the problem (2023-01-31).
In the issue the goal was to measure the turns of a rotating device at around 3800 RPM.
3800 RPM == 64 rounds / second.
To measure speed one need at least 3 angle measurements per rotation.
This results in at least 192 measurements per second which is about 1 per 5 milliseconds.
Given that the ESP32 can block for a second, it can not be guaranteed to be up to date.
Not for speed, but also not for total number of rotations.
## Interface
```cpp
#include "AS5600.h"
```
#### Constants
Most important are:
```cpp
// setDirection
const uint8_t AS5600_CLOCK_WISE = 0; // LOW
const uint8_t AS5600_COUNTERCLOCK_WISE = 1; // HIGH
// 0.087890625;
const float AS5600_RAW_TO_DEGREES = 360.0 / 4096;
// 0.00153398078788564122971808758949;
const float AS5600_RAW_TO_RADIANS = PI * 2.0 / 4096;
// 4.06901041666666e-6
const float AS5600_RAW_TO_RPM = 1.0 / 4096 / 60;
// getAngularSpeed
const uint8_t AS5600_MODE_DEGREES = 0;
const uint8_t AS5600_MODE_RADIANS = 1;
const uint8_t AS5600_MODE_RPM = 2;
```
See AS5600.h file (and datasheet) for all constants.
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 = AS5600_SW_DIRECTION_PIN)**
set the value for the directionPin.
If the pin is set to AS5600_SW_DIRECTION_PIN, the default value,
there will be software direction control instead of hardware control.
See below.
- **bool isConnected()** checks if the address 0x36 (AS5600) is on the I2C bus.
- **uint8_t getAddress()** returns the fixed device address 0x36 (AS5600).
#### Direction
To define in which way the sensor counts up.
- **void setDirection(uint8_t direction = AS5600_CLOCK_WISE)** idem.
- **uint8_t getDirection()** returns AS5600_CLOCK_WISE (0) or
AS5600_COUNTERCLOCK_WISE (1).
See Software Direction Control below for more information.
#### Configuration registers
Please read the datasheet for details.
- **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.
- **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.
- **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.
#### Configuration bits
Please read datasheet for details.
- **bool setConfigure(uint16_t value)** value == 0..0x3FFF
Access the register as bit mask.
Returns false if parameter is out of range.
- **uint16_t getConfigure()** returns the current configuration register a bit mask.
| Bit | short | Description | Values |
|:-------:|:--------|:----------------|:-------------------------------------------------------|
| 0-1 | PM | Power mode | 00 = NOM, 01 = LPM1, 10 = LPM2, 11 = LPM3 |
| 2-3 | HYST | Hysteresis | 00 = OFF, 01 = 1 LSB, 10 = 2 LSB, 11 = 3 LSB |
| 4-5 | OUTS | Output Stage | 00 = analog (0-100%), 01 = analog (10-90%), 10 = PWM |
| 6-7 | PWMF | PWM frequency | 00 = 115, 01 = 230, 10 = 460, 11 = 920 (Hz) |
| 8-9 | SF | Slow Filter | 00 = 16x, 01 = 8x, 10 = 4x, 11 = 2x |
| 10-12 | FTH | Fast Filter | Threshold 000 - 111 check datasheet |
| 13 | WD | Watch Dog | 0 = OFF, 1 = ON |
| 15-14 | | not used |
The library has functions to address these fields directly.
The setters() returns false if parameter is out of range.
- **bool setPowerMode(uint8_t powerMode)**
- **uint8_t getPowerMode()**
- **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.
- **uint8_t getHysteresis()**
- **bool setOutputMode(uint8_t outputMode)**
- **uint8_t getOutputMode()**
- **bool setPWMFrequency(uint8_t pwmFreq)**
- **uint8_t getPWMFrequency()**
- **bool setSlowFilter(uint8_t mask)**
- **uint8_t getSlowFilter()**
- **bool setFastFilter(uint8_t mask)**
- **uint8_t getFastFilter()**
- **bool setWatchDog(uint8_t mask)**
- **uint8_t getWatchDog()**
#### Read Angle
- **uint16_t rawAngle()** returns 0 .. 4095. (12 bits)
Conversion factor AS5600_RAW_TO_DEGREES = 360 / 4096 = 0.087890625
or use AS5600_RAW_TO_RADIANS if needed.
- **uint16_t readAngle()** returns 0 .. 4095. (12 bits)
Conversion factor AS5600_RAW_TO_DEGREES = 360 / 4096 = 0.087890625
or use AS5600_RAW_TO_RADIANS if needed.
The value of this register can be affected by the configuration bits above.
This is the one most used.
- **bool setOffset(float degrees)** overwrites the **existing** offset.
It sets an offset in degrees, e.g. to calibrate the sensor after mounting.
Typical values are -359.99 - 359.99 probably smaller.
Larger values will be mapped back to this interval.
Be aware that larger values will affect / decrease the precision of the
measurements as floats have only 7 significant digits.
Verify this for your application.
Returns false if **degrees** > 360000.
- **float getOffset()** returns offset in degrees.
- **bool increaseOffset(float degrees)** adds degrees to the **existing** offset.
If **setOffset(20)** is called first and **increaseOffset(-30)** thereafter the
new offset is -10 degrees.
Returns false if **degrees** > 360000.
In issue #14 there is a discussion about **setOffset()**.
A possible implementation is to ignore all values outside the
-359.99 - 359.99 range.
This would help to keep the precision high. User responsibility.
In #51 increaseOffset is discussed.
```cpp
// offset == 0;
as.setOffset(20);
// offset == 20;
as.setOffset(-30);
// offset = -30;
// versus
// offset == 0;
as.setOffset(20);
// offset == 20;
as.increaseOffset(-30);
// offset = -10;
```
#### Angular Speed
- **float getAngularSpeed(uint8_t mode = AS5600_MODE_DEGREES)** is an experimental function that returns
an approximation of the angular speed in rotations per second.
The function needs to be called at least **four** times per rotation
or once per second to get a reasonably precision.
| mode | value | description | notes |
|:----------------------|:-------:|:---------------|:--------|
| AS5600_MODE_RADIANS | 1 | radians /sec | |
| AS5600_MODE_DEGREES | 0 | degrees /sec | default |
| other | - | degrees /sec | |
Negative return values indicate reverse rotation.
What that exactly means depends on the setup of your project.
Note: the first call will return an erroneous value as it has no
reference angle or time.
Also if one stops calling this function
for some time the first call after such delays will be incorrect.
Note: the frequency of calling this function of the sensor depends on the application.
The faster the magnet rotates, the faster it may be called.
Also if one wants to detect minute movements, calling it more often is the way to go.
An alternative implementation is possible in which the angle is measured twice
with a short interval. The only limitation then is that both measurements
should be within 180° = half a rotation.
#### Cumulative position (experimental)
Since 0.3.3 an experimental cumulative position can be requested from the library.
The sensor does not provide interrupts to indicate a movement or revolution
Therefore one has to poll the sensor at a frequency at least **three** times
per revolution with **getCumulativePosition()**
The cumulative position (32 bits) consists of 3 parts
| bit | meaning | notes |
|:-------:|:--------------|:--------|
| 31 | sign | typical + == CW, - == CCW
| 30-12 | revolutions |
| 11-00 | raw angle | call getCumulativePosition()
Functions are:
- **int32_t getCumulativePosition()** reads sensor and updates cumulative position.
- **int32_t getRevolutions()** converts last position to whole revolutions.
Convenience function.
- **int32_t resetPosition(int32_t position = 0)** resets the "revolutions" to position (default 0).
It does not reset the delta (rotation) since last call to **getCumulativePosition()**.
Returns last position (before reset).
- **int32_t resetCumulativePosition(int32_t position = 0)** completely resets the cumulative counter.
This includes the delta (rotation) since last call to **getCumulativePosition()**.
Returns last position (before reset).
As this code is experimental, names might change in the future (0.4.0)?
As the function are mostly about counting revolutions the current thoughts for new names are:
```cpp
int32_t updateRevolutions() replaces getCumulativePosition()
int32_t getRevolutions()
int32_t resetRevolutions() replaces resetPosition()
```
#### Status registers
- **uint8_t readStatus()** see Status bits below.
- **uint8_t readAGC()** returns the Automatic Gain Control.
0..255 in 5V mode, 0..128 in 3V3 mode.
- **uint16_t readMagnitude()** reads the current internal magnitude.
(page 9 datasheet)
Scale is unclear, can be used as relative scale.
- **bool detectMagnet()** returns true if device sees a magnet.
- **bool magnetTooStrong()** idem.
- **bool magnetTooWeak()** idem.
#### Status bits
Please read datasheet for details.
| Bit | short | Description | Values |
|:-----:|:------|:-------------:|:----------------------|
| 0-2 | | not used | |
| 3 | MH | overflow | 1 = magnet too strong |
| 4 | ML | underflow | 1 = magnet too weak |
| 5 | MD | magnet detect | 1 = magnet detected |
| 6-7 | | not used | |
#### Error handling
Since 0.5.2 the library has added **experimental** error handling.
For now only lowest level I2C errors are checked for transmission errors.
Error handling might be improved upon in the future.
Note: The public functions do not act on error conditions.
This might change in the future.
So the user should check for error conditions.
```cpp
int e = lastError();
if (e != AS5600_OK)
{
// handle error
}
```
- **int lastError()** returns the last error code.
After reading the error status is cleared to **AS5600_OK**.
| Error codes | value | notes |
|:--------------------------|:-------:|:----------|
| AS5600_OK | 0 | default |
| AS5600_ERROR_I2C_READ_0 | -100 |
| AS5600_ERROR_I2C_READ_1 | -101 |
| AS5600_ERROR_I2C_READ_2 | -102 |
| AS5600_ERROR_I2C_READ_3 | -103 |
| AS5600_ERROR_I2C_WRITE_0 | -200 |
| AS5600_ERROR_I2C_WRITE_1 | -201 |
## Make configuration persistent. BURN
#### Read burn count
- **uint8_t getZMCO()** reads back how many times the ZPOS and MPOS
registers are written to permanent memory.
You can only burn a new Angle 3 times to the AS5600, and only 2 times for the AS5600L. This function is safe as it is readonly.
#### BURN function
The burn functions are used to make settings persistent.
These burn functions are permanent, therefore they are commented in the library.
Please read datasheet twice, before uncomment them.
**USE AT OWN RISK**
Please read datasheet **twice** as these changes are not reversible.
The risk is that you make your AS5600 / AS5600L **USELESS**.
**USE AT OWN RISK**
These are the two "unsafe" functions:
- **void burnAngle()** writes the ZPOS and MPOS registers to permanent memory.
You can only burn a new Angle maximum **THREE** times to the AS5600
and **TWO** times for the AS5600L.
- **void burnSetting()** writes the MANG register to permanent memory.
You can write this only **ONE** time to the AS5600.
Some discussion about burning see issue #38
(I have no hands on experience with this functions)
**USE AT OWN RISK**
## Software Direction Control
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.
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 clockwise mode. Then 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 a processor IO pin.
The user still calls **setDirection()** as before to change the direction
of the increments and decrements.
The advantage is one does not need that extra IO pin from the processor.
This makes connecting the sensor a bit easier.
TODO: measure performance impact.
TODO: investigate impact on functionality of other registers.
## Analog OUT
(details datasheet - page 25 = AS5600)
The OUT pin can be configured with the function:
- **bool setOutputMode(uint8_t outputMode)**
#### AS5600
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.
#### AS5600L
The AS5600L does **NOT** support analog OUT.
Both mode 0 and 1 will set the OUT pin to VDD (+5V0 or 3V3).
## PWM OUT
(details datasheet - page 27 = AS5600)
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.
----
## AS5600L class
- **AS5600L(uint8_t address = 0x40, TwoWire \*wire = &Wire)** constructor.
As the I2C address can be changed in the AS5600L, the address is a parameter of the constructor.
This is a difference with the AS5600 constructor.
#### Setting I2C address
- **bool setAddress(uint8_t address)** Returns false if the I2C address is not in valid range (8-119).
#### Setting I2C UPDT
UPDT = update page 30 - AS5600L
- **bool setI2CUPDT(uint8_t value)**
- **uint8_t getI2CUPDT()**
These functions seems to have only a function in relation to **setAddress()** so possibly obsolete in the future.
If you got other insights on these functions please let me know.
----
## Operational
The base functions are:
```cpp
AS5600 as5600;
void setup()
{
Serial.begin(115200);
...
as5900.begin(4); // set the direction pin
as5600.setDirection(AS5600_CLOCK_WISE);
...
}
void loop()
{
...
Serial.println(as5600.readAngle());
delay(1000);
...
}
```
See examples.
## Future
Some ideas are kept here so they won't get lost.
priority is relative.
#### Must
- re-organize readme
- rename revolution functions
- to what?
#### Should
- implement extended error handling in public functions.
- will increase footprint !! how much?
- **call writeReg() only if readReg() is OK** ==> prevent incorrect writes
- ```if (_error != 0) return false;```
- idem readReg2()
- set AS5600_ERROR_PARAMETER e.g. setZPosition()
- a derived class with extended error handling?
- investigate **readMagnitude()**
- combination of AGC and MD, ML and MH flags?
- investigate **OUT** behaviour in practice
- analogue
- PWM
- need AS5600 on breakout with support
- check / verify Power-up time
- 1 minute (need HW)
- check Timing Characteristics (datasheet)
- is there improvement possible.
#### Could
- investigate **PGO** programming pin.
- check for compatible devices
- AS5200 ?
- investigate performance
- basic performance per function
- I2C improvements
- software direction
- write examples:
- as5600_calibration.ino (needs HW and lots of time)
- different configuration options
#### Wont (unless)
- fix for AS5600L as it does not support analog OUT.
- type field?
- other class hierarchy?
- base class with commonalities?
- ==> just ignore for now.
- add mode parameter to offset functions.
- see getAngularSpeed()
## Support
If you appreciate my libraries, you can support the development and maintenance.
Improve the quality of the libraries by providing issues and Pull Requests, or
donate through PayPal or GitHub sponsors.
Thank you,
@@ -0,0 +1,56 @@
//
// FILE: AS5600L_set_address.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AS5600
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600L ASL; // use default Wire
// AS5600 ASL; // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
ASL.begin(4); // set direction pin.
ASL.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
int b = ASL.isConnected();
Serial.print("Connect: ");
Serial.println(b);
Serial.print("ADDR: ");
Serial.println(ASL.getAddress());
ASL.setAddress(0x38);
Serial.print("ADDR: ");
Serial.println(ASL.getAddress());
}
void loop()
{
// Serial.print(millis());
// Serial.print("\t");
Serial.print(ASL.readAngle());
Serial.print("\t");
Serial.println(ASL.rawAngle() * AS5600_RAW_TO_DEGREES);
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,65 @@
//
// FILE: AS5600_I2C_frequency.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AS5600
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600L as5600; // use default Wire
uint32_t clk = 0;
uint32_t start, stop;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
as5600.begin(4); // set direction pin.
// as5600.setAddress(0x40); // AS5600L only
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
int b = as5600.isConnected();
Serial.print("Connect: ");
Serial.println(b);
}
void loop()
{
clk += 100000;
if (clk > 800000) clk = 100000;
Wire.setClock(clk);
delay(10);
start = micros();
int angle = as5600.readAngle();
stop = micros();
Serial.print(clk);
Serial.print("\t");
Serial.print(stop - start);
Serial.print("\t");
Serial.print(angle);
Serial.print("\t");
Serial.println(as5600.rawAngle() * AS5600_RAW_TO_DEGREES);
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,52 @@
//
// FILE: AS5600_angular_speed.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AS5600
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600L as5600; // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
Serial.println(as5600.getAddress());
// as5600.setAddress(0x40); // AS5600L only
int b = as5600.isConnected();
Serial.print("Connect: ");
Serial.println(b);
delay(1000);
}
void loop()
{
// Serial.print("\ta = ");
// Serial.print(as5600.readAngle());
// Serial.print("\tω = ");
Serial.println(as5600.getAngularSpeed());
delay(25);
}
// -- END OF FILE --
@@ -0,0 +1,52 @@
//
// FILE: AS5600_angular_speed_RPM.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AS5600
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600 as5600; // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
Serial.println(as5600.getAddress());
// as5600.setAddress(0x40); // AS5600L only
int b = as5600.isConnected();
Serial.print("Connect: ");
Serial.println(b);
delay(1000);
}
void loop()
{
// Serial.print("\ta = ");
// Serial.print(as5600.readAngle());
Serial.print("\tω = ");
Serial.println(as5600.getAngularSpeed(AS5600_MODE_RPM));
delay(100);
}
// -- END OF FILE --
@@ -0,0 +1,176 @@
//
// FILE: AS5600_burn_conf_mang.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo (not tested yet - see issue #38)
// URL: https://github.com/RobTillaart/AS5600
//
// WARNING
// As burning the settings can only be done once this sketch has to be used with care.
//
// You need to
// - read the datasheet so you understand what you do
// - read issue #38 to understand the discussion that lead to this sketch
// - uncomment burnSettings() in AS5600.h and AS5600.cpp.
// - adjust settings and MaxAngle in burn_mang() function below ==> line 77++
// - uncomment line 167 of this sketch
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600 as5600; // use default Wire
// AS5600L as5600;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
if (as5600.isConnected())
{
Serial.println("Connected");
}
else
{
Serial.println("Failed to connect. Check wires and reboot.");
while (1);
}
Serial.println("\nWARNING WARNING WARNING WARNING WARNING WARNING\n");
Serial.println("This sketch will burn settings to your AS5600.");
Serial.println("Adjust the settings in the sketch to your needs.");
Serial.println("Press any key to continue.");
Serial.println("\nWARNING WARNING WARNING WARNING WARNING WARNING\n\n");
while (Serial.available()) Serial.read();
while (!Serial.available());
Serial.read();
while (Serial.available()) Serial.read();
Serial.print("Are you sure to burn settings + maxangle? [Y for Yes]");
while (!Serial.available());
char c = Serial.read();
if (c == 'Y')
{
burn_mang();
}
Serial.println("\nDone..");
}
void loop()
{
}
void burn_mang()
{
// ADJUST settings
const uint16_t POWERMODE = 0;
const uint16_t HYSTERESIS = 0;
const uint16_t OUTPUTMODE = 0;
const uint16_t PWMFREQUENCY = 0;
const uint16_t SLOWFILTER = 0;
const uint16_t FASTFILTER = 0;
const uint16_t WATCHDOG = 0;
const uint16_t MAXANGLE = 0;
bool OK = true;
OK = OK && as5600.setPowerMode(POWERMODE);
OK = OK && (POWERMODE == as5600.getPowerMode());
if (OK == false)
{
Serial.println("ERROR: POWERMODE.");
return;
}
OK = OK && as5600.setHysteresis(HYSTERESIS);
OK = OK && (HYSTERESIS == as5600.getHysteresis());
if (OK == false)
{
Serial.println("ERROR: HYSTERESIS");
return;
}
OK = OK && as5600.setOutputMode(OUTPUTMODE);
OK = OK && (OUTPUTMODE == as5600.getOutputMode());
if (OK == false)
{
Serial.println("ERROR: OUTPUTMODE");
return;
}
OK = OK && as5600.setPWMFrequency(PWMFREQUENCY);
OK = OK && (PWMFREQUENCY == as5600.getPWMFrequency());
if (OK == false)
{
Serial.println("ERROR: PWMFREQUENCY");
return;
}
OK = OK && as5600.setSlowFilter(SLOWFILTER);
OK = OK && (SLOWFILTER == as5600.getSlowFilter());
if (OK == false)
{
Serial.println("ERROR: SLOWFILTER");
return;
}
OK = OK && as5600.setFastFilter(FASTFILTER);
OK = OK && (FASTFILTER == as5600.getFastFilter());
if (OK == false)
{
Serial.println("ERROR: FASTFILTER");
return;
}
OK = OK && as5600.setWatchDog(WATCHDOG);
OK = OK && (WATCHDOG == as5600.getWatchDog());
if (OK == false)
{
Serial.println("ERROR: WATCHDOG");
return;
}
OK = OK && as5600.setMaxAngle(MAXANGLE);
OK = OK && (MAXANGLE == as5600.getMaxAngle());
if (OK == false)
{
Serial.println("ERROR: MAXANGLE");
return;
}
Serial.println();
Serial.println("burning in 5 seconds");
delay(1000);
Serial.println("burning in 4 seconds");
delay(1000);
Serial.println("burning in 3 seconds");
delay(1000);
Serial.println("burning in 2 seconds");
delay(1000);
Serial.println("burning in 1 seconds");
delay(1000);
Serial.print("burning ...");
// uncomment next line
// as5600.burnSettings();
Serial.println(" done.");
Serial.println("Reboot AS5600 to use the new settings.");
}
// -- END OF FILE --
@@ -0,0 +1,115 @@
//
// FILE: AS5600_burn_zpos.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo (not tested yet - see issue #38)
// URL: https://github.com/RobTillaart/AS5600
//
// WARNING
// As burning the angle can only be done three times this sketch has to be used with care.
//
// You need to
// - read the datasheet so you understand what you do
// - read issue #38 to understand the discussion that lead to this sketch
// - uncomment burnAngle() in AS5600.h and AS5600.cpp.
// - adjust settings and MaxAngle in burn_zpos() function below ==> line 77++
// - uncomment line 105 of this sketch
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600 as5600; // use default Wire
// AS5600L as5600;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
if (as5600.isConnected())
{
Serial.println("Connected");
}
else
{
Serial.println("Failed to connect. Check wires and reboot.");
//while (1);
}
Serial.println("\nWARNING WARNING WARNING WARNING WARNING WARNING\n");
Serial.println("This sketch will burn settings to your AS5600.");
Serial.println("Adjust the settings in the sketch to your needs.");
Serial.println("Press any key to continue.");
Serial.println("\nWARNING WARNING WARNING WARNING WARNING WARNING\n\n");
while (Serial.available()) Serial.read();
while (!Serial.available());
Serial.read();
while (Serial.available()) Serial.read();
Serial.print("Are you sure to burn zpos? [Y for Yes]");
while (!Serial.available());
char c = Serial.read();
if (c == 'Y')
{
burn_zpos();
}
Serial.println("\nDone..");
}
void loop()
{
}
void burn_zpos()
{
// ADJUST ZPOS
const uint16_t ZPOS = 0;
bool OK = true;
OK = OK && as5600.setZPosition(ZPOS);
OK = OK && (ZPOS == as5600.getZPosition());
if (OK == false)
{
Serial.println("\nERROR: in settings, burn_zpos() cancelled.");
return;
}
Serial.println();
Serial.println("burning in 5 seconds");
delay(1000);
Serial.println("burning in 4 seconds");
delay(1000);
Serial.println("burning in 3 seconds");
delay(1000);
Serial.println("burning in 2 seconds");
delay(1000);
Serial.println("burning in 1 seconds");
delay(1000);
Serial.print("burning ...");
delay(1000);
// uncomment next line
// as5600.burnAngle();
Serial.println(" done.");
Serial.println("Reboot AS5600 to use the new settings.");
}
// -- END OF FILE --
@@ -0,0 +1,49 @@
//
// FILE: AS5600_demo.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AS5600
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600L as5600; // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
int b = as5600.isConnected();
Serial.print("Connect: ");
Serial.println(b);
delay(1000);
}
void loop()
{
// Serial.print(millis());
// Serial.print("\t");
Serial.print(as5600.readAngle());
Serial.print("\t");
Serial.println(as5600.rawAngle());
// Serial.println(as5600.rawAngle() * AS5600_RAW_TO_DEGREES);
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,28 @@
platforms:
rpipico:
board: rp2040:rp2040:rpipico
package: rp2040:rp2040
gcc:
features:
defines:
- ARDUINO_ARCH_RP2040
warnings:
flags:
packages:
rp2040:rp2040:
url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
# - uno
# - due
# - zero
# - leonardo
# - m4
- esp32
# - esp8266
# - mega2560
# - rpipico
@@ -0,0 +1,49 @@
//
// FILE: AS5600_demo.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AS5600
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600L as5600; // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin(14, 15);
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
int b = as5600.isConnected();
Serial.print("Connect: ");
Serial.println(b);
delay(1000);
}
void loop()
{
// Serial.print(millis());
// Serial.print("\t");
Serial.print(as5600.readAngle());
Serial.print("\t");
Serial.println(as5600.rawAngle());
// Serial.println(as5600.rawAngle() * AS5600_RAW_TO_DEGREES);
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,28 @@
platforms:
rpipico:
board: rp2040:rp2040:rpipico
package: rp2040:rp2040
gcc:
features:
defines:
- ARDUINO_ARCH_RP2040
warnings:
flags:
packages:
rp2040:rp2040:
url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
# - uno
# - due
# - zero
# - leonardo
# - m4
# - esp32
# - esp8266
# - mega2560
- rpipico
@@ -0,0 +1,51 @@
//
// FILE: AS5600_demo.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AS5600
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600L as5600; // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.setSDA(14);
Wire.setSCL(15);
Wire.begin();
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
int b = as5600.isConnected();
Serial.print("Connect: ");
Serial.println(b);
delay(1000);
}
void loop()
{
// Serial.print(millis());
// Serial.print("\t");
Serial.print(as5600.readAngle());
Serial.print("\t");
Serial.println(as5600.rawAngle());
// Serial.println(as5600.rawAngle() * AS5600_RAW_TO_DEGREES);
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,28 @@
platforms:
rpipico:
board: rp2040:rp2040:rpipico
package: rp2040:rp2040
gcc:
features:
defines:
- ARDUINO_ARCH_RP2040
warnings:
flags:
packages:
rp2040:rp2040:
url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
# - uno
# - due
# - zero
# - leonardo
# - m4
# - esp32
# - esp8266
# - mega2560
# - rpipico
@@ -0,0 +1,53 @@
//
// FILE: AS5600_demo.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AS5600
//
// tested compilation with Nucleo-64
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600L as5600; // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.setSDA(14);
Wire.setSCL(15);
Wire.begin();
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
int b = as5600.isConnected();
Serial.print("Connect: ");
Serial.println(b);
delay(1000);
}
void loop()
{
// Serial.print(millis());
// Serial.print("\t");
Serial.print(as5600.readAngle());
Serial.print("\t");
Serial.println(as5600.rawAngle());
// Serial.println(as5600.rawAngle() * AS5600_RAW_TO_DEGREES);
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,53 @@
//
// FILE: AS5600_demo_offset.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AS5600
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600 as5600; // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
}
void loop()
{
float offset = 0;
while (offset < 360)
{
as5600.setOffset(offset);
Serial.print(millis());
Serial.print("\t");
Serial.print(as5600.getOffset(), 2);
Serial.print("\t");
Serial.print(as5600.readAngle());
Serial.print("\t");
Serial.println(as5600.rawAngle() * AS5600_RAW_TO_DEGREES);
delay(100);
offset += 12.34;
}
Serial.println();
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,44 @@
//
// FILE: AS5600_demo_radians.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AS5600
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600 as5600; // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
}
void loop()
{
Serial.print(millis());
Serial.print("\t");
Serial.print(as5600.readAngle());
Serial.print("\t");
Serial.println(as5600.rawAngle() * AS5600_RAW_TO_RADIANS);
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,55 @@
//
// FILE: AS5600_demo_software_direction.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo software direction control
// URL: https://github.com/RobTillaart/AS5600
//
// connect the DIR pin of the AS5600 to GND
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600 as5600; // use default Wire
uint8_t counter = 0;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
as5600.begin(); // set software direction control. default parameter == 255
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
}
void loop()
{
// toggle direction every 10 reads.
counter++;
if (counter < 10) as5600.setDirection(AS5600_CLOCK_WISE);
else as5600.setDirection(AS5600_COUNTERCLOCK_WISE);
if (counter >= 20) counter = 0;
Serial.print(millis());
Serial.print("\t");
Serial.print(as5600.getDirection());
Serial.print("\t");
Serial.print(as5600.readAngle());
Serial.print("\t");
Serial.println(as5600.rawAngle() * AS5600_RAW_TO_DEGREES);
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,54 @@
//
// FILE: AS5600_demo_status.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AS5600
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600L as5600(0x40); // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
}
void loop()
{
Serial.print("STATUS:\t ");
Serial.println(as5600.readStatus(), HEX);
Serial.print("CONFIG:\t ");
Serial.println(as5600.getConfigure(), HEX);
Serial.print(" GAIN:\t ");
Serial.println(as5600.readAGC(), HEX);
Serial.print("MAGNET:\t ");
Serial.println(as5600.readMagnitude(), HEX);
Serial.print("DETECT:\t ");
Serial.println(as5600.detectMagnet(), HEX);
Serial.print("M HIGH:\t ");
Serial.println(as5600.magnetTooStrong(), HEX);
Serial.print("M LOW:\t ");
Serial.println(as5600.magnetTooWeak(), HEX);
Serial.println();
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,28 @@
platforms:
rpipico:
board: rp2040:rp2040:rpipico
package: rp2040:rp2040
gcc:
features:
defines:
- ARDUINO_ARCH_RP2040
warnings:
flags:
packages:
rp2040:rp2040:
url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
# - uno
# - due
# - zero
# - leonardo
# - m4
# - esp32
# - esp8266
# - mega2560
- rpipico
@@ -0,0 +1,60 @@
//
// FILE: AS5600_demo_two_I2C.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo two I2C busses
// URL: https://github.com/RobTillaart/AS5600
//
// Works only if Wire1 bus is present e.g.
// - nano33 ble
// - teensy 4.1
// - RP2040
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600 as5600_0(&Wire);
AS5600 as5600_1(&Wire1);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
Wire1.begin();
as5600_0.begin(4); // set direction pin.
as5600_0.setDirection(AS5600_CLOCK_WISE);
Serial.print("Connect device 0: ");
Serial.println(as5600_0.isConnected() ? "true" : "false");
delay(1000);
as5600_1.begin(5); // set direction pin.
as5600_1.setDirection(AS5600_COUNTERCLOCK_WISE);
Serial.print("Connect device 1: ");
Serial.println(as5600_1.isConnected() ? "true" : "false");
delay(1000);
}
void loop()
{
Serial.print(millis());
Serial.print("\t");
Serial.print(as5600_0.readAngle());
Serial.print("\t");
Serial.print(as5600_1.readAngle());
Serial.print("\n");
delay(100);
}
// -- END OF FILE --
@@ -0,0 +1,45 @@
//
// FILE: AS5600_outmode_analog_100.ino
// AUTHOR: Rob Tillaart
// PURPOSE: experimental demo
// URL: https://github.com/RobTillaart/AS5600
//
// connect the OUT pin to the analog port of the processor
//
// The AS5600L does not support analog OUT.
#include "AS5600.h"
AS5600 as5600; // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
as5600.setOutputMode(AS5600_OUTMODE_ANALOG_100);
}
void loop()
{
Serial.print(millis());
Serial.print("\t");
Serial.print(as5600.readAngle());
Serial.print("\t");
Serial.println(analogRead(A0));
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,44 @@
//
// FILE: AS5600_outmode_analog_90.ino
// AUTHOR: Rob Tillaart
// PURPOSE: experimental demo
// URL: https://github.com/RobTillaart/AS5600
// connect the OUT pin to the analog port of the processor
//
// The AS5600L does not support analog OUT.
#include "AS5600.h"
AS5600 as5600; // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
as5600.setOutputMode(AS5600_OUTMODE_ANALOG_90);
}
void loop()
{
Serial.print(millis());
Serial.print("\t");
Serial.print(as5600.readAngle());
Serial.print("\t");
Serial.println(analogRead(A0));
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,53 @@
//
// FILE: AS5600_outmode_analog_pwm.ino
// AUTHOR: Rob Tillaart
// PURPOSE: experimental
// URL: https://github.com/RobTillaart/AS5600
//
// connect the OUT pin to the analog port of the processor
// use a resistor and a capacitor to create a low pass filter
// so the PWM behave a bit like a analog signal
//
// alternative one can read the PWM with interrupt pin and
// determine the duty cycle
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600 as5600; // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
as5600.setOutputMode(AS5600_OUTMODE_PWM);
as5600.setPWMFrequency(AS5600_PWM_920);
}
void loop()
{
Serial.print(millis());
Serial.print("\t");
Serial.print(as5600.readAngle());
Serial.print("\t");
Serial.println(analogRead(A0));
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,64 @@
//
// FILE: AS5600_outmode_pwm_interrupt.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AS5600
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600 as5600; // use default Wire
const uint8_t irqPin = 2;
volatile uint32_t duration = 0;
void capturePWM()
{
static uint32_t lastTime = 0;
uint32_t now = micros();
if (digitalRead(2) == HIGH)
{
duration = now - lastTime;
}
}
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
attachInterrupt(digitalPinToInterrupt(2), capturePWM, CHANGE);
as5600.begin(4);
as5600.setOutputMode(AS5600_OUTMODE_PWM);
as5600.setPWMFrequency(AS5600_PWM_115);
}
void loop()
{
Serial.print(millis());
Serial.print("\t");
Serial.print(as5600.readAngle());
Serial.print("\t");
Serial.print(as5600.rawAngle() * AS5600_RAW_TO_DEGREES);
Serial.print("\t");
Serial.println(duration);
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,99 @@
// FILE: AS5600_plus_AS5600L.ino
// AUTHOR: laptophead
// PURPOSE: showing working of a AS5600 and AS5600L side by side
// URL: https://github.com/RobTillaart/AS5600
// URL: https://github.com/RobTillaart/AS5600/issues/48
#include "AS5600.h"
AS5600 as5600_R; // uses default Wire
AS5600L as5600_L; // uses default Wire
int Raw_R;
int Raw_Prev;
float Deg_R, Deg_L;
float Deg_Prev_R, Deg_Prev_L;
static uint32_t lastTime = 0;
void setup()
{
Serial.begin(230400);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
// as5600_L.setAddress(0x40); // AS5600L only
as5600_L.begin(15); // set direction pin.
as5600_L.setDirection(AS5600_CLOCK_WISE); //
delay(1000);
as5600_R.begin(14); // set direction pin.
as5600_R.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
delay(1000);
Serial.print ("Address For AS5600 R ");
Serial.println(as5600_R.getAddress());
Serial.print ("Address For AS5600 L ");
Serial.println(as5600_L.getAddress());
Serial.print("Connect_R: ");
Serial.println(as5600_R.isConnected() ? "true" : "false");
Serial.print("Connect device LEFT: ");
Serial.println(as5600_L.isConnected() ? "true" : "false");
delay(1000);
as5600_R.resetPosition();
as5600_L.resetPosition();
}
void loop()
{
Deg_R = convertRawAngleToDegrees(as5600_R.getCumulativePosition());
Deg_L = convertRawAngleToDegrees(as5600_L.getCumulativePosition());
// update every 100 ms
// should be enough up to ~200 RPM
if (millis() - lastTime >= 100 and Deg_R != Deg_Prev_R)
{
lastTime = millis();
Serial.println("REV R: " + String(as5600_R.getRevolutions()));
Serial.println("REV L: " + String(as5600_L.getRevolutions()));
Serial.println("DEG R= " + String(Deg_R, 0) + "°" );
Serial.println("DEG L= " + String(Deg_L, 0) + "°" );
Deg_Prev_R = Deg_R;
}
// just to show how reset can be used
if (as5600_R.getRevolutions() >= 1 )
{
as5600_R.resetPosition();
if ( as5600_R.getRevolutions() <= -1 )
{
as5600_R.resetPosition();
}
}
}
float convertRawAngleToDegrees(uint32_t newAngle)
{
// Raw data reports 0 - 4095 segments, which is 0.087890625 of a degree
float retVal = newAngle * 0.087890625;
retVal = round(retVal);
// retVal=retVal/10;
return retVal;
}
// -- END OF FILE --
@@ -0,0 +1,67 @@
//
// FILE: AS5600_position.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AS5600
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600 as5600; // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin();
as5600.begin(4); // set direction pin.
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
Serial.println(as5600.getAddress());
// as5600.setAddress(0x40); // AS5600L only
int b = as5600.isConnected();
Serial.print("Connect: ");
Serial.println(b);
delay(1000);
}
void loop()
{
static uint32_t lastTime = 0;
// set initial position
as5600.getCumulativePosition();
// update every 100 ms
// should be enough up to ~200 RPM
if (millis() - lastTime >= 100)
{
lastTime = millis();
Serial.print(as5600.getCumulativePosition());
Serial.print("\t");
Serial.println(as5600.getRevolutions());
}
// just to show how reset can be used
if (as5600.getRevolutions() >= 10)
{
as5600.resetPosition();
}
}
// -- END OF FILE --
@@ -0,0 +1,72 @@
//
// FILE: AS5600_pwm_test.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/AS5600
// https://forum.arduino.cc/t/questions-using-the-as5600-in-pwm-mode/1266957/3
//
// monitor the PWM output to determine the angle
// not tested with hardware yet
int PWMpin = 7;
long fullPeriod = 0;
float measureAngle()
{
// wait for LOW
while (digitalRead(PWMpin) == HIGH);
// wait for HIGH
while (digitalRead(PWMpin) == LOW);
uint32_t rise = micros();
// wait for LOW
while (digitalRead(PWMpin) == HIGH);
uint32_t highPeriod = micros() - rise;
// wait for HIGH
while (digitalRead(PWMpin) == LOW);
fullPeriod = micros() - rise;
float bitTime = fullPeriod / 4351.0;
float dataPeriod = highPeriod - 128 * bitTime;
float angle = 360.0 * dataPeriod / (4095 * bitTime);
return angle;
}
//float testMath(uint32_t fullPeriod, uint32_t highPeriod)
//{
// float bitTime = fullPeriod / 4351.0;
// float dataPeriod = highPeriod - 128 * bitTime;
// float angle = 360.0 * dataPeriod / (4095 * bitTime);
// return angle;
//}
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
pinMode(PWMpin, INPUT_PULLUP);
// // test code
// for (int pwm = 0; pwm < 4096; pwm++)
// {
// Serial.println(testMath(4351, 128 + pwm), 2);
// }
}
void loop()
{
float angle = measureAngle();
Serial.println(angle, 1); // print with 1 decimal
delay(1000);
}
// -- END OF FILE --
@@ -0,0 +1,28 @@
platforms:
rpipico:
board: rp2040:rp2040:rpipico
package: rp2040:rp2040
gcc:
features:
defines:
- ARDUINO_ARCH_RP2040
warnings:
flags:
packages:
rp2040:rp2040:
url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
# - uno
# - due
# - zero
# - leonardo
# - m4
- esp32
# - esp8266
# - mega2560
# - rpipico
@@ -0,0 +1,53 @@
//
// FILE: AS5600_resetCumulativeCounter.ino
// AUTHOR: Daniel-Frenkel, (slightly modified by Rob Tillaart)
// PURPOSE: demo - see issue #30
// URL: https://github.com/RobTillaart/AS5600
//
// Examples may use AS5600 or AS5600L devices.
// Check if your sensor matches the one used in the example.
// Optionally adjust the code.
#include "AS5600.h"
AS5600L as5600; // use default Wire
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("AS5600_LIB_VERSION: ");
Serial.println(AS5600_LIB_VERSION);
Wire.begin(14, 15); // ESP32
as5600.begin();
as5600.setAddress(0x40); // AS5600L has address
as5600.setDirection(AS5600_CLOCK_WISE); // default, just be explicit.
delay(1000);
as5600.resetCumulativePosition(777);
}
void loop()
{
static uint32_t lastTime = 0;
// set initial position
as5600.getCumulativePosition();
// update every 100 ms
if (millis() - lastTime >= 100)
{
lastTime = millis();
Serial.println(as5600.getCumulativePosition());
}
}
// -- END OF FILE --
+149
View File
@@ -0,0 +1,149 @@
# Syntax Colouring Map For AS5600
# Data types (KEYWORD1)
AS5600 KEYWORD1
AS5600L KEYWORD1
# Methods and Functions (KEYWORD2)
begin KEYWORD2
isConnected KEYWORD2
setAddress KEYWORD2
getAddress KEYWORD2
setDirection KEYWORD2
getDirection KEYWORD2
getZMCO KEYWORD2
setZPosition KEYWORD2
getZPosition KEYWORD2
setMPosition KEYWORD2
getMPosition KEYWORD2
setMaxAngle KEYWORD2
getMaxAngle KEYWORD2
setConfigure KEYWORD2
getConfigure KEYWORD2
setPowerMode KEYWORD2
getPowerMode KEYWORD2
setHysteresis KEYWORD2
getHysteresis KEYWORD2
setOutputMode KEYWORD2
getOutputMode KEYWORD2
setPWMFrequency KEYWORD2
getPWMFrequency KEYWORD2
setSlowFilter KEYWORD2
getSlowFilter KEYWORD2
setFastFilter KEYWORD2
getFastFilter KEYWORD2
setWatchDog KEYWORD2
getWatchDog KEYWORD2
rawAngle KEYWORD2
readAngle KEYWORD2
setOffset KEYWORD2
increaseOffset KEYWORD2
getOffset KEYWORD2
readStatus KEYWORD2
readAGC KEYWORD2
readMagnitude KEYWORD2
detectMagnet KEYWORD2
magnetTooStrong KEYWORD2
magnetTooWeak KEYWORD2
burnAngle KEYWORD2
burnSetting KEYWORD2
getAngularSpeed KEYWORD2
getCumulativePosition KEYWORD2
getRevolutions KEYWORD2
resetPosition KEYWORD2
resetCumulativePosition KEYWORD2
lastError KEYWORD2
# CONFIGURATION FIELDS
setPowerMode KEYWORD2
getPowerMode KEYWORD2
setHysteresis KEYWORD2
getHysteresis KEYWORD2
setOutputMode KEYWORD2
getOutputMode KEYWORD2
setPWMFrequency KEYWORD2
getPWMFrequency KEYWORD2
setSlowFilter KEYWORD2
getSlowFilter KEYWORD2
setFastFilter KEYWORD2
getFastFilter KEYWORD2
setWatchDog KEYWORD2
getWatchDog KEYWORD2
# Constants (LITERAL1)
AS5600_LIB_VERSION LITERAL1
AS5600_DEFAULT_ADDRESS LITERAL1
AS5600L_DEFAULT_ADDRESS LITERAL1
AS5600_CLOCK_WISE LITERAL1
AS5600_COUNTERCLOCK_WISE LITERAL1
AS5600_RAW_TO_DEGREES LITERAL1
AS5600_DEGREES_TO_RAW LITERAL1
AS5600_RAW_TO_RADIANS LITERAL1
AS5600_RAW_TO_RPM LITERAL1
AS5600_MODE_DEGREES LITERAL1
AS5600_MODE_RADIANS LITERAL1
AS5600_MODE_RPM LITERAL1
AS5600_OUTMODE_ANALOG_100 LITERAL1
AS5600_OUTMODE_ANALOG_90 LITERAL1
AS5600_OUTMODE_PWM LITERAL1
AS5600_POWERMODE_NOMINAL LITERAL1
AS5600_POWERMODE_LOW1 LITERAL1
AS5600_POWERMODE_LOW2 LITERAL1
AS5600_POWERMODE_LOW3 LITERAL1
AS5600_PWM_115 LITERAL1
AS5600_PWM_230 LITERAL1
AS5600_PWM_460 LITERAL1
AS5600_PWM_920 LITERAL1
AS5600_HYST_OFF LITERAL1
AS5600_HYST_LSB1 LITERAL1
AS5600_HYST_LSB2 LITERAL1
AS5600_HYST_LSB3 LITERAL1
AS5600_SLOW_FILT_16X LITERAL1
AS5600_SLOW_FILT_8X LITERAL1
AS5600_SLOW_FILT_4X LITERAL1
AS5600_SLOW_FILT_2X LITERAL1
AS5600_FAST_FILT_NONE LITERAL1
AS5600_FAST_FILT_LSB6 LITERAL1
AS5600_FAST_FILT_LSB7 LITERAL1
AS5600_FAST_FILT_LSB9 LITERAL1
AS5600_FAST_FILT_LSB18 LITERAL1
AS5600_FAST_FILT_LSB21 LITERAL1
AS5600_FAST_FILT_LSB24 LITERAL1
AS5600_FAST_FILT_LSB10 LITERAL1
AS5600_WATCHDOG_OFF LITERAL1
AS5600_WATCHDOG_ON LITERAL1
+23
View File
@@ -0,0 +1,23 @@
{
"name": "AS5600",
"keywords": "AS5600,AS5600L",
"description": "Arduino library for AS5600 and AS5600L magnetic rotation meter.",
"authors":
[
{
"name": "Rob Tillaart",
"email": "Rob.Tillaart@gmail.com",
"maintainer": true
}
],
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/AS5600.git"
},
"version": "0.6.1",
"license": "MIT",
"frameworks": "*",
"platforms": "*",
"headers": "AS5600.h"
}
+11
View File
@@ -0,0 +1,11 @@
name=AS5600
version=0.6.1
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for AS5600 and AS5600L magnetic rotation meter.
paragraph=
category=Sensors
url=https://github.com/RobTillaart/AS5600
architectures=*
includes=AS5600.h
depends=
+41
View File
@@ -0,0 +1,41 @@
## notes on PWM
#### Description
If you do not know the PWM frequency you can determine the angle
with PWM in the following way.
In one period there are 4351 bits (128 + 4095 + 128)
These come typically in one pulse like
```
00001111111111111111111111111111111111100000000
HEADER DATA POSTLOW
```
#### Step 1
Determine the duration of one cycle by measuring the time between two RISING edges.
Lets call this time FULLSCALE == 4351 bits.
Then the time for one bit BITTIME = round(FULLSCALE / 4351.0);
#### Step 2
Measure the duration of the HIGHPERIOD == HEADER + DATA
We know the HEADER is 128 bits and FULLSCALE = 4351 bits.
DATAPERIOD = HIGHPERIOD - 128 \* BITTIME
ANGLE = 360 \* DATAPERIOD / (4095 \* BITTIME)
Code: AS5600_pwm_test.ino
#### Related
https://github.com/RobTillaart/AS5600
https://forum.arduino.cc/t/questions-using-the-as5600-in-pwm-mode/1266957/3
+284
View File
@@ -0,0 +1,284 @@
//
// FILE: unit_test_001.cpp
// AUTHOR: Rob Tillaart
// DATE: 2022-05-28
// PURPOSE: unit tests for the AS5600 library
// https://github.com/RobTillaart/AS5600
// https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md
//
// supported assertions
// ----------------------------
// assertEqual(expected, actual); // a == b
// assertNotEqual(unwanted, actual); // a != b
// assertComparativeEquivalent(expected, actual); // abs(a - b) == 0 or (!(a > b) && !(a < b))
// assertComparativeNotEquivalent(unwanted, actual); // abs(a - b) > 0 or ((a > b) || (a < b))
// assertLess(upperBound, actual); // a < b
// assertMore(lowerBound, actual); // a > b
// assertLessOrEqual(upperBound, actual); // a <= b
// assertMoreOrEqual(lowerBound, actual); // a >= b
// assertTrue(actual);
// assertFalse(actual);
// assertNull(actual);
// // special cases for floats
// assertEqualFloat(expected, actual, epsilon); // fabs(a - b) <= epsilon
// assertNotEqualFloat(unwanted, actual, epsilon); // fabs(a - b) >= epsilon
// assertInfinity(actual); // isinf(a)
// assertNotInfinity(actual); // !isinf(a)
// assertNAN(arg); // isnan(a)
// assertNotNAN(arg); // !isnan(a)
#include <ArduinoUnitTests.h>
#include "AS5600.h"
#include "Wire.h"
unittest_setup()
{
fprintf(stderr, "AS5600_LIB_VERSION: %s\n", (char *) AS5600_LIB_VERSION);
}
unittest_teardown()
{
}
unittest(test_constants_base)
{
assertEqual(0, AS5600_CLOCK_WISE);
assertEqual(1, AS5600_COUNTERCLOCK_WISE);
assertEqual(0, AS5600_MODE_DEGREES);
assertEqual(1, AS5600_MODE_RADIANS);
assertEqual(2, AS5600_MODE_RPM);
assertEqualFloat(360.0/4096, AS5600_RAW_TO_DEGREES, 0.0001);
assertEqualFloat(4096/360.0, AS5600_DEGREES_TO_RAW, 0.0001);
assertEqualFloat((PI*2.0)/4096, AS5600_RAW_TO_RADIANS, 0.0001);
assertEqualFloat(60.0/4096, AS5600_RAW_TO_RPM, 0.0001);
assertEqual(0x36, AS5600_DEFAULT_ADDRESS);
assertEqual(0x40, AS5600L_DEFAULT_ADDRESS);
assertEqual(255, AS5600_SW_DIRECTION_PIN);
}
unittest(test_constants_configuration)
{
assertEqual(0, AS5600_OUTMODE_ANALOG_100);
assertEqual(1, AS5600_OUTMODE_ANALOG_90);
assertEqual(2, AS5600_OUTMODE_PWM);
assertEqual(0, AS5600_POWERMODE_NOMINAL);
assertEqual(1, AS5600_POWERMODE_LOW1);
assertEqual(2, AS5600_POWERMODE_LOW2);
assertEqual(3, AS5600_POWERMODE_LOW3);
assertEqual(0, AS5600_PWM_115);
assertEqual(1, AS5600_PWM_230);
assertEqual(2, AS5600_PWM_460);
assertEqual(3, AS5600_PWM_920);
assertEqual(0, AS5600_HYST_OFF);
assertEqual(1, AS5600_HYST_LSB1);
assertEqual(2, AS5600_HYST_LSB2);
assertEqual(3, AS5600_HYST_LSB3);
assertEqual(0, AS5600_SLOW_FILT_16X);
assertEqual(1, AS5600_SLOW_FILT_8X);
assertEqual(2, AS5600_SLOW_FILT_4X);
assertEqual(3, AS5600_SLOW_FILT_2X);
assertEqual(0, AS5600_FAST_FILT_NONE);
assertEqual(1, AS5600_FAST_FILT_LSB6);
assertEqual(2, AS5600_FAST_FILT_LSB7);
assertEqual(3, AS5600_FAST_FILT_LSB9);
assertEqual(4, AS5600_FAST_FILT_LSB18);
assertEqual(5, AS5600_FAST_FILT_LSB21);
assertEqual(6, AS5600_FAST_FILT_LSB24);
assertEqual(7, AS5600_FAST_FILT_LSB10);
assertEqual(0, AS5600_WATCHDOG_OFF);
assertEqual(1, AS5600_WATCHDOG_ON);
}
unittest(test_constructor)
{
AS5600 as5600;
Wire.begin();
as5600.begin(4);
assertTrue(as5600.isConnected()); // keep CI happy
AS5600L asl(0x40);
asl.begin(5);
assertTrue(asl.isConnected()); // keep CI happy
}
unittest(test_address)
{
AS5600 as5600;
Wire.begin();
as5600.begin(4);
assertEqual(0x36, as5600.getAddress());
AS5600L asl;
as5600.begin(5);
assertEqual(0x40, asl.getAddress());
AS5600L asl2(0x41);
asl2.begin(6);
assertEqual(0x41, asl2.getAddress());
}
unittest(test_hardware_direction)
{
AS5600 as5600;
Wire.begin();
as5600.begin(4);
assertEqual(AS5600_CLOCK_WISE, as5600.getDirection());
as5600.setDirection();
assertEqual(AS5600_CLOCK_WISE, as5600.getDirection());
as5600.setDirection(AS5600_COUNTERCLOCK_WISE);
assertEqual(AS5600_COUNTERCLOCK_WISE, as5600.getDirection());
as5600.setDirection(AS5600_CLOCK_WISE);
assertEqual(AS5600_CLOCK_WISE, as5600.getDirection());
}
unittest(test_software_direction)
{
AS5600 as5600;
Wire.begin();
as5600.begin(255);
assertEqual(AS5600_CLOCK_WISE, as5600.getDirection());
as5600.setDirection();
assertEqual(AS5600_CLOCK_WISE, as5600.getDirection());
as5600.setDirection(AS5600_COUNTERCLOCK_WISE);
assertEqual(AS5600_COUNTERCLOCK_WISE, as5600.getDirection());
as5600.setDirection(AS5600_CLOCK_WISE);
assertEqual(AS5600_CLOCK_WISE, as5600.getDirection());
}
unittest(test_offset_I)
{
AS5600 as5600;
Wire.begin();
as5600.begin();
for (int of = 0; of < 360; of += 40)
{
as5600.setOffset(of);
assertEqualFloat(of, as5600.getOffset(), 0.05);
}
assertTrue(as5600.setOffset(-40.25));
assertEqualFloat(319.75, as5600.getOffset(), 0.05);
assertTrue(as5600.setOffset(-400.25));
assertEqualFloat(319.75, as5600.getOffset(), 0.05);
assertTrue(as5600.setOffset(753.15));
assertEqualFloat(33.15, as5600.getOffset(), 0.05);
assertFalse(as5600.setOffset(36000.1));
assertFalse(as5600.setOffset(-36000.1));
}
unittest(test_offset_II)
{
AS5600 as5600;
Wire.begin();
as5600.begin();
as5600.setOffset(200);
assertEqualFloat(200, as5600.getOffset(), 0.05);
as5600.setOffset(30);
assertEqualFloat(30, as5600.getOffset(), 0.05);
as5600.setOffset(200);
assertEqualFloat(200, as5600.getOffset(), 0.05);
as5600.setOffset(-30);
assertEqualFloat(330, as5600.getOffset(), 0.05);
// as cummulative error can be larger ==> 0.1
as5600.setOffset(200);
assertEqualFloat(200, as5600.getOffset(), 0.1);
as5600.increaseOffset(30);
assertEqualFloat(230, as5600.getOffset(), 0.1);
as5600.setOffset(200);
assertEqualFloat(200, as5600.getOffset(), 0.1);
as5600.increaseOffset(-30);
assertEqualFloat(170, as5600.getOffset(), 0.1);
as5600.setOffset(200);
assertEqualFloat(200, as5600.getOffset(), 0.1);
as5600.increaseOffset(-300);
assertEqualFloat(260, as5600.getOffset(), 0.1);
}
unittest(test_failing_set_commands)
{
AS5600 as5600;
Wire.begin();
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));
}
// FOR REMAINING ONE NEED A STUB
unittest_main()
// -- END OF FILE --