Архитектура AVR в примерах/Калькулятор на асинхронном порту
В этой работе мы рассмотрим использование встроенного в МК ATmega8 универсального асинхронного приемопередатчика (УАПП; англ. UART) на примере программы, реализующей простейший целочисленный калькулятор с постфиксной записью.
Перед началом
[править]Для данной работы нам потребуется наличие связи между простейшим устройством и адаптером «USB—асинхронный порт» — которая, впрочем, уже существует, поскольку необходима для взаимодействия с загрузчиком Optiboot.[1]
Рассматриваемый код полагается на библиотеку УАПП за авторством Peter Fleury (uartlibrary.zip
), которая является свободным программным обеспечением и доступна на условиях GNU General Public License.[2]
Со стороны системы разработчика, для связи с устройством можно применить такие программы, как cu (англ. call up или call Unix) или PuTTY.
Все прочие требования к среде разработки и устройству также сохраняются.
Сборка кода
[править]/*** uartc.c — A simplistic calculator on UART -*- C -*- */
#include <avr/interrupt.h> /* for sei (), ISR () */
#include <avr/io.h> /* for DDRB, PORTB, etc. */
#include <avr/pgmspace.h> /* FIXME: should be in uart.h */
#include <avr/sleep.h> /* for sleep_enable (), etc. */
#include <avr/wdt.h> /* for wdt_enable (), etc. */
#include <ctype.h> /* for tolower () */
#include <stdlib.h> /* for ltoa () */
#include <util/setbaud.h> /* for UBRRH_VALUE, UBRRL_VALUE */
#include "uart.h"
static volatile uint8_t seen_interrupt_p = 0;
ISR (TIMER0_OVF_vect) {
seen_interrupt_p = 1;
}
int
main ()
{
/* expecting a reset about every 10 ms */
wdt_enable (WDTO_1S);
/* initialize the UART library */
uart_init ((UBRR_VALUE
| (USE_2X ? 0x8000 : 0)));
/* initialize timer 0 to ensure we are interrupted once in a
while; CS0 = 101 _2: Divide F_CPU by 1024 */
TCCR0 = ((1 << CS02)
| (1 << CS00));
/* Timer 0 overflow frequency is thus F_CPU / 1024 / 256,
or 28 Hz to 76 Hz for F_CPU of 7.3728 MHz to 20 MHz */
/* enable interrupts */
TIMSK |= (1 << TOIE0); /* timer 0 overflow */
sei ();
/* show a test message */
uart_puts_P ("\r\nREADY\r\n");
/* main loop */
uint8_t number_p;
long x, y;
for (x = y = 0, number_p = 1; ; ) {
unsigned int c = uart_getc ();
if ((c & (~ 0xff)) == 0) {
uint8_t
echo_p = 1,
new_number_p = 0;
/* ignore all errors, including UART_NO_DATA */
switch (tolower (c)) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if (! number_p) {
x = y;
y = 0;
}
y *= 10;
y += (c - '0');
new_number_p = 1;
break;
case '+': x += y; y = x; break;
case '*': x *= y; y = x; break;
case '/': x /= y; y = x; break;
case '-': x -= y; y = x; break;
case 'p': case 'n':
/* inhibit echo later, do it now */
echo_p = 0;
uart_putc (c);
/* NB: it takes some 3.32 bits per a decimal digit */
char s[4 + (sizeof (y) * 8 - 1) / 3]
= "\r\n";
ltoa (y, 2 + s, 10);
uart_puts (s);
uart_putc ('\r');
uart_putc ('\n');
break;
case ' ': case '\t':
/* ignore whitespace (but still echo it) */
break;
case '\r': case '\n':
/* ensure \r\n */
echo_p = 0;
uart_putc ('\r');
uart_putc ('\n');
break;
default:
echo_p = 0;
uart_puts_P ("\r\nHuh?\r\n");
break;
}
if (echo_p) {
uart_putc (c);
}
number_p = new_number_p;
}
/* reset the watchdog timer */
wdt_reset ();
/* sleep until the next event */
sleep_cpu ();
}
/* not reached */
/* . */
return 0;
}
/*** uartc.c ends here */
Внесем в созданный ранее
Makefile
следующие изменения.Добавим новую переменную
BAUD
, определив ее так:BAUD = 115200
Дополним определение переменной
CPPFLAGS
параметром-DBAUD=$(BAUD)
.Добавим следующие цели:
default: uartc.hex uartc uartc: uartc.c uart.c
Создадим файл
uartc.c
приведенного выше содержания.Скопируем файлы
uart.c
иuart.h
используемой библиотеки УАПП (uartlibrary.zip
) в директорию с исходным кодом.[2]Соберем рассматриваемый пример выполнив команду
make
:$ make uartc.hex avr-gcc -O2 -Wall -std=gnu11 -mmcu=atmega8 -DF_CPU=7372800 -DBAUD=115200 uartc.c -o uartc avr-objcopy -O ihex uartc uartc.hex $
- NB
При использовании микроконтроллера, отличного от ATmega8, кварцевого резонатора на частоту, отличную от 7.3728 MHz, или же желаемой скорости передачи данных, отличной от 115200 bit⁄s, следует явно указать параметры сборки
MCU
,F_CPU
, илиBAUD
, соответственноТак, для платы Arduino Uno R3, поставляемой с МК ATmega328P и кварцевым резонатором на 20 MHz, команда сборки может быть следующей:
$ make MCU=atmega328p F_CPU=20000000 uartc.hex
Удостоверимся в отсутствии ошибок сборки и в появлении файла
uartc.hex
, содержащего результирующий машинный код в формате Intel hex.
Загрузка кода и проверка работоспособности
[править]Загрузку кода в МК выполним аналогично ранее рассмотренному примеру.
Проверим работоспособность кода и устройства установив соединение и вычислив (5 + 4) × 3 ∕ 2 − 1 (в целых числах):
$ cu -l /dev/ttyUSB1 -s 115200 Connected. READY 5 4 + 3 * 2 / 1 - n 12 ~. Disconnected. $
- NB
Программа cu настаивает на использовании сигналов RTS и CTS порта RS-232. Поскольку сигнал CTS не формируется МК, передача данных устройству не будет выполняться. Отключить ожидание можно выполнив после запуска cu (но перед вводом каких-либо данных!) команду, подобную:
$ stty -crtscts -F /dev/ttyUSB1 $
Исследование
[править]Попробуйте реализовать в коде обработку вводимых кодов BS (8 или
'\b'
; C-h) и DEL (127; C-8) — один из этих кодов, как правило, закрепляется за клавишей Backspace — для отмены ввода последнего символа, если такой символ — цифра.Код выше не предусматривает возможность непосредственного ввода отрицательных чисел — ввод
-
немедленно приводит к выполнению операции вычитания. Попробуйте дополнить код буфером введенной операции так, чтобы:- коды BS и DEL приводили к отмене ввода также и последнего символа операции;
- операция вычитания выполнялась только при вводе после
-
символа, отличного от цифры; - ввод
-
непосредственно перед цифрой воспринимался как знак вводимого числа.
Наконец, можно попробовать реализовать в коде полноценный буфер введенной строки, с возможностью удаления любого символа кодами BS и DEL, или всей строки в целом кодом 21 (C-u).
Примечания
[править]- ↑ optiboot — An optimised bootloader for Arduino platforms. Проверено 15 марта 2014.
- ↑ 2,0 2,1 Peter Fleury AVR-GCC libraries. Проверено 15 марта 2014.