Magnetic Encoder not working with teensy 4.1 but is with teensy 3.2

jizquie3

New member
Hello everyone,

Im currently working on a part of a big project that requires me to read the the absolute position of a magnet that's being read on an encoder. I'm using a Teensy 3.2 but want to move over to using a teensy 4.1. The reason why I'm making this post is because the current set up that I have right now, works perfectly fine with my teensy 3.2 but when I try to recreate the same results on my teensy 4.1, the system doesn't work properly. For context, I've attached both the library that I'm using to communicate with the encoder and a simple Arduino script that is supposed to output the position of the magnet to the serial monitor. When I try to recreate my original setup with the teensy 4.1, I get inaccurate/volatile readings. I suspect that it's a software issue where the logic allows for the bits to be read correctly on the 3.2 but not the 4.1

The encoder that I'm using is the AS5045 (https://docs.rs-online.com/e9dd/A700000006770281.pdf). This uses the SSI communication protocol which is very similar to an SPI protocol where there is a Clock, CS, and DATA OUT (which would be the MISO pin on the teensy) pin. However the library that I'm using disregards any of this information and just uses the pins on the teensy as digital IO pins. This is a little confusing because the person that wrote the library named this library "SPIEncoder.h" but IT IS NOT AN SPI PROTOCOL, the person that wrote it just happened to name it that way.

The AS5045 has 5 pins:
3.3V, Ground on the encoder=pin 3.3V and GND respectively
CS on the encoder= pin 10 on teensy
CLK on the encoder= pin 13 on teensy
DATA OUT on the encoder= pin 12 on teensy

*********this is also the case in my original setup with the teensy 3.2 (where it worked!!!) ***************

Arduino Code:
#include "SPIencoder.h"

uint8_t csPin=10;
uint8_t clkPin=13;
uint8_t dataPin=12;
uint8_t nBits=12;
SPIencoder myAS5045(csPin, clkPin, dataPin, nBits);
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);

}
void loop() {
// put your main code here, to run repeatedly:
Serial.println(myAS5045.EncDeg());
delay(200);
}
 

Attachments

  • SPIencoder.h
    1.8 KB · Views: 286
  • SPIencoder.cpp
    2.9 KB · Views: 270
Don't know much about this encoder to be honest but my guess the problem could be with the delays in this function:
Code:
uint32_t SPIencoder::readRegister(void){
    // Initiate variables
    uint8_t inputstream = 0;
    int outputVal = 0;
    uint8_t nbitsRead = _nbits + statbits;
    // Pull down csPin and clkPin to start spi
    digitalWrite(_csPin, HIGH);
    digitalWrite(_clkPin, HIGH);
    [CODE]delay(1);
    digitalWrite(_csPin, LOW);
    digitalWrite(_clkPin, LOW);
    for (int i = 0; i < nbitsRead; i++){
        digitalWrite(_clkPin, HIGH);
        delayMicroseconds(3);
        inputstream = digitalRead(_dataPin);
        outputVal = (outputVal << 1) + inputstream;
        digitalWrite(_clkPin, LOW);
    }
    return outputVal;
}

You could try tweaking delayMicroseconds or can try this lib:

then constructor:
Code:
AS5045 (byte pinCS, byte pinCLK, byte pinDO, byte pinPROG = 0xFF, unsigned int clockDelay = 0) ;

the constructor allows you to assign a delay - but it looks like it starts out at 0.

Sorry could not be of more help.
 
Since a Teensy 4.x is so fast, I would add a delayMicroseconds(1); after the clockpin going low as well, like so:

C++:
uint32_t SPIencoder::readRegister(void){
    // Initiate variables
    uint8_t inputstream = 0;
    int outputVal = 0;
    uint8_t nbitsRead = _nbits + statbits;
    // Pull down csPin and clkPin to start spi
    digitalWrite(_csPin, HIGH);
    digitalWrite(_clkPin, HIGH);
    delay(1);
    digitalWrite(_csPin, LOW);
    digitalWrite(_clkPin, LOW);
    for (int i = 0; i < nbitsRead; i++){
        digitalWrite(_clkPin, HIGH);
        delayMicroseconds(3);
        inputstream = digitalRead(_dataPin);
        outputVal = (outputVal << 1) + inputstream;
        digitalWrite(_clkPin, LOW);
        delayMicroseconds(1);        // <---- insert here
    }
    return outputVal;
}

If I understand the datasheet correctly, there must be a minimum of 500ns between clock going low and clock going high [TCLK/2]:

1713451284955.png

Without this delay, I'm pretty sure the Teensy switches faster than 500ns from low to high in the loop.

Hope this works,
Paul
 
Last edited:
The T4 can switch extremely fast, 10ns or something like that, even digitalWrite is very fast (though not quite that fast!). There is a delayNanoseconds() call which can be useful on the T4.
 
Since a Teensy 4.x is so fast, I would add a delayMicroseconds(1); after the clockpin going low as well, like so:

C++:
uint32_t SPIencoder::readRegister(void){
    // Initiate variables
    uint8_t inputstream = 0;
    int outputVal = 0;
    uint8_t nbitsRead = _nbits + statbits;
    // Pull down csPin and clkPin to start spi
    digitalWrite(_csPin, HIGH);
    digitalWrite(_clkPin, HIGH);
    delay(1);
    digitalWrite(_csPin, LOW);
    digitalWrite(_clkPin, LOW);
    for (int i = 0; i < nbitsRead; i++){
        digitalWrite(_clkPin, HIGH);
        delayMicroseconds(3);
        inputstream = digitalRead(_dataPin);
        outputVal = (outputVal << 1) + inputstream;
        digitalWrite(_clkPin, LOW);
        delayMicroseconds(1);        // <---- insert here
    }
    return outputVal;
}

If I understand the datasheet correctly, there must be a minimum of 500ns between clock going low and clock going high [TCLK/2]:

View attachment 34058
Without this delay, I'm pretty sure the Teensy switches faster than 500ns from low to high in the loop.

Hope this works,
Paul
Eureka!!

That solved the problem. Thank you to Paul and anyone and else that contributed to the thread.
 
Actually, the fact that I was getting true values led me to believe that the system was truly working. However, the values that I am getting are correct but only range from 180-360 degrees which is weird. Does anyone know why this might be the case?
 
However, the values that I am getting are correct but only range from 180-360 degrees which is weird. Does anyone know why this might be the case?
Could be a library issue. What if you change the line Serial.println(myAS5045.EncDeg()); to Serial.println(myAS5045.EncRaw());? You should see values between 0 and 4095 for a full rotation of the magnet.

Paul
 
Thought about again and I wonder if you are just not missing the highest bit (D11) due to improper timing.
This piece of code from SPIencoder.cpp is possibly not correct:
C++:
digitalWrite(_csPin, HIGH);
digitalWrite(_clkPin, HIGH);
delay(1);
digitalWrite(_csPin, LOW);
// needs >500ns delay here?
digitalWrite(_clkPin, LOW);
If you look at the timing diagram above [message #3], you see tCLKFE and that is specified as:
1713679425483.png

So my suggestion is to put a delayMicroseconds(1); between digitalWrite(_csPin, LOW); and digitalWrite(_clkPin, LOW);.

Paul
 
Hi,
a logic/scope connected to CS, CLK, Data will tell you the truth -always helps (especially in discussion with a SW-engineer who believes in his code 😁).
Setup- and Hold-times need to be respected as specified in the timing diagram above.
Otherwise try-and-error-sessions experimenting with delays will cost you time....
To see where and when you take Data in: use a free port pin to output a signal when accessing and the scope will show you then.
Berthold
 
Agree. I would have pulled out the logic analyzer if I had the module laying around.

Paul
 
Back
Top