Operácie

Digitálny potenciometer: Rozdiel medzi revíziami

Zo stránky SensorWiki

StudentMIPS (diskusia | príspevky)
StudentMIPS (diskusia | príspevky)
 
(9 medziľahlých úprav od rovnakého používateľa nie je zobrazených.)
Riadok 41: Riadok 41:
*Funkcia init_all():
*Funkcia init_all():
Konfiguruje piny ako vstupné, zapína interné pull-up rezistory a inicializuje počiatočný stav last_state
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...


Riadok 221: Riadok 224:
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 ===


Na overenie funkčnosti digitálneho potenciometra použijeme posielanie dát cez sériový terminál pomocou knižnice UART.
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.
[[Súbor:Photo 2026-05-15 18-01-32.jpg|400px|thumb|center|Zapojenie]]
[[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.