Operácie

Digitálny potenciometer: Rozdiel medzi revíziami

Zo stránky SensorWiki

StudentMIPS (diskusia | príspevky)
StudentMIPS (diskusia | príspevky)
 
(5 medziľahlých úprav od rovnakého používateľa nie je zobrazených.)
Riadok 42: Riadok 42:
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:
* Tlačidlo a debouncing:
10 ms delay: eliminuje mechanické zákmity (debouncing) pred potvrdením stlačenia. Atómický reset: bezpečné vynulovanie encoder_pos pod ochranou inštrukcií cli() a sei().
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: slučka while bráni cyklickému resetovaniu, kým je tlačidlo stlačené.
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 231: Riadok 231:
* 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.
* 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).
* 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, pričom softvérový debounce (10 ms) účinne eliminoval akékoľvek viacnásobné resetovanie pri jednom stlačení.
* 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.