try to fix submodule
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* AS5048A.cpp
|
||||
*
|
||||
* Created on: 8 Mar 2021
|
||||
* Author: runger
|
||||
*/
|
||||
|
||||
#include "AS5048A.h"
|
||||
|
||||
AS5048A::AS5048A(SPISettings settings, int nCS) : settings(settings), nCS(nCS) {
|
||||
// nix
|
||||
}
|
||||
|
||||
AS5048A::~AS5048A() {
|
||||
}
|
||||
|
||||
|
||||
void AS5048A::init(SPIClass* _spi) {
|
||||
spi = _spi;
|
||||
if (nCS>=0)
|
||||
pinMode(nCS, OUTPUT);
|
||||
digitalWrite(nCS, HIGH);
|
||||
//SPI has an internal SPI-device counter, it is possible to call "begin()" from different devices
|
||||
spi->begin();
|
||||
readRawAngle(); // read an angle
|
||||
}
|
||||
|
||||
float AS5048A::getCurrentAngle(){
|
||||
readRawAngle();
|
||||
return ((float)readRawAngle())/(float)AS5048A_CPR * 2.0f * (float)PI;
|
||||
}
|
||||
|
||||
float AS5048A::getFastAngle(){
|
||||
return ((float)readRawAngle())/(float)AS5048A_CPR * 2.0f * (float)PI;
|
||||
}
|
||||
|
||||
|
||||
uint16_t AS5048A::readRawAngle(){
|
||||
uint16_t command = AS5048A_ANGLE_REG | AS5048A_PARITY | AS5048A_RW; // set r=1 and parity=1, result ix 0xFFFF
|
||||
uint16_t lastresult = spi_transfer16(command)&AS5048A_RESULT_MASK;
|
||||
return lastresult;
|
||||
}
|
||||
|
||||
|
||||
uint16_t AS5048A::readMagnitude(){
|
||||
uint16_t command = AS5048A_MAGNITUDE_REG | AS5048A_RW; // set r=1, result ix 0x7FFE
|
||||
/*uint16_t cmdresult =*/ spi_transfer16(command);
|
||||
uint16_t result = nop();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool AS5048A::isErrorFlag(){
|
||||
return errorflag;
|
||||
}
|
||||
|
||||
|
||||
AS5048Error AS5048A::clearErrorFlag(){
|
||||
uint16_t command = AS5048A_ERROR_REG | AS5048A_RW; // set r=1, result ix 0x4001
|
||||
/*uint16_t cmdresult =*/ spi_transfer16(command);
|
||||
uint16_t result = nop();
|
||||
AS5048Error err = {
|
||||
.parityError = ((result&0x0004)!=0x0000),
|
||||
.commandInvalid = ((result&0x0002)!=0x0000),
|
||||
.framingError = ((result&0x0001)!=0x0000)
|
||||
};
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
AS5048Diagnostics AS5048A::readDiagnostics(){
|
||||
uint16_t command = AS5048A_DIAGNOSTICS_REG | AS5048A_RW; // set r=1, result ix 0x7FFD
|
||||
/*uint16_t cmdresult =*/ spi_transfer16(command);
|
||||
AS5048Diagnostics result = {
|
||||
.reg = nop()
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
uint16_t AS5048A::setZero(uint16_t){
|
||||
// TODO implement me!
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint16_t AS5048A::enableOneTimeProgramming(){
|
||||
// no plans to implement this at the moment. one-time-programming a $10 chip isn't really a "maker" thing to do. The zero
|
||||
// position can be easily retained in software, stored on the MCU, and thereby the sensor can be reused in another project.
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint16_t AS5048A::programZero(){
|
||||
// no plans to implement this at the moment. one-time-programming a $10 chip isn't really a "maker" thing to do. The zero
|
||||
// position can be easily retained in software, stored on the MCU, and thereby the sensor can be reused in another project.
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t AS5048A::nop(){
|
||||
uint16_t result = spi_transfer16(0xFFFF); // using 0xFFFF as nop instead of 0x0000, then next call to fastAngle will return an angle
|
||||
return result&AS5048A_RESULT_MASK;
|
||||
}
|
||||
|
||||
uint16_t AS5048A::spi_transfer16(uint16_t outdata) {
|
||||
if (nCS>=0)
|
||||
digitalWrite(nCS, 0);
|
||||
spi->beginTransaction(settings);
|
||||
uint16_t result = spi->transfer16(outdata);
|
||||
spi->endTransaction();
|
||||
if (nCS>=0)
|
||||
digitalWrite(nCS, 1);
|
||||
// TODO check parity
|
||||
errorflag = ((result&AS5048A_ERRFLG)>0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* AS5048A.h
|
||||
*
|
||||
* Created on: 8 Mar 2021
|
||||
* Author: runger
|
||||
*/
|
||||
|
||||
#ifndef AS5048A_H_
|
||||
#define AS5048A_H_
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "SPI.h"
|
||||
|
||||
|
||||
|
||||
|
||||
union AS5048Diagnostics {
|
||||
struct {
|
||||
uint16_t agc:8;
|
||||
uint16_t ocf:1;
|
||||
uint16_t cof:1;
|
||||
uint16_t compLow:1;
|
||||
uint16_t compHigh:1;
|
||||
};
|
||||
uint16_t reg;
|
||||
};
|
||||
|
||||
|
||||
struct AS5048Error {
|
||||
bool parityError;
|
||||
bool commandInvalid;
|
||||
bool framingError;
|
||||
};
|
||||
|
||||
|
||||
#define AS5048A_CPR 16384
|
||||
#define AS5048A_ANGLE_REG 0x3FFF
|
||||
#define AS5048A_ERROR_REG 0x0001
|
||||
#define AS5048A_PROGCTL_REG 0x0003
|
||||
#define AS5048A_OTPHIGH_REG 0x0016
|
||||
#define AS5048A_OTPLOW_REG 0x0017
|
||||
#define AS5048A_DIAGNOSTICS_REG 0x3FFD
|
||||
#define AS5048A_MAGNITUDE_REG 0x3FFE
|
||||
#define AS5048A_PARITY 0x8000
|
||||
#define AS5048A_RW 0x4000
|
||||
#define AS5048A_ERRFLG 0x4000
|
||||
#define AS5048A_RESULT_MASK 0x3FFF
|
||||
|
||||
|
||||
#define AS5048_BITORDER MSBFIRST
|
||||
|
||||
|
||||
static SPISettings AS5048SPISettings(8000000, AS5048_BITORDER, SPI_MODE1); // @suppress("Invalid arguments")
|
||||
|
||||
|
||||
class AS5048A {
|
||||
public:
|
||||
AS5048A(SPISettings settings = AS5048SPISettings, int nCS = -1);
|
||||
virtual ~AS5048A();
|
||||
|
||||
virtual void init(SPIClass* _spi = &SPI);
|
||||
|
||||
float getCurrentAngle(); // angle in radians, return current value
|
||||
float getFastAngle(); // angle in radians, return last value and read new
|
||||
|
||||
uint16_t readRawAngle(); // 14bit angle value
|
||||
uint16_t readMagnitude(); // 14bit magnitude value
|
||||
|
||||
bool isErrorFlag();
|
||||
AS5048Error clearErrorFlag();
|
||||
|
||||
AS5048Diagnostics readDiagnostics();
|
||||
|
||||
uint16_t setZero(uint16_t);
|
||||
uint16_t enableOneTimeProgramming();
|
||||
uint16_t programZero();
|
||||
|
||||
private:
|
||||
|
||||
uint16_t nop();
|
||||
uint16_t spi_transfer16(uint16_t outdata);
|
||||
SPIClass* spi;
|
||||
SPISettings settings;
|
||||
bool errorflag = false;
|
||||
int nCS = -1;
|
||||
|
||||
};
|
||||
|
||||
#endif /* AS5048A_H_ */
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
#include "./MagneticSensorAS5048A.h"
|
||||
#include "common/foc_utils.h"
|
||||
#include "common/time_utils.h"
|
||||
|
||||
MagneticSensorAS5048A::MagneticSensorAS5048A(int nCS, bool fastMode, SPISettings settings) : AS5048A(settings, nCS), fastMode(fastMode) {
|
||||
|
||||
}
|
||||
|
||||
MagneticSensorAS5048A::~MagneticSensorAS5048A(){
|
||||
|
||||
}
|
||||
void MagneticSensorAS5048A::init(SPIClass* _spi) {
|
||||
this->AS5048A::init(_spi);
|
||||
this->Sensor::init();
|
||||
}
|
||||
|
||||
float MagneticSensorAS5048A::getSensorAngle() {
|
||||
float angle_data = readRawAngle();
|
||||
if (!fastMode) // read again to ensure current value
|
||||
angle_data = readRawAngle();
|
||||
|
||||
angle_data = ( angle_data / (float)AS5048A_CPR ) * _2PI;
|
||||
// return the shaft angle
|
||||
return angle_data;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
#ifndef __MAGNETICSENSORAS5048A_H__
|
||||
#define __MAGNETICSENSORAS5048A_H__
|
||||
|
||||
#include "common/base_classes/Sensor.h"
|
||||
#include "./AS5048A.h"
|
||||
|
||||
class MagneticSensorAS5048A : public Sensor, public AS5048A {
|
||||
public:
|
||||
MagneticSensorAS5048A(int nCS = -1, bool fastMode = false, SPISettings settings = AS5048SPISettings);
|
||||
virtual ~MagneticSensorAS5048A();
|
||||
|
||||
virtual float getSensorAngle() override;
|
||||
|
||||
virtual void init(SPIClass* _spi = &SPI);
|
||||
protected:
|
||||
bool fastMode = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* PreciseMagneticSensorAS5048A.cpp
|
||||
*
|
||||
* Created on: 1 May 2021
|
||||
* Author: runger
|
||||
*/
|
||||
|
||||
#include <encoders/as5048a/PreciseMagneticSensorAS5048A.h>
|
||||
#include "common/foc_utils.h"
|
||||
#include "common/time_utils.h"
|
||||
|
||||
PreciseMagneticSensorAS5048A::PreciseMagneticSensorAS5048A(int nCS, bool fastMode, SPISettings settings) : AS5048A(settings, nCS), fastMode(fastMode) { }
|
||||
|
||||
PreciseMagneticSensorAS5048A::~PreciseMagneticSensorAS5048A() { }
|
||||
|
||||
|
||||
void PreciseMagneticSensorAS5048A::init(SPIClass* _spi) {
|
||||
this->AS5048A::init(_spi);
|
||||
// velocity calculation init
|
||||
current_ts = _micros();
|
||||
/*uint16_t angle_data =*/ readRawAngle();
|
||||
current_angle = PreciseAngle(readRawAngle(), 0);
|
||||
getAngle();
|
||||
}
|
||||
|
||||
|
||||
|
||||
float PreciseMagneticSensorAS5048A::getSensorAngle() {
|
||||
previous_ts = current_ts;
|
||||
previous_angle = current_angle;
|
||||
uint16_t angle_data = readRawAngle();
|
||||
if (!fastMode) // read again to ensure current value
|
||||
angle_data = readRawAngle();
|
||||
current_ts = _micros();
|
||||
current_angle.update(angle_data);
|
||||
return current_angle.asFloat();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
unlike the normal MagneticSensorSPI implementation, this one uses the angle previously read by the last call to getAngle to do its
|
||||
calculation, and does not directly poll any data from the sensor.
|
||||
This is an optimisation for speed, based on the assumption that loopFOC() (which calls getAngle()) is invoked at least as often as
|
||||
move() (which calls getVelocity()). If this is the case, getVelocity() should always have a sufficiently "fresh" value to work with.
|
||||
If using this function in a different context, simply call getAngle() first to be sure of a fresh angle value.
|
||||
*/
|
||||
float PreciseMagneticSensorAS5048A::getVelocity() {
|
||||
return current_angle.velocity(previous_angle, (current_ts-previous_ts));
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* PreciseMagneticSensorAS5048A.h
|
||||
*
|
||||
* Created on: 1 May 2021
|
||||
* Author: runger
|
||||
*/
|
||||
|
||||
#ifndef LIBRARIES_ARDUNIO_FOC_DRIVERS_SRC_ENCODERS_AS5048A_PRECISEMAGNETICSENSORAS5048A_H_
|
||||
#define LIBRARIES_ARDUNIO_FOC_DRIVERS_SRC_ENCODERS_AS5048A_PRECISEMAGNETICSENSORAS5048A_H_
|
||||
|
||||
#include "common/base_classes/Sensor.h"
|
||||
#include "./AS5048A.h"
|
||||
#include "utilities/PreciseAngle.h"
|
||||
|
||||
class PreciseMagneticSensorAS5048A : public Sensor, public AS5048A {
|
||||
public:
|
||||
PreciseMagneticSensorAS5048A(int nCS = -1, bool fastMode = false, SPISettings settings = AS5048SPISettings);
|
||||
virtual ~PreciseMagneticSensorAS5048A();
|
||||
|
||||
virtual float getSensorAngle() override;
|
||||
virtual float getVelocity() override;
|
||||
|
||||
virtual void init(SPIClass* _spi = &SPI) override;
|
||||
|
||||
protected:
|
||||
bool fastMode = false;
|
||||
PreciseAngle previous_angle = PreciseAngle();
|
||||
PreciseAngle current_angle = PreciseAngle();
|
||||
unsigned long previous_ts = 0;
|
||||
unsigned long current_ts = 1;
|
||||
};
|
||||
|
||||
#endif /* LIBRARIES_ARDUNIO_FOC_DRIVERS_SRC_ENCODERS_AS5048A_PRECISEMAGNETICSENSORAS5048A_H_ */
|
||||
@@ -0,0 +1,86 @@
|
||||
# AS5048A SimpleFOC driver
|
||||
|
||||
While AS5048A absolute position magnetic rotary encoder is supported by the standard MagneticSensorSPI driver included in the base distribution, this AS5048A-specific driver includes some optimisations:
|
||||
|
||||
- access to the other registers of the AS5048A, including the magnitude value which can be used to check the magnet strength, and the diagnostics register
|
||||
- access to the error state of the sensor, and ability to clear errors
|
||||
- it has a fastMode setting, in which the sensor is sent only 1 command per getAngle() call - the value returned will be from previous getAngle() invocation
|
||||
|
||||
|
||||
## Hardware setup
|
||||
|
||||
Connect as per normal for your SPI bus. No special hardware setup is needed to use this driver.
|
||||
|
||||
## Software setup
|
||||
|
||||
Its actually easier to use than the standard SPI sensor class, because it is less generic:
|
||||
|
||||
```c++
|
||||
#include "Arduino.h"
|
||||
#include "Wire.h"
|
||||
#include "SPI.h"
|
||||
#include "SimpleFOC.h"
|
||||
#include "SimpleFOCDrivers.h"
|
||||
#include "encoders/as5048a/MagneticSensorAS5048A.h"
|
||||
|
||||
#define SENSOR1_CS 5 // some digital pin that you're using as the nCS pin
|
||||
MagneticSensorAS5048A sensor1(SENSOR1_CS);
|
||||
|
||||
|
||||
void setup() {
|
||||
sensor1.init();
|
||||
}
|
||||
```
|
||||
|
||||
Set some options:
|
||||
|
||||
```c++
|
||||
MagneticSensorAS5048A sensor1(SENSOR1_CS, true, mySPISettings);
|
||||
```
|
||||
|
||||
Use another SPI bus:
|
||||
|
||||
```c++
|
||||
void setup() {
|
||||
sensor1.init(SPI2);
|
||||
}
|
||||
```
|
||||
|
||||
Here's how you can use it:
|
||||
|
||||
```c++
|
||||
// update the sensor (only needed if using the sensor without a motor)
|
||||
sensor1.update();
|
||||
|
||||
// get the angle, in radians, including full rotations
|
||||
float a1 = sensor1.getAngle();
|
||||
|
||||
// get the velocity, in rad/s - note: you have to call getAngle() on a regular basis for it to work
|
||||
float v1 = sensor1.getVelocity();
|
||||
|
||||
// get the angle, in radians, no full rotations
|
||||
float a2 = sensor1.getCurrentAngle();
|
||||
|
||||
// get the raw 14 bit value
|
||||
uint16_t raw = sensor1.readRawAngle();
|
||||
|
||||
// read the CORDIC magnitude value, a measure of the magnet field strength
|
||||
float m1 = sensor1.readMagnitude();
|
||||
|
||||
// check for errors
|
||||
if (sensor1.isErrorFlag()) {
|
||||
AS5048Error error = sensor1.clearErrorFlag();
|
||||
if (error.parityError) { // also error.framingError, error.commandInvalid
|
||||
// etc...
|
||||
}
|
||||
}
|
||||
|
||||
// get diagnostics
|
||||
AS5048Diagnostics diagnostics = sensor1.readDiagnostics();
|
||||
```
|
||||
|
||||
|
||||
## PreciseMagneticSensorAS5048A
|
||||
|
||||
This is a variant of the sensor that uses [PreciseAngle](../utilities) to represent the angle, allowing a (much) greater range of rotation.
|
||||
|
||||
Reference in New Issue
Block a user