Digitálny potenciometer: Rozdiel medzi revíziami
Zo stránky SensorWiki
| (3 medziľahlé úpravy od rovnakého používateľa nie sú zobrazené.) | |||
| Riadok 235: | Riadok 235: | ||
'''Video:''' | '''Video:''' | ||
<center><youtube> | <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).

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.

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.

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.

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.