AMT203 Absolute encoder and Teensy 3.1

Status
Not open for further replies.

Scott Atta

New member
I've used this code on an Arduino UNO and it works perfectly and outputs a 12 bit value from 0-4095 via serial.print.
When I load it into my Teensy 3.1 I get a erratic stream of values, see screenshot.
Screen Shot 2015-04-13 at 10.47.05 PM.png
Rotating the Encoder position doesn't affect the Serial.print values.

CUI Amt203 datasheet.


Can someone please point out what I'm doing wrong???

#include <SPI.h>

const int CS = 15; //Chip or Slave select

uint16_t ABSposition = 0;
uint16_t ABSposition_last = 0;
uint8_t temp[2];



void setup()
{
pinMode(CS,OUTPUT);//Slave Select
digitalWrite(CS,HIGH);
SPI.begin();



Serial.begin(9600);
delay(1000);
Serial.println("starting");
}

uint8_t SPI_T (uint8_t msg) //Repetive SPI transmit sequence
{
uint8_t msg_temp = 0; //vairable to hold recieved data
digitalWrite(CS,LOW); //select spi device
msg_temp = SPI.transfer(msg); //send and recieve
digitalWrite(CS,HIGH); //deselect spi device
return(msg_temp); //return recieved byte
}

void loop()
{
uint8_t recieved = 0xA5; //just a temp vairable
ABSposition = 0; //reset position vairable

SPI.begin(); //start transmition
digitalWrite(CS,LOW);

SPI_T(0x10); //issue read command

recieved = SPI_T(0x00); //issue NOP to check if encoder is ready to send

while (recieved != 0x10) //loop while encoder is not ready to send
{
recieved = SPI_T(0x00); //check again if encoder is still working
delay(20); //wait a bit
}

temp[0] = SPI_T(0x00); //Recieve MSB
temp[1] = SPI_T(0x00); // recieve LSB

digitalWrite(CS,HIGH); //just to make sure
SPI.end(); //end transmition

temp[0] &=~ 0xF0; //mask out the first 4 bits

ABSposition = temp[0] << 8; //shift MSB to correct ABSposition in ABSposition message
ABSposition += temp[1]; // add LSB to ABSposition message to complete message

if (ABSposition != ABSposition_last) //if nothing has changed dont wast time sending position
{
ABSposition_last = ABSposition; //set last position to current position

Serial.println(ABSposition); //send position in degrees
}

delay(10); //wait a bit till next check

}
 

Attachments

  • AMT203_encoder_test_teensy.ino
    1.8 KB · Views: 258
Last edited:
Did you ever figure this out? Found your message today after traveling the same path from the same arduino script from elsewhere. UNO works perfect, Teensy not so much. I've been able to get garbage data returned by utilizing SPI.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE0)); down in SPI_T() to set the clock speed. But like you, I get repeating readings but ABSposition is either 0,1, or 165. Rotating the encoder doesn't change the return. and there doesn't seem to be a pattern to the returns either. If I go to 4MHz I get an additional 16 thrown into the loop.

Using T3.2 with a sparkfun level convertor https://www.sparkfun.com/products/12009 sometimes. Doesn't seem to change anything in or out of the loop though.
 
OK, so I spent probably too much time on trying to get Teensy 3.2 to talk to this sensor. Fail fast and often. Time/money = use cheap Arduino as an intermediary since it works with the sensor. This allowed me to change the SPI 'sensor' into I2C comms and save a few wires in the process on the Teensy side. Kludged this through an Arduino Pro Mini 5V to the Teensy... my circuit is all the SPI and 5V/GND from the mini to the AMT203, then I2C from A4/A5 I2C on the mini to Teensy. I eventually picked up the 1per Z pin as an interrupt for my project as well. Make sure to have a ground going between Arduino and Teensy! I found the easytransfer library (and all it's limitations) and put it to work. http://www.billporter.info/2011/05/30/easytransfer-arduino-library/

These bits of code will get the absolute position counter and an error check to the Teensy. All the deg/RPM etc math needs to happen on the Teensy. I wish I could do all of that in the Arduino to make the timing/calculations more honest, but I can't figure out how to pass a float or unsigned long through easytransfer. Since the AMT203 doesn't timestamp it's output, I'm not sure that it will matter much realistically unless you are doing several RPM.

Yes, I put a lot of notes in my code... hopefully I never have to see this again, but more then likely it will need changes in 5 years and I don't want to relearn it.

Arduino Mini Pro:


============================================
============================================

//arduino pro mini 5v 16Mhz coupled with AMT203 absolute position sensor
//feeds into Teensy 3.2 with EasyTransfer I2C since I cannot figure out how to get
//Teensy to communicate with the AMT203 directly. PITA!

boolean verbose = 1;

//sensor
#include <SPI.h>
#define CS 10 //Chip or Slave select
long SPIspeed = 2000000; //2000000 stable // the orignal script was 4000000. I could not get reliable results at those speeds
SPISettings AMT203(SPIspeed, MSBFIRST, SPI_MODE0); //set up SPI using transactions

//transfer
#include <Wire.h>
#include <EasyTransferI2C.h>
EasyTransferI2C ET;

struct SEND_DATA_STRUCTURE {
//put your variable definitions here for the data you want to send
//THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
//unsigned long AMT_curTime; //couldn't get this to work in the library. uint32_t doesn't work either
uint16_t AMT_ABSposition; // this is basically the only data type that works
// float AMT_deg; //floats don't work in the library either
uint16_t AMT_cycles; //pass some sort of errorchecking

//uint8_t wouldn't work either. dunno why. seems basic enough.
};

//give a name to the group of data
SEND_DATA_STRUCTURE mydata;

//define slave i2c address
#define I2C_SLAVE_ADDRESS 9


//program
uint16_t ABSposition = 0;
uint16_t ABSposition_last = 0;
uint8_t temp[2];
float deg = 0.00;
unsigned long curTime = millis();
String NOTES; //goes at the end of the logging line for various references
uint16_t cycles = 0;




void setup()
{
delay(200); //init the sensor
pinMode(CS, OUTPUT); //Slave Select
digitalWrite(CS, HIGH);

SPI.begin();
Wire.begin();
ET.begin(details(mydata), &Wire);
if (verbose) {
Serial.begin(115200);
Serial.print("Starting Pro Mini 5V with AMT203 and Easy Transfer TX ");
Serial.println(SPIspeed);
Serial.flush();
delay(5);
}
}

void loop()
{
curTime = millis();
//if (curTime % 25 == 0) { //do this bit every 25ms //commented out to just run full speed

uint8_t recieved = 0xA5; //temp var
ABSposition = 0; //reset position var
SPI_T(0x10); //issue read command

recieved = SPI_T(0x00); //issue NOP to check if encoder is ready to send
delay (1);// 3 stable

while (recieved != 0x10) //loop while encoder is not ready to send
{
recieved = SPI_T(0x00); //cleck again if encoder is still working
delay(1);// 3 stable //wait a bit
cycles++; //if something barfs on the SPI comms I want to know about it on the Teensy side. should = 2
}

temp[0] = SPI_T(0x00); //Recieve MSB
temp[1] = SPI_T(0x00); // recieve LSB
temp[0] &= ~ 0xF0; //mask out the first 4 bits

ABSposition = temp[0] << 8; //shift MSB to correct ABSposition in ABSposition message
ABSposition += temp[1]; // add LSB to ABSposition message to complete message


//dont need this bit here, just send what you've got all the time regardless
// if (ABSposition != ABSposition_last) { //if nothing has changed dont wast time sending position

float deg = ABSposition * 0.087890625; // 360/4096

ABSposition_last = ABSposition; //set last position to current position

//blow data out to Teensy
mydata.AMT_ABSposition = ABSposition;
mydata.AMT_cycles = cycles;
ET.sendData(I2C_SLAVE_ADDRESS);

if (verbose) {
//streamline output for serial in one print if needed
NOTES += String (curTime, DEC);
NOTES += " ";
NOTES += ABSposition;
NOTES += " ";
NOTES += deg;
NOTES += " ";
// NOTES += ABSdistance;
// NOTES += " ";
// NOTES += RPM;
// NOTES += " ";
NOTES += cycles;
Serial.println(NOTES);
NOTES = "";
}

//} // if (ABSposition != ABSposition_last)
cycles = 0;
delay(1);

//} //if (curTime % 25 == 0) {

}

uint8_t SPI_T (uint8_t msg) //Repetive SPI transmit sequence
{
uint8_t msg_temp = 0; //var to hold recieved data
SPI.beginTransaction(AMT203);
digitalWrite(CS, LOW); //select spi device
msg_temp = SPI.transfer(msg); //send and receive
digitalWrite(CS, HIGH); //deselect spi device
SPI.endTransaction();
return (msg_temp); //return received byte
}


============================================
============================================
and then on the Teesy side:
============================================
============================================

#include <Wire.h>
#include <EasyTransferI2C.h>

//create object
EasyTransferI2C ET;

uint32_t curTime;
uint16_t ABSposition;
float deg;

struct RECEIVE_DATA_STRUCTURE {
//put your variable definitions here for the data you want to send
//THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
uint16_t AMT_ABSposition;
uint16_t AMT_cycles;
};

//give a name to the group of data
RECEIVE_DATA_STRUCTURE mydata;

//define slave i2c address
#define I2C_SLAVE_ADDRESS 9


String NOTES;

void setup() {
Serial.begin(115200);
delay(3000);
Serial.println("Starting");
Wire.begin(I2C_SLAVE_ADDRESS);
delay(100);
//start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc.
ET.begin(details(mydata), &Wire);
delay(100);
//define handler function on receiving data
Wire.onReceive(receive);

}

void loop() {
curTime = millis();

if (curTime % 25 == 0) { //do this bit every 25ms

if (ET.receiveData()) { //check and see if a data packet has come in.
ABSposition = mydata.AMT_ABSposition;
}
deg = ABSposition * 0.087890625; // 360/4096

//just leaving this in here for future use
//The equation for calculating speed is:
//S = (C/PPR) / (T/60)
//
//Therefore if 60 pulses were counted in 10 seconds from a 250 PPR encoder, the speed can be calculated as
//S = (60/250) / (10/60) = (0.24) / (0.1667) = 1.44 RPM



NOTES += String (curTime, DEC);
NOTES += " ";
NOTES += ABSposition;
NOTES += " ";
NOTES += deg;
NOTES += " ";
NOTES += mydata.AMT_cycles; //show the errorchecking
Serial.println(NOTES);
NOTES = "";
}

}

void receive(int numBytes) {}

============================================
Remember if you are working with Arduino plugged into one USB and Teensy plugged into another and two IDEs open that you verify the board/port in Tools before trying to upload!!!!
 
AMT-200 series Absolute Encoder interfacing

OK. 4-year later reply to this thread... But this came up on Google for AMT encoders.

A couple of things to note:

1) From the datasheet these AMT-203/233 sensors are 1MHz SPI, 2MHz max. So use 1MHz maximum speed.

2) These use a 5V level SPI interface. So your 3.3V clock at high speed may not work well. Also, you must power them with 5V not 3.3V.

3) I used SPIMODE1 with an AMT-233B (14-bit) sensor recently and it works correctly.

Happy Easter 2018!
 
Hey ChrisBaron.

Do you mind posting your working code. I am working with the same encoder on a teensy 3.2 and have not had any luck.
Thank you.
 
Here you go. I know this could be cleaned up and a bunch of stuff combined but I do it this way so I can see each step during debugging and then clean up if I care later.

Code:
// *********************************************
// ***** getAMT2xxPos()
// Read AMT2xx Absolute Encoder - 14-bit
// Uses SPI to read out the current encoder value
uint16_t getAMT2xxPos(uint8_t cs_pin) {
  uint8_t high_byte, low_byte;
  uint16_t absvalue = 0;
    
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));  // 2Mhz max, 1Mhz typical from datasheet

  digitalWrite(cs_pin, LOW);  // select chip
  high_byte = SPI.transfer(0);
  low_byte = SPI.transfer(0);
  digitalWrite(cs_pin,HIGH); //end read sequence
 
  SPI.endTransaction();

  absvalue = (high_byte << 6) + (low_byte >> 2);  // 14-bits total for the "B" model
 
  return absvalue;
}

Hey ChrisBaron.

Do you mind posting your working code. I am working with the same encoder on a teensy 3.2 and have not had any luck.
Thank you.
 
Thanks for sharing. It's very much appreciated.

Sorry to ask more question, but it's not yet working for me.

Don't you have to do the 0xA5 and 0x10 stuff to query the sensor?

I only get this print out all the time:
absvalue 10560
absvalue 10560
absvalue 10560
absvalue 10561

I have tested the encoder with the regular teensy Encoder.h example and it works well.
But the SPI seems not to respond correctly or at all. ???

Here my testing code based on your code
Code:
//testinng this absolute encoder type: https://www.cui.com/product/resource/digikeypdf/amt20.pdf
//specifiaclly AMT203-V

#include <SPI.h>

#define CS 10 //Chip or Slave select 

void setup() {

  Serial.begin(9600);
  Serial.println("starting encoder_chrisBaron");
  Serial.flush();
  delay(2000);

  // put your setup code here, to run once:
  pinMode(CS, OUTPUT); //Slave Select
  digitalWrite(CS, HIGH);

  SPI.begin();

}

void loop() {
  // put your main code here, to run repeatedly:
  getAMT2xxPos(CS);
}

// *********************************************
// ***** getAMT2xxPos()
// Read AMT2xx Absolute Encoder - 14-bit
// Uses SPI to read out the current encoder value
uint16_t getAMT2xxPos(uint8_t cs_pin) {
  uint8_t high_byte, low_byte;
  uint16_t absvalue = 0;

  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));  // 2Mhz max, 1Mhz typical from datasheet

  digitalWrite(cs_pin, LOW);  // select chip
  high_byte = SPI.transfer(0);
  low_byte = SPI.transfer(0);
  digitalWrite(cs_pin, HIGH); //end read sequence

  SPI.endTransaction();

  absvalue = (high_byte << 6) + (low_byte >> 2);  // 14-bits total for the "B" model
  Serial.print("absvalue ");
  Serial.print(absvalue);
  Serial.println();
  return absvalue;
}
 
yes I am using the Vin from the teensy to feed the encoder. Vin is 5 volts since it gets it from the USB power.
I am using the standard SPI pins
MISO 12
MOSI 11
CS 10
SCK 13

it's very strange.
I tripple checked that I'm connected to the right pins on the encoder too.
And the fact that the AB encoder method works confirms that I'm giving it power correctly.

Here a photo of my setup. I realize it's not very helpful, but ...
shot.jpg
 
Hmmm. I don't have this still connected up. I know it worked properly with that code.

Also, I remember now that I was using the Teensy 2.0++ CPU with the AMT233. So there may be some differences there. The AMT203 has both ABZ and the SPI mode. I didn't use that exact one so I don't know if you have to do something to switch modes or what.

Without a level translator your SCK pin and MOSI pins will be at 3.3V which the AMT may or may not like. The datasheet doesn't specify SPI voltages but does mention CMOS levels.

Also, it looks like you have to send the encoder the 0x10 command over SPI to read the position data out. Datasheet p9
 
Thanks again for your input.
I will try a 2.0 teensy too.

Interesting to learn you used the AMT233 which uses SSI communication while the AMT203 used SPI. Aren't they different things?

I think the teensy 3.2 should be able to handle 3.3 V and 5 V SPI signal.
The SPI protocol allows for a range of transmission speeds ranging from 1Mhz to 100MHz. SPI slaves vary in the maximum speed at which they can reliably work. Slower speeds are usually needed when 5 volt signals are converted to 3 volts using only resistors.

cheers
 
Status
Not open for further replies.
Back
Top