Skip to content

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.

Version

const char* v = as5047u::AS5047U<MySpi>::GetDriverVersion();   // "1.0.0"