UART Kalkulačka: Rozdiel medzi revíziami
Zo stránky SensorWiki
| (11 medziľahlých úprav od rovnakého používateľa nie je zobrazených.) | |||
| Riadok 4: | Riadok 4: | ||
== Zadanie == | == 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 | Ú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'. | ||
[[Obrázok:arduinoUNO.png|400px|thumb|center|Vývojová doska Arduino UNO.]] | [[Obrázok:arduinoUNO.png|400px|thumb|center|Vývojová doska Arduino UNO.]] | ||
| Riadok 17: | Riadok 17: | ||
== Analýza a opis riešenia == | == 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 | 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. | |||
[[Súbor:ZapojenieUNO.jpg|400px|thumb|center|Celkový pohľad na zariadenie.]] | |||
[[Súbor: | |||
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. | |||
[[Súbor:ZapUnoSchem.png|400px|thumb|center|Schéma zapojenia.]] | |||
=== Algoritmus a program === | === Algoritmus a program === | ||
Program je rozdelený do niekoľkých funkcií. Po spustení sa inicializuje UART | 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. | |||
| Riadok 376: | Riadok 386: | ||
</tabs> | </tabs> | ||
Zdrojový kód: [[Médiá: | Zdrojový kód: [[Médiá:ProjektDzivjakovaBronaMIPS.zip|zdrojaky.zip]] | ||
=== Overenie === | === 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 číslami, a aj chybové stavy. | 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: | • 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* | • Desatinné čísla: 3.14*7 = 21.98 | ||
• Chybové stavy: delenie nulou, zadanie písmena namiesto čísla | • Chybové stavy: delenie nulou, zadanie písmena namiesto čísla | ||
• História: príkaz 'h' zobrazí posledné 3 príklady, ' | • 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 | • LED: po každom správnom výpočte blikne vstavaná LED na pine 13 | ||
| Riadok 396: | Riadok 405: | ||
[[Súbor: | [[Súbor:Dzbarduino.jpg|400px|thumb|center|Bliknutie LED pri výpočte.]] | ||
'''Video:''' | '''Video:''' | ||
<center><youtube> | <center><youtube>Wl-AcCXg7Nw</youtube></center> | ||
== Čo by som urobil inak == | == Čo by som urobil inak == | ||
Aktuálna revízia z 21:40, 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'.

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.

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.

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

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.