Support for AMT22xB Absolute Encoder


I have an AMT222B Kit here. Its a 14 bit absolute encoder with SPI interface. Compared to the other supported encoders it responds with the angle value when accessing SPI. So only one 16bit SPI transfer per update cycle is required.

It would be great to support this type of Encoder. I guess the standard SPI interface of SimpleFOC Lib will not work because of the two transfers per update cycle. Is that correct or is there a way to setup the generic SPI Encoder class that should work?

Here is a working example that i used to get the raw value read out of the sensor.

#include <SPI.h>

#define chipSelectPin 10
#define spi_mode SPI_MODE0
#define clock_speed 100000
#define angle_register 0x00
#define angle_resolution 14

SPISettings settings(clock_speed,MSBFIRST,spi_mode);

void setup() {
  // monitoring port


  //setup pins
  pinMode(chipSelectPin, OUTPUT);
  Serial.println("Sensor ready");

void loop() {

    digitalWrite(chipSelectPin, LOW);
    word result=SPI.transfer16(angle_register);    
    digitalWrite(chipSelectPin, HIGH);

    result=result & (0xFFFF >> (16-angle_resolution));


The only sensor I could speak for is the AS SPI sensors, such as AS5047p and the likes. They need two rounds because they are rather sophisticated and the first round you “ask” for the type of data to receive, the sensor prepares the data for you, and the second round you “fetch” the data you asked in round one. Why this is designed like that, is due to the way the SPI protocol works, which we shall leave out of the conversation, there are threads on this board this has been discussed ad nauseum.

The sensor you show is I guess a lot more simplistic, and always responds with the angle, so you just “fetch” the angle. I see no reason why not to implement the sensor, if it’s not already implemented. Perhaps someone more knowledgeable with the angle sensors code could provide more input?


Edit: The only issue I’m seeing is that this sensor is rather expensive, so it would be hard to implement without testing. I would not write code without having the device to test, perhaps there are souls a lot braver than me.

Hi Paul!

It should be a doozy to get it running if you’re already reading the raw values!

Take a look at the GenericSensor class you can find on the dev branch of SimpleFOC: Arduino-FOC/GenericSensor.h at dev · simplefoc/Arduino-FOC · GitHub

You can either git clone the dev branch of the library. or just copy this class into your project.

Alternatively, you can also implement a subclass of Sensor, see:

You only have to implement the “getSensorAngle()” method.

Alternatively, if you wait a little I will be happy to add a Sensor implementation for this sensor to the drivers repository…

Hi Runger,

thanks for the tips. I coded that sensor class already and it works well. Thank you.

I had to change the SPI calls a bit, but it works great. You can use my read method in case you want to add it to the repo. Propably it can be a bit more optimized (no use of command and parity) :slight_smile:

word MagneticSensorSRSPI::read(word angle_register){

  word command = angle_register;

  if (command_rw_bit > 0) {
    command = angle_register | (1 << command_rw_bit);
  if (command_parity_bit > 0) {
   	//Add a parity bit on the the MSB
  	command |= ((word)spiCalcEvenParity(command) << command_parity_bit);

  //SPI - begin transaction

  //Read the response
  digitalWrite(chip_select_pin, LOW);
  delayMicroseconds(3); // 2.5us delay Data shifted to output buffer, see datasheet of AMT22
  word register_value = spi->transfer(command)<<8;
  delayMicroseconds(3); // 2.5us time between bytes, see datasheet of AMT22
  register_value = register_value | spi->transfer(command);
  delayMicroseconds(3); // 3us time before CS can be released, see datasheet of AMT22
  digitalWrite(chip_select_pin, HIGH);
  delayMicroseconds(40); // 40us time between reads, see datasheet of AMT22
  //SPI - end transaction

  register_value = register_value >> (1 + data_start_bit - bit_resolution);  //this should shift data to the rightmost bits of the word

  const static word data_mask = 0xFFFF >> (16 - bit_resolution);

	return register_value & data_mask;  // Return the data, stripping the non data (e.g parity) bits

Excellent. Very nice.

For clarification, if the delay is 50us, then the limit on RPM for FOC is around 1000 RPM before we see issues with delayed field adjustment, am I right? I’m estimating 1% delay before the FOC becomes somewhat unstable? Or am I oversimplifying? If the delay is up to 5%, and FOC is still stable, that would hit the 4000 rpm max on the sensor anyway? So it should be OK? Perhaps there will be some cogging.

Hmm thats a good point which I did not think about. I will have a look into this after I found a working parameter set for the Controllers. My setup is this sensor and a 4pp BLDC with 1.3Ohms.

Hmmm… its maybe not quite so simple, but in essence I think you are right, @Valentine… but I think it is less the 50us delay that’s the issue here, but rather other latencies, the speed of the FOC loop, and the accuracy of the sensor that weigh more heavily.

50us delay limits the FOC iteration speed to about 20kHz - but that’s not so little. most MCUs can’t do that anyway.
So you can think of the effects this has on the control latency - at 1000RPM (105rad/s) the motor moves only 0.005rad in 50us… that’s well below the accuracy of the magnetic sensors anyway, even if the motor isn’t moving.

The 4kRPM limit on the sensor will have a different reason.

@Paul - thanks so much for this! It might take me a little while, but I’ll get round to assimilating this code into the drivers repository. Thanks you!
Note that when I do so, I’ll remove the hard-coded 40us delay - SimpleFOC calls the sensor once per iteration, so on most MCUs the iteration speed of SimpleFOC will provide the needed delay. Or maybe I’ll add a check and delay at the start of the read, only if necessary. I think that approach will lead to the best performance.

1 Like

That may be the best approach. On a teensy with 800MHz clock speed or the H series STM32 the delay will be easily exceeded.

So Im working with a Nucleo F446RE Board. In FOC current control mode the 40us delay made no difference on the position signal. I was expecting that the position signal would get laggy because the sensor can not write the buffer fast enough, if i skip the 40us delay. Fortunately that was not the case. Current waveform on the oscilloscope looked equal at 1000 rpm.

1 Like