Questions using the AS5600 in PWM mode

Hello,

I've got a couple of questions regarding using the AS5600 magnetic rotary encoder.
Full Datasheet: https://html.alldatasheet.com/html-pdf/621657/AMSCO/AS5600/149/1/AS5600.html
I am looking into using this sensor with PWM output mode, mainly due to this sensor being a 12Bit resolution sensor while most arduino boards only have 10bit ADC resolution.

Using interrupts is not an option for my program as timing is an essential part of what i'm trying to do.

that said, i have never worked with PWM inputs. after some preparation i'm kinda stuck before i can begin:

let's say the PWM frequency is set to the standard vallue of 115Hz (= 8.69ms/rx)
The function i was thinking to use to read the PWM singal is pulseIn. This function will return the length of the pulse (in microseconds).

Now when we look at the datasheet at the PWM output explanation:


We see that the output is in a timely manner expressed in Clock Periods, but for PWM mode to work we don't need to use the SCL/SDL pins, so how long is one clock period seeing this sensor has no clock of its own?

Now for the next part, I want to capture the entire sequence, how can i ensure the pulseIn function starts at the very start of the sequence and ensure it reads the full sequence and stops right after the full sequence has passed? As we can see above the full sequence consists of 128 clock periods HIGH followed by 4095(12bit) clock periods of data(wich can be either all LOWS, or highs then lows or only lows) followed by 128 clock periods LOW.
I was thinking to look at all the HIGH clock periods, subtract 128 and then continue with the data to convert it to the angle. But pulseIn returns the amount of microseconds and i have no idea how long a single Clock period is, is it the clock frequency of the microcontroller (16MHz in my case) but that doesnt make sense because the sensor has no common with the arduino clock.

Is this approach correct and if so, are there other approaches to the same end?

Does this approach mean that a single query for the data i need to process can take up to:
At default 115Hz:

  • a max of 8695,652.. *2 -1,9986... (17 389,30..) microseconds
  • and in best case a minimum query time of 255,82.. microseconds (1,9986210066651344518501493909446 * 128) ?
    At Max PWM frequency of 920Hz:
  • Max: 1000000/920= 1 086,95.. *2 -(1086.95../4351=0,249..) = 2 173,66.. microseconds
  • Min (best case): (1086.95../4351=0,249..) * 128 = 31,976.. microseconds
// Check the PWM input 
  // 1 000 000 (1second in us) / 115 (Hz pwm frequency from the sensor) = 8 695,6521739130434782608695652174 microseconds
  PWMRaw = pulseIn(PWMINpin, HIGH, 8696UL);
  if( PWMRaw != 0)
  {
    // 8696 / 4351(128 begin + 4095 data + 128 end) == 1 clock period in us = 1,9986210066651344518501493909446
    PWMData = PWMRaw - (128 * 1,9986210066651344518501493909446);
  } else {/* Failed to get PWM input */}

Have you looked at @RobTillaart's AS5600 library?

https://reference.arduino.cc/reference/en/libraries/as5600/

I just did and i understand nothing of how he does it, he's using a dutycycle but i have no clue what he is doing there or why, it makes 0 sense to me, seems like an unnescesarily complicated way to go about.

I'm here simply trying to understand the basic mode of operandi, i'd like to understand what i'm doing instead of just copy/paste coding like so many do

(update) This is wrong interpretation of the bit pattern, did not have coffee yet.

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 two pulses something like

11111000000011111111111111111111111111111100000
     A          DATA                      B

Step 1:
Determine the duration of one cycle by measuring the time between FALLING edges A and B. This period is the DATA period used for 4095 bits, lets call this time FULLSCALE.

11111000000011111111111111111111111111111100000
CCCCC       DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD

Step 2:
Measure the duration of BOTH positive pulses C and D.

Then one of the following scenarios must occur:

scenario 1:
duration C == 128/4095 * FULLSCALE ==> D is the data.
ANGLE = D / FULLSCALE * 360°

scenario 2:
duration C <> 128/4095 * FULLSCALE ===> C is the data
ANGLE = C / FULLSCALE * 360°

if by any change the duration of C and D are equal scenario 1 applies.

Please be aware that measuring time must be done in microseconds, and that the comparison of C == 128/4095*FULLSCALE can differ a few microseconds so you have to take that into account.

Hope this helps to fix your project.
(I will copy the text above near the library in some future)

Improved interpretation after enough coffee

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.
The time for one bit is now
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.

DATA = HIGHPERIOD - 128 * BITTIME

ANGLE = 360 * DATA / (4095 * BITTIME)

Hope this helps to fix your project.

1 Like

In code

//
//    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);

/*
  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 --
1 Like

Hello there robtillaart, I'm honored to see you here explaining how you did this .

I don't quite understand your bitstream layout; If i interpret this correctly then A and B are actually the same part of the sequence, being the end of the seq?
because what comes before A here should be the 128 opening HIGH's to announce the start of a new sequence, then A here is actually the start of the data bits.

PWM streams layout:

0.00° angle= 128cp HIGH(OPENING) - 4095cp LOW		  - 128cp LOW(CLOSING) - 128cp HIGH(OPENING) ...
45° angle= 128cp HIGH(OPENING) - 1024cp HIGH, 3072cp LOW - 128cp LOW(CLOSING) - 128cp HIGH(OPENING) ...
is this correct or is it reversed like:
45° angle= 128cp HIGH(1)(OPENING) - 3072cp 0, 1024cp 1  - 128cp LOW(0)(CLOSING) - 128cp HIGH(OPENING) ...
	   |--------------------------FULLSCALE-------------------------------|
	   |-----------C-----------|---A----|-----------|---------B-----------|
	   |-------OPENING---------|-------DATA---------|------CLOSING--------|

Step 1:
determine the time of the fullscale sequence:
issues:

  • to measure the time from a single sequence and not from multiple sequences that could have variating amount of ones and zeros in the datastream due to the sensor being in a diffrent angle and therefore have diffrent results,
    the sensor must either be in 0.00° angle or 99.99% of the full range (iow databits need to be all high or all low)
  • arduino board 16MHz has a micros resolution of 4 microseconds, wich basicly means that its possible that in the code when you check for the first rising edge 3microseconds have already passed that the program missed

Isnt it better then to stick to the theoretical timeframe a single sequence takes by doing the calculation I did in my previous post?
another issue is the use of the while function, this will halt the continuous loop of the program, I need my loop to be as fast as possible.
preferably i need to be able to get the angle of the sensor as fast as possible, and seeing from my theoretical calculations, the halting times can be way to long, the max i would want to halt my loop is around a couple of hundreds of microseconds tops.
Seeing that, i doubt i can actually use the pwm output for the sensor or am I missing something?

Please note that the post with A and B is using an incorrect picture of the PWM pulses,
I updated it strikethrough. The second post (with coffee) is correct afaik.

Then you should not use PWM in a polling way imho. As you need to measure the time that the line is HIGH, you first have to find the RISING edge before you can start measuring. Worst case you have to measure almost 2 cycles, and at least the angle period + 128 + 2(LOW bits).

Best case minimum = 1 (data) + 128 (header) + 2(Low) = 131 bits.
Best case maximum = 4095 (data) + 128 (header) + 2(low) = 4225 bits.
Best case average = 4356 / 2 = 2178 bits.
Worst case = 4350 bits before you find the RISING edge + 4225 (BCmax) = 8575 bits.

If you know the frequency in advance, you know the duration of the 4351 bits, and thus the duration of 128 bits. So math might be simpler. But you still have to wait for the RISING edge before you can start measuring, and on average that takes half of a FULLSCALE / full period.

The only way to have an updated angle with PWM is using interrupts (ON CHANGE).
Then you can get the timestamps needed in the background, so you can have always a measurement of the last pulse and the interrupts costs only little time to handle.

1 Like

Thanks for clarifying,
I have made a test sketch to time how long it takes for every angle pull request using the 3 diffrent output modes.

For some reason l2c is not working, i can't get the sensor to connect.
I had already written another program to edit the configs of the sensor and that worked perfectly, but now that i'm using your library it seems i can't connect with l2c and i have no idea why, I already tried adding 2 pull up resistors(5K) from vcc to both sda and scl lines but this doesn't help.

Below is the program i wrote to test how long it takes to get the angle from the sensor with varying settings, i feel this is an important detaill to take into account when writing fast paced programs where timing is very important, feel free to optimise/correct it and add it to your library examples.

There also seems to be a problem with the pwmmeasure function, but i'm pretty sure thats because I2C isn't working though and therefore i cant change the output mode.

Do you know what i'm missing here why l2c isnt working? i'm using the arduino Mega on 5V.
The program stops after the analog testing no matter what i try, these are the serial output results:

AS5600_output_speedtest.ino
00:52:10.498 -> AS5600_LIB_VERSION: 0.6.1
00:52:10.498 -> Connect: 0
00:52:11.514 -> [Analog] - 0 	224µs to retrieve angle: 230.50	angle in register: 0
00:52:11.715 -> [Analog] - 1 	124µs to retrieve angle: 230.50	angle in register: 0
.
.
.
00:52:31.315 -> [Analog] - 98 	128µs to retrieve angle: 358.94	angle in register: 0
00:52:31.489 -> [Analog] - 99 	128µs to retrieve angle: 358.94	angle in register: 0
00:52:31.532 -> 
00:52:31.532 -> 
00:52:31.532 -> 
00:52:31.532 -> finished ANALOG ANGLE timing, avarage query time(µs): 127
00:52:31.532 -> Fastest: 120 (3)
00:52:31.532 -> Slowest: 224 (1)
00:52:31.532 -> 
00:52:31.532 -> 
00:52:31.532 -> 
00:52:31.721 -> as5600 not connected
00:52:31.721 -> Connect: 0

the program: (Check later post for updated version, this is off)

//
//    FILE: AS5600_output_speedtest.ino
//  AUTHOR: Pollyscracker
// PURPOSE: demo/testing
//     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 <Arduino_BuiltIn.h>
#include <AS5600.h>
#include <Wire.h>


int iQueryN = 100;    // amount of times to request the angle per outputmode (minimum total process time per mode: iQueryN * delay(200)(at the end of the loop) 
int Directionpin = 4; // sensor input, connect to dir of the sensor
int Analogpin = A7;   // sensor output, connect to both pins
int PWMpin = 7;       // sensor output, connect to both pins
// Set false if your microcontroller or setup works with 12Bit ADC conversion
// for use with Analog output only
bool b10BitADC = true;



AS5600L as5600;   //  use default Wire
bool bOutmodeAnal = false;
bool bOutmodePWM = false;
bool bHighFreq = false;
bool bOutmodeReg = false;
bool bOutmodeRegRaw = false;
int iQcount = 0;
int iNmax = 0;
int iNmin = 0;
unsigned long ulStartTime = 0UL;
unsigned long ulTempTime = 0UL;
float fAngle = 0;
unsigned long ulAvarageTime = 0;
int iLongest = 0;
int iShortest = 2147483647;

int b = 0;

void setup()
{
  Serial.begin(460800);
  while(!Serial);
  Serial.println(__FILE__);
  Serial.print("AS5600_LIB_VERSION: ");
  Serial.println(AS5600_LIB_VERSION);
  //delay(1000);
  Wire.begin();
  //while(! Wire)  Serial.print("0");

  as5600.begin();  //  set direction pin or leave blank when tied directly to gnd or vcc
  //while(! as5600) Serial.print("1");
  as5600.setDirection(AS5600_CLOCK_WISE);  //  default, just be explicit.
  as5600.setOutputMode(AS5600_OUTMODE_ANALOG_100);
  b = as5600.isConnected();
  Serial.print("Connect: ");
  Serial.println(b);

  if(as5600.detectMagnet())
  {
    Serial.print("Magnet detected, AGC: "); Serial.print(as5600.readAGC()); Serial.println("\t(Optimal value: 255/2 for 5V mcu's, 128/2 for 3V3 mcu's)");
  } else {
    if(as5600.magnetTooStrong())  Serial.println("Magnet to strong/close to sensor.");
    if(as5600.magnetTooWeak())  Serial.println("Magnet to weak/far from sensor.");
  }

  //////////////////////////////////////////////////////
  // Configs that can influence the pull/call times

  /*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;*/
  as5600.setPowerMode(0);

  /*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; */
  as5600.setHysteresis(0);

  /*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;*/
  as5600.setSlowFilter(0);

  /*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;*/
  as5600.setFastFilter(0);

  delay(1000);
}

float measurePWMAngle()
{
  // 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);
  uint32_t fullPeriod = micros() - rise;

  float bitTime = fullPeriod / 4351.0;
  float dataPeriod = highPeriod - 128 * bitTime;
  float angle = 360.0 * dataPeriod / (4095 * bitTime);
  return angle;
}

float measureAnalAngle()
{
  float angle = 0.0;
  uint16_t rawangle = analogRead(Analogpin);
  if(b10BitADC) angle = 360.0/1023 * rawangle;
  else angle = 360.0/4095 * rawangle;
  return angle;
}

void loop()
{
  if( !bOutmodeAnal)
  {
    if(iQcount == 0)
    {
      as5600.setOutputMode(AS5600_OUTMODE_ANALOG_100);
      delay(10);
    }
    if( iQcount < iQueryN) 
    {
      ulStartTime = micros();
      fAngle = measureAnalAngle();
      ulTempTime = (micros()-ulStartTime);

      Serial.print("[Analog] - "); Serial.print(iQcount); Serial.print(" \t"); Serial.print(ulTempTime); Serial.print("µs to retrieve angle: "); Serial.print(fAngle); Serial.print("\tangle in register: "); Serial.println(as5600.readAngle());

      if( ulTempTime > iLongest ) { iLongest = ulTempTime; iNmax++; }
      if( ulTempTime < iShortest) { iShortest = ulTempTime; iNmin++; }
      ulAvarageTime += ulTempTime;
      iQcount++;

      if( iQcount == iQueryN )
      {
        ulAvarageTime = ulAvarageTime/iQcount;

        Serial.println("\n");
        Serial.println();
        Serial.print("finished ANALOG ANGLE timing, avarage query time(µs): ");Serial.println(ulAvarageTime);
        Serial.print("Fastest: "); Serial.print(iShortest); Serial.print(" ("); Serial.print(iNmin); Serial.println(")"); 
        Serial.print("Slowest: "); Serial.print(iLongest); Serial.print(" ("); Serial.print(iNmax); Serial.println(")"); 
        Serial.println("\n");
        Serial.println();
        // Set output mode to PWM
        as5600.setOutputMode(AS5600_OUTMODE_PWM);
        as5600.setPWMFrequency(AS5600_PWM_115);
        bOutmodeAnal = true;
        bOutmodePWM = false;
        bHighFreq = false;
        iQcount = 0;
        iShortest = 2147483647;
        iLongest = 0;
        iNmax = 0;
        iNmin = 0;
        ulAvarageTime = 0UL;
      } 
    }
  }
  else if(!bOutmodePWM)
  {
    if(iQcount == 0)
    {
      if( !b )
      {
        Serial.println("as5600 not connected");
        as5600.begin();
        b = as5600.isConnected();
        Serial.print("Connect: ");
        Serial.println(b);
      }
      // Set output mode to PWM
      as5600.setOutputMode(AS5600_OUTMODE_PWM);
      if( !bHighFreq )  as5600.setPWMFrequency(AS5600_PWM_115);  
      else  as5600.setPWMFrequency(AS5600_PWM_920);
      delay(10);
    }
    if( iQcount < iQueryN) 
    {
      uint16_t preangle = as5600.readAngle();
      ulStartTime = micros();
      fAngle = measurePWMAngle();
      ulTempTime = (micros()-ulStartTime);

      Serial.print("[PWM] - "); Serial.print(iQcount); Serial.print(" \t"); Serial.print(ulTempTime); Serial.print("µs to retrieve angle: "); Serial.print(fAngle); Serial.print("\tPRE-call angle from register: "); Serial.print(preangle); Serial.print("\tPOST-call angle from register: "); Serial.println(as5600.readAngle());

      if( ulTempTime > iLongest ) { iLongest = ulTempTime; iNmax++; }
      if( ulTempTime < iShortest) { iShortest = ulTempTime; iNmin++; }
      ulAvarageTime += ulTempTime;
      iQcount++;

      if( iQcount == iQueryN )
      {
        ulAvarageTime = ulAvarageTime/iQcount;

        Serial.println("\n");
        Serial.println();
        if( !bHighFreq ) {
          Serial.print("finished PWM ANGLE timing at lowest frequency (115Hz), avarage query time(µs): ");Serial.println(ulAvarageTime);
          Serial.print("Fastest: "); Serial.print(iShortest); Serial.print(" ("); Serial.print(iNmin); Serial.println(")"); 
          Serial.print("Slowest: "); Serial.print(iLongest); Serial.print(" ("); Serial.print(iNmax); Serial.println(")");
          bHighFreq = true;
        } else {
          Serial.print("finished PWM ANGLE timing at highest frequency (920Hz), avarage query time(µs): ");Serial.println(ulAvarageTime);
          Serial.print("Fastest: "); Serial.print(iShortest); Serial.print(" ("); Serial.print(iNmin); Serial.println(")"); 
          Serial.print("Slowest: "); Serial.print(iLongest); Serial.print(" ("); Serial.print(iNmax); Serial.println(")");
          bOutmodePWM = true;
          bOutmodeReg = false;
          bOutmodeRegRaw = false;
        }
        Serial.println();
        Serial.println();
        iQcount = 0;
        iShortest = 2147483647;
        iLongest = 0;
        ulAvarageTime = 0UL;
        iNmax = 0;
        iNmin = 0;
      } 

    }
  }
  else if(!bOutmodeRegRaw) {
    if( iQcount < iQueryN) 
    {
      if( !bOutmodeReg ) 
      {
        ulStartTime = micros();
        fAngle = as5600.readAngle();
      } else {
        ulStartTime = micros();
        fAngle = as5600.rawAngle() * AS5600_RAW_TO_DEGREES;
      }
      ulTempTime = (micros()-ulStartTime);

      if( ulTempTime > iLongest ) { iLongest = ulTempTime; iNmax++; }
      if( ulTempTime < iShortest) { iShortest = ulTempTime; iNmin++; }
      ulAvarageTime += ulTempTime;
      iQcount++;

      if( iQcount == iQueryN )
      {
        ulAvarageTime = ulAvarageTime/iQcount;

        Serial.println();
        Serial.println();
        if( !bOutmodeReg ) {
          bOutmodeReg = true;
          Serial.print("finished register read ANGLE timing, avarage query time: ");Serial.println(ulAvarageTime);
          Serial.print("Fastest: "); Serial.print(iShortest); Serial.print(" ("); Serial.print(iNmin); Serial.println(")"); 
          Serial.print("Slowest: "); Serial.print(iLongest); Serial.print(" ("); Serial.print(iNmax); Serial.println(")");
        }
        else if(!bOutmodeRegRaw) {
          bOutmodeRegRaw = true;
          Serial.print("finished register read RAW ANGLE timing, avarage query time: ");Serial.println(ulAvarageTime);
          Serial.print("Fastest: "); Serial.print(iShortest); Serial.print(" ("); Serial.print(iNmin); Serial.println(")"); 
          Serial.print("Slowest: "); Serial.print(iLongest); Serial.print(" ("); Serial.print(iNmax); Serial.println(")");
          bOutmodeAnal = false;
          bOutmodePWM = false;
          bHighFreq = false;
        }
        Serial.println();
        Serial.println();
        iQcount = 0;
        iShortest = 2147483647;
        iLongest = 0;
        ulAvarageTime = 0UL;
        iNmax = 0;
        iNmin = 0;
      } 
    }
  }
  delay(200);
}

After changing the setup part to this:

void setup()
{
  Serial.begin(460800);
  while(!Serial);
  Serial.println(__FILE__);
  Serial.print("AS5600_LIB_VERSION: ");
  Serial.println(AS5600_LIB_VERSION);
  //delay(1000);
  Wire.begin();
  //while(! Wire)  Serial.print("0");

  as5600.begin();  //  set direction pin or leave blank when tied directly to gnd or vcc
  Serial.print("Error?: \t"); Serial.println(as5600.lastError());

  b = as5600.isConnected();
  Serial.print("Error connected?: \t"); Serial.println(as5600.lastError());

  int c = as5600.getOutputMode();
  Serial.print("Error read?: \t"); Serial.println(as5600.lastError());

  as5600.setDirection(AS5600_CLOCK_WISE);  //  default, just be explicit. cw= dir->GND, ccw= dir->VCC
  Serial.print("Error dir?: \t"); Serial.println(as5600.lastError());

  as5600.setOutputMode(AS5600_OUTMODE_ANALOG_100);
  Serial.print("Error outmode?: \t"); Serial.println(as5600.lastError());
  
  b = as5600.isConnected();
  Serial.print("Connect: ");
  Serial.println(b);
  if( !b ) {
    Serial.print("Encountered error connecting to as5600: \t"); Serial.println(as5600.lastError());
  }

  if(as5600.detectMagnet())
  {
    Serial.print("Magnet detected, AGC: "); Serial.print(as5600.readAGC()); Serial.println("\t(Optimal value: 255/2 for 5V mcu's, 128/2 for 3V3 mcu's)");
  } else {
    if(as5600.magnetTooStrong())  Serial.println("Magnet to strong/close to sensor.");
    if(as5600.magnetTooWeak())  Serial.println("Magnet to weak/far from sensor.");
  }

  //////////////////////////////////////////////////////
  // Configs that can influence the pull/call times

  /*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;*/
  as5600.setPowerMode(0);

  /*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; */
  as5600.setHysteresis(2);

  /*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;*/
  as5600.setSlowFilter(2);

  /*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;*/
  as5600.setFastFilter(2);

  delay(1000);
}

The serial output is:

/*
AS5600_output_speedtest.ino
03:14:57.382 -> AS5600_LIB_VERSION: 0.6.1
03:14:57.428 -> Error?: 	0
03:14:57.428 -> Error connected?: 	0
03:14:57.428 -> Error read?: 	-100
03:14:57.428 -> Error dir?: 	0
03:14:57.428 -> Error outmode?: 	-200
03:14:57.428 -> Connect: 0
03:14:57.428 -> Encountered error connecting to as5600: 	0
03:14:58.424 -> [Analog] - 0 	220µs to retrieve angle: 206.22	angle in register: 0
*/

I quadriple checked my sda and scl connections and they are correct even switched them to the sda and scl connectors just above the AREF rail, same results with 5K resister from +5V to scl and another to sda. GPO pin is connected to +5V, DIR is connected to GND.

I found the problem I had to change 'AS5600L as5600;' to 'AS5600 as5600;' :face_with_hand_over_mouth:

Please, if you want use this sketch and add it to your library examples.
Don't hesitate to optimise or edit it how you see fit, i'm always keen to learn improvements.

//
//    FILE: AS5600_output_speedtest.ino
//  AUTHOR: Pollyscracker
// PURPOSE: demo/testing
//     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 <Arduino_BuiltIn.h>
#include <AS5600.h>
#include <Wire.h>


int iQueryN = 100;    // amount of times to request the angle per outputmode (minimum total process time per mode: iQueryN * delay(100)(at the end of the loop) 
int Directionpin = 4; // sensor input, connect to dir of the sensor
int Analogpin = A7;   // sensor output, connect to both pins
int PWMpin = 7;       // sensor output, connect to both pins
// Set false if your microcontroller or setup works with 12Bit ADC conversion
// for use with Analog output only
bool b10BitADC = true;



AS5600 as5600;   //  use default Wire

bool bOutmodeAnal = false;
bool bOutmodePWM = false;
bool bHighFreq = false;
bool bOutmodeReg = false;
bool bOutmodeRegRaw = false;
int iQcount = 0;
int iNmax = 0;
int iNmin = 0;
unsigned long ulStartTime = 0UL;
unsigned long ulTempTime = 0UL;
float fAngle = 0;
unsigned long ulAvarageTime = 0;
int iLongest = 0;
int iShortest = 2147483647;

int b = 0;

/*
//  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;
*/
void setup()
{
  Serial.begin(460800);
  while(!Serial);
  Serial.println(__FILE__);
  Serial.print("AS5600_LIB_VERSION: ");
  Serial.println(AS5600_LIB_VERSION);
  //delay(1000);
  Wire.begin();
  //while(! Wire)  Serial.print("0");

  as5600.begin();  //  set direction pin or leave blank when tied directly to gnd or vcc
  Serial.print("Error?:\t"); Serial.println(as5600.lastError());
  delay(10);
  b = as5600.isConnected();
  Serial.print("Connect: ");
  Serial.println(b);
  if( !b ) {
    Serial.print("Encountered error connecting to as5600: \t"); Serial.println(as5600.lastError());
  }
  as5600.setDirection(AS5600_CLOCK_WISE);  //  default, just be explicit. cw= dir->GND, ccw= dir->VCC
  as5600.setOutputMode(AS5600_OUTMODE_ANALOG_100);
  

  if(as5600.detectMagnet())
  {
    Serial.print("Magnet detected, AGC: "); Serial.print(as5600.readAGC()); Serial.println("\t(Optimal value: 255/2 for 5V mcu's, 128/2 for 3V3 mcu's)");
  } else {
    if(as5600.magnetTooStrong())  Serial.println("Magnet to strong/close to sensor.");
    if(as5600.magnetTooWeak())  Serial.println("Magnet to weak/far from sensor.");
  }

  //////////////////////////////////////////////////////
  // Configs that can influence the pull/call times

  /*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;*/
  as5600.setPowerMode(0);

  /*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; */
  as5600.setHysteresis(2);

  /*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;*/
  as5600.setSlowFilter(2);

  /*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;*/
  as5600.setFastFilter(2);

  delay(1000);
}

float measurePWMAngle()
{
  // 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);
  uint32_t fullPeriod = micros() - rise;

  float bitTime = fullPeriod / 4351.0;
  float dataPeriod = highPeriod - 128 * bitTime;
  float angle = 360.0 * dataPeriod / (4095 * bitTime);
  return angle;
}

float measureAnalAngle()
{
  float angle = 0.0;
  uint16_t rawangle = analogRead(Analogpin);
  if(b10BitADC) angle = 360.0/1023 * rawangle;
  else angle = 360.0/4095 * rawangle;
  return angle;
}

void loop()
{
  if( !bOutmodeAnal)
  {
    if(iQcount == 0)
    {
      as5600.setOutputMode(AS5600_OUTMODE_ANALOG_100);
      Serial.println("Starting analog timing..");
      delay(10);
    }
    if( iQcount < iQueryN) 
    {
      ulStartTime = micros();
      fAngle = measureAnalAngle();
      ulTempTime = (micros()-ulStartTime);

      // Uncomment to see angle results per request
      //Serial.print("[Analog] - "); Serial.print(iQcount); Serial.print("    \t"); Serial.print(ulTempTime); Serial.print("µs to retrieve angle: "); Serial.print(fAngle); Serial.print("  \tangle in register: "); Serial.println(as5600.readAngle()*AS5600_RAW_TO_DEGREES);

      if( ulTempTime > iLongest ) { iLongest = ulTempTime; iNmax++; }
      if( ulTempTime < iShortest) { iShortest = ulTempTime; iNmin++; }
      ulAvarageTime += ulTempTime;
      iQcount++;

      if( iQcount == iQueryN )
      {
        ulAvarageTime = ulAvarageTime/iQcount;

        
        Serial.println();
        Serial.print("finished ANALOG ANGLE timing, avarage query time(µs): ");Serial.println(ulAvarageTime);
        Serial.print("Fastest: "); Serial.print(iShortest); Serial.print(" ("); Serial.print(iNmin); Serial.println(")"); 
        Serial.print("Slowest: "); Serial.print(iLongest); Serial.print(" ("); Serial.print(iNmax); Serial.println(")"); 
        Serial.println();
        // Set output mode to PWM
        as5600.setOutputMode(AS5600_OUTMODE_PWM);
        as5600.setPWMFrequency(AS5600_PWM_115);
        bOutmodeAnal = true;
        bOutmodePWM = false;
        bHighFreq = false;
        iQcount = 0;
        iShortest = 2147483647;
        iLongest = 0;
        iNmax = 0;
        iNmin = 0;
        ulAvarageTime = 0UL;
      } 
    }
  }
  else if(!bOutmodePWM)
  {
    if(iQcount == 0)
    {
      if( !b )
      {
        Serial.print("Encountered error connecting to as5600: \t"); Serial.println(as5600.lastError());
        Serial.println("as5600 not connected");
        //as5600.begin();
        b = as5600.isConnected();
        Serial.print("Connect: ");
        Serial.println(b);
        if(!b)  Serial.print("Failed to establish connection(check adress), error: \t"); Serial.println(as5600.lastError());
      }
      // Set output mode to PWM
      as5600.setOutputMode(AS5600_OUTMODE_PWM);
      if( !bHighFreq ) {
        as5600.setPWMFrequency(AS5600_PWM_115);  
        Serial.println("Starting Low frequency(115) PWM timing..");
      }
      else  {
        as5600.setPWMFrequency(AS5600_PWM_920);
        Serial.println("Starting High frequency(920) PWM timing..");
      }
      delay(10);
    }
    if( iQcount < iQueryN) 
    {
      uint16_t preangle = as5600.readAngle();
      ulStartTime = micros();
      fAngle = measurePWMAngle();
      ulTempTime = (micros()-ulStartTime);

      // Uncomment to see angle results per request
      /*
      if(bHighFreq) {
        Serial.print("[PWM HIGH] - "); Serial.print(iQcount); Serial.print("  \t"); Serial.print(ulTempTime); Serial.print("µs to retrieve angle: "); Serial.print(fAngle); Serial.print("   \tPRE-call angle from register: "); Serial.print(preangle*AS5600_RAW_TO_DEGREES); Serial.print("\tPOST-call angle from register: "); Serial.println(as5600.readAngle()*AS5600_RAW_TO_DEGREES);
      }
      else {
        Serial.print("[PWM LOW] -  "); Serial.print(iQcount); Serial.print("  \t"); Serial.print(ulTempTime); Serial.print("µs to retrieve angle: "); Serial.print(fAngle); Serial.print("   \tPRE-call angle from register: "); Serial.print(preangle*AS5600_RAW_TO_DEGREES); Serial.print("\tPOST-call angle from register: "); Serial.println(as5600.readAngle()*AS5600_RAW_TO_DEGREES);
      }*/
      if( ulTempTime > iLongest ) { iLongest = ulTempTime; iNmax++; }
      if( ulTempTime < iShortest) { iShortest = ulTempTime; iNmin++; }
      ulAvarageTime += ulTempTime;
      iQcount++;

      if( iQcount == iQueryN )
      {
        ulAvarageTime = ulAvarageTime/iQcount;

        Serial.println();
        if( !bHighFreq ) {
          Serial.print("finished PWM ANGLE timing at lowest frequency (115Hz), avarage query time(µs): ");Serial.println(ulAvarageTime);
          Serial.print("Fastest: "); Serial.print(iShortest); Serial.print(" ("); Serial.print(iNmin); Serial.println(")"); 
          Serial.print("Slowest: "); Serial.print(iLongest); Serial.print(" ("); Serial.print(iNmax); Serial.println(")");
          bHighFreq = true;
        } else {
          Serial.print("finished PWM ANGLE timing at highest frequency (920Hz), avarage query time(µs): ");Serial.println(ulAvarageTime);
          Serial.print("Fastest: "); Serial.print(iShortest); Serial.print(" ("); Serial.print(iNmin); Serial.println(")"); 
          Serial.print("Slowest: "); Serial.print(iLongest); Serial.print(" ("); Serial.print(iNmax); Serial.println(")");
          bOutmodePWM = true;
          bOutmodeReg = false;
          bOutmodeRegRaw = false;
        }
        Serial.println();
        iQcount = 0;
        iShortest = 2147483647;
        iLongest = 0;
        ulAvarageTime = 0UL;
        iNmax = 0;
        iNmin = 0;
      } 

    }
  }
  else if(!bOutmodeRegRaw) {
    if( iQcount == 0) {
      if(!bOutmodeReg)  Serial.println("Starting read angle register timing..");
      else Serial.println("Starting read RAW angle register timing..");
    }
    if( iQcount < iQueryN) 
    {
      if( !bOutmodeReg ) 
      {
        ulStartTime = micros();
        fAngle = as5600.readAngle()* AS5600_RAW_TO_DEGREES;
      } else {
        ulStartTime = micros();
        fAngle = as5600.rawAngle() * AS5600_RAW_TO_DEGREES;
      }
      ulTempTime = (micros()-ulStartTime);

      // Uncomment to see angle results per request
      /*
      if(!bOutmodeReg) {
        Serial.print("[REGISTER] - "); Serial.print(iQcount); Serial.print(" \t"); Serial.print(ulTempTime); Serial.print("µs to retrieve angle: "); Serial.print(fAngle); Serial.print("   \tPOST-call angle from register: "); Serial.println(as5600.readAngle()*AS5600_RAW_TO_DEGREES);
      }
      else {
        Serial.print("[RAW REGISTER] - "); Serial.print(iQcount); Serial.print(" \t"); Serial.print(ulTempTime); Serial.print("µs to retrieve angle: "); Serial.print(fAngle); Serial.print("   \tPOST-call angle from register: "); Serial.println(as5600.readAngle()*AS5600_RAW_TO_DEGREES);
      }*/

      if( ulTempTime > iLongest ) { iLongest = ulTempTime; iNmax++; }
      if( ulTempTime < iShortest) { iShortest = ulTempTime; iNmin++; }
      ulAvarageTime += ulTempTime;
      iQcount++;

      if( iQcount == iQueryN )
      {
        ulAvarageTime = ulAvarageTime/iQcount;

        Serial.println();
        if( !bOutmodeReg ) {
          bOutmodeReg = true;
          Serial.print("finished register read ANGLE timing, avarage query time: ");Serial.println(ulAvarageTime);
          Serial.print("Fastest: "); Serial.print(iShortest); Serial.print(" ("); Serial.print(iNmin); Serial.println(")"); 
          Serial.print("Slowest: "); Serial.print(iLongest); Serial.print(" ("); Serial.print(iNmax); Serial.println(")");
        }
        else if(!bOutmodeRegRaw) {
          bOutmodeRegRaw = true;
          Serial.print("finished register read RAW ANGLE timing, avarage query time: ");Serial.println(ulAvarageTime);
          Serial.print("Fastest: "); Serial.print(iShortest); Serial.print(" ("); Serial.print(iNmin); Serial.println(")"); 
          Serial.print("Slowest: "); Serial.print(iLongest); Serial.print(" ("); Serial.print(iNmax); Serial.println(")");
          bOutmodeAnal = false;
          bOutmodePWM = false;
          bHighFreq = false;
        }
        Serial.println();
        iQcount = 0;
        iShortest = 2147483647;
        iLongest = 0;
        ulAvarageTime = 0UL;
        iNmax = 0;
        iNmin = 0;
      } 
    }
  }
  delay(100);
}

The serial output from a run with settings from above:

AS5600_output_speedtest.ino
05:27:00.311 -> AS5600_LIB_VERSION: 0.6.1
05:27:00.311 -> Error?:	0
05:27:00.355 -> Connect: 1
05:27:00.355 -> Magnet detected, AGC: 16	(Optimal value: 255/2 for 5V mcu's, 128/2 for 3V3 mcu's)
05:31:19.796 -> Starting analog timing..
05:31:29.748 -> 
05:31:29.748 -> finished ANALOG ANGLE timing, avarage query time(µs): 113
05:31:29.748 -> Fastest: 108 (2)
05:31:29.748 -> Slowest: 120 (2)
05:31:29.748 -> 
05:31:29.830 -> Starting Low frequency(115) PWM timing..
05:31:41.060 -> 
05:31:41.060 -> finished PWM ANGLE timing at lowest frequency (115Hz), avarage query time(µs): 12162
05:31:41.060 -> Fastest: 11632 (1)
05:31:41.060 -> Slowest: 12192 (3)
05:31:41.060 -> 
05:31:41.154 -> Starting High frequency(920) PWM timing..
05:31:51.276 -> 
05:31:51.276 -> finished PWM ANGLE timing at highest frequency (920Hz), avarage query time(µs): 1639
05:31:51.276 -> Fastest: 1412 (1)
05:31:51.276 -> Slowest: 1660 (6)
05:31:51.276 -> 
05:31:51.357 -> Starting read angle register timing..
05:32:01.321 -> 
05:32:01.321 -> finished register read ANGLE timing, avarage query time: 574
05:32:01.321 -> Fastest: 564 (4)
05:32:01.321 -> Slowest: 616 (1)
05:32:01.380 -> 
05:32:01.447 -> Starting read RAW angle register timing..
05:32:11.385 -> 
05:32:11.385 -> finished register read RAW ANGLE timing, avarage query time: 572
05:32:11.385 -> Fastest: 564 (4)
05:32:11.385 -> Slowest: 608 (1)
05:32:11.385 -> 
05:32:11.518 -> Starting analog timing..
1 Like

You found that one already,

@pollyscracker
I will add your sketch in my develop branch and when time permits dive into it.
It will pop up in the next (not planned any yet) release.

Thanks for your testing, appreciated!

1 Like

@pollyscracker

Had to patch some datatypes as Int is not 32 bit on all platforms.

Just get the code compiling on a 8 bit UNO and 32 bit ESP32 and get both without warnings is one of the simplest test to check this.

Added some minor edits (layout and naming).

ah ok, good to know.
I look forward to see your edits when it gets there, i already know what names you probably wanted to change :joy:

Thank you!