Hello - i'm attempting to read along the AZ of my telescope using an absolute encoder. With the code below from the manufacturing company i'm able to receive 0-4096 figures back from the AMT23 device, but it starts over every 90 degrees. Any ideas how I can change the code to determine where its positioned as an absolute value rather than relative? Appreciate the assistance.
Thank you,
Smalls
/*
* AMT23_SSI_Sample_Code.ino
* Company: CUI Inc.
* Author: Jason Kelly
* Version: 1.0.0.1
* Date: February 14, 2020
*
* This sample code can be used with the Arduino Mega to control the AMT22 encoder.
* It uses SSI to control the encoder and the the Arduino UART to report back to the PC
* via the Arduino Serial Monitor. Code can be modified for any Arduino including the UNO.
* The pins are the same on the Arduino UNO but you will need to make sure to change the board
* type in the Tools Menu.
* For more information or assistance contact CUI Inc for support.
*
* After uploading code to Arduino Mego open the open the Serial Monitor under the Tools
* menu and set the baud rate to 115200 to view the serial stream the position from the AMT23.
*
* Arduino Pin Connections
* SSI Chip Select: Pin 7
* SSI Clock (SCK): Pin 10
* SSI Data (SDO): Pin 11
*
*
* AMT23 Pin Connections
* Vdd (5V): Pin 1
* SSI DATA (SDO): Pin 2
* SSI CLOCK (SCK): Pin 3
* GND: Pin 4
* Mode (unconnnected: Pin 5
* SSI Chip Select: Pin 6
*
*
* This is free and unencumbered software released into the public domain.
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/* NOP */
#define NOP __asm__ __volatile__ ("nop\n\t")
/* Define special ascii characters */
#define carriageReturn 0x0D
#define newLine 0x0A
#define tab 0x09
/* boolean */
#define OFF 0
#define ON 1
/* pins */
#define SSI_CS 7
#define SSI_SCK 10
#define SSI_SDO 11
/* available resolutions */
#define res12 12
#define res14 14
/* Serial */
#define baudRate 115200
void setup()
{
//pin modes for AMT23 SSI
pinMode(SSI_SCK, OUTPUT);
pinMode(SSI_CS, OUTPUT);
pinMode(SSI_SDO, INPUT);
//serial port for debugging
Serial.begin(baudRate);
}
void loop()
{
//we don't need to redeclare variables so they can go here
//and then we can have our own infinite loop below
uint16_t encoderPosition; //holder for encoder position
uint8_t attempts; //we can use this for making sure position is valid
while(1)
{
//set attemps counter at 0 so we can try again if we get bad position
attempts = 0;
//this function gets the encoder position and returns it as a uint16_t
//send the function either res12 or res14 for your encoders current resolution
encoderPosition = getPositionSSI(res12);
while (encoderPosition == 0xFFFF && attempts++ < 3)
{
delay(1);
encoderPosition = getPositionSSI(res12); //try again
}
Serial.print(encoderPosition, DEC); //print the position
Serial.write(newLine);
delay(500);
}
}
/*
* Use this function to understand how the SSI protocol works first, then you can use the function
* below that has better efficiency.
*/
uint16_t getPositionSSI(uint8_t resolution)
{
uint8_t i, j; //we'll use these incrementers
uint16_t currentPosition;
uint8_t _clockCounts = resolution + 2; //the AMT23 includes 2 additional bits in the response that are used as checkbits
bool binaryArray[_clockCounts]; //we'll read each bit one at a time and put in array. SSI comes out reversed so this helps reorder
bool bitHolder; //this variable holds the current bit in our read loop
bool checkBit0, checkBit1; //the frist two bits in the position response are checkbits used to check the validity of the position response
//drop cs low and wait the minimum required time. This is done with NOPs
digitalWrite(SSI_CS, LOW);
for (i = 0; i < 5; i++) NOP;
//We will clock the encoder the number of times (resolution + 2), incrementing with 'j'
//note that this method of bit-banging doesn't give a reliable clock speed.
//in applications where datarate is important, the Arduino is not the best solution unless you
//can find a way to make the SPI interface work for this protocol format.
for (j = 0; j < _clockCounts; j++)
{
//first we lower the clock line and wait until the pin state has fully changed
digitalWrite(SSI_SCK, LOW);
for (i = 0; i < 10; i++) NOP;
//now we go high with the clock. no need to wait with NOPs because the pin read we'll do next times sufficient time
digitalWrite(SSI_SCK, HIGH);
//Grab the data off of the SDO line and place it into the binary array
binaryArray[j] = digitalRead(SSI_SDO);
}
//release cs line, position has been fully received
digitalWrite(SSI_CS, HIGH);
//now we'll reverse the order of the binary array so that the bit ordering matches binary
for (i = 0, j = _clockCounts - 1; i < (_clockCounts / 2); i++, j--)
{
bitHolder = binaryArray[i];
binaryArray[i] = binaryArray[j];
binaryArray[j] = bitHolder;
}
//create uint16_t from binary array by masking and bit shifting
for (i = 0; i < _clockCounts - 2; i++) currentPosition |= binaryArray[i] << i;
//grab check bits in highest bit slots
checkBit1 = binaryArray[_clockCounts - 1];
checkBit0 = binaryArray[_clockCounts - 2];
//use the checkbit equation from the ATM23 datasheet
if (resolution == res12) //if we're in 12-bit
{
if (!(checkBit1 == !(binaryArray[11] ^ binaryArray[9] ^ binaryArray[7] ^ binaryArray[5] ^ binaryArray[3] ^ binaryArray[1]))
&& (checkBit0 == !(binaryArray[10] ^ binaryArray[8] ^ binaryArray[6] ^ binaryArray[4] ^ binaryArray[2] ^ binaryArray[0])))
{
currentPosition = 0xFFFF; //bad pos, return 0xFFFF which is not a valid value
}
}
else if (resolution == res14) //if we're in 14-bit
{
if (!(checkBit1 == !(binaryArray[13] ^ binaryArray[11] ^ binaryArray[9] ^ binaryArray[7] ^ binaryArray[5] ^ binaryArray[3] ^ binaryArray[1]))
&& (checkBit0 == !(binaryArray[12] ^ binaryArray[10] ^ binaryArray[8] ^ binaryArray[6] ^ binaryArray[4] ^ binaryArray[2] ^ binaryArray[0])))
{
currentPosition = 0xFFFF; //bad pos, return 0xFFFF which is not a valid value
}
}
return currentPosition;
}
/*
* This function is a more efficient version of the getPositionSSI() function
* that strips out some of the bit reordering and improves the speed of calculating the checkbit.
* Once the SSI protocol is fully understood, this function will be more useful in an end application.
*/
uint16_t getPositionSSI_efficient(uint8_t resolution)
{
uint8_t i, j; //we'll use these incrementers
uint8_t odd, even; //bit parity counters
uint16_t currentPosition = 0;
uint8_t _clockCounts = resolution + 2; //the AMT23 includes 2 additional bits in the response that are used as checkbits
uint8_t checkBit1, checkBit0; //the frist two bits in the position response are checkbits used to check the validity of the position response
//drop cs low and wait the minimum required time. This is done with NOPs
digitalWrite(SSI_CS, LOW);
for (i = 0; i < 5; i++) NOP;
//We will clock the encoder the number of times (resolution + 2), incrementing with 'j'
//note that this method of bit-banging doesn't give a reliable clock speed.
//in applications where datarate is important, the Arduino is not the best solution unless you
//can find a way to make the SPI interface work for this protocol format.
for (j = 0; j < _clockCounts; j++)
{
//first we lower the clock line and wait until the pin state has fully changed
digitalWrite(SSI_SCK, LOW);
for (i = 0; i < 10; i++) NOP;
//now we go high with the clock. no need to wait with NOPs because the pin read we'll do next times sufficient time
digitalWrite(SSI_SCK, HIGH);
//throw the pin value into the position, note that it's reversing it as well
currentPosition |= (digitalRead(SSI_SDO) << (_clockCounts - j - 1));
}
//release cs line, position has been fully received
digitalWrite(SSI_CS, HIGH);
//grab the highest two bits and put them into the checkbit holders
checkBit1 = (currentPosition >> (_clockCounts - 1)) & 0x01;
checkBit0 = (currentPosition >> (_clockCounts - 2)) & 0x01;
//at this point currentPosition still holds the checkbits. So if we're in 14 bit mode, there's 16 bits
//we only move up 14 bits here (resolution) because we're only tallying up the 1's in the position
//we're counting the bits in even slots and the ones in odd slots
for (uint8_t i = 0; i < resolution; i++) (i % 2 == 0) ? even += ((currentPosition >> i) & 0x01) : odd += ((currentPosition >> i) & 0x01);
//check the counts against the checkbits
if ((checkBit1 == odd % 2) || (checkBit0 == even % 2)) currentPosition = 0xFFFF;
else
{
//this isn't the 'fastest' since we're introducting an if/else but doing
// currentPosition &= 2^resolution; doesn't work because arduino has a problem with
// powers.
if (resolution == res12) currentPosition &= 0xFFF;
else if (resolution == res14) currentPosition &= 0x3FFF;
}
return currentPosition;
}