Operácie

UART Kalkulačka: Rozdiel medzi revíziami

Zo stránky SensorWiki

StudentMIPS (diskusia | príspevky)
StudentMIPS (diskusia | príspevky)
Riadok 405: Riadok 405:




[[Súbor:GeminiAI-image1.jpg|400px|thumb|center|Aplikácia.]]
[[Súbor:Dzbarduino.jpg|400px|thumb|center|Aplikácia.]]


'''Video:'''
'''Video:'''

Verzia z 21:18, 9. jún 2026

Záverečný projekt predmetu MIPS / LS2026 - Broňa Dzivjaková


Zadanie

Úlohou bolo implementovať kalkulačku komunikujúcu cez sériové rozhranie UART (minimálne použiť: +, -, *, /). Používateľ zadáva matematický príklad (napr. 10+5 alebo 3.14*2) priamo cez sériový terminál a Arduino UNO výsledok vypočíta a odošle späť. Po úspešnom výpočte sa rozsvieti vstavaná LED dióda na pine 13. Program taktiež uchováva históriu posledných desiatich príkladov v pamäti EEPROM, ktorá zostane zachovaná aj po vypnutí zariadenia. Históriu je možné zobraziť príkazom 'h' a vymazať príkazom 'c'.

Vývojová doska Arduino UNO.

Literatúra:


Analýza a opis riešenia

Projekt je realizovaný na vývojovej doske Arduino UNO s mikrokontrolérom ATmega328P. Komunikácia prebieha cez UART rozhranie na rýchlosti 9600 baudov.

Nakoľko matematické operácie sú na 8-bitových mikrokontroléroch nepresné, tak príklady sú riešené pomocou fixnej desatinnej čiarky. Všetky zadané hodnoty sú spracované ako stotiny (násobené *100) s použitím vlastnej funkcie. Týmto spôsobom vie kalkulačka dosiahnuť presnosť na 2 desatinné miesta.

Celkový pohľad na zariadenie.


Na vstup ani výstup nie sú potrebné žiadne externé súčiastky, nakoľko som využila vstavanú LED diódu na pine 13 (PB5) a sériový port cez USB kábel.

Schéma zapojenia.


Algoritmus a program

Program je rozdelený do niekoľkých funkcií. Po spustení sa inicializuje UART (funkcia uart_init) a LED pin ako výstup. Hlavná nekonečná slučka čaká na vstup od používateľa. Funkcia 'read_line' číta znaky zo sériového portu jeden po druhom a ukladá ich do vyrovnávacej pamäte (buffer), kým nepríde 'Enter'.

Hlavné funkcie programu:

• Funkcia 'na_stotiny', ktorá nám spracuje načítaný vstup z pamäte. Preskakuje medzery, spracuje nám záporné čísla a prenásobí ich *100.

• Funkcia 'process' je jadro kalkulačky. Kontroluje nám validitu vstupu a obsahuje iba čísla. Následne vyhľadáva operátor, podľa ktorého vykonáva matematickú operáciu. Ošetruje taktiež aj delenie nulou, výsledky so zápornými znamienkami a korektné formátovanie.

• Funkcie 'hist_save', 'hist_print' a 'hist_clear' zabezpečujú cyklický posun a ukladanie reťazcov do EEPROM pamäte. Pri zápise nového príkladu sa staré záznamy posunú o index vyššie. Pamäť ukladá 10 operácií. Pamäť sa maže zapísaním nulových bitov.

• Funkcia 'led_blink' nám rieši nastavenie pinu PB na vstavanej LED do logickej jednotky po dobu 1000 ms.


#define F_CPU 16000000UL

#include <avr/io.h>
#include <avr/eeprom.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "uart.h"


#define LED_DDR   DDRB
#define LED_PORT  PORTB
#define LED_PIN   PB5 //vstavaná LED

#define HIST_COUNT  10  // História definovaná - uloží mi posledných 10 operácií
#define HIST_LEN    32 
char EEMEM ee_hist[HIST_COUNT][HIST_LEN];

static FILE uart_stream = FDEV_SETUP_STREAM(uart_putc, uart_getc, _FDEV_SETUP_RW);

 // používané funkcie
void    led_init(void);
void    led_blink(void);
uint8_t read_line(char *buf, uint8_t max);
void    process(const char *line);
void    hist_save(const char *line);
void    hist_print(void);
void    hist_clear(void);
int     na_stotiny(const char *str);


int main(void)
{
    char buf[HIST_LEN];

    uart_init();				//basic uart inicializácia
    stdin  = &uart_stream;
    stdout = &uart_stream;
    led_init();

	printf("=================================\r\n\r");
	printf("======= MIPS 2026 ZADANIE: ======\r\n");
    printf("======== UART Kalkulačka ========\r\n");
    printf("   Zadaj príklad (napr.: 3.14*2)\r\n");
	printf("   Dostupné operandy: +, -, *, / \r\n");
    printf("     Pre pozretie histórie: h \r\n");
    printf("    Pre vymazanie histórie: c \r\n");
    printf("=================================\r\n\r\n");

    while (1)
    {
        printf("> ");
        if (read_line(buf, HIST_LEN))
        {
            if (strcmp(buf, "h") == 0) {		//čítanie zadaného vstupu 
                hist_print();					//kontrola, či nie je 'h' alebo 'c'
            }
            else if (strcmp(buf, "c") == 0) { 
                hist_clear();
            }
            else {
                process(buf);
            }
        }
    }
    return 0;
}


void led_init(void)
{
    LED_DDR  |=  (1 << LED_PIN);
    LED_PORT &= ~(1 << LED_PIN);
}
									//basic LED blikanie
void led_blink(void)
{
    LED_PORT |=  (1 << LED_PIN);
    _delay_ms(1000);
    LED_PORT &= ~(1 << LED_PIN);
}


uint8_t read_line(char *buf, uint8_t max)	//čítanie vstupu a 
{											// ukladanie do bufferu, kde to potom spracuvávam
    uint8_t i = 0;
    char c;
    while (i < max - 1)
    {
        c = uart_getc(stdin);
        if (c == '\r' || c == '\n') { uart_puts("\r\n"); break; }
        if (c == '\b' && i > 0)    { i--; uart_puts("\b \b"); continue; } //možný backspace
        uart_putc(c, stdout);
        buf[i++] = c;
    }
    buf[i] = '\0';
    return (i > 0);
}



int na_stotiny(const char *str)  //keďže nefunguje float dobre a ani iné operácie s desatinami
{								//musím to manuálne spracovať - zvolila som si prácu s max 
    int cela = 0;				// 2 desatinnými číslami 
    int desatina = 0;
    int znamienko = 1;
    const char *p = str;

    while (*p == ' ') p++;

    if (*p == '-') { znamienko = -1; p++; }
    else if (*p == '+') p++;

    //hlavným cieľom je nájsť bodku/čiarku - tu čítam čísla pred
    while (*p >= '0' && *p <= '9') {
        cela = cela * 10 + (*p - '0');
        p++;
    }

    // ak prídem na . akebo , 
    if (*p == '.' || *p == ',') {
        p++;
        if (*p >= '0' && *p <= '9') {
            desatina += (*p - '0') * 10; // desatina
            p++;
        }
        if (*p >= '0' && *p <= '9') {
            desatina += (*p - '0');      // stotina
            p++;
        }
    }

    return (cela * 100 + desatina) * znamienko;
}

void process(const char *line)
{
    // Kontrola na nepovolené znaky (písmená a iné)
    const char *check = line;
    while (*check) {
        if (!isdigit((unsigned char)*check) && *check != '.' && 
            *check != '+' && *check != '-' && *check != '*' && *check != '/') {
            printf("Chyba: neplatný znak v príklade!\r\n\r\n");
            return;
        }
        check++;
    }

    const char ops[] = "*/+-";
    const char *op_pos = NULL;
    uint8_t i;
    
    // aký operátor
    for (i = 0; i < 4; i++) {
        op_pos = strchr(line + 1, ops[i]);
        if (op_pos) break;
    }

    if (!op_pos) {
        printf("Chyba: nebol zadaný operátor!\r\n\r\n");
        return;
    }

    char op = *op_pos;

    // a-pred čiarkou, b-za čiarkou
    long a = na_stotiny(line);
    long b = na_stotiny(op_pos + 1);
    long r = 0;

    if (op == '/' && b == 0) {		//kontrola delenia s 0
        printf("Chyba: delenie nulou!\r\n\r\n");
        return;
    }

    //operácie poriešené cez * a / 100 
    if (op == '+') r = a + b;
    else if (op == '-') r = a - b;
    else if (op == '*') r = (a * b) / 100; 
    else if (op == '/') r = (a * 100) / b;

    long cela_cast = r / 100;
    long des_cast = r % 100;
    if (des_cast < 0) des_cast = -des_cast;


    // rozlišovanie, či výsledok má desatinné číslo
     printf("Vysledok: ");
    if (r < 0 && cela_cast == 0) {
        printf("-");
    }

    printf("%ld", cela_cast);

    // ak zistím, ... vypíšem bodku
    if (des_cast != 0) {
        printf(".");
        if (des_cast < 10) {
            printf("0"); // ak mám 0.05 napr.
        }
        printf("%ld", des_cast);
    }
    printf("\r\n\r\n");

    char hist_buffer[HIST_LEN];
    if (des_cast != 0) {
        if (r < 0 && cela_cast == 0) {
            sprintf(hist_buffer, "%s=-%ld.%02ld", line, cela_cast, des_cast);
        } else {
            sprintf(hist_buffer, "%s=%ld.%02ld", line, cela_cast, des_cast);
        }
    } else {
        sprintf(hist_buffer, "%s=%ld", line, cela_cast);
    }

    hist_save(hist_buffer); // uložím do histórie
    led_blink();
}

void hist_save(const char *line)
{
    char tmp[HIST_LEN];
    int8_t i;
    
    for (i = HIST_COUNT - 1; i > 0; i--) {
        eeprom_read_block(tmp, ee_hist[i - 1], HIST_LEN);
        eeprom_write_block(tmp, ee_hist[i], HIST_LEN);
    }
    eeprom_write_block(line, ee_hist[0], HIST_LEN);
}

void hist_print(void)   
{
    char tmp[HIST_LEN];
    uint8_t found = 0;
    uint8_t i;
   
    printf("============ História ===========\r\n", HIST_COUNT);
    for (i = 0; i < HIST_COUNT; i++) {
        eeprom_read_block(tmp, ee_hist[i], HIST_LEN);
        tmp[HIST_LEN - 1] = '\0';
        
        if ((unsigned char)tmp[0] == 0xFF || tmp[0] == '\0') continue;
        
        printf("  %d: %s\r\n", i + 1, tmp);
        found = 1;
    }
    if (!found) printf("       História je prázdna.\r\n");
    printf("=================================\r\n\r");
}

void hist_clear(void)  //vymazanie histórie
{
    char prazdne[HIST_LEN];
    uint8_t i;
    memset(prazdne, 0, HIST_LEN);
    for (i = 0; i < HIST_COUNT; i++) {
        eeprom_write_block(prazdne, ee_hist[i], HIST_LEN);
    }
    printf("       História je vymazaná.\r\n\r\n");
}
#ifndef UART_H_
#define UART_H_

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

void uart_init(void);
//void uart_putc(char c);
 
 
//char uart_getc(void);
int  uart_putc(char c, FILE *stream);
int  uart_getc(FILE *stream);

void uart_puts(const char *s);

#endif /* UART_H_ */
#define F_CPU 16000000UL
#define BAUD  9600
#define MYUBRR (F_CPU / 16 / BAUD - 1)

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

void uart_init(void)
{
    unsigned int ubrr = MYUBRR;

    /* nastavenie baud rate */
    UBRR0H = (unsigned char)(ubrr >> 8);
    UBRR0L = (unsigned char)(ubrr);

    /* zapni RX a TX */
    UCSR0B = (1 << RXEN0) | (1 << TXEN0);

    /* 8 datových bitov, 1 stop bit, bez parity */
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}

int uart_putc(char c, FILE *stream)
{
    if (c == '\n')
        uart_putc('\r', stream);

    while (!(UCSR0A & (1 << UDRE0)));   /* čakaj kým je buffer prázdny */
    UDR0 = c;

    return 0;
}

int uart_getc(FILE *stream)
{
    while (!(UCSR0A & (1 << RXC0)));    /* čakaj na prijatý znak */
    return UDR0;
}

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


Zdrojový kód: zdrojaky.zip

Overenie

Funkčnosť som overila pomocou sériového terminálu s knižnicou UART. Testovala som každú z ponúknutých operácií, operáciu s desatinnými a zápornými číslami, a aj chybové stavy.

• Základné operácie: 5+8 = 13, 8*3 = 24, -9-4 = -13, 10/4 = 2.5, 89/0 = Delenie nulou

• Desatinné čísla: 3.14*7 = 21.98

• Chybové stavy: delenie nulou, zadanie písmena namiesto čísla

• História: príkaz 'h' zobrazí posledné 3 príklady, 'c' ich vymaže

• LED: po každom správnom výpočte blikne vstavaná LED na pine 13


Aplikácia.

Video:

Čo by som urobil inak

Projekt splnil požiadavky zadania, no v budúcnosti by som ho rozšírila o LCD displej, kde by sa príklady a výsledky zobrazovali priamo na zariadení bez potreby počítača a sériového terminálu. Taktiež by bolo zaujímavé pridať podporu pre viac operácií v jednom výraze s dodržaním matematickej priority (násobenie pred sčítaním).


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