Encoder sensor problem with AMT103

Hello,

I’m running into a problem with encoder sensor. I’m running SimpleFOC on an STM32F303RE Nucleo board. Motor is iPower GM4108 with L6234D driver IC. I managed to get the motor running, however, initialization fails (“Failed to notice movement”). Encoder signal looks good on an oscilloscope, and STM32 is able to perform digitalRead() on the pins I’m using for the encoder.

To debug this issue, I removed the motor code, and focused only on the encoder. I used the example from the bottom of this page, hardware interrupts. I can get some readings, so the MCU definitely reads the encoder. getAngle() returns increasing numbers when rotating in both directions. getVelocity() does return some negative values, but they are predominantly positive.

With Quadrature ON 1 full rotation results in angle of 2-2.5rads. With Quadrature OFF I get closer values of 3.x, despite AMT103 being quadrature.

I also tried different resolutions (5120PPR, 2500PPR, 640PPR), but the result is the same.

Pin assignment:

// PWM pins
const int pin_pwm_a = PC0;     // phase A
const int pin_pwm_b = PC1;     // phase B
const int pin_pwm_c = PC2;     // phase C

// current sense pins
const int pin_is_a = PA1;      // current sense for phase A
const int pin_is_b = PA4;      // current sense for phase B

// encoder pins (initially tried commented-out values, but changed them to the ones shown here)
const int pin_enc_a = PA8; // PC10;    // encoder A
const int pin_enc_b = PB10; // PC12;    // encoder B
const int pin_enc_x = PB4; // PA13;    // encoder X

// enable pins
const int pin_enable = PB0;    // enable for motor 1

I would appreciate any help troubleshooting this. I was unable to find similar issues here, sorry if it’s a duplicate. Thank you!

Hi @aventadoro-ish , welcome to SimpleFOC!

Testing with just the encoder is the right approach.

For one full turn of the motor, the sensor has to return ±6.28 radians (2π). Until it does that, no point in trying closed loop control.

Would you mind sharing the code you use to initialize the encoder? You should specify the PPR value (not the CPR, which is 4x higher with quadrature).

On STM32 MCUs you can also try the STM32HWEncoder class from our drivers library: Arduino-FOC-drivers/src/encoders/stm32hwencoder at master · simplefoc/Arduino-FOC-drivers · GitHub
You’ll have to use CH1 and CH2 of a general purpose timer like TIM3 to connect the encoder pins.
The advantage is that this implementation does not use interrupts, and uses the STM32s built in hardware encoder driver, so you get the encoder working with no overheads at all.

How is power supplied to the encoder?
How long are the wires between encoder and MCU?

Hi @aventadoro-ish,

In the dataheet the of the atm103 there are only couple of PPR that you can choose from and none of them seems to correspond to the ones you try. Do you have some other variant of the amt103?
Here is the datasheet: https://www.mouser.fr/datasheet/2/670/amt10_v-1775837.pdf

And the resolution setting
image

Any encoder can be used in quadrature mode or not, its related to how the pulses are counted rather than to the mechanics/electronics of the sensor. So you should get the same result regardless of is the quadrature mode is on or off.
The difference being that if you dont enable the quadrature mode, the resolution of the sensor is PPR and if you enable the quadrature mode the resolution is 4xPPR. But this is all handled within the Encoder class, and if you provide the correct PPR you should not see any difference when you call getAngle (except maybe a difference in precision/resolution).

Hi @runger,

Here is my encoder initialization code with pin assigned as shown above:


Encoder sensor = Encoder(
    pin_enc_a,      // pin_B
    pin_enc_b,      // pin_B
    640,           // PPR
    pin_enc_x       // index pin
);
// channel A and B callbacks
void doA(){sensor.handleA();}
void doB(){sensor.handleB();}

void setup() {
    // monitoring port
    Serial.begin(115200);

    // enable/disable quadrature mode
    sensor.quadrature = Quadrature::ON;

    // check if you need internal pullups
    sensor.pullup = Pullup::USE_EXTERN;
    
    // initialize encoder hardware
    sensor.init();
    // hardware interrupt enable
    sensor.enableInterrupts(doA, doB);

    Serial.println("Encoder ready");
    _delay(1000);
}

void loop() {
    // IMPORTANT - call as frequently as possible
    // update the sensor values 
    sensor.update();

    // display the angle and the angular velocity to the terminal
    Serial.print(sensor.getAngle());
    Serial.print("\t");
    Serial.println(sensor.getVelocity());
}

My encoder is configured as follows:


source: [AMT10E3-V datasheet](https://www.cuidevices.com/product/resource/amt10e-v.pdf

Thank you for the STM32HWEncoder suggestion! I’ll give it a try.

My test setup is as follows:

  • I’m using a rev.1 driver PCB that I designed for current sensing, motor phases are connected via screw terminals on the board. It is powered with a lab power supply set to 12V. It has 3.3V LDO for powering current sense ICs (MAX4372HEUK+TDKR).
  • I’m using L6234D breakout board with supporting components soldered to is as per its datasheet. Output and power supply wires are soldered to the PCB, input wires and enable are connected to the MCU via a breadboard
  • Encoder is powered from the PCB with jumper cables and a pin header. Encoder A,B, and X outputs are wired through the breadboard and to STM32 with 2x 20cm jumper cables
  • STM32 is powered with my laptop over USB. Ground is shared to the breadboard and PCB

Thank you for your help!

Ah ok, so its not the same senor, its not the amt103. :smiley:

Just to make it clear, do you have the atm103 or atm10e sensor?

Hi @Antun_Skuric,

Yes, I’m using AMT10E3-V, which can operate at 3.3V. Here is its datasheet. You can see resolution settings table in my reply above.

That table shows PPR values. And the datasheet also states that CPR = PPR * 4. So, I use the PPR value from the table and enable quadrature mode.

Thank you for pointing out the difference between PPR and CPR and the implementation of encoder class, it’s very helpful

It’s AMT10E3-V, my apologies for the confusion caused by the title…

It’s strange… the code looks ok to me.

I’m less familiar with STM32F3 series, but this is just basic interrupt code, it should work just fine.

One thing you could check is if using different pins helps at all.

I’m also not sure how the encoder interfaces - you configure external pull-ups but I’m not sure this encoder needs any pull-ups.
You mentioned checking the signal with a scope - was that with everything connected, or just the sensor?

Is is possible there is a mechanical problem with the connection to the motor shaft?

Otherwise I’m afraid I’m out of ideas for the moment…

@runger, I’ve tried STM32HWEncoder with no luck. It outputs 0s. I tried various pins on TIM2, TIM3, and TIM4. Ch1 → A, Ch2 → B, Ch3 → index (however, STM32HWEncoder docs say that index is not used).

@Antun_Skuric. Through experimentation, I found that only Quadrature::ON works for some reason. When I set it to OFF, I get readings of 0. With it ON, I’m unable to get angle anymore (displays 0). I used a spreadsheet to sum all the velocities readings, and it’s not 0.

The velocities are point in time measurements, you’d have to print them with their timestamps and integrate the area between the resulting curve and the time axis…

Even so it would not work out very exactly as the velocity signal is differentiated from the position and has quite some error.

I haven’t tried the HWEncoder with F3 MCUs but it should work. It can be tricky to get the pins right, as sometimes you have to use the alternate functions like encoder._pinA = PB_7_ALT2;
For technical reasons you can’t set the alternate pin names via the constructor, you have to set them afterwards.

But since neither interrupt or HWEncoder are working it could be indicative of another issue.

Thanks for the ALT pin suggestion, @runger! Encoders work now!

I’m including my code for future reference. Using STM32F303RE Nucleo board and STM32HWEncoder class. 2 TIMs tested TIM3 and TIM2, both use CH1 and CH2. Code for TIM3 is shown, code for TIM2 is in the comments

#include <SimpleFOC.h>
#include "SimpleFOCDrivers.h"
#include "encoders/stm32hwencoder/STM32HWEncoder.h"
...
STM32HWEncoder sensor = STM32HWEncoder(
    960, 
    PC6, // PA0 for TIM2
    PC7  // PA1 for TIM2
    // index pin is not used
);

void setup() {
    sensor._pinA = PC_7_ALT1; // PA_0
    sensor._pinB = PC_6_ALT1; // PA_1
    // initialize encoder hardware
    sensor.init();
    Serial.printf("Is encoder initialized? %u\r\n", sensor.initialized);
}

void loop() {
    // IMPORTANT - call as frequently as possible
    // update the sensor values 
    sensor.update();
    if (abs(sensor.getVelocity()) > 0.01) {
        // display the angle and the angular velocity to the terminal
        Serial.print(sensor.getAngle());
        Serial.print("\t");
        Serial.println(sensor.getVelocity());
    }
}

Also, for anyone troubleshooting STM32HWEncoder setup, there are 4 conditional checks in the STM32HWEncoder::init() function that may stop initialization. Adding print statements to those 4 fail conditions helped me a lot with finding the solution.

@runger, I saw it was you who worked on STM32HWEncoder class implementation. Would you like to update the README file to include that it was tested and works on F3-series with TIM2 and TIM3?

Proceeding to test it with a motor, there are still some issues:

MOT: Align current sense.
CS: Err A - all currents same magnitude!
MOT: Align error!
MOT: Init FOC failed.

I will do more testing next week, but since this issue is not related to the encoder, I can end this thread.

@Antun_Skuric and @runger, thank you so much for your help!

1 Like