AVR92: Kiniškas rotary encoder

Va kartais internetas nepadeda. Prireikė į vieną projektą sudėti valdymui rotary encoderį. Tą su sukiojama rankenėle. Projekto sąlygos, kad tas enkoderis bus pats prasčiausias kiniškas. Ir jokios papildomos hardwarės. Tingėjau programuoti pats, ir pamaniau, kad tikrai kas nor pridėjo visokiausių pavyzdukų AVR (ir ne tik) kontroleriams. Aha, pridėti kalnai, bet visi kopypaste vienas nuo kito.

Išsiskiria keli variantai:
1. Naudojamas INT ant MCU kojos (LH) ir patikrinama kita enkoderio koja. Ir tas pats su kita koja. Sunaudojami du INT. Galima ir INT nuo vienos kojos, bet reikia tikrinti LH ir HL variantus. Viskas gerai teoriškai arba naudojant optinius enkoderius. Naudojant kiniškus, mechaninius- daugybė trukšmo nuo persijunginėjimų ir rezultate kalnas pertraukimu ir falšyvi suveikimai. Praktikoje- enkoderis pavirsta atsitiktinių skaičių generatorium.
2. Naudojamas main loop amžinas ciklas ir daroma viską softwariškai. Kad apsisaugoti nuo kontaktų kibirkščiavimo dedami programiniai uždelsimai. Kas suėda procesoriaus ciklus.
3. Ir dar yra per taimerį, arduino bibliotekos kurios source supranta tik cpp fanatai ir šiaip vartojantys kokią žolę. Aišku, naudojant aukšto lygio arduino makro programavimą viskas tikriausiai veikia. Bet kai reikia daryti optimizuotą ir eksportuojamą kodą prasideda nesamonės.

Todėl susinervinau ir pasirašiau savo variantą. Veikimas supaprastintas visiškai ir eina per taimerį. Taimerio greitis kažkur 200Hz (0.005s). Toks greitis kaip tink tinka mano naudojamam enkoderiui ir protingam sukimo greičiui. Panaudojus per didelį sukimo greitį enkoderis dažniausiai praleidžia impulsus, gal net ir sustoja ir kartais pavaro ne į tą pusę. Tačiau sukant normaliai arba labai lėtai viskas gerai veikia.

ISR(TIMER1_COMPA_vect)
{
unsigned char d,a,b;

d=PIND & 0b00001100;
if(d != old_d)
{
a=0; b=0;
if(d & 0b00000100) a=1;
if(d & 0b00001000) b=1;

if(inta==0 && a==1) rotar=rotar+b;
if(intb==0 && b==1) rotar=rotar-a;

inta=a;
intb=b;
old_d=d;
}
}


Veikimas paprastas- pirmiausia pasitikrinam, ar kas nors iš esmės pakito (old_d) ir jei nieko nebuvo, tai tepam slides, nes reikia taupyti MCU. O jei jau pasikeitė, tai analizuojamės. Aišku reikėtu nenaudoti to “d & 0b00000100”, bet aš dar nenusprendžiau, kas bus toliau. Ir aišku, kintamieji “rotar”, “old_d”, “inta” ir “intb” yra globalūs ir “volatile”.

4 replies on “AVR92: Kiniškas rotary encoder”

  1. Kadangi namų apšvietimą dariausi reguliuojamą su tais kiniškais rotaciniais enkoderiais, tai pavargt teko gan ilgai, bet išmąsčiau vieną ilgą algoritmą, kuris dabar veikia 100% ir bile kokiais greičiais. Tiesa, ant STM32F103, beigi naudojant pertraukimus ant visų pirstelėjimų. Esmė buvo atkapstyme, kad per vieną spragtelėjimą enkoderis duoda keturis signalus (per abi kojas, žinoma). Ir apie tai, į kurią pusę sukama, sprendimas priimamas tik po keturių signalų.

    Dar vienas niuansas, kurio nesupratau ir neatradau logikos: jei du enkoderiai sujungti lygegriačiai, vienas duoda signalus sukamas į vieną pusę, o kitas — į priešingą. Jaučiu, kažkuris iš jų „pastringa“ ant „on“ padėties, o kitas tada signalizuoja trimis signalais ir algoritmas puses „apverčia“.

    Mano rotary libas: https://github.com/darauble/STM32-ARM-Libs/tree/master/Rotary

  2. Sauliaus variantas kiek panašesnis į Arduino- naudojama lentelė.

    Man greitis gal nesvarbiausia. Man reikėjo, kad vienas “klikas” būtų vienas “skaičiukas”. Nes mano projekte su tuo enkoderiu nustatomas generatoriaus dažnis.

    Užtenka 200Hz GUI ir valdymo aplausai. Pati logika naudoja pertraukime sugeneruotus “valdymo duomenius”: kiek ir į kur pasukta, koks mygtukas buvo nuspaustas ir koks mygtukas tebe nuspaustas.

  3. neblogai veikia toks principas:

    static const signed char encDeltaArray[16] = {
    0, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 // 1:4 tikslumas
    // 0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0// 1:1 tiikslumas
    };
    // returns the direction the rotary encoder was rotated: -1, 1 oder 0
    static char getEncoderDelta(unsigned char a, unsigned char oldA, unsigned char b, unsigned char oldB) {
    return encDeltaArray[oldB | (b IDR & ENC_LEFT) == (uint32_t)Bit_RESET;
    unsigned char b = (ENC_GPIO->IDR & ENC_RIGHT) == (uint32_t)Bit_RESET;
    signed char delta = getEncoderDelta(a, oldA, b, oldB);
    oldA = a;
    oldB = b;
    return delta;
    }

    encoderRead klibinam task’e ir su gauta delta incrementinam aktualu kintamaji

Leave a Reply

Your email address will not be published. Required fields are marked *