Operácie

UART Metronóm

Zo stránky SensorWiki

Záverečný projekt predmetu MIPS / LS2026 - Karol Kilian


Zadanie

UART Metronóm

Cieľom projektu je vytvoriť jednoduchý elektronický metronóm riadený mikrokontrolérom AVR. Program komunikuje s používateľom prostredníctvom rozhrania UART, cez ktoré je možné zadať hodnotu BPM (beats per minute/počet úderov za minútu). Zadaná hodnota určuje rýchlosť metronómu.

Vývojová doska ACROB.

Literatúra:


Analýza a opis riešenia

Projekt bol realizovaný pomocou mikrokontroléra ATmega328P, ktorý zabezpečuje spracovanie vstupných údajov z počítača a generovanie zvukového signálu pre metronóm. Používateľ zadáva hodnotu BPM cez sériovú komunikáciu UART pomocou terminálu v počítači. Program následne vypočíta časový interval medzi jednotlivými údermi a v pravidelných intervaloch aktivuje bzučiak.

Komunikácia medzi počítačom a mikrokontrolérom prebieha pomocou rozhrania UART s prenosovou rýchlosťou 9600 baudov. Používateľ zadá číselnú hodnotu BPM do terminálu a po potvrdení klávesom Enter sa hodnota odošle do mikrokontroléra. Program prijaté znaky uloží do poľa znakov (buffer) a následne ich pomocou funkcie atoi() prevedie na celé číslo. Ak je hodnota v povolenom rozsahu, nastaví sa ako nová hodnota BPM.

Na generovanie zvuku bol použitý hardvérový časovač Timer0 v režime CTC (Clear Timer on Compare Match). Výstup OC0A automaticky prepína svoj stav pri zhode časovača s hodnotou registra OCR0A, čím vzniká obdĺžnikový signál vhodný na ovládanie piezoelektrického bzučiaka. Zvuk metronómu vzniká krátkym zapnutím generovania signálu na približne 50 ms, čím sa vytvorí krátke „tiknutie“.

Čas medzi jednotlivými tiknutiami sa vypočíta podľa vzťahu:

t = 60000/BPM​

kde: t je interval medzi údermi v milisekundách, BPM je počet úderov za minútu.

Napríklad pri hodnote 120 BPM bude interval medzi jednotlivými údermi:

t = 60000/120 = 500 ms

Program neustále kontroluje prítomnosť nových dát na UART rozhraní. Táto kontrola je realizovaná neblokujúcim spôsobom pomocou príznaku RXC0 v registri UCSR0A, takže metronóm môže pokračovať v činnosti aj počas čakania na nový vstup od používateľa.

Použité komponenty:

1.Piezoelektrický bzučiak: Piezoelektrický bzučiak slúži na generovanie zvukového signálu metronómu. Je pripojený na výstup OC0A mikrokontroléra (pin PD6). Mikrokontrolér generuje obdĺžnikový signál, ktorý spôsobuje vibrácie piezoelektrického prvku a tým vznik zvuku.

2. Časovač Timer0: Timer0 je 8-bitový hardvérový časovač zabudovaný v mikrokontroléri ATmega328P. V projekte bol použitý na generovanie obdĺžnikového signálu pre bzučiak.

Použité nastavenia:

-režim CTC, -prepínanie výstupu OC0A pri zhode, -prescaler 256.

drôtiková schéma :D

Schéma zapojenia a opis obvodu

Zapojenie metronómu je založené na mikrokontroléri ATmega328P, ktorý je napájaný napätím +5 V. Napájanie je privedené na piny VCC a AVCC, pričom oba piny sú pripojené na rovnaký zdroj napätia.

Reset pin je pripojený cez pull-up rezistor (10 kΩ) na +5 V, čím sa zabraňuje nechcenému resetovaniu mikrokontroléra.

Zvukový výstup metronómu je realizovaný pomocou piezoelektrického bzučiaka, ktorý je pripojený na pin PD6 (OC0A). Tento pin je riadený hardvérovým časovačom Timer0, ktorý generuje obdĺžnikový signál. Bzučiak je druhým vývodom pripojený na zem (GND).

Komunikácia s počítačom prebieha prostredníctvom rozhrania UART. Piny PD0 (RX) a PD1 (TX) sú pripojené na USB–UART prevodník, ktorý zabezpečuje prenos dát medzi mikrokontrolérom a terminálom v počítači. Používateľ tak môže zadávať hodnotu BPM v reálnom čase.

Schéma zapojenia.


Algoritmus a program

Algoritmus programu je založený na neustálom cyklickom vykonávaní dvoch hlavných činností: spracovaní vstupu z UART rozhrania a generovaní pravidelného zvukového signálu metronómu.

V prvej časti program kontroluje, či boli prijaté nové dáta cez sériovú komunikáciu UART. Ak je detegovaný prichádzajúci znak, program ho načíta a postupne ukladá do textového bufferu. Po prijatí ukončovacieho znaku (Enter) sa celý reťazec prevedie na celočíselnú hodnotu pomocou funkcie atoi(). Táto hodnota predstavuje nové BPM (beats per minute), ktoré sa následne nastaví ako rýchlosť metronómu.

Druhá časť algoritmu zabezpečuje generovanie rytmických „tikov“. Na základe aktuálnej hodnoty BPM sa vypočíta časový interval medzi jednotlivými údermi podľa vzťahu t = 60000/BPM ​

. Po uplynutí tohto intervalu sa aktivuje funkcia tick(), ktorá na krátky čas zapne výstup Timer0 a tým vytvorí zvukový impulz na bzučiaku.


#define F_CPU 16000000
#define BAUD       9600
#define BAUD_PRESCALE  (((F_CPU / (BAUD * 16UL))) - 1) // vypocet pre UART podla datasheetu

#include <util/delay.h>
#include <avr/io.h>
#include "uart.h"
#include <stdio.h>
#include <stdlib.h>   // pre atoi() - prevod textu na cislo

// makra na nastavenie a vymazanie bitov
#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))

// prepojenie printf a getchar na UART
FILE uart_output = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);
FILE uart_input  = FDEV_SETUP_STREAM(NULL, uart_getc, _FDEV_SETUP_READ);

// funkcia na oneskorenie v milisekundach (lebo _delay_ms ma obmedzenie)
void delay_ms(int ms)
{
    for (int i = 0; i < ms; i++)
        _delay_ms(1);
}

// funkcia ktora vytvori kratky "tick" zvuk
void tick(void)
{
    TCCR0A |= (1 << COM0A0);   // zapne generovanie signalu na vystupe OC0A
    delay_ms(50);              // dlzka kliknutia (50 ms)
    TCCR0A &= ~(1 << COM0A0);  // vypne signal
}

int main(void)
{
    uart_init();               // inicializacia UART komunikacie
    stdout = &uart_output;     // presmerovanie printf na UART
    stdin  = &uart_input;      // presmerovanie getchar z UART

    DDRD   |= (1 << PD6);      // nastavenie pinu PD6 (OC0A) ako vystup

    // nastavenie Timer0:
    TCCR0A  = (1 << COM0A0) | (1 << WGM01); // CTC rezim + toggle vystupu
    TCCR0B  = (1 << CS02);                  // prescaler 256
    OCR0A   = 70;                           // hodnota pre frekvenciu zvuku

    int bpm = 120;   // predvolena hodnota beats per minute

    char buffer[10]; // pole na ulozenie prijateho cisla ako text
    int index = 0;   // aktualna pozicia v bufferi

    printf("Enter BPM:\n"); // vyzva pre pouzivatela

    while (1)
    {
        // kontrola ci prisli data cez UART (neblokujuca kontrola)
        if (UCSR0A & (1 << RXC0))   // ak je prijaty znak
        {
            char c = getchar();     // nacitanie znaku

            // ak bol stlaceny ENTER (koniec vstupu)
            if (c == '\r' || c == '\n')
            {
                buffer[index] = '\0';     // ukoncenie retazca
                int new_bpm = atoi(buffer); // prevod textu na cislo

                // kontrola ci je hodnota v rozumnom rozsahu
                if (new_bpm > 0 && new_bpm < 300)
                {
                    bpm = new_bpm; // nastavenie noveho BPM
                    printf("BPM set to %d\n", bpm);
                }
                else
                {
                    printf("Invalid BPM\n"); // chybny vstup
                }

                index = 0; // reset bufferu pre dalsi vstup
            }
            else if (index < 9)
            {
                buffer[index++] = c; // ulozenie znaku do bufferu
            }
        }

        // vypocet intervalu medzi tickmi (v ms)
        int interval_ms = 60000 / bpm;

        tick();                          // prehranie kliknutia
        delay_ms(interval_ms - 50);      // pockanie do dalsieho ticku
    }
}
#ifndef UART_H_
#define UART_H_

void uart_init( void );
     
void uart_putc( char c );
void uart_puts( const char *s );

char uart_getc( void );



#endif /* UART_H_ */
#define F_CPU 16000000
#define BAUD       9600

#include <avr/io.h>
#include "uart.h"
#include <util/setbaud.h>

void uart_init( void ) 
{
    UBRR0H = UBRRH_VALUE;
    UBRR0L = UBRRL_VALUE;

#if USE_2X
    UCSR0A |= _BV(U2X0);
#else
    UCSR0A &= ~(_BV(U2X0));
#endif

    UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */
    UCSR0B = _BV(RXEN0) | _BV(TXEN0);   /* Enable RX and TX */
}


void uart_putc(char c) 
{
   if (c == '\n') 
    {
       uart_putc('\r');
    }
   loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
   UDR0 = c;
}


void uart_puts(const char *s)
{
  /* toto je vasa uloha */
}

char uart_getc(void) {
    loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */
    return UDR0;
}


Zdrojový kód: Projekt_metronom_Kilian.zip

Overenie

Funkčnosť programu bola overená praktickým testovaním v reálnych podmienkach. Používateľ zadával rôzne hodnoty BPM prostredníctvom UART rozhrania v sériovom termináli a následne bola kontrolovaná správnosť generovaného rytmu.

Overenie prebiehalo tak, že po zadaní konkrétnej hodnoty BPM (napr. 60, 120, 180) bol čas medzi jednotlivými „tikmi“ meraný pomocou stopiek. Namerané hodnoty boli následne porovnané s teoretickými intervalmi.

Týmto spôsobom bola overená správna funkcia výpočtu časovania aj samotného generovania signálu.

Pri testovaní sa zároveň overilo, že zmena BPM počas behu programu sa okamžite prejaví na rýchlosti metronómu bez nutnosti reštartu zariadenia. Program sa správal stabilne aj pri opakovaných zmenách vstupnej hodnoty.

Okrem bežných hodnôt bolo vykonané aj testovanie krajných a nesprávnych vstupov. Boli skúšané veľmi nízke hodnoty (napr. 1–10 BPM) a aj vyššie hodnoty (napr. 200–250 BPM), pri ktorých metronóm pracoval na hranici použiteľnosti. V prípade neplatného vstupu (napr. nulová hodnota, záporné číslo alebo nečíselný vstup) program správne reagoval vypísaním hlásenia „Invalid BPM“ do sériového terminálu a hodnota BPM nebola zmenená.

Na používanie aplikácie je potrebný iba sériový terminál v počítači a základné pripojenie zariadenia, pričom celý proces ovládania je realizovaný zadaním číselnej hodnoty BPM.

Metronóm UART.

Video:

Čo by som urobil inak

Pri spätnej analýze riešeného problému sa ukázalo, že základná funkčnosť metronómu a UART komunikácie funguje správne. Napriek tomu sa počas práce objavil menší problém v spôsobe komunikácie s používateľom v sériovom termináli.

Program priebežne vypisuje text „Enter BPM:“ do terminálu, čo spôsobuje, že pri zadávaní hodnoty BPM nie je vždy prehľadne viditeľný samotný vstup používateľa. Terminál je tak čiastočne zahltený opakovanými výpismi, čo znižuje komfort ovládania.

V budúcej verzii by bolo vhodné upraviť spôsob práce s terminálom tak, aby sa výpisy zobrazovali prehľadnejšie, napríklad formou jednoduchšieho textového menu alebo jednorazovej výzvy na zadanie hodnoty BPM bez opakovaného vypisovania. Tým by sa zvýšila čitateľnosť a používateľská prívetivosť celej aplikácie.


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