Arduino + encoder — обработка высоких оборотов

от автора

Небольшой очерк как решить простую практическую задачу по обработке показаний с инкрементарного энкодера (E6B2 -CWZ1X) на arduino. Данная задача возникла в связи с необходимостью точного измерения пройденного расстояния в помещении. Энкодер соединен с колесом достаточно большого диаметра через редуктор. Размеры колеса, редуктора для целей задачи пока не имеют значение. Первично — считывать показания энкодера на достаточно больших оборотах.

Шаг первый. Uno

За основу был взят «золотой стандарт»: arduino uno и код, скорость работы которого, не подвергалась скептическому анализу:

код для arduino

/*  Максимально быстрый универсальный код для обработки энкодера  Работает на перывании (используется одно)  Тут код построен на bitRead(PIND..) - только для Arduino NANO! */ #define ENC_A 2       // пин энкодера #define ENC_B 4       // пин энкодера #define ENC_TYPE 1    // тип энкодера, 0 или 1 volatile int encCounter; volatile boolean state0, lastState, turnFlag; void setup() {   Serial.begin(9600);   attachInterrupt(0, int0, CHANGE); } void int0() {   state0 = bitRead(PIND, ENC_A);   if (state0 != lastState) { #if (ENC_TYPE == 1)     turnFlag = !turnFlag;     if (turnFlag)       encCounter += (bitRead(PIND, ENC_B) != lastState) ? -1 : 1; #else     encCounter += (bitRead(PIND, ENC_B) != lastState) ? -1 : 1; #endif     lastState = state0;   } } void loop() {   Serial.println(encCounter);   delay(100); } 

*ссылка на оригинал кода и статью.

Код работал без нареканий, однако после крепления энкодера на вал (через редуктор), выяснилось следующее. При движении, энкодер шлет слишком большой поток показаний (ticks) и вывод быстро ими забивается и виснет. Это связано, как выяснилось, не только с самой моделью энкодера, который выдавал 1000 ticks на оборот, но и с микроконтроллером arduino.
Были предприняты попытки выводить не все шаги энкодера, а каждый 10 или каждый 100 шаг, заменить arduino uno на nano, увеличить скорость serial portа до максимума, использовать иные варианты кода для arduino. Однако проблему это не решило, и arduino все так же умирал на высоких оборотах энкодера.

Встал вопрос: брать энкодер с меньшим количеством шагов (минимальный 100 против текущих 1000) у того же производителя либо заменить arduino на что-то еще. Пошли по второму пути, поглядывая на первый.

Шаг второй. Чем заменить Uno

Выбор пал на достаточно доступную в продаже Nodemcu v.3 на esp8266, у которой и достаточное количество пинов и частота (тактовая частота: 80 – 160 МГц против 16 МГц arduino). Однако найти внятный, быстрый код под плату не удалось, а колхозить не было времени и желания. Кроме того, плата оказалась с дефектом и не работала через micro-usb.

Очень интересной показалась Wemos ESP32, на которой еще и уютно расположился micro-display, но на шаге вытянутой руки ее не было, и тут на глаза попалась raspberry pico. Но, с pico тоже оказалось не все так гладко в части скорости работы с прерываниями — »
*фото из видео.

Поэтому решили временно в ее сторону не смотреть.

Самый мелкий arduino

В итоге, как всегда, остановились на том, что было «под рукой» — на Seeeduino-XIAO, который по размерам чем-то напоминает digispark, но выгодно отличается по характеристикам (до 48 МГц против 16 МГц).

Подробно о том как с ним работать можно почитать на странице разработчика.

Так как данный микроконтроллер можно условно отнести к семейству arduino, предыдущий код на нем заработал с небольшими косметическими правками.

Код для xiao

#define ENC_A 0       // пин энкодера #define ENC_B 1       // пин энкодера volatile int encCounter; volatile boolean flag, resetFlag; volatile byte curState, prevState; void setup() {   Serial1.begin(115200);   while (!Serial);   attachInterrupt(0, int0, CHANGE);   attachInterrupt(1, int0, CHANGE); } void int0() {   encTick(); }  // алгоритм со сбросом от Ярослава Куруса void encTick() {   curState = digitalRead(ENC_A) | digitalRead(ENC_B) << 1;  // digitalRead хорошо бы заменить чем-нибудь более быстрым   if (resetFlag && curState == 0b11) {     if (prevState == 0b10) encCounter++;     if (prevState == 0b01) encCounter--;     resetFlag = 0;     flag = true;   }   if (curState == 0b00) resetFlag = 1;   prevState = curState; } void loop() {     if (flag) {     Serial1.println(encCounter);     flag = 0;   } } 

Вместо serial — serial1, пины 0,1. Не все digital пины можно использовать, как оказалось: 4-й, 5й и 7й одновременно. Сам seral port «висит» на 6,7 пинах. Однако, этого достаточно, так как для общения с энкодером нужно 2 пина.

Еще один момент который необходимо иметь в виду, это то, что xiao использует 3,3 V логику и подключать напрямую к нему энкодер небезопасно. Поэтому использовался логический согласователь уровней 5V-3,3V.

Общая схема сопряжения выглядит так:

При работе с xiao также есть небольшие особенности. Контроллер имеет type-C вход и на это сразу «покупаешься», когда видишь. Однако, этот вход только для питания и прошивки. Как serial port его использовать нельзя, а жаль.

Также для перезагрузки xiao нет никакой внешней кнопки (в виду размеров самого контроллера видимо) и чтобы его перезагрузить необходимо замкнуть два контакта на лицевой стороне либо на оборотной стороне (об этом написано на странице разработчика). В остальном работа с ним такая же как и с другими представителями семейства arduino.

Итог

В результате замены arduino uno на собрата меньшего размера с лучшими характеристиками проблема была решена. Задержек при выводе в serial и подвисаний не выявлено:


ссылка на оригинал статьи https://habr.com/ru/post/649845/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *