Operácie

Digitálny potenciometer: Rozdiel medzi revíziami

Zo stránky SensorWiki

StudentMIPS (diskusia | príspevky)
Bez shrnutí editace
StudentMIPS (diskusia | príspevky)
 
(38 medziľahlých úprav od rovnakého používateľa nie je zobrazených.)
Riadok 11: Riadok 11:


'''Literatúra:'''  
'''Literatúra:'''  
* [http://ap.urpi.fei.stuba.sk/sensorwiki/index.php/Acrob_technical_description Dokumentácia k doske Acrob]
* [https://docs.arduino.cc/hardware/uno-rev3/ Dokumentácia k arduino UNO]
* [https://docs.arduino.cc/hardware/uno-rev3/ Dokumentácia k arduino UNO]


Riadok 19: Riadok 18:
== Analýza  a opis riešenia ==
== Analýza  a opis riešenia ==


Opíšte sem čo a ako ste spravili, ak treba, doplňte obrázkami...
Pre riešenie úlohy bol zvolený prístup využívajúci hardvérové prerušenia a stavový automat (FSM). Na detekciu smeru otáčania sa využíva stavová tabuľka (state table), ktorá vyhodnocuje prechody v Grayovom kóde. Každá zmena na pinoch vyvolá prerušenie, kde sa porovnáva predchádzajúci stav s aktuálnym.
Podrobne opíšte použité komponenty (okrem základnej dosky s ATmega328P procesorom), pridajte linky na datasheety alebo opis obvodu.  
Použité komponenty:
* ATmega328P (Arduino Uno)
* Rotačný enkóder KY-040: Inkrementálny enkóder generujúci dva signály (CLK a DT).
* [https://www.rcscomponents.kiev.ua/datasheets/ky-040-datasheet.pdf?srsltid=AfmBOooZffelqefbJSubrHjM7WgmpMoAZWkroVXYnVCgRyxkQOjlL_Fk/ Datasheet KY-040]
* Na potlačenie mechanických zákmitov boli použité dva externé keramické kondenzátory s kapacitou 47 nF pripojené medzi signálne piny (CLK, DT) a zem.


[[Súbor:GeminiAI-image3.jpg|400px|thumb|center|Celkový pohľad na zariadenie.]]
[[Súbor:KY-040.jpg|400px|thumb|center|KY-040.]]


Nezabudnite doplniť schému zapojenia! V texte by ste mali opísať základné veci zo zapojenia, samotná schéma nie je dostačujúci opis.


[[Súbor:GeminiAI-image2.jpg|400px|thumb|center|Schéma zapojenia.]]
Schéma zapojenia: Enkodér je napájaný cez 5V a GND z Arduina. Signály CLK, DT a SW pripájam k digitálnym vstupom D2, D3 a D4. Na potlačenie šumu používam 47nF kondenzátory zapojené medzi CLK/DT a zemou.
[[Súbor:Schem zap12312.jpg|400px|thumb|center|Schéma zapojenia.]]




=== Algoritmus a program ===
=== Algoritmus a program ===


Algoritmus programu využíva toto a toto, základné funkcie sú takéto a voláma ich tuto...  
Program je rozdelený na obsluhu prerušení a hlavnú slučku (spracovanie výstupu).
*Obsluha prerušení (ISR):
Používame externé prerušenia INT0 (PD2) a INT1 (PD3). Obe funkcie volajú spoločnú funkciu handle_encoder(). static inline void handle_encoder(): Táto funkcia je optimalizovaná pre rýchlosť. Číta celý port PIND naraz, čím zabezpečuje synchronizáciu dát CLK a DT.
* Atómový prístup (Atómia dát):
Keďže int32_t je 32-bitová premenná na 8-bitovom procesore, jej čítanie v hlavnej slučke prebieha v kritickej sekcii medzi cli() a sei().
*Funkcia init_all():
Konfiguruje piny ako vstupné, zapína interné pull-up rezistory a inicializuje počiatočný stav last_state
* Tlačidlo a debouncing:
10 ms delay: odstraňuje mechanické zákmity (debouncing) pred potvrdením stlačenia. Atómický reset umožňuje bezpečné vynulovanie encoder_pos pod ochranou inštrukcií cli() a sei().
Kontrola uvoľnenia pomocou slučky while bráni opakovanému resetovaniu, kým je tlačidlo stlačené.
Výpis kódu je nižšie...
Výpis kódu je nižšie...




<tabs>
<tabs>
<tab name="AVR C-code"><syntaxhighlight  lang="c++" style="background: LightYellow;">
<tab name="main.c"><syntaxhighlight  lang="c++" style="background: LightYellow;">
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
 
#include <avr/io.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
#include "uart.h"
// Makrá pre prácu s bitmi
#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
// Piny pre KY-040 (PD2=CLK, PD3=DT, PD4=SW)
#define CLK_PIN PD2
#define DT_PIN  PD3
#define SW_PIN  PD4
// premenné
volatile int32_t encoder_pos = 0;
volatile uint8_t last_state = 0;
// Stavová tabuľka pre spracovanie Grayovho kódu
const int8_t state_table[] = {0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0};
// Nastavenie výstupu cez UART
FILE mystdout = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);


int main(void)
// Logika spracovania enkodéra
{
// static inline umožňuje kompilátoru vložiť kód priamo do ISR
  unsigned int measuredValue;
static inline void handle_encoder(void) {
    uint8_t pins = PIND; // Prečítame celý port jedenkrát
    uint8_t current_state = 0;


  while (1)
    // Formovanie 2-bitového čísla: bit1 = CLK, bit0 = DT
  {
    if (pins & (1 << CLK_PIN)) current_state |= 2;
     /* relax  */ 
     if (pins & (1 << DT_PIN)) current_state |= 1;
  }


  return(0);
    // Výpočet indexu: (predošlý_stav << 2) | aktuálny_stav
    uint8_t index = (last_state << 2) | current_state;
   
    // Aktualizujeme pozíciu podľa tabuľky
    encoder_pos += state_table[index & 0x0F];
   
    // Uloženie stavu pre nasledujúci krok
    last_state = current_state;
}
}


// Prerušenia na oboch kanáloch pre maximálnu presnosť
ISR(INT0_vect) {
    handle_encoder();
}
ISR(INT1_vect) {
    handle_encoder();
}
void init_all() {
    uart_init();
    stdout = &mystdout;
    // Nastavenie pinov na vstup
    clear_bit(DDRD, CLK_PIN);
    clear_bit(DDRD, DT_PIN);
    clear_bit(DDRD, SW_PIN);
   
    // Zapnutie vnútorných prťahovacích odporov
    set_bit(PORTD, CLK_PIN);
    set_bit(PORTD, DT_PIN);
    set_bit(PORTD, SW_PIN);
    // Inicializácia predvoleného stavu PRED povolením prerušení
    last_state = (PIND & (1 << CLK_PIN) ? 2 : 0) | (PIND & (1 << DT_PIN) ? 1 : 0);
    // Nastavenie externých prerušení na akúkoľvek logickú zmenu
    EICRA |= (1 << ISC00) | (1 << ISC10);
    EIMSK |= (1 << INT0) | (1 << INT1);
   
    sei();
}
int main(void) {
    init_all();
    int32_t last_printed = 0;
    printf("\n--- Digital Potentiometer Ready ---\n");
    while(1) {
        // Atomické čítanie pozície (ochrana pred prerušením počas čítania 4 bajtov)
        int32_t safe_pos;
        cli();
        safe_pos = encoder_pos;
        sei();
        // Spracovanie stlačenia tlačidla (reset to zero)
        if (!(PIND & (1 << SW_PIN))) {
            _delay_ms(10); // Softvérový debounce pre tlačidlo
            if (!(PIND & (1 << SW_PIN))) {
                cli();
                encoder_pos = 0;
                safe_pos = 0;
                sei();
                printf("[ RESET TO ZERO ]\n");
                while(!(PIND & (1 << SW_PIN))); // Čakáme na uvoľnenie
            }
        }
        // Výpis do konzoly iba pri zmene
        if (safe_pos != last_printed) {
            printf("Steps: %ld \n", safe_pos);
            last_printed = safe_pos;
        }
    }
}
</syntaxhighlight ></tab>
<tab name="uart.c"><syntaxhighlight  lang="c++" style="background: LightYellow;">
#define F_CPU 16000000UL
#define BAUD 9600
#include <util/setbaud.h>
#include "uart.h"
void uart_init(void) {
    UBRR0H = UBRRH_VALUE;
    UBRR0L = UBRRL_VALUE;
#if USE_2X
    UCSR0A |= (1 << U2X0);
#else
    UCSR0A &= ~(1 << U2X0);
#endif
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
    UCSR0B = (1 << RXEN0) | (1 << TXEN0);
}
int uart_putc(char c, FILE *stream) {
    if (c == '\n') uart_putc('\r', stream);
    loop_until_bit_is_set(UCSR0A, UDRE0);
    UDR0 = c;
    return 0;
}
int uart_getc(FILE *stream) {
    loop_until_bit_is_set(UCSR0A, RXC0);
    return UDR0;
}
void uart_puts(const char *s) {
    while (*s) {
        uart_putc(*s++, NULL);
    }
}
</syntaxhighlight ></tab>
</syntaxhighlight ></tab>
<tab name="filename.h"><syntaxhighlight  lang="c++" style="background: LightYellow;">
<tab name="uart.h"><syntaxhighlight  lang="c++" style="background: LightYellow;">
#ifndef UART_H_
#define UART_H_
 
#include <avr/io.h>
#include <avr/io.h>
#include <stdio.h>


void adc_init(void);                                   // A/D converter initialization
void uart_init(void);
 
int uart_putc(char c, FILE *stream);
int uart_getc(FILE *stream);
void uart_puts(const char *s);
 
#endif


unsigned int adc_read(char a_pin);
</syntaxhighlight ></tab>
</syntaxhighlight ></tab>
</tabs>
</tabs>


Pridajte sem aj zbalený kompletný projekt, napríklad takto (použite jednoznačné pomenovanie, nemôžeme mať na serveri 10x ''zdrojaky.zip'':  
Pridajte sem aj zbalený kompletný projekt, napríklad takto (použite jednoznačné pomenovanie, nemôžeme mať na serveri 10x ''zdrojaky.zip'':  


Zdrojový kód: [[Médiá:projektMenoPriezvisko.zip|zdrojaky.zip]]
Zdrojový kód: [[Médiá:ProjectYaroslavBuryk.zip|zdrojaky.zip]]


=== Overenie ===
=== Overenie ===


Ako ste overili funkciu, napríklad... Na používanie našej aplikácie stačia dve tlačítka a postup používania je opísaný v sekcii popis riešenia.  
Na overenie funkčnosti digitálneho potenciometra som použil posielanie dát cez sériový terminál pomocou knižnice UART.
Na konci uvádzame fotku hotového zariadenia.  
* Pri otáčaní v smere hodinových ručičiek hodnoty v termináli narastali a pri opačnom pohybe klesali bez akýchkoľvek náhodných skokov.
 
* Testovanie presnosti: Vykonal som test rýchleho otáčania hriadeľa v oboch smeroch. Systém sa pri návrate do východiskovej polohy vždy vrátil k 0(ak začínalo z 0).
[[Súbor:GeminiAI-image1.jpg|400px|thumb|center|Aplikácia.]]
* Overenie tlačidla: Krátkym stlačením hriadeľa (tlačidlo SW) som overil okamžité vynulovanie počítadla.
[[Súbor:Photo 2026-05-15 18-01-32.jpg|400px|thumb|center|Zapojenie]]


'''Video:'''
'''Video:'''
<center><youtube>D0UnqGm_miA</youtube></center>
<center><youtube>kjprDCVZCww</youtube></center>
 


== Čo by som urobil inak ==
== Čo by som urobil inak ==

Aktuálna revízia z 18:50, 15. máj 2026

Záverečný projekt predmetu MIPS / LS2026 - Yaroslav Buryk


Zadanie

Cieľom bolo navrhnúť a realizovať program pre mikrokontrolér (ATmega328P), ktorý slúži ako digitálny potenciometer.Program musí presne načítavať impulzy bez straty krokov aj pri rýchlej zmene smeru otáčania a realizovať ošetrenie voči mechanickým zákmitom kontaktov (debouncing).

Arduino UNO

https://docs.arduino.cc/static/cb2219c4f549dd9a7fe040362b668567/a6d36/Pinout-UNOrev3_latest.png

Literatúra:


Analýza a opis riešenia

Pre riešenie úlohy bol zvolený prístup využívajúci hardvérové prerušenia a stavový automat (FSM). Na detekciu smeru otáčania sa využíva stavová tabuľka (state table), ktorá vyhodnocuje prechody v Grayovom kóde. Každá zmena na pinoch vyvolá prerušenie, kde sa porovnáva predchádzajúci stav s aktuálnym. Použité komponenty:

  • ATmega328P (Arduino Uno)
  • Rotačný enkóder KY-040: Inkrementálny enkóder generujúci dva signály (CLK a DT).
  • Datasheet KY-040
  • Na potlačenie mechanických zákmitov boli použité dva externé keramické kondenzátory s kapacitou 47 nF pripojené medzi signálne piny (CLK, DT) a zem.
KY-040.


Schéma zapojenia: Enkodér je napájaný cez 5V a GND z Arduina. Signály CLK, DT a SW pripájam k digitálnym vstupom D2, D3 a D4. Na potlačenie šumu používam 47nF kondenzátory zapojené medzi CLK/DT a zemou.

Schéma zapojenia.


Algoritmus a program

Program je rozdelený na obsluhu prerušení a hlavnú slučku (spracovanie výstupu).

  • Obsluha prerušení (ISR):

Používame externé prerušenia INT0 (PD2) a INT1 (PD3). Obe funkcie volajú spoločnú funkciu handle_encoder(). static inline void handle_encoder(): Táto funkcia je optimalizovaná pre rýchlosť. Číta celý port PIND naraz, čím zabezpečuje synchronizáciu dát CLK a DT.

  • Atómový prístup (Atómia dát):

Keďže int32_t je 32-bitová premenná na 8-bitovom procesore, jej čítanie v hlavnej slučke prebieha v kritickej sekcii medzi cli() a sei().

  • Funkcia init_all():

Konfiguruje piny ako vstupné, zapína interné pull-up rezistory a inicializuje počiatočný stav last_state

  • Tlačidlo a debouncing:

10 ms delay: odstraňuje mechanické zákmity (debouncing) pred potvrdením stlačenia. Atómický reset umožňuje bezpečné vynulovanie encoder_pos pod ochranou inštrukcií cli() a sei(). Kontrola uvoľnenia pomocou slučky while bráni opakovanému resetovaniu, kým je tlačidlo stlačené. Výpis kódu je nižšie...


#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
#include "uart.h"

// Makrá pre prácu s bitmi	
#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))

// Piny pre KY-040 (PD2=CLK, PD3=DT, PD4=SW)
#define CLK_PIN PD2
#define DT_PIN  PD3
#define SW_PIN  PD4

// premenné
volatile int32_t encoder_pos = 0; 
volatile uint8_t last_state = 0;

// Stavová tabuľka pre spracovanie Grayovho kódu
const int8_t state_table[] = {0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0};

// Nastavenie výstupu cez UART
FILE mystdout = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE); 

// Logika spracovania enkodéra
// static inline umožňuje kompilátoru vložiť kód priamo do ISR
static inline void handle_encoder(void) {
    uint8_t pins = PIND; // Prečítame celý port jedenkrát
    uint8_t current_state = 0;

    // Formovanie 2-bitového čísla: bit1 = CLK, bit0 = DT
    if (pins & (1 << CLK_PIN)) current_state |= 2;
    if (pins & (1 << DT_PIN))  current_state |= 1;

    // Výpočet indexu: (predošlý_stav << 2) | aktuálny_stav
    uint8_t index = (last_state << 2) | current_state;
    
    // Aktualizujeme pozíciu podľa tabuľky
    encoder_pos += state_table[index & 0x0F];
    
    // Uloženie stavu pre nasledujúci krok
    last_state = current_state;
}

// Prerušenia na oboch kanáloch pre maximálnu presnosť
ISR(INT0_vect) {
    handle_encoder();
}

ISR(INT1_vect) {
    handle_encoder();
}

void init_all() {
    uart_init(); 
    stdout = &mystdout; 

    // Nastavenie pinov na vstup
    clear_bit(DDRD, CLK_PIN);
    clear_bit(DDRD, DT_PIN);
    clear_bit(DDRD, SW_PIN);
    
    // Zapnutie vnútorných prťahovacích odporov
    set_bit(PORTD, CLK_PIN);
    set_bit(PORTD, DT_PIN);
    set_bit(PORTD, SW_PIN);

    // Inicializácia predvoleného stavu PRED povolením prerušení
    last_state = (PIND & (1 << CLK_PIN) ? 2 : 0) | (PIND & (1 << DT_PIN) ? 1 : 0);

    // Nastavenie externých prerušení na akúkoľvek logickú zmenu
    EICRA |= (1 << ISC00) | (1 << ISC10); 
    EIMSK |= (1 << INT0) | (1 << INT1);
    
    sei(); 
}

int main(void) {
    init_all();
    int32_t last_printed = 0;

    printf("\n--- Digital Potentiometer Ready ---\n");

    while(1) {
        // Atomické čítanie pozície (ochrana pred prerušením počas čítania 4 bajtov)
        int32_t safe_pos;
        cli();
        safe_pos = encoder_pos;
        sei();

        // Spracovanie stlačenia tlačidla (reset to zero)
        if (!(PIND & (1 << SW_PIN))) {
            _delay_ms(10); // Softvérový debounce pre tlačidlo
            if (!(PIND & (1 << SW_PIN))) {
                cli();
                encoder_pos = 0;
                safe_pos = 0;
                sei();
                printf("[ RESET TO ZERO ]\n");
                while(!(PIND & (1 << SW_PIN))); // Čakáme na uvoľnenie
            }
        }

        // Výpis do konzoly iba pri zmene
        if (safe_pos != last_printed) {
            printf("Steps: %ld \n", safe_pos);
            last_printed = safe_pos;
        }
    }
}
#define F_CPU 16000000UL
#define BAUD 9600
#include <util/setbaud.h>
#include "uart.h"

void uart_init(void) {
    UBRR0H = UBRRH_VALUE;
    UBRR0L = UBRRL_VALUE;
#if USE_2X
    UCSR0A |= (1 << U2X0);
#else
    UCSR0A &= ~(1 << U2X0);
#endif
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
    UCSR0B = (1 << RXEN0) | (1 << TXEN0);
}

int uart_putc(char c, FILE *stream) {
    if (c == '\n') uart_putc('\r', stream);
    loop_until_bit_is_set(UCSR0A, UDRE0);
    UDR0 = c;
    return 0;
}

int uart_getc(FILE *stream) {
    loop_until_bit_is_set(UCSR0A, RXC0);
    return UDR0;
}

void uart_puts(const char *s) {
    while (*s) {
        uart_putc(*s++, NULL);
    }
}
#ifndef UART_H_
#define UART_H_

#include <avr/io.h>
#include <stdio.h> 

void uart_init(void);

int uart_putc(char c, FILE *stream);
int uart_getc(FILE *stream);
void uart_puts(const char *s);

#endif


Pridajte sem aj zbalený kompletný projekt, napríklad takto (použite jednoznačné pomenovanie, nemôžeme mať na serveri 10x zdrojaky.zip:

Zdrojový kód: zdrojaky.zip

Overenie

Na overenie funkčnosti digitálneho potenciometra som použil posielanie dát cez sériový terminál pomocou knižnice UART.

  • Pri otáčaní v smere hodinových ručičiek hodnoty v termináli narastali a pri opačnom pohybe klesali bez akýchkoľvek náhodných skokov.
  • Testovanie presnosti: Vykonal som test rýchleho otáčania hriadeľa v oboch smeroch. Systém sa pri návrate do východiskovej polohy vždy vrátil k 0(ak začínalo z 0).
  • Overenie tlačidla: Krátkym stlačením hriadeľa (tlačidlo SW) som overil okamžité vynulovanie počítadla.
Zapojenie

Video:

Čo by som urobil inak

Zamyslite sa spätne nad problémom, ktorý ste riešili a napíšte, čo sa vám nepodarilo a nabudúce by ste spravili inak.


Kľúčové slová 'Category', ktoré sú na konci stránky nemeňte.