en EN   ru RU   uk UK

Головна

ОС A2

ДРАКОН

Arduino

Encoder

Програми

Утиліти

Посилання

Веб-майстру

Зв'язок з автором

Резюме


A2 OS forum

 
  Версія для друку

Алгоритм для надійного зчитування показань енкодера кута повороту для Arduino


SAGE


У процесі експериментів з аналогом плати Arduino Uno з'ясувалося, що немає достатньо надійного коду для читання показань механічного енкодера кута повороту, витягнутого з мишки. Представлено лише вагон і маленький візок полурабочие прикладів, які працюють вкрай нестабільно. Довелося усунути цю прикру неприємність і написати цей код. Проблема взаємодії з механічним енкодером полягає в тому, що треба ефективно вирішити проблему брязкоту контактів, яка властива цьому пристрою, так само як і кнопкку. Для кнопки досить влаштувати затримку в кілька мілісекунд. Для енкодера ж треба аналізувати його стан з високою частотою, і затримки тут відразу знизять ефективність. У прикладах досить недвозначно дають зрозуміти, що більш ефективний алгоритм дожен використовувати переривання. Чим я і скористався.

Читання стану виходів енкодера здійснюється 2-мя переривань по зміні рівня. Кожна подія переривання забезпечується унікальним ідентифікатором розрядністю в 1 байт, чого цілком достатньо, якщо в основному циклі Arduino аналізувати надійшли події без додаткових затримок. Для ідентифікації використовується однобайтовое число зі знаком ( short ). Постійний інкремент його значення природно дає переповнення, але в цьому немає нічого страшного якщо знати як з такими числами правильно працювати. Зокрема, не дивлячись на переповнення, ми можемо ввести для кожного з двох чисел получающейся послідовності, якщо вони не надто віддалені один від одного, таку операцію як відстань між двома такими числами (функція EventsBetween ).

Через кожну 1 мс здійснюється аналіз надійшли подій за цей період. Аналізується кількість подій (використовуємо EventsBetween ) і спеціальна сума, яка допомагає визначити, який рівень вхідного сигналу в даних подіях переважав. Будь-яка подія зниження рівня входить в суму як доданок, рівне -1 , а будь-яке подія підвищення рівня як доданок, рівне +1 . Таким чином, якщо сума вийшла негативною найімовірніше сталося зниження рівня, якщо сума позитивна - швидше за все відбулося підвищення рівня, якщо сума дорівнювала 0 , такий результат не несе ніякої певної корисної інформації про рівень (є просто шумом ) на даному вході і відкидається, а за рівень соответвуют виходу енкодера приймається попереднє значення. Тобто для розгляду подій в якості достовірного джерела інформації необхідно що-б були зафіксовані якісь події (лічильник кількості події не дорівнює 0 ) і сума за рівнями цих подій теж не дорівнювала 0 . Цей прийом ефективно вирішує проблему брязкоту контактів і не вимагає яких-небудь додаткових тимчасових затримок. Аналіз стану через кожні 1 мс показав високу ефективність. Тобто опитування енкодера йде реально з максимальною частотою з якою мікроконтролер здатний обробляти переривання, а аналіз інформації, накопиченої при обробці переривань відбувається з частотою близько 1 кГц, чого цілком достатньо. Під час налагодження алгоритму було видно, що за період 1 мс може відбуватися до 40-50 переривань і передбачені мною прийоми обробляють такий значний для мікроконтролера потік інформації досить ефективно.

Схему підключення енкодера, думаю , немає сенсу тут показувати, в інтернеті такої інформації вистачає. Виходи енкодера підключені до другого і третього піну, що відповідає нульовому і першому переривань Arduino Uno . енкодер управляє яскравістю світлодіода, підключеного до дев'ятого виходу, що використовується в режимі ШІМ.

 // Encoder pins #define encoder0PinA 2 #define encoder0PinB 3 // LED pin #define LED_PIN 9 // Encoder inputs in high state unsigned short A_high, B_high; // Count of encoder events between two timer events unsigned short A_between = 0, B_between = 0; // Previous decission on encoder state unsigned short A_statePrev = 0, B_statePrev = 0; // Current event id for each encoder input short A_event = 0, B_event = 0; // Id of event that already accounted short A_eventPrev = 0, B_eventPrev = 0; // Timestamps for time measurements unsigned long timeCur, timePrev; // Value for estimation of dominant inputs state short A_level = 0, B_level = 0; int encoder0Pos = 127; int fadeAmount = 10; // Number of events between two event id's int EventsBetween (short iCur, short iPrev) {if ((iCur <0)&&(iPrev <0)) {return (int (iCur) + 256) - (int (iPrev) + 256); } Else if (iCur <0) {return (int (iCur) + 256) - iPrev; } Return iCur - iPrev; } // Keep value in limits int clamp (int value, int low, int high) {if (value  high) {return high; } Return value; } Void setup () {pinMode (encoder0PinA, INPUT); pinMode (encoder0PinB, INPUT); pinMode (LED_PIN, OUTPUT); // encoder pin on interrupt 0 (pin 2) attachInterrupt (0, doEncoderA, CHANGE); // encoder pin on interrupt 1 (pin 3) attachInterrupt (1, doEncoderB, CHANGE); //Serial.begin(9600); analogWrite (LED_PIN, encoder0Pos); timePrev = millis (); } Void loop () {timeCur = millis (); if (timeCur> timePrev) {timePrev = timeCur; boolean A_change = A_between&&A_level; boolean B_change = B_between&&B_level; if (A_change || B_change) {boolean A_state, B_state; if (A_change) {A_state = A_level> 0; } Else {A_state = A_statePrev; } If (B_change) {B_state = B_level> 0; } Else {B_state = B_statePrev; } If ((A_change&&((! A_state&&A_statePrev&&B_state) || (A_state&&! A_statePrev&&! B_state))) || (B_change&&((! B_state&&B_statePrev&&! A_state) || (B_state&&! B_statePrev&&A_state)))) {encoder0Pos + = fadeAmount; } If ((A_change&&((! A_state&&A_statePrev&&! B_state) || (A_state&&! A_statePrev&&B_state))) || (B_change&&((! B_state&&B_statePrev&&A_state) || (B_state&&! B_statePrev&&! A_state)))) {encoder0Pos - = fadeAmount; } Encoder0Pos = clamp (encoder0Pos, 0, 255); analogWrite (LED_PIN, encoder0Pos); A_statePrev = A_state; B_statePrev = B_state; } A_between = 0; B_between = 0; A_level = 0; B_level = 0; } Else {if (EventsBetween (A_event, A_eventPrev)> = 1) {A_eventPrev = A_event; if (A_high) {A_level ++; } Else {A_level--; } A_between ++; } If (EventsBetween (B_event, B_eventPrev)> = 1) {B_eventPrev = B_event; if (B_high) {B_level ++; } Else {B_level--; } B_between ++; }}} // Interrupt on A changing state void doEncoderA () {A_high = (digitalRead (encoder0PinA) == HIGH); A_event ++; } // Interrupt on B changing state void doEncoderB () {B_high = (digitalRead (encoder0PinB) == HIGH); B_event ++; } 




Дата останнього оновлення: 31-5-19 08:02:17


 

alt CodeTyphon

Copyright © 2005-2019 SAGE. Всі права захищено.