API guide¶
How to do the common jobs with the driver: create it, read angle and velocity, check the field, configure, set up the outputs, use any register and handle errors.
Every snippet on this page is compiled by the host CI from
tests/docs/api_tasks.cpp, with Encoder standing for
as5047u::AS5047U<MySpi>. The API reference lists every member.
The shape of the API¶
One AS5047U<SpiType> object per sensor. It offers three levels of access:
| Level | Example | Use it for |
|---|---|---|
| Measurements | GetAngleDegrees(), GetVelocityRPM(), GetAGC() |
Reading the sensor in a loop |
| Setters | SetZeroPosition(), SetABIResolution(), ConfigureInterface() |
One setting each, read-modify-write, verified |
| Typed registers | ReadReg<AS5047U_REG::SETTINGS2>(), WriteReg(s2) |
Any field a setter does not cover |
| Group | Members |
|---|---|
| Angle | GetAngle(), GetAngle(AngleUnit), GetAngleDegrees(), GetAngleRadians(), GetRawAngle() |
| Velocity | GetVelocity(), GetVelocity(VelocityUnit), GetVelocityDegPerSec(), GetVelocityRadPerSec(), GetVelocityRPM() |
| Field and status | GetAGC(), GetMagnitude(), GetDiagnostics(), GetErrorFlags(), GetStickyErrorFlags(), ClearStickyErrorFlags(), DumpStatus() |
| Angle settings | SetZeroPosition(), GetZeroPosition(), SetDirection(), SetDynamicAngleCompensation(), SetAngleOutputSource() |
| Filter | SetFilterPreset(), SetAdaptiveFilter(), SetFilterParameters(), GetFilterParameters(), GetAdaptiveFilterEnabled() |
| Outputs | ConfigureInterface(), SetABIResolution(), SetIndexPulseLength(), SetUVWPolePairs(), SetHysteresis() |
| Chip | Set150CTemperatureMode(), ProgramOTP(), VerifyOTP() |
| Bus | SetFrameFormat(), SetPad(), ComputeCRC8() |
Setters return bool: false means the value did not read back as written, or a frame failed on the bus (the
port's transfer() returned false). Measurements return the value (0 when the read failed); their errors are
reported through the error flags (below). ReadReg(reg) reads any register and returns whether the read worked.
Nothing throws and nothing allocates.
Every call that takes a retries argument (default AS5047U_CFG::CRC_RETRIES, 0) repeats the access that many
times when the chip reports a CRC or framing error.
Create the driver¶
// SPI_16 (no CRC), SPI_24 (CRC) or SPI_32 (CRC and a PAD byte)
Encoder encoder(spi, FrameFormat::SPI_24);
encoder.SetFrameFormat(FrameFormat::SPI_16); // later, e.g. for the fastest reads
The constructor stores the port reference and the frame format; it sends nothing. Use SPI_24 unless you need
the highest read rate (SPI_16) or share the bus in a daisy chain (SPI_32 with SetPad()).
SPI protocol explains the formats.
Read angle and velocity¶
const uint16_t lsb = encoder.GetAngle(); // ANGLECOM, 0..16383 (14 bit)
const float deg = encoder.GetAngleDegrees(); // [0, 360)
const float rad = encoder.GetAngle(as5047u::AngleUnit::Radians);
const uint16_t raw = encoder.GetRawAngle(); // ANGLEUNC: without DAEC
const int16_t vel = encoder.GetVelocity(); // signed 14 bit, 24.141 deg/s per LSB
const float rpm = encoder.GetVelocity(as5047u::VelocityUnit::Rpm);
std::printf("%u (%.2f deg, %.4f rad) raw %u, vel %d (%.1f rpm)\n", lsb, deg, rad,
raw, vel, rpm);
| Value | Register | Range | Unit |
|---|---|---|---|
GetAngle() |
ANGLECOM (0x3FFF) | 0 to 16383 | 360° / 16384 = 0.022° per LSB |
GetRawAngle() |
ANGLEUNC (0x3FFE) | 0 to 16383 | Same, without dynamic angle error compensation |
GetVelocity() |
VEL (0x3FFC) | −8192 to 8191 | 24.141 °/s per LSB (4.02 rpm per LSB) |
ANGLECOM includes the zero position and the direction. Address 0x3FFF returns ANGLEUNC instead when
SetAngleOutputSource(UseANGLEUNC) is set. The datasheet specifies the velocity for −28000 rpm to 28000 rpm with
a total error of ±5 %.
Check the magnetic field¶
const uint8_t agc = encoder.GetAGC(); // 0..255; 0 or 255: field out of range
const uint16_t mag = encoder.GetMagnitude(); // CORDIC magnitude, kept steady by AGC
const auto dia = encoder.GetDiagnostics(); // DIA (0x3FF5)
std::printf("AGC %u MAG %u 5V mode %u AGC low %u high %u\n", agc, mag,
static_cast<unsigned>(dia.bits.VDD_mode),
static_cast<unsigned>(dia.bits.Comp_l),
static_cast<unsigned>(dia.bits.Comp_h));
encoder.DumpStatus(); // every register, through printf
| Reading | Healthy | Problem |
|---|---|---|
GetAGC() |
Between 0 and 255, not at either end | 0: field too strong (magnet too close). 255: field too weak |
GetMagnitude() |
Steady while the AGC is in range | Falls when the AGC is at 255 |
dia.bits.LoopsFinished |
1 | 0: the chip has not finished start-up |
dia.bits.MagHalf_flag |
0 | 1: magnitude below half its target |
dia.bits.Comp_l, Comp_h |
0 | AGC at its low or high limit |
Configure¶
using Hys = AS5047U_REG::SETTINGS3::Hysteresis;
bool ok = true;
ok = ok && encoder.SetZeroPosition(encoder.GetAngle()); // here is 0 degrees
ok = ok && encoder.SetDirection(true); // clockwise (from the top) counts up
ok = ok && encoder.SetDynamicAngleCompensation(true); // DAEC on (default)
ok = ok && encoder.SetFilterPreset(FilterPreset::Balanced); // K = 2..3
ok = ok && encoder.SetHysteresis(Hys::LSB_1);
ok = ok && encoder.Set150CTemperatureMode(false); // 3.3 V: low noise, up to 125 C
return ok; // false: a write did not read back as written
These settings live in volatile registers until you burn them (OTP programming). Configuration lists every setting with its register and default, and two complete setups.
Set up the outputs¶
bool ok = true;
ok = ok && encoder.ConfigureInterface(true, false, false); // ABI on, UVW off, no PWM
ok = ok && encoder.SetABIResolution(12); // 4096 steps, 1024 pulses per turn
ok = ok && encoder.SetIndexPulseLength(3); // index pulse 3 LSB wide (or 1)
ok = ok && encoder.SetUVWPolePairs(4); // UVW for a 4 pole-pair motor
return ok;
SetABIResolution() takes 10 to 14 bits (clamped) and writes the binary ABIRES code;
SetUVWPolePairs() takes 1 to 7 (clamped). For decimal pulse counts, use the register directly (next section).
Use any register directly¶
auto s2 = encoder.ReadReg<AS5047U_REG::SETTINGS2>(); // fields in .bits
s2.bits.ABI_DEC = 1; // decimal ABI counts (1000, 500, ... pulses)
s2.bits.IWIDTH = 1; // 1 LSB index pulse
if (!encoder.WriteReg(s2, 2)) { // verified, 2 retries
return false;
}
using Res = AS5047U_REG::SETTINGS3::ABIResolution;
auto s3 = encoder.ReadReg<AS5047U_REG::SETTINGS3>();
s3.bits.ABIRES = static_cast<uint8_t>(Res::Code0); // with ABI_DEC: 1000 pulses
return encoder.WriteReg(s3);
Each struct in AS5047U_REG has ADDRESS, a 16-bit value and named bits. ReadReg<T>() reads it (and
ERRFL); WriteReg(reg, retries) writes value and checks the read-back. The datasheet's register tables are in
its "Volatile Registers" and "Non-Volatile Registers (OTP)" sections.
Handle errors¶
The chip reports errors in ERRFL (0x0001), which clears when it is read (datasheet Figure 33). Every register
read inside the driver reads ERRFL after the register and ORs its bits into a set of sticky flags; so does
GetErrorFlags() and ReadReg<AS5047U_REG::ERRFL>(), so no ERRFL value the driver reads is lost.
GetStickyErrorFlags() returns the flags and leaves them set; ClearStickyErrorFlags() clears them and returns
what it cleared, in one atomic step.
(void)encoder.ClearStickyErrorFlags(); // start a clean window
const uint16_t angle = encoder.GetAngle(2); // then ERRFL; 2 retries on CRC
const auto flags = static_cast<uint16_t>(encoder.GetStickyErrorFlags()); // kept until cleared
if (flags != 0U) {
if (flags & static_cast<uint16_t>(AS5047U_Error::CrcError)) {
std::printf("CRC error: check wiring, SPI mode 1, clock\n");
}
if (flags & (static_cast<uint16_t>(AS5047U_Error::AgcWarning) |
static_cast<uint16_t>(AS5047U_Error::MagHalf))) {
std::printf("magnet out of range\n");
}
return false; // do not use this sample
}
std::printf("angle %u\n", static_cast<unsigned>(angle));
return true;
The getters decide on a retry from the ERRFL of their own read, so they never consume the sticky flags: the flags
of every sample stay visible until you clear them. GetErrorFlags() returns the chip's ERRFL, which only shows
what happened since the driver's previous read of it; the sticky flags hold everything since your last clear.
Bits 0 to 10 are the ERRFL bits. Bits 11 and 12 are errors the driver detects itself; bits 13 and 14 are the warning and error bits of the chip's replies (bit 15, 14 of a 16-bit frame; datasheet Figures 22, 25 and 28).
Flag (AS5047U_Error) |
Bit | Meaning | What to do |
|---|---|---|---|
AgcWarning |
0 | AGC at 0 or 255 | Check the magnet distance; read DIA for which end |
MagHalf |
1 | AGC at 255 and the magnitude below half its target | Magnet too far or missing |
P2ramWarning |
2 | ECC corrected one bit of the OTP settings | Note it; the value is still right |
P2ramError |
3 | ECC found an uncorrectable OTP error | Settings are not reliable |
FramingError |
4 | The chip could not frame the SPI transfer (for example a wrong number of clocks) | Check CSn per frame and the port's len |
CommandError |
5 | Invalid command | Check frame format and wiring |
CrcError |
6 | CRC of a MOSI frame was wrong | Check SPI mode 1, clock, signal integrity |
WatchdogError |
7 | Internal oscillator or watchdog fault | Power-cycle; replace the part if it persists |
OffCompError |
9 | Offset compensation not finished | Normal right after power-on; wait 10 ms |
CordicOverflow |
10 | CORDIC overflow | The angle is not valid; check the field |
BusError |
(driver) 11 | The port's transfer() returned false |
Check the SPI driver and the bus; see Porting |
ReplyCrcError |
(driver) 12 | The CRC of a 24- or 32-bit reply was wrong: MISO was corrupted | Same as CrcError; the read was rejected |
ReplyWarning |
(reply) 13 | Warning bit set in a reply | Look at ERRFL bits 0 to 2 in the same flags |
ReplyError |
(reply) 14 | Error bit set in a reply | Look at the ERRFL error bits in the same flags |
After any flag the datasheet asks for a read of DIA (GetDiagnostics()) for the details.
Troubleshooting maps symptoms to causes.