luni64 / EncoderTool

The EncoderTool is a library to manage and read out rotary encoders connected either directly or via multiplexers to ARM based boards. Encoder push buttons are supported. Callback functions can be attached to encoder changes and button presses to allow for event driven applications
MIT License
47 stars 11 forks source link

Suggestions/guideline for porting over to other boards? #2

Closed FerGT50 closed 2 years ago

FerGT50 commented 3 years ago

Hello, excellent library. The only one that REALLY works. I've tried them all. Unfortunately, it's Teensy-only. Teensy is Great, but I'd like to port it to SAMD21 and RISC-V, to exploit cheaper boards. Do you think it's feasible? Where to start, any hint? Thanks!

luni64 commented 3 years ago

Glad you like it.

I'd like to port it to SAMD21 and RISC-V, to exploit cheaper boards. Do you think it's feasible? Where to start, any hint?

It doesn't use a lot of processor specific code. Something in delay.h. So, porting should be not too difficult. Did you try compiling for your targets? It also uses functional, don't know if your targets support the STL? If not, you can opt out in the config file. Let me know if you run against some walls...

FerGT50 commented 3 years ago

Hello, thanks for replying! I'm starting with SAMD21 at the moment, in the form of SeeedStudio XIAO. Indeed "delay.h" needs "core_pins.h", which is missing for this platform. I'll look into it.

luni64 commented 3 years ago

You only need some method to generate a short delay for the multiplexers here. You can always start with using delayMicroseconds which is not optimal but should be available on any platform.

I have one of those MicroMod SAMD boards (https://www.sparkfun.com/products/16791) lying around. Let me know if you have something I can test...

FerGT50 commented 3 years ago

Hi, I'm a bit stuck: encountered quite some Teensy-only instructions, like digitalWriteFast, INPUT_DISABLE within pinMode, etc.

Tried to patch but, for example, I'm not sure which is the equivalent of "pinMode(pin, INPUT_DISABLE)" (as in EncPlex74165.h and other places) for a non-Teensy MCU. A bit out of my depth, I'm afraid.

luni64 commented 3 years ago

I currently want to finish another project (solder paste dispenser). If you can wait a few days I'll have a closer look and try to restructure the lib so that it can be ported more easily

FerGT50 commented 3 years ago

Absolutely no problem, I'm so grateful you may have a look whenever you have spare time! I can wait for weeks, months, no problem.

In the meantime, I had a closer look; maybe this could help. Not much problem overcoming errors about core_pins.h, digitalreadfast, digitalwritefast, INPUT_DISABLE. But there are some errors which are a bit tougher; for example:

attachInterruptEx.cpp:27:9: error: 'callbacks' was not declared in this scope
         callbacks[nr](states[nr]);
attachInterruptEx.cpp:27:23: error: 'states' was not declared in this scope
         callbacks[nr](states[nr]);
attachInterruptEx.cpp:31:49: error: template argument 2 is invalid
     constexpr array<void (*)(), NUM_DIGITAL_PINS> MakeRelays(index_sequence<nr...>)
attachInterruptEx.cpp:43:9: error: 'attachInterrupt' was not declared in this scope
         attachInterrupt(pin, relays[pin], mode);

this is just to (hopefully) restrict the scope.

luni64 commented 3 years ago

Meanwhile I did a few experiments and can compile for SAMD51 and STM32 under Platform IO. I don't find a working Arduino configuration for RISC V. If you tell me which boards you would like to test I can try to select the right configuration.

FerGT50 commented 3 years ago

Hello, I'm using a Longan Nano (https://longan.sipeed.com/en/) with Arduino IDE. Their "Longduino" core is a bit lacking (lacks Serial.print, even) but basic functions are present: https://github.com/sipeed/Longduino

I forked their core and added some improvements from https://github.com/scpcom/Longduino/
you can find my Json here http://fcprojects.altervista.org/Longan/package_longduino_multi_index.json

FerGT50 commented 3 years ago

While as far as SAMD21 is concerned, I'm using SeeedStudio Seeeduino Xiao https://wiki.seeedstudio.com/Seeeduino-XIAO/ and their core for Arduino: https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json

luni64 commented 3 years ago

Thanks, the XIAO is one of the boards I tested compilation with. Compiles at least. If you are using the Arduino IDE you need to switch the g++ standard from gnu++11 to gnu++14 in boards.txt. If feasible, I try to remove the g++14 dependent parts later.

I did some real life tests with the sparkfun micromod SAMD51 which at least works in the polling version. Looks like those samd processors don't have interrupt capability on all digital pins.

So far I did all my tests with PlatformIO because I can setup a project with multiple targets which makes this much easier. Would be good to know if the XIAO board compiles with the IDE as well...

I uploaded the current version to the development branch. Would be cool if you could do some tests.

FerGT50 commented 3 years ago

That's great, thanks!

Sure, I'll test with Arduino IDE 1.8.13 and report back.

I think XIAO has interrupts on all exposed pins, at least as level-change (edge). But I'm out of XIAOs right now, will order a few next week. 11 marzo 2021 10:26, "luni64" @. @*.**@*.***>)> wrote: Thanks, the XIAO is one of the boards I tested compilation with. Compiles at least. If you are using the Arduino IDE you need to switch the g++ standard from gnu++11 to gnu++14 in boards.txt. If feasible, I try to remove the g++14 dependent parts later.

I did some real life tests with the sparkfun micromod SAMD51 which at least works in the polling version. Looks like those samd processors don't have interrupt capability on all digital pins.

So far I did all my tests with PlatformIO because I can setup a project with multiple targets which makes this much easier. Would be good to know if the XIAO board compiles with the IDE as well...

I uploaded the current version to the development branch. Would be cool if you could do some tests.

—

You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub (https://github.com/luni64/EncoderTool/issues/2#issuecomment-796596524), or unsubscribe (https://github.com/notifications/unsubscribe-auth/AQKCTLO7QL4AKKJGGUWKFSTTDCECVANCNFSM4X2IVIGA).

Questo messaggio e' stato analizzato con Libraesva ESG ed e' risultato non infetto. Clicca qui per segnalarlo come spam. (https://esva.gt50.org:4431/action/903C140A28.AA5CE/learn-spam) Clicca qui per metterlo in blacklist (https://esva.gt50.org:4431/cgi-bin/learn-msg.cgi?blacklist=1&id=903C140A28.AA5CE)

FerGT50 commented 3 years ago

Tried your "devlop" branch with Arduino IDE 1.8.13 and Seeeduino XIAO latest core 1.8.1. The basic "Hello Encoder" demo uses "elapsedMillis" which is not present in XIAO core

` "C:\Users\Fernando\AppData\Local\Arduino15\packages\Seeeduino\tools\arm-none-eabi-gcc\7-2017q4/bin/arm-none-eabi-g++" -mcpu=cortex-m0plus -mthumb -c -g -Os -w -std=gnu++14 -ffunction-sections -fdata-sections -fno-threadsafe-statics -nostdlib --param max-inline-insns-single=500 -fno-rtti -fno-exceptions -MMD "-DSKETCH_NAME__=\"\"\"01_HelloEncoder.ino\"\"\"" -DF_CPU=48000000L -DARDUINO=10813 -DARDUINO_SEEED_XIAO_M0 -DARDUINO_ARCH_SAMD -DARDUINO_SAMD_ZERO -DSAMD21 -DSAMD21G18A -DARM_MATH_CM0PLUS -DSEEED_XIAO_M0 -DUSB_VID=0x2886 -DUSB_PID=0x802F -DUSBCON -DUSB_CONFIG_POWER=100 "-DUSB_MANUFACTURER=\"Seeed\"" "-DUSB_PRODUCT=\"Seeed XIAO M0\"" "-IC:\Users\Fernando\AppData\Local\Arduino15\packages\Seeeduino\hardware\samd\1.8.1\cores\arduino/TinyUSB" "-IC:\Users\Fernando\AppData\Local\Arduino15\packages\Seeeduino\hardware\samd\1.8.1\cores\arduino/TinyUSB/Adafruit_TinyUSB_ArduinoCore" "-IC:\Users\Fernando\AppData\Local\Arduino15\packages\Seeeduino\hardware\samd\1.8.1\cores\arduino/TinyUSB/Adafruit_TinyUSB_ArduinoCore/tinyusb/src" -DARDUINO_SAMD_ZERO -DSAMD21 -DSAMD21G18A__ -DARM_MATH_CM0PLUS -DSEEED_XIAO_M0 -DUSB_VID=0x2886 -DUSB_PID=0x802F -DUSBCON -DUSB_CONFIG_POWER=100 "-DUSB_MANUFACTURER=\"Seeed\"" "-DUSB_PRODUCT=\"Seeed XIAO M0\"" "-IC:\Users\Fernando\AppData\Local\Arduino15\packages\Seeeduino\hardware\samd\1.8.1\cores\arduino/TinyUSB" "-IC:\Users\Fernando\AppData\Local\Arduino15\packages\Seeeduino\hardware\samd\1.8.1\cores\arduino/TinyUSB/Adafruit_TinyUSB_ArduinoCore" "-IC:\Users\Fernando\AppData\Local\Arduino15\packages\Seeeduino\hardware\samd\1.8.1\cores\arduino/TinyUSB/Adafruit_TinyUSB_ArduinoCore/tinyusb/src" "-IC:\Users\Fernando\AppData\Local\Arduino15\packages\Seeeduino\tools\CMSIS\5.4.0/CMSIS/Core/Include/" "-IC:\Users\Fernando\AppData\Local\Arduino15\packages\Seeeduino\tools\CMSIS\5.4.0/CMSIS/DSP/Include/" "-IC:\Users\Fernando\AppData\Local\Arduino15\packages\Seeeduino\tools\CMSIS-Atmel\1.2.1/CMSIS-Atmel/CMSIS/Device/ATMEL/" "-IC:\Users\Fernando\AppData\Local\Arduino15\packages\Seeeduino\hardware\samd\1.8.1\cores\arduino" "-IC:\Users\Fernando\AppData\Local\Arduino15\packages\Seeeduino\hardware\samd\1.8.1\variants\XIAO_m0" "-IF:\Arduino\libraries\EncoderTool\src" "-IF:\Arduino\libraries\Bounce2\src" "b:\temp\arduino_build_619291\sketch\01_HelloEncoder.ino.cpp" -o "b:\temp\arduino_build_619291\sketch\01_HelloEncoder.ino.cpp.o"

01_HelloEncoder:12:1: error: 'elapsedMillis' does not name a type

elapsedMillis stopwatch;

^~~~~

F:\Arduino\libraries\EncoderTool\examples\01_HelloEncoder\01_HelloEncoder.ino: In function 'void loop()':

01_HelloEncoder:16:9: error: 'stopwatch' was not declared in this scope

 if (stopwatch > 50)

     ^~~~~~~~~

F:\Arduino\libraries\EncoderTool\examples\01_HelloEncoder\01_HelloEncoder.ino:16:9: note: suggested alternative: 'rpmatch'

 if (stopwatch > 50)

     ^~~~~~~~~

     rpmatch

Using library EncoderTool at version 2.2.0 in folder: F:\Arduino\libraries\EncoderTool

Using library Bounce2 at version 2.55 in folder: F:\Arduino\libraries\Bounce2

exit status 1

'elapsedMillis' does not name a type

`

FerGT50 commented 3 years ago

but commenting out that part, it compiles!

As soon as I get a few XIAOs, I will do more tests.

Thanks!

luni64 commented 3 years ago

Didn't try the examples so far. Was happy that it compiled at all :-)

luni64 commented 3 years ago

Looks like you commented to the wrong library. I assume you meant this one: https://github.com/luni64/EncSim

Anyway, EncSim only works with PJRC Teensy boards not UNO boards. The example section shows how to use it. There is also a hex file you can download to a Teensy board and control the simulator via a serial terminal.

Hope that helps. If you have further questions please post at the EncSim repository

FerGT50 commented 3 years ago

I finally managed to perform some tests with the Seeeduino XIAO and the Wemos SAMD21 M0-Mini. It builds and runs. :)

While still very good (much better than every other rotary encoder library out there), it does not work as flawlessy as with Teensies: it misses a step now and then (manually turned cheap rotary encoders). I suspect it has to do with the lack of digitalReadFast/digitalWriteFast on these non-Teensy boards (digitalRead/Write is quite slow indeed for a 48 MHz Cortex M0+). This is just for the record: it's still a great great work, many thanks!

luni64 commented 3 years ago

Good to hear. It should be possible to speed that up a bit. Are you using directly coupled encoders or multiplexed ones?

FerGT50 commented 3 years ago

Directly coupled. Just for the sake of precision, it does not miss steps because I turn the encoder too fast, it happens even if I turn it slowly; I have the impression that with the ordinary digitalRead being so slow, the debouncing routine sometimes fails.

luni64 commented 3 years ago

That may be, anyway, which read mode do you use? X1 / X2 or X4? Polling or interrupt?

FerGT50 commented 3 years ago

I use the simplified constructor

Encoder::begin(pinA, pinB)

which AFAIK uses CountMode::quarter and interrupts. I have yet to try inputMode = 0 (I think it defaults to 2 = INPUT_PULLUP).

My encoders are the cheap ones with breakout board from Amazon, KY-040 and the likes.

FerGT50 commented 3 years ago

Just for completeness: also tried

encoder.begin(dirPin, clkPin, EncoderTool::CountMode::quarter, INPUT)

had the same results

luni64 commented 3 years ago

Thanks, I'll have a look at that on the weekend. Need to setup some hardware first to be able to do some testing.

luni64 commented 3 years ago

I did some tests with a SAMD51 (Sparkfun micromod version) and could reproduce the missing counts.

I changed the reading from digitalRead to a method directly reading from the registers which is as fast as it can be. Unfortunately, the interrupt based encoders still loose counts. I had a closer look and found that the pin interrupts are awfully slow. It also looks like the processor discards interrupts which occur while in an interrupt. The Teensy processors (core?) ensures that such an interrupt is never lost. -> If the last interrupt of the bouncing sequence gets lost the algorithm ends up with a wrong final state -> count gets lost. Together with the slow interrupt processing and heavily bouncing encoders this happens quite often.

The polling encoders don't have this problem since they will always catch the final state and work as good as with the Teensies. In case you never used them here a quick example:

(Please download the current version of the lib from the development branch to test)

#include "Arduino.h"
#include "EncoderTool.h"

using namespace EncoderTool;

PolledEncoder e1;

void setup()
{
    e1.begin(2, 3);
    pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
    e1.tick();  // tick the encoder as often as possible (> ~3kHz) other than the ticking they can be used exactly as the interrupt encoders
    if(e1.valueChanged())
    {
        Serial.println(e1.getValue());
    }
}

Instead of manually polling in loop you can also override yield to do the polling. The following example shows how to use the encoder button, print changed values with callbacks and override yield:

#include "Arduino.h"
#include "EncoderTool.h"

using namespace EncoderTool;

PolledEncoder e1;

// in this examaple we use callbacks to print out changed encoder values or button states
void onButtonChanged(int32_t b)
{
    Serial.print("Button: ");
    Serial.println(b == 0 ? "pressed" : "released");
}

void onValueChanged(int32_t value, int32_t delta)
{
    Serial.print("Encoder: ");
    Serial.println(value);
}

void setup()
{
    pinMode(LED_BUILTIN, OUTPUT);
    e1.begin(2, 3, 4);  // encoder button on pin 4

    e1.attachButtonCallback(onButtonChanged);
    e1.attachCallback(onValueChanged);}

void loop()
{
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    delay(250);
}

// use yield to tick the used encoders in the background
void yield()
{
    e1.tick();
    // e2.tick();
    // etc
}

This prints: image

I'll see if I can find out why the performance of the interrupt system is so bad on this processor but I recommend to use the polling versions. (Interrupt based reading of encoders is not a good idea anyway...)

FerGT50 commented 3 years ago

Thank you very much luni! Much appreciated.

luni64 commented 3 years ago

You're welcome. Never did this kind of platform independent programming. Lots to learn :-)

I meanwhile found out what's wrong with the interrupt handler in the SAMD core:

file: WInterrupts.c image

I needed to swap line 264 and 265 (picture shows the swapped state). Originally the handler resets the interrupt flag AFTER the user callback. That means, if an interrupt occurs while the callback runs it will be deleted . After swapping, the code deletes the flag before the callback. If an interrupt happens while the callback runs, it will be stored and the ISR is invoked again.

With this change the interrupt encoders run as good as they run on a teensy.

Please note: The shown code is for SAMD51, the SAMD21 code starts at line 352.

FerGT50 commented 3 years ago

Great!! Works wonders :D !!

Maybe SAMD core maintainers should be alerted? Sounds like an actual bug!

luni64 commented 3 years ago

I leave that to you :-)

FerGT50 commented 3 years ago

Eheh, sure, I'll write them!

luni64 commented 2 years ago

Seems like that was fixed on the Seed SAMD library by now.

XFer012 commented 2 years ago

Excellent! I'm out of SAMD boards since a couple months, but great news anyway. Thanks luni.