UART Kalkulačka: Rozdiel medzi revíziami
Zo stránky SensorWiki
| Riadok 269: | Riadok 269: | ||
} | } | ||
void hist_print(void) | void hist_print(void) | ||
{ | { | ||
char tmp[HIST_LEN]; | char tmp[HIST_LEN]; | ||
Verzia z 20:31, 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 troch 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 'clear'.

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. 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.

Nezabudnite doplniť schému zapojenia! V texte by ste mali opísať základné veci zo zapojenia, samotná schéma nie je dostačujúci opis.

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á 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 buffera, kým nepríde 'Enter'.
Funkcia 'process' spracuje zadaný reťazec. Najprv vyhľadá operátor (+, -, *, /) v reťazci, následne skontroluje, či sú oba vstupné operandy platné čísla. Ak používateľ zadá písmeno namiesto čísla, program vypíše chybu. Funkcia 'atof()' konvertuje reťazce na desatinné čísla, čo umožňuje pracovať aj s desatinnými hodnotami (napr. 3.14*2). Výsledok sa vypíše cez UART a príklad sa uloží do EEPROM histórie.
#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);
}
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
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.
• Základné operácie: 10+5 = 15, 8*3 = 24, 9-4 = 5, 10/4 = 2.5000
• Desatinné čísla: 3.14*2 = 6.2800
• Chybové stavy: delenie nulou, zadanie písmena namiesto čísla
• História: príkaz 'h' zobrazí posledné 3 príklady, 'clc' 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.