The AS5048A Magnetic Rotary Encoder reading data with Arduino. HOWTO.

The AS5048A is an easy to use 360° angle position sensor (absolute encoder) with a 14-bit high resolution output and SPI interface. The maximum system accuracy is 0.05° assuming linearization and averaging is done by the external microcontroller.

Please use Diametrically Magnetized Magnets.
like http://www.amazon.com/Neodymium-Magnets-Disc-Diametrically-Magnetized/dp/B0089FFO68/ref=sr_1_2?ie=UTF8&qid=1361910412&sr=8-2&keywords=Diametrically+magnet
or http://www.ams.com/eng/Products/Magnetic-Position-Sensors/Magnets3/AS5000-MD6H-3

Tested code:

#include "SPI.h"
  unsigned int result = 0;
  unsigned int result1 = 0;
  unsigned int result2 = 0;

void setup ()
{
Serial.begin(9600);
SPI.begin();
SPI.setBitOrder(MSBFIRST);
}

void loop () {

digitalWrite(SS, LOW);

  //Reading 8 bit frame (the first half of 16-bit SPI transfer)
  result1 = SPI.transfer(0b00000000);
  Serial.print("byte 1: ");
  Serial.println(result1, BIN);
  
  // removing (masking) first 2 bit
  result1 &= 0b00111111;
  Serial.print("byte 1 masked: ");
  Serial.println(result1, BIN);
  
  //shifting 8 bit to left. to create emty space for last 8 bit transfer
  result1 = result1 << 8;
  Serial.print("byte 1 shifted: ");
  Serial.println(result, BIN);
  
  // getting last 8 bits (the last half of 16-bit SPI transfer)
  result2 = SPI.transfer(0b00000000);
  Serial.print("byte 2: ");
  Serial.println(result2, BIN);
  
  // merging
  result = result1 | result2;
  Serial.print("Result: ");
  Serial.print(result, BIN);
  Serial.print(" = ");
  Serial.println(result, DEC);
  Serial.println();
    
digitalWrite(SS, HIGH);
delay(1000);

}

Serial output:

byte 1: 10110110
byte 1 masking: 110110
byte 1 shifted: 11011000000000
byte 2: 111010
Result: 11011000111010 = 13882

But,
My Result data(when I rotate magnet in 360° angle) in range between 8192 - 16384 it's exactly half of 14bit

Why Result data is not from 0 - 16384 ???

Thank you!

I have the same problem with data range. Only 8192 - 16383 values are available.
Does someone knows where is the problem?

Second question is about speed measurment. I'm looking for a solution, to calculate speed of motor using this encoder.
But during rotation, I'm receiving values from 11966 to 13761. Is it normal?

I'm a bit worried that you hold the SPI bus open for vast amounts of time while sending Serial
data - I would always do the SPI transaction at fullspeed and only then unpack it...

void loop () {

  digitalWrite(SS, LOW);
  result1 = SPI.transfer(0);
  result2 = SPI.transfer(0);
  digitalWrite(SS, HIGH);    // release SPI device.

  // now you can spend time unpacking the results
}

But more importantly you need to set the right SPI mode, this chip uses falling clock edge,
not the more usual rising edge.

[ looks like you need:

  SPI.setDataMode (SPI_MODE1) ;

Accidently I set up MODE2 instead of MODE1. Now value range is OK.

But I'm still not able to calculate speed of motor. Is it even possible?
I'm reading angle once per (for example) 6000 microseconds.
I'm reading 2 positions difference and treat them as a speed (don't care about unit, only value is important).
But I'm receiving almost only noise. For example: differences are smaller, when motor is spinning at higher speed.

Motor is spinning, more or less, at 8000RPM.

Do someone hava an idea, how to calculate speed?

differences are smaller, when motor is spinning at higher speed.

Is that not what you would expect?
You have to make sure that your two samples do not involve the wrap round value or if it does compensate for it.

Sorry, I couldn't express myself.
In attachmet, there is a plot of given value (above) and speed feedback from encoder.

At first, it is making one spin, which is visible on plot below. Engine is hold for a moment. Then, there is an increasing value of speed, and after few second, speed is decreasing (basing on received values).
I'm calculating this speed as current_angle - previous_angle. Nothing else.

There is no way for motor to make full round in 6000microseconds.

Grumpy_Mike:

differences are smaller, when motor is spinning at higher speed.

Is that not what you would expect?

No. If motor is spinning faster, it should rotate much more in given time, so the angle differences should be bigger.

600.pdf (141 KB)

I also tried to connect the AS5048A with an Arduino UNO. For the first try i used the code from qubit. But i have the problem that i just receive 0 or 11111111 for result1 and result2. I tryed to fix the problem by studying the data sheet - but had no success.

I aslo connected the PWM-pin of the AS5048A to a capacitor so that i can meassure the voltage with an multimeter. That worked perfect. So the chip and the magnet position should be ok. For good measure i tried another AS5048A chip - same result - correct signal at pwm-pin but i receive just 0 or 11111111.

Here is my actual code, which is very similar to the code from qubit:

#include "SPI.h"

#define SPI_CMD_READ   0x4000  // read command
#define SPI_REG_AGC    0x3ffd  /* agc register
                                  Diagnostics flags */
#define SPI_REG_MAG    0x3ffe  /* magnitude Register
                                  Magnitude information after ATAN calculation */
#define SPI_REG_DATA   0x3fff  /* data register
                                  Angle information after ATAN calculation
                                  and zero position adder */
#define SPI_REG_CLRERR 0x0001  /* clear error register
                                  All errors are cleared by access */

word result;
unsigned int HighByte = 0;
unsigned int LowByte = 0;

int ss_own = 6;  // CSn is connected to PIN 6

void setup ()    {
  Serial.begin(9600);
  pinMode(ss_own, OUTPUT);  
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode (SPI_MODE1) ;
}

void loop () {
  digitalWrite(ss_own, LOW);
  HighByte = SPI.transfer(0x00);
  LowByte = SPI.transfer(0x00);
  digitalWrite(ss_own, HIGH);    // release SPI device.
  
  
  // masking
  HighByte &= 0b00111111;
  
  Serial.print("HighByte = "); 
  Serial.println(HighByte,BIN);
  Serial.print("LowByte = "); 
  Serial.println(LowByte,BIN);
  
  // merging
  result = word(HighByte,LowByte);
  Serial.println(result, DEC);
  
  delay(1000);
    
}

Here is an example of the output on serial monitor:

...
HighByte = 0
LowByte = 0
0
HighByte = 0
LowByte = 0
0
HighByte = 0
LowByte = 0
0
HighByte = 11111111
LowByte = 11111111
65535
HighByte = 11111111
LowByte = 11111111
65535
...

As other members suggested i tried to set DataMode to SPI_MODE1, SPI_MODE2 and SPI_MODE3.

Information to the circuit:
using 5V supply voltage from arduino (with capacitors according to data sheet)
CSn -> Port 6
CLK -> Port 13
MISO -> Port 12
MOSI -> Port 11 (which i dont really need?)

This is the first time that i use SPI connection, which is the problem i think. I've tried to fix it for nearly six hours but cant figure out the problem.

chrisi1656:
I also tried to connect the AS5048A with an Arduino UNO. For the first try i used the code from qubit. But i have the problem that i just receive 0 or 11111111 for result1 and result2. I tryed to fix the problem by studying the data sheet - but had no success.

I aslo connected the PWM-pin of the AS5048A to a capacitor so that i can meassure the voltage with an multimeter. That worked perfect. So the chip and the magnet position should be ok. For good measure i tried another AS5048A chip - same result - correct signal at pwm-pin but i receive just 0 or 11111111.

Here is my actual code, which is very similar to the code from qubit:

#include "SPI.h"

#define SPI_CMD_READ   0x4000  // read command
#define SPI_REG_AGC    0x3ffd  /* agc register
                                  Diagnostics flags /
#define SPI_REG_MAG    0x3ffe  /
magnitude Register
                                  Magnitude information after ATAN calculation /
#define SPI_REG_DATA   0x3fff  /
data register
                                  Angle information after ATAN calculation
                                  and zero position adder /
#define SPI_REG_CLRERR 0x0001  /
clear error register
                                  All errors are cleared by access */

word result;
unsigned int HighByte = 0;
unsigned int LowByte = 0;

int ss_own = 6;  // CSn is connected to PIN 6

void setup ()    {
  Serial.begin(9600);
  pinMode(ss_own, OUTPUT); 
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode (SPI_MODE1) ;
}

void loop () {
  digitalWrite(ss_own, LOW);
  HighByte = SPI.transfer(0x00);
  LowByte = SPI.transfer(0x00);
  digitalWrite(ss_own, HIGH);    // release SPI device.
 
 
  // masking
  HighByte &= 0b00111111;
 
  Serial.print("HighByte = ");
  Serial.println(HighByte,BIN);
  Serial.print("LowByte = ");
  Serial.println(LowByte,BIN);
 
  // merging
  result = word(HighByte,LowByte);
  Serial.println(result, DEC);
 
  delay(1000);
   
}




Here is an example of the output on serial monitor:


...
HighByte = 0
LowByte = 0
0
HighByte = 0
LowByte = 0
0
HighByte = 0
LowByte = 0
0
HighByte = 11111111
LowByte = 11111111
65535
HighByte = 11111111
LowByte = 11111111
65535
...




As other members suggested i tried to set DataMode to SPI_MODE1, SPI_MODE2 and SPI_MODE3.

Information to the circuit:
using 5V supply voltage from arduino (with capacitors according to data sheet)
CSn -> Port 6
CLK -> Port 13
MISO -> Port 12
MOSI -> Port 11 (which i dont really need?)

This is the first time that i use SPI connection, which is the problem i think. I've tried to fix it for nearly six hours but cant figure out the problem.

I tried the one of qubit in the begining, with 3-wire setup, 3.3V from arduino and it didnt work correctly, the ERROR FLAG was ON all the time. Then I changed to EXTERNAL power supply and it worked perfectly without any EF, you can try to see if it works

Dear All i Tried to read angle data on serial monitor on arduino uno by below code but it is not working. Please suggest.
connection. PWM is perfect. supply is external from SMPS
AS5048 Ardiuno uo
Chip select 1 10
clock 2 13
MISO 3 12
MOSI 4 +5Vdc

#include "SPI.h"
unsigned int result = 0;
unsigned int result1 = 0;
unsigned int result2 = 0;

void setup ()
{
Serial.begin(9600);
SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode (SPI_MODE1) ;
}

void loop () {

digitalWrite(SS, LOW);

//Reading 8 bit frame (the first half of 16-bit SPI transfer)
result1 = SPI.transfer(0b00000000);
Serial.print("byte 1: ");
Serial.println(result1, BIN);

// removing (masking) first 2 bit
result1 &= 0b00111111;
Serial.print("byte 1 masked: ");
Serial.println(result1, BIN);

//shifting 8 bit to left. to create emty space for last 8 bit transfer
result1 = result1 << 8;
Serial.print("byte 1 shifted: ");
Serial.println(result, BIN);

// getting last 8 bits (the last half of 16-bit SPI transfer)
result2 = SPI.transfer(0b00000000);
Serial.print("byte 2: ");
Serial.println(result2, BIN);

// merging
result = result1 | result2;
Serial.print("Result: ");
Serial.print(result, BIN);
Serial.print(" = ");
Serial.println(result, DEC);
Serial.println();

digitalWrite(SS, HIGH);
delay(1000);

}

If that chip is anything like the AS5040 then you have to operate the various
signals in exactly the right way to get it to work - I have a little library for the
AS5040 that allows setting the mode and then accessing the position on serial,
which might perhaps be useful here (I haven't read the AS5048 datasheet though).

Simple angle measurement - #21 by MarkT - Sensors - Arduino Forum posting #20

If that chip is anything like the AS5040 then ......

I had a look at the data sheets this morning and it is not.

Sir this is new chip. Minimum clock period is 10MHZ as per datasheet which is too high.
Yes this is different chip than AS5040. If anybody has decoded please provide.

Minimum clock period is 10MHZ as per datasheet which is too high.

NO!
Minimum clock period is 100nS, this translates into the MAXIMUM clock frequency of 10MHz.

Observations:

a) Why mask out the error or fault bit? If it really is useless, the chip designer would have not included it!

b) Bitwise OR (|) will work to add the LSB to the MSB that was just shifted left, but it really isn't logical. Simply add them together.

c) Word(msb,lsb) I believe uses two bytes not two integers to create a Word (integer)?

a) Why mask out the error or fault bit?

Because once you have set it up and know it is working then you might not be interested in them. Only look at error bits if you have something useful to do with them.

b) Bitwise OR (|) will work to add the LSB to the MSB that was just shifted left, but it really isn't logical.

Yes it is a logic operation you are blending two bytes together to make an 16 bit word. It is the addition operation that only works by fluke, because there will be no carry over. In machine code if you were to use an add operation instead of an OR operation you would have to make sure the carry bit were cleared to avoid a false result.

c) Word(msb,lsb) I believe uses two bytes not two integers to create a Word (integer)?

The function reduces the two parameters passed to it into bytes so it does not matter. Passing it only one parameter is a bit useless.

Apart from this I agree 100% with you. :slight_smile:

a) Why mask out the error or fault bit?
Because once you have set it up and know it is working then you might not be interested in them. Only look at error bits if you have something useful to do with them.

I really don't understand why anyone would ignore the error codes.
A line or two of code to check for error(s) before proceeding seems like the 'right thing to do' in any case.

I really don't understand why anyone would ignore the error codes.

Because you have to have something to do if you get an error, some action that will mitigate the error or deal with it in some way.
Depending on the application this might be more disruptive than the error itself.
That is to throw away the whole reading rather than live with any error in that reading might be a worst strategy. It entirelly depends on the context.
If you can't see that then you haven done enough projects.

Also you do not want those error bits in the actual reading, so you have to take them out anyway.

If you can't see that then you haven done enough projects.

Also you do not want those error bits in the actual reading, so you have to take them out anyway.

Have done many projects, some of which ran for years without intervention.

Well of course you deal with the error bits, log an error, post a text message and depending on what type of error handle it appropriately.

Otherwise I agree with you 100% :stuck_out_tongue:

Have done many projects,

Done any with a rotary shaft encoder.
My project is in:-

:stuck_out_tongue: :stuck_out_tongue: :stuck_out_tongue: