AS5040, AS5045, and interface code

For the AS5040 and AS5045 rotary encoder.

Re: AS5040, AS5045, and interface code

Postby navanod » Tue May 21, 2013 5:19 am

Hi all first time post here so firstly nice to meet you all.
Finally I finished putting together the V2 board and everything seems to be working nicely with the chip. Now I have to merge the angle reading code with my L298n code running over serial..@ Greg could you post the code that you used with the fastWrite.Lib? Im having issues trying to read serial data with the long delay..actually it might be even more un trivial than this but to get the reading faster may help
navanod
 
Posts: 2
Joined: Fri May 03, 2013 4:34 am

Re: AS5040, AS5045, and interface code

Postby navanod » Wed May 22, 2013 7:08 am

Having some trouble on how to figure out how to count rotations into a new variable that I can use to control the sensor over many rotations..any help would be highly appreciated
adam

Code: Select all
////////////////////// as5040
#include <PWM.h>

//AS5040 stuff
const int clockPin = 7; //output to clock
const int CSnPin = 6; //output to chip select
const int inputPin = 2; //read AS5040
int inputstream = 0; //one bit read from pin
long packeddata = 0; //two bytes concatenated from inputstream
long ReadEncoder = 0; //holds processed angle value
long anglemask = 65472; //0x1111111111000000: mask to obtain first 10 digits with position info
long statusmask = 63; //0x000000000111111; mask to obtain last 6 digits containing status info
long statusbits; //holds status/error information
int DECn; //bit holding decreasing magnet field error data
int INCn; //bit holding increasing magnet field error data
int OCF; //bit holding startup-valid bit
int COF; //bit holding cordic DSP processing error data
int LIN; //bit holding magnet field displacement error data
int debug = 0; //SET THIS TO 0 TO DISABLE PRINTING OF ERROR CODES
int shortdelay = 1; // this is the microseconds of delay in the data clock
int longdelay = 100;
//int rotations = 0;  //how many times have I spun around
//int overallpostion = 0;  //count rotations plus postions into a new value
//int global_counter = 0;
int PreviousEncoder = 0;
int Position = 0;
int Encoder = 0;
int difference = 0;
int Globaladd = 0;
long GlobalPosition = 0;
// Serial for VVVV
String readString;

//PWM fast
int ENA = 9;//fast PWM
int IN1 = 12;
int IN2 = 13;
int32_t frequency = 16000; //frequency (in Hz)


void setup_sv4()
{
  Serial.begin(115200);
  InitTimersSafe();
  bool success = SetPinFrequencySafe(ENA, frequency);
  pinMode (ENA, OUTPUT);
  pinMode (IN1, OUTPUT);
  pinMode (IN2, OUTPUT);
  //Serial.println("V4_Arduino_L298N_AS5040");//track what I have loaded
 
}

void setup()
{
  Serial.begin(9600);
  pinMode(clockPin, OUTPUT); // SCK
  pinMode(CSnPin, OUTPUT); // CSn -- has to toggle high and low to signal chip to start data transfer
  pinMode(inputPin, INPUT); // SDA
 
  setup_sv4();
}
void loop_sv4() {

  if (Serial.available()) 
  {
    char c = Serial.read();  //gets one byte from serial buffer
    if (c == 'a')
   {
      if (readString.length() >0)
      {
         int SerialInput = readString.toInt();  //convert readString into a number
                        int Xaxis = ReadEncoder;//magnet position
                     //   Serial.print("global_counter: "); // and, finally, print it.
                       // Serial.println(global_counter, DEC);
                        int DiffLeft = SerialInput - Xaxis;
                        int DiffRight = Xaxis - SerialInput;
                       
                        if (SerialInput > Xaxis)
                                            {
                                             digitalWrite (IN1, HIGH);
                                             digitalWrite (IN2, LOW);
                                             int Lspd = map(DiffLeft, 0, 1023, 404, 490);
                                             pwmWrite  (ENA, (Lspd) );
                                           //  Serial.println(DiffLeft, DEC);
                                             }
                       
                        else
                        {
                        digitalWrite (IN1, LOW);
                        digitalWrite (IN2, HIGH);
                        int Rspd = map(DiffRight, 0, 1023, 404, 490);
                        pwmWrite  (ENA, (Rspd) );
                        readString=""; //clears variable for new input
                      //  Serial.println(DiffRight, DEC);
                       
                        }
                       // Serial.println(ReadEncoder, DEC);
                       
                         
      }
   } 
    else if (c == 'b')
   {

      if (readString.length() >0)
      {
         int n = readString.toInt();  //convert readString into a number
         readString=""; //clears variable for new input
      }
   }     
    else if (c == 'c')
   {

      if (readString.length() >0)
      {
         int n = readString.toInt();  //convert readString into a number
         readString=""; //clears variable for new input
      }
   }     
   else
   {     
      /// be sure this value is >= '0' && <= '9'
      readString += c; //makes the string readString
   }
  }
}



void loop()
{
  loop_sv4();
// CSn needs to cycle from high to low to initiate transfer. Then clock cycles. As it goes high
// again, data will appear on sda
  digitalWrite(CSnPin, HIGH); // CSn high
  digitalWrite(clockPin, HIGH); // CLK high
  delay(shortdelay);// time between readings
  digitalWrite(CSnPin, LOW); // CSn low: start of transfer
  delayMicroseconds(shortdelay); // delay for chip initialization
  digitalWrite(clockPin, LOW); // CLK goes low: start clocking
  delayMicroseconds(shortdelay); // hold low
  for (int x=0; x <16; x++) // clock signal, 16 transitions, output to clock pin
  {
    digitalWrite(clockPin, HIGH); //clock goes high
    delayMicroseconds(shortdelay); //
    inputstream =digitalRead(inputPin); // read one bit of data from pin
    packeddata = ((packeddata << 1) + inputstream);// left-shift summing variable, add pin value
    digitalWrite(clockPin, LOW);
    delayMicroseconds(shortdelay); // end of one clock cycle
  }

  ReadEncoder = packeddata & anglemask; // mask rightmost 6 digits of packeddata to zero, into angle.
  ReadEncoder = (ReadEncoder >> 6); // shift 16-digit angle right 6 digits to form 10-digit value
 
  Position = ReadEncoder;
  PreviousEncoder = Position;
  Encoder = ReadEncoder;
  difference = Position - PreviousEncoder;
 
             if (difference == 0)
             {
             Globaladd = 0;}
             else if (difference < 512){
             Globaladd = difference - 1024;}
             else if (difference > 512){
             Globaladd = difference + 1024;}
             
 GlobalPosition = GlobalPosition + Globaladd;
                                     
 //Serial.print("difference: "); // and, finally, print it.
 //Serial.println(difference, DEC);
 
// Serial.print("GlobalPosition: "); // and, finally, print it.
 Serial.println(GlobalPosition, DEC);

 // angle = angle * 0.3515; // angle * (360/1024) == actual degrees
  //Serial.print("angle: "); // and, finally, print it.
  //Serial.println(angle, DEC);

  if (debug)
  {
    statusbits = packeddata & statusmask;
    DECn = statusbits & 2; // goes high if magnet moved away from IC
    INCn = statusbits & 4; // goes high if magnet moved towards IC
    LIN = statusbits & 8; // goes high for linearity alarm
    COF = statusbits & 16; // goes high for cordic overflow: data invalid
    OCF = statusbits & 32; // this is 1 when the chip startup is finished.
    if (DECn && INCn) { Serial.println("magnet moved out of range"); }
    else
    {
      if (DECn) { Serial.println("magnet moved away from chip"); }
      if (INCn) { Serial.println("magnet moved towards chip"); }
    }
    if (LIN) { Serial.println("linearity alarm: magnet misaligned? Data questionable."); }
    if (COF) { Serial.println("cordic overflow: magnet misaligned? Data invalid."); }
  }

  packeddata = 0; // reset both variables to zero so they don't just accumulate
  //angle = 0;
 
 
}

navanod
 
Posts: 2
Joined: Fri May 03, 2013 4:34 am

Re: AS5040, AS5045 fast interface

Postby Tom_G_2010 » Thu Feb 04, 2016 2:53 pm

I'm using a 5045 but missed the fact that the code below was 5040 specific. However, it seems to be working for me. I'll make the changes though just to make sure all is working correctly.

The primary reason for my post though is to ask what changes might be made to the code to get one decimal place of precision for the angle. I am presently getting just the integer value but for my project and need to go to tenths of a degree. Unless of course the 5040 vs 5045 changes that I missed are at fault. Is it possible to modify the below code to get the additional precision? I looked over the code and it's a bit more complex than my current understanding so I wasn't able to work out the changes myself.

Thanks!
Tom G.

john wrote:Here's a version that's a lot faster on updates. I pretty much just changed all the short delays to microseconds and left the longer delays as milliseconds. This will report 90 samples a second or thereabouts. If you want to play with making it faster, just start decreasing longdelay (but probably not less than 1) and shortdelay (but probably not less than 40, because of the Arduino's clock rate.)
Note that because of some issues with word length, the AS5040 works both straight and in debug mode (where it displays errors like the magnet being too far away) while the AS5045 works fine when it's not in debug mode but in debug mode gives the right angle and the right error codes but detects an error with every reading, even if there isn't actually an error. I haven't gotten around to fixing that, since it does work in normal mode.
This is the version for the 5040. If you want to use the 5045 instead you need to change the anglemask value, make x go to < 18, and a couple other minor changes that should be obvious from the previously posted '45 code.


const int ledPin = 13; //LED connected to digital pin 13
const int clockPin = 7; //output to clock
const int CSnPin = 6; //output to chip select
const int inputPin = 2; //read AS5040

int inputstream = 0; //one bit read from pin
long packeddata = 0; //two bytes concatenated from inputstream
long angle = 0; //holds processed angle value
long anglemask = 65472; //0x1111111111000000: mask to obtain first 10 digits with position info
long statusmask = 63; //0x000000000111111; mask to obtain last 6 digits containing status info
long statusbits; //holds status/error information
int DECn; //bit holding decreasing magnet field error data
int INCn; //bit holding increasing magnet field error data
int OCF; //bit holding startup-valid bit
int COF; //bit holding cordic DSP processing error data
int LIN; //bit holding magnet field displacement error data
int debug = 0; //SET THIS TO 0 TO DISABLE PRINTING OF ERROR CODES
int shortdelay = 100; // this is the microseconds of delay in the data clock
int longdelay = 10; // this is the milliseconds between readings

void setup()
{
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT); // visual signal of I/O to chip
  pinMode(clockPin, OUTPUT); // SCK
  pinMode(CSnPin, OUTPUT); // CSn -- has to toggle high and low to signal chip to start data transfer
  pinMode(inputPin, INPUT); // SDA
}

void loop()
{
// CSn needs to cycle from high to low to initiate transfer. Then clock cycles. As it goes high
// again, data will appear on sda
  digitalWrite(CSnPin, HIGH); // CSn high
  digitalWrite(clockPin, HIGH); // CLK high
  delay(longdelay);// time between readings
  digitalWrite(ledPin, HIGH); // signal start of transfer with LED
  digitalWrite(CSnPin, LOW); // CSn low: start of transfer
  delayMicroseconds(shortdelay); // delay for chip initialization
  digitalWrite(clockPin, LOW); // CLK goes low: start clocking
  delayMicroseconds(shortdelay); // hold low
  for (int x=0; x <16; x++) // clock signal, 16 transitions, output to clock pin
  {
    digitalWrite(clockPin, HIGH); //clock goes high
    delayMicroseconds(shortdelay); //
    inputstream =digitalRead(inputPin); // read one bit of data from pin
//Serial.print(inputstream, DEC);
    packeddata = ((packeddata << 1) + inputstream);// left-shift summing variable, add pin value
    digitalWrite(clockPin, LOW);
    delayMicroseconds(shortdelay); // end of one clock cycle
  }
// end of entire clock cycle
//Serial.println(" ");
  digitalWrite(ledPin, LOW); // signal end of transmission
// lots of diagnostics for verifying bitwise operations
//Serial.print("packed:");
//Serial.println(packeddata,DEC);
//Serial.print("pack bin: ");
//Serial.println(packeddata,BIN);
  angle = packeddata & anglemask; // mask rightmost 6 digits of packeddata to zero, into angle.
//Serial.print("mask: ");
//Serial.println(anglemask, BIN);
//Serial.print("bin angle:");
//Serial.println(angle, BIN);
//Serial.print("angle: ");
//Serial.println(angle, DEC);
  angle = (angle >> 6); // shift 16-digit angle right 6 digits to form 10-digit value
//Serial.print("angleshft:");
//Serial.println(angle, BIN);
//Serial.print("angledec: ");
//Serial.println(angle, DEC);
  angle = angle * 0.3515; // angle * (360/1024) == actual degrees
  Serial.print("angle: "); // and, finally, print it.
  Serial.println(angle, DEC);
//Serial.println("--------------------");
//Serial.print("raw: "); // this was the prefix for the bit-by-bit diag output inside the loop.
  if (debug)
  {
    statusbits = packeddata & statusmask;
    DECn = statusbits & 2; // goes high if magnet moved away from IC
    INCn = statusbits & 4; // goes high if magnet moved towards IC
    LIN = statusbits & 8; // goes high for linearity alarm
    COF = statusbits & 16; // goes high for cordic overflow: data invalid
    OCF = statusbits & 32; // this is 1 when the chip startup is finished.
    if (DECn && INCn) { Serial.println("magnet moved out of range"); }
    else
    {
      if (DECn) { Serial.println("magnet moved away from chip"); }
      if (INCn) { Serial.println("magnet moved towards chip"); }
    }
    if (LIN) { Serial.println("linearity alarm: magnet misaligned? Data questionable."); }
    if (COF) { Serial.println("cordic overflow: magnet misaligned? Data invalid."); }
  }

  packeddata = 0; // reset both variables to zero so they don't just accumulate
  angle = 0;
}

Tom_G_2010
 
Posts: 2
Joined: Sun Jan 31, 2016 9:43 pm

Re: AS5040, AS5045, and interface code

Postby john » Fri Feb 05, 2016 9:30 pm

I haven't checked in here in far too long, obviously.
I posted this on the blog but forgot to post it on the relevant forum topic. I rewrote the code as an Arduino library. The code's here:
https://github.com/smellsofbikes/AS5045_arduino_library
So if you make a directory in your arduino libraries folder, called AS5045 (in this case), and dump all those files in it, you have access to the library version of the code.
What this gives you:
You can declare a chip with
encoder1 AS5045(4,5,6) -- where the numbers are the three pins used for the clock, data, and chip select functions
The IDE will nicely color-code the functions you call
AND!
you can just call some procedures I wrote to get degrees, raw sensor values, or error values.
So: once you've declared an encoder as above, you can call:
degrees = encoder1.encoder_degrees();
or if you want the max resolution the chip supports, the raw value (0-255 for the '40 and 0-1023 for the '45)
value = encoder1.encoder_value();

And that's all the code you have to use to get values out: two lines. The library handles all the bit-banging and delay times.
john
Site Admin
 
Posts: 7
Joined: Tue Sep 21, 2010 9:20 am

Re: AS5040, AS5045, and interface code

Postby Tom_G_2010 » Sat Feb 06, 2016 8:02 am

john wrote:I haven't checked in here in far too long, obviously.
I posted this on the blog but forgot to post it on the relevant forum topic. I rewrote the code as an Arduino library. The code's here:
https://github.com/smellsofbikes/AS5045_arduino_library
So if you make a directory in your arduino libraries folder, called AS5045 (in this case), and dump all those files in it, you have access to the library version of the code.
What this gives you:
You can declare a chip with
encoder1 AS5045(4,5,6) -- where the numbers are the three pins used for the clock, data, and chip select functions
The IDE will nicely color-code the functions you call
AND!
you can just call some procedures I wrote to get degrees, raw sensor values, or error values.
So: once you've declared an encoder as above, you can call:
degrees = encoder1.encoder_degrees();
or if you want the max resolution the chip supports, the raw value (0-255 for the '40 and 0-1023 for the '45)
value = encoder1.encoder_value();

And that's all the code you have to use to get values out: two lines. The library handles all the bit-banging and delay times.


Hi, Thanks for the reply.

I do have the library but I was having trouble using it. I am using the AS5045 to control a continuous rotation servo and found that at the speed the servo was roatating I wasn't getting fast enough reads using the library. When I switched to using the raw code it works great. Maybe I was doing something else wrong???

An angular value is sent to my Arduino from a pc to tell the servo where to move to. at present it's 0 to 359 but (to be changed to 0.0 to 359.9) and I use a while statement to activate the servo until the sensor returns a matching value.

Code: Select all
    while (pcAngle != servoAngle) {
      myServo.write(120);
      servoAngle = encoder1.encoder_degrees();
    }
    myServo.write(90);


With the library I was getting reads about every 2 to 3 degrees si it might take multiple rotations before the servo would stop at the requested position.
When I created a function using the raw code you posted and called that function I had no trouble getting fast enough reads.

If the library should be as fast as the raw code I guess I need to figure out what I'm doing wrong. But, either way I need to ultimately get 0.1 degree resolution.

Again, thanks for the reply! Any thoughts you have on this would be greatly appreciated.

Tom G.
Tom_G_2010
 
Posts: 2
Joined: Sun Jan 31, 2016 9:43 pm

Previous

Return to 10/12Bit Rotary Encoder

Who is online

Users browsing this forum: Bing [Bot] and 2 guests

cron