From 2b38adb74d23a0deea2d16031fb49cf8d553ffb9 Mon Sep 17 00:00:00 2001 From: MightyPork Date: Mon, 27 Apr 2015 19:31:03 +0200 Subject: [PATCH] added new uart stuff --- projects/uart_key_analyzer/Makefile | 137 +++++ projects/uart_key_analyzer/lib/uart.c | 228 +++++++ projects/uart_key_analyzer/lib/uart.h | 88 +++ projects/uart_key_analyzer/main.c | 32 + projects/uart_keyhandler_test/Makefile | 137 +++++ projects/uart_keyhandler_test/lib/uart.c | 228 +++++++ projects/uart_keyhandler_test/lib/uart.h | 88 +++ projects/uart_keyhandler_test/lib/uart_ansi.c | 580 ++++++++++++++++++ projects/uart_keyhandler_test/lib/uart_ansi.h | 192 ++++++ projects/uart_keyhandler_test/main.c | 46 ++ 10 files changed, 1756 insertions(+) create mode 100644 projects/uart_key_analyzer/Makefile create mode 100644 projects/uart_key_analyzer/lib/uart.c create mode 100644 projects/uart_key_analyzer/lib/uart.h create mode 100644 projects/uart_key_analyzer/main.c create mode 100644 projects/uart_keyhandler_test/Makefile create mode 100644 projects/uart_keyhandler_test/lib/uart.c create mode 100644 projects/uart_keyhandler_test/lib/uart.h create mode 100644 projects/uart_keyhandler_test/lib/uart_ansi.c create mode 100644 projects/uart_keyhandler_test/lib/uart_ansi.h create mode 100644 projects/uart_keyhandler_test/main.c diff --git a/projects/uart_key_analyzer/Makefile b/projects/uart_key_analyzer/Makefile new file mode 100644 index 0000000..c05c801 --- /dev/null +++ b/projects/uart_key_analyzer/Makefile @@ -0,0 +1,137 @@ +## === CPU settings === +# CPU type +MCU = atmega328p +# CPU frequency +F_CPU = 16000000 +# Fuses +LFUSE = 0xFF +HFUSE = 0xDE +EFUSE = 0x05 + + +## === Source files === +# Main C file +MAIN = main.c +# Extra C files in this folder +LOCAL_SOURCE = + +# Library directory (with C files) +EXTRA_SOURCE_DIR = lib/ +# C files in the library directory +EXTRA_SOURCE_FILES = uart.c + + +## === Programmer === +PROGRAMMER_TYPE = arduino +PROGRAMMER_ARGS = -b 57600 -P /dev/ttyUSB0 + + +## === C flags === + +CFLAGS = -std=gnu99 -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL -I. -I$(EXTRA_SOURCE_DIR) +CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +CFLAGS += -Wall -Wno-main -Wno-strict-prototypes -Wno-comment +CFLAGS += -g2 -Wextra -Wfatal-errors -Wno-unused-but-set-variable +CFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--relax +# CFLAGS += -lm ## Math +# CFLAGS += -Wl,-u,vfprintf -lprintf_flt -lm ## for floating-point printf +# CFLAGS += -Wl,-u,vfprintf -lprintf_min ## for smaller printf +CFLAGS_BUILD = $(CFLAGS) -Os + + +# --------------------------------------------------------------------------- + +## Defined programs / locations +CC = avr-gcc +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump +AVRSIZE = avr-size +AVRDUDE = avrdude + +## === File lists === +TARGET = $(strip $(basename $(MAIN))) +SRC1 = $(TARGET).c +SRC = $(SRC1) +EXTRA_SOURCE = $(addprefix $(EXTRA_SOURCE_DIR), $(EXTRA_SOURCE_FILES)) +SRC += $(EXTRA_SOURCE) +SRC += $(LOCAL_SOURCE) + +HEADERS = $(SRC:.c=.h) +OBJ = $(SRC:.c=.o) + + +## === File generation === +all: $(TARGET).hex size +pre: $(TARGET).pre + +%.hex: %.elf + $(OBJCOPY) -R .eeprom -O ihex $< $@ + +%.elf: $(SRC) + $(CC) $(CFLAGS_BUILD) $(SRC) --output $@ + +%.pre: $(SRC1) + $(CC) $(CFLAGS) -E $(SRC1) --output $@ + +%.eeprom: %.elf + $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@ + +%.lst: %.elf + $(OBJDUMP) -S $< > $@ + +# Show debug info +debug: + @echo + @echo "Source files:" $(SRC) + @echo "MCU, F_CPU, BAUD:" $(MCU), $(F_CPU), $(BAUD) + @echo + + +# Disassemble the ELF +disassemble: $(TARGET).lst +dis: disassemble +lst: disassemble + +# Make eeprom file +eeprom: $(TARGET).eeprom + +# Show how big the resulting program is +size: $(TARGET).elf + $(AVRSIZE) -C --mcu=$(MCU) $(TARGET).elf + +# Clean all produced trash +clean: + rm -f $(TARGET).elf $(TARGET).hex $(TARGET).obj \ + $(TARGET).o $(TARGET).d $(TARGET).eep $(TARGET).lst \ + $(TARGET).lss $(TARGET).sym $(TARGET).map $(TARGET)~ \ + $(TARGET).eeprom + +# Clean all trash +purge: + rm -f *.elf *.hex *.obj *.o *.d *.eep *.lst *.lss *.sym *.map *~ + + +## === avrdude === + +flash: $(TARGET).hex + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U flash:w:$< + +flashe: $(TARGET).eeprom + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U eeprom:w:$< + +shell: + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nt + + +# === fuses === + +FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m + +fuses: + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) \ + $(PROGRAMMER_ARGS) $(FUSE_STRING) +show_fuses: + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nv + +set_default_fuses: FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m +set_default_fuses: fuses diff --git a/projects/uart_key_analyzer/lib/uart.c b/projects/uart_key_analyzer/lib/uart.c new file mode 100644 index 0000000..4f18f52 --- /dev/null +++ b/projects/uart_key_analyzer/lib/uart.c @@ -0,0 +1,228 @@ +#include +#include +#include +#include +#include +#include + +#include "uart.h" + + +void _uart_init_do(uint16_t ubrr) { + /*Set baud rate */ + UBRR0H = (uint8_t) (ubrr >> 8); + UBRR0L = (uint8_t) ubrr; + + // Enable Rx and Tx + UCSR0B = (1 << RXEN0) | (1 << TXEN0); + + // 8-bit data, 1 stop bit + UCSR0C = (0b11 << UCSZ00); +} + + +/** Enable or disable RX ISR */ +void uart_isr_rx(bool yes) +{ + if(yes) { + UCSR0B |= (1 << RXCIE0); + } else { + UCSR0B &= ~(1 << RXCIE0); + } +} + + +/** Enable or disable TX ISR (1 byte is sent) */ +void uart_isr_tx(bool yes) +{ + if(yes) { + UCSR0B |= (1 << TXCIE0); + } else { + UCSR0B &= ~(1 << TXCIE0); + } +} + + +/** Enable or disable DRE ISR (all is sent) */ +void uart_isr_dre(bool yes) +{ + if(yes) { + UCSR0B |= (1 << UDRIE0); + } else { + UCSR0B &= ~(1 << UDRIE0); + } +} + + +/** Send byte over UART */ +void uart_tx(uint8_t data) +{ + // Wait for transmit buffer + while (!uart_tx_ready()); + // send it + UDR0 = data; +} + + +/** Receive one byte over UART */ +uint8_t uart_rx() +{ + // Wait for data to be received + while (!uart_rx_ready()); + // Get and return received data from buffer + return UDR0; +} + + +/** Send string over UART */ +void uart_puts(const char* str) +{ + while (*str) { + uart_tx(*str++); + } +} + + +/** Send progmem string over UART */ +void uart_puts_pgm(const char* str) +{ + char c; + while ((c = pgm_read_byte(str++))) { + uart_tx(c); + } +} + + +/** Clear receive buffer */ +void uart_flush() +{ + uint8_t dummy; + while (UCSR0A & (1 << RXC0)) + dummy = UDR0; +} + + +/** Send CRLF */ +void uart_nl() +{ + uart_tx(13); + uart_tx(10); +} + + +char tmpstr[12]; // buffer for number rendering + +void _uart_putnf(const uint8_t places); + + +/** Send signed int8 */ +void uart_putu(const uint8_t num) +{ + utoa(num, tmpstr, 10); + uart_puts(tmpstr); +} + + +/** Send unsigned int8 */ +void uart_putn(const int8_t num) +{ + itoa(num, tmpstr, 10); + uart_puts(tmpstr); +} + + +/** Send unsigned int as float */ +void uart_putiu(const uint16_t num, const uint8_t places) +{ + if (!places) { + utoa(num, tmpstr, 10); + uart_puts(tmpstr); + } else { + utoa(num, tmpstr, 10); + _uart_putnf(places); + } +} + + +/** Send signed int as float */ +void uart_puti(const int16_t num, const uint8_t places) +{ + if (!places) { + itoa(num, tmpstr, 10); + uart_puts(tmpstr); + } else { + if (num < 0) { + uart_tx('-'); + itoa(-num, tmpstr, 10); + } else { + itoa(num, tmpstr, 10); + } + + _uart_putnf(places); + } +} + + +/** Send unsigned long as float */ +void uart_putlu(const uint32_t num, const uint8_t places) +{ + if (!places) { + ultoa(num, tmpstr, 10); + uart_puts(tmpstr); + } else { + ultoa(num, tmpstr, 10); + _uart_putnf(places); + } +} + + +/** Send signed long as float */ +void uart_putl(const int32_t num, const uint8_t places) +{ + if (!places) { + ltoa(num, tmpstr, 10); + uart_puts(tmpstr); + } else { + if (num < 0) { + uart_tx('-'); + ltoa(-num, tmpstr, 10); + } else { + ltoa(num, tmpstr, 10); + } + + _uart_putnf(places); + } +} + + +/** Print number in tmp string as float with given decimal point position */ +void _uart_putnf(const uint8_t places) +{ + // measure text length + uint8_t len = 0; + while(tmpstr[len] != 0) len++; + + int8_t at = len - places; + + // print virtual zeros + if (at <= 0) { + uart_tx('0'); + uart_tx('.'); + while(at <= -1) { + uart_tx('0'); + at++; + } + at = -1; + } + + // print the number + uint8_t i = 0; + while(i < len) { + if (at-- == 0) { + uart_tx('.'); + } + + uart_tx(tmpstr[i++]); + } +} + diff --git a/projects/uart_key_analyzer/lib/uart.h b/projects/uart_key_analyzer/lib/uart.h new file mode 100644 index 0000000..1a44d6e --- /dev/null +++ b/projects/uart_key_analyzer/lib/uart.h @@ -0,0 +1,88 @@ +#pragma once + +// +// Utilities for UART communication. +// +// First, init uart with desired baud rate using uart_init(baud). +// Then enable interrupts you want with uart_isr_XXX(). +// + +#include +#include +#include +#include +#include + + +/** Init UART for given baudrate */ +void _uart_init_do(uint16_t ubrr); // internal, needed for the macro. +#define uart_init(baud) _uart_init_do(F_CPU / 16 / (baud) - 1) + +/** Check if there's a byte in the RX register */ +#define uart_rx_ready() (0 != (UCSR0A & (1 << RXC0))) + +/** Check if transmission of everything is done */ +#define uart_tx_ready() (0 != (UCSR0A & (1 << UDRE0))) + + + +// Enable UART interrupts + +/** Enable or disable RX ISR */ +void uart_isr_rx(bool enable); + +/** Enable or disable TX ISR (1 byte is sent) */ +void uart_isr_tx(bool enable); + +/** Enable or disable DRE ISR (all is sent) */ +void uart_isr_dre(bool enable); + + + +// Basic IO + +/** Receive one byte over UART */ +uint8_t uart_rx(); + +/** Send byte over UART */ +#define uart_putc(data) uart_tx((data)) +void uart_tx(uint8_t data); + +/** Clear receive buffer */ +void uart_flush(); + + + +// Strings + +/** Send string over UART */ +void uart_puts(const char* str); + +/** Send progmem string over UART */ +void uart_puts_pgm(const char* str); + + +// Numbers + +/** Send unsigned int */ +void uart_putn(const int8_t num); + +/** Send signed int */ +void uart_putu(const uint8_t num); + +/** Send unsigned int */ +void uart_puti(const int16_t num, const uint8_t places); + +/** Send signed int */ +void uart_putiu(const uint16_t num, const uint8_t places); + +/** Send unsigned long */ +void uart_putlu(const uint32_t num, const uint8_t places); + +/** Send signed long */ +void uart_putl(const int32_t num, const uint8_t places); + +// Extras + +/** Send CRLF */ +void uart_nl(); diff --git a/projects/uart_key_analyzer/main.c b/projects/uart_key_analyzer/main.c new file mode 100644 index 0000000..5f6b8f8 --- /dev/null +++ b/projects/uart_key_analyzer/main.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "lib/uart.h" + + +ISR(USART_RX_vect) +{ + uint8_t c = uart_rx(); + + uart_putc(c); + uart_putc(' '); + uart_putn(c); + uart_nl(); +} + + +void main() +{ + uart_init(9600); + uart_isr_rx(1); + sei(); + + uart_puts_pgm(PSTR("UART key analyzer\r\n")); + + while(1); +} diff --git a/projects/uart_keyhandler_test/Makefile b/projects/uart_keyhandler_test/Makefile new file mode 100644 index 0000000..c73aa9c --- /dev/null +++ b/projects/uart_keyhandler_test/Makefile @@ -0,0 +1,137 @@ +## === CPU settings === +# CPU type +MCU = atmega328p +# CPU frequency +F_CPU = 16000000 +# Fuses +LFUSE = 0xFF +HFUSE = 0xDE +EFUSE = 0x05 + + +## === Source files === +# Main C file +MAIN = main.c +# Extra C files in this folder +LOCAL_SOURCE = + +# Library directory (with C files) +EXTRA_SOURCE_DIR = lib/ +# C files in the library directory +EXTRA_SOURCE_FILES = uart.c uart_ansi.c + + +## === Programmer === +PROGRAMMER_TYPE = arduino +PROGRAMMER_ARGS = -b 57600 -P /dev/ttyUSB0 + + +## === C flags === + +CFLAGS = -std=gnu99 -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL -I. -I$(EXTRA_SOURCE_DIR) +CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +CFLAGS += -Wall -Wno-main -Wno-strict-prototypes -Wno-comment +CFLAGS += -g2 -Wextra -Wfatal-errors -Wno-unused-but-set-variable +CFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--relax +# CFLAGS += -lm ## Math +# CFLAGS += -Wl,-u,vfprintf -lprintf_flt -lm ## for floating-point printf +# CFLAGS += -Wl,-u,vfprintf -lprintf_min ## for smaller printf +CFLAGS_BUILD = $(CFLAGS) -Os + + +# --------------------------------------------------------------------------- + +## Defined programs / locations +CC = avr-gcc +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump +AVRSIZE = avr-size +AVRDUDE = avrdude + +## === File lists === +TARGET = $(strip $(basename $(MAIN))) +SRC1 = $(TARGET).c +SRC = $(SRC1) +EXTRA_SOURCE = $(addprefix $(EXTRA_SOURCE_DIR), $(EXTRA_SOURCE_FILES)) +SRC += $(EXTRA_SOURCE) +SRC += $(LOCAL_SOURCE) + +HEADERS = $(SRC:.c=.h) +OBJ = $(SRC:.c=.o) + + +## === File generation === +all: $(TARGET).hex size +pre: $(TARGET).pre + +%.hex: %.elf + $(OBJCOPY) -R .eeprom -O ihex $< $@ + +%.elf: $(SRC) + $(CC) $(CFLAGS_BUILD) $(SRC) --output $@ + +%.pre: $(SRC1) + $(CC) $(CFLAGS) -E $(SRC1) --output $@ + +%.eeprom: %.elf + $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@ + +%.lst: %.elf + $(OBJDUMP) -S $< > $@ + +# Show debug info +debug: + @echo + @echo "Source files:" $(SRC) + @echo "MCU, F_CPU, BAUD:" $(MCU), $(F_CPU), $(BAUD) + @echo + + +# Disassemble the ELF +disassemble: $(TARGET).lst +dis: disassemble +lst: disassemble + +# Make eeprom file +eeprom: $(TARGET).eeprom + +# Show how big the resulting program is +size: $(TARGET).elf + $(AVRSIZE) -C --mcu=$(MCU) $(TARGET).elf + +# Clean all produced trash +clean: + rm -f $(TARGET).elf $(TARGET).hex $(TARGET).obj \ + $(TARGET).o $(TARGET).d $(TARGET).eep $(TARGET).lst \ + $(TARGET).lss $(TARGET).sym $(TARGET).map $(TARGET)~ \ + $(TARGET).eeprom + +# Clean all trash +purge: + rm -f *.elf *.hex *.obj *.o *.d *.eep *.lst *.lss *.sym *.map *~ + + +## === avrdude === + +flash: $(TARGET).hex + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U flash:w:$< + +flashe: $(TARGET).eeprom + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U eeprom:w:$< + +shell: + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nt + + +# === fuses === + +FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m + +fuses: + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) \ + $(PROGRAMMER_ARGS) $(FUSE_STRING) +show_fuses: + $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nv + +set_default_fuses: FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m +set_default_fuses: fuses diff --git a/projects/uart_keyhandler_test/lib/uart.c b/projects/uart_keyhandler_test/lib/uart.c new file mode 100644 index 0000000..4f18f52 --- /dev/null +++ b/projects/uart_keyhandler_test/lib/uart.c @@ -0,0 +1,228 @@ +#include +#include +#include +#include +#include +#include + +#include "uart.h" + + +void _uart_init_do(uint16_t ubrr) { + /*Set baud rate */ + UBRR0H = (uint8_t) (ubrr >> 8); + UBRR0L = (uint8_t) ubrr; + + // Enable Rx and Tx + UCSR0B = (1 << RXEN0) | (1 << TXEN0); + + // 8-bit data, 1 stop bit + UCSR0C = (0b11 << UCSZ00); +} + + +/** Enable or disable RX ISR */ +void uart_isr_rx(bool yes) +{ + if(yes) { + UCSR0B |= (1 << RXCIE0); + } else { + UCSR0B &= ~(1 << RXCIE0); + } +} + + +/** Enable or disable TX ISR (1 byte is sent) */ +void uart_isr_tx(bool yes) +{ + if(yes) { + UCSR0B |= (1 << TXCIE0); + } else { + UCSR0B &= ~(1 << TXCIE0); + } +} + + +/** Enable or disable DRE ISR (all is sent) */ +void uart_isr_dre(bool yes) +{ + if(yes) { + UCSR0B |= (1 << UDRIE0); + } else { + UCSR0B &= ~(1 << UDRIE0); + } +} + + +/** Send byte over UART */ +void uart_tx(uint8_t data) +{ + // Wait for transmit buffer + while (!uart_tx_ready()); + // send it + UDR0 = data; +} + + +/** Receive one byte over UART */ +uint8_t uart_rx() +{ + // Wait for data to be received + while (!uart_rx_ready()); + // Get and return received data from buffer + return UDR0; +} + + +/** Send string over UART */ +void uart_puts(const char* str) +{ + while (*str) { + uart_tx(*str++); + } +} + + +/** Send progmem string over UART */ +void uart_puts_pgm(const char* str) +{ + char c; + while ((c = pgm_read_byte(str++))) { + uart_tx(c); + } +} + + +/** Clear receive buffer */ +void uart_flush() +{ + uint8_t dummy; + while (UCSR0A & (1 << RXC0)) + dummy = UDR0; +} + + +/** Send CRLF */ +void uart_nl() +{ + uart_tx(13); + uart_tx(10); +} + + +char tmpstr[12]; // buffer for number rendering + +void _uart_putnf(const uint8_t places); + + +/** Send signed int8 */ +void uart_putu(const uint8_t num) +{ + utoa(num, tmpstr, 10); + uart_puts(tmpstr); +} + + +/** Send unsigned int8 */ +void uart_putn(const int8_t num) +{ + itoa(num, tmpstr, 10); + uart_puts(tmpstr); +} + + +/** Send unsigned int as float */ +void uart_putiu(const uint16_t num, const uint8_t places) +{ + if (!places) { + utoa(num, tmpstr, 10); + uart_puts(tmpstr); + } else { + utoa(num, tmpstr, 10); + _uart_putnf(places); + } +} + + +/** Send signed int as float */ +void uart_puti(const int16_t num, const uint8_t places) +{ + if (!places) { + itoa(num, tmpstr, 10); + uart_puts(tmpstr); + } else { + if (num < 0) { + uart_tx('-'); + itoa(-num, tmpstr, 10); + } else { + itoa(num, tmpstr, 10); + } + + _uart_putnf(places); + } +} + + +/** Send unsigned long as float */ +void uart_putlu(const uint32_t num, const uint8_t places) +{ + if (!places) { + ultoa(num, tmpstr, 10); + uart_puts(tmpstr); + } else { + ultoa(num, tmpstr, 10); + _uart_putnf(places); + } +} + + +/** Send signed long as float */ +void uart_putl(const int32_t num, const uint8_t places) +{ + if (!places) { + ltoa(num, tmpstr, 10); + uart_puts(tmpstr); + } else { + if (num < 0) { + uart_tx('-'); + ltoa(-num, tmpstr, 10); + } else { + ltoa(num, tmpstr, 10); + } + + _uart_putnf(places); + } +} + + +/** Print number in tmp string as float with given decimal point position */ +void _uart_putnf(const uint8_t places) +{ + // measure text length + uint8_t len = 0; + while(tmpstr[len] != 0) len++; + + int8_t at = len - places; + + // print virtual zeros + if (at <= 0) { + uart_tx('0'); + uart_tx('.'); + while(at <= -1) { + uart_tx('0'); + at++; + } + at = -1; + } + + // print the number + uint8_t i = 0; + while(i < len) { + if (at-- == 0) { + uart_tx('.'); + } + + uart_tx(tmpstr[i++]); + } +} + diff --git a/projects/uart_keyhandler_test/lib/uart.h b/projects/uart_keyhandler_test/lib/uart.h new file mode 100644 index 0000000..1a44d6e --- /dev/null +++ b/projects/uart_keyhandler_test/lib/uart.h @@ -0,0 +1,88 @@ +#pragma once + +// +// Utilities for UART communication. +// +// First, init uart with desired baud rate using uart_init(baud). +// Then enable interrupts you want with uart_isr_XXX(). +// + +#include +#include +#include +#include +#include + + +/** Init UART for given baudrate */ +void _uart_init_do(uint16_t ubrr); // internal, needed for the macro. +#define uart_init(baud) _uart_init_do(F_CPU / 16 / (baud) - 1) + +/** Check if there's a byte in the RX register */ +#define uart_rx_ready() (0 != (UCSR0A & (1 << RXC0))) + +/** Check if transmission of everything is done */ +#define uart_tx_ready() (0 != (UCSR0A & (1 << UDRE0))) + + + +// Enable UART interrupts + +/** Enable or disable RX ISR */ +void uart_isr_rx(bool enable); + +/** Enable or disable TX ISR (1 byte is sent) */ +void uart_isr_tx(bool enable); + +/** Enable or disable DRE ISR (all is sent) */ +void uart_isr_dre(bool enable); + + + +// Basic IO + +/** Receive one byte over UART */ +uint8_t uart_rx(); + +/** Send byte over UART */ +#define uart_putc(data) uart_tx((data)) +void uart_tx(uint8_t data); + +/** Clear receive buffer */ +void uart_flush(); + + + +// Strings + +/** Send string over UART */ +void uart_puts(const char* str); + +/** Send progmem string over UART */ +void uart_puts_pgm(const char* str); + + +// Numbers + +/** Send unsigned int */ +void uart_putn(const int8_t num); + +/** Send signed int */ +void uart_putu(const uint8_t num); + +/** Send unsigned int */ +void uart_puti(const int16_t num, const uint8_t places); + +/** Send signed int */ +void uart_putiu(const uint16_t num, const uint8_t places); + +/** Send unsigned long */ +void uart_putlu(const uint32_t num, const uint8_t places); + +/** Send signed long */ +void uart_putl(const int32_t num, const uint8_t places); + +// Extras + +/** Send CRLF */ +void uart_nl(); diff --git a/projects/uart_keyhandler_test/lib/uart_ansi.c b/projects/uart_keyhandler_test/lib/uart_ansi.c new file mode 100644 index 0000000..2e36975 --- /dev/null +++ b/projects/uart_keyhandler_test/lib/uart_ansi.c @@ -0,0 +1,580 @@ +#include +#include +#include +#include +#include + +#include "uart.h" +#include "uart_ansi.h" + +void _vt_apply_style(); +void _vt_reset_attribs_do(); +void _vt_style_do(); +void _vt_color_do(); + + +void vt_goto(uint8_t x, uint8_t y) +{ + uart_putc(27); + uart_putc('['); + uart_putu(x); + uart_putc(';'); + uart_putu(y); + uart_putc('H'); +} + + +void vt_goto_x(uint8_t x) +{ + uart_putc(27); + uart_putc('['); + uart_putu(x); + uart_putc('`'); +} + + +void vt_goto_y(uint8_t y) +{ + uart_putc(27); + uart_putc('['); + uart_putu(y); + uart_putc('d'); +} + + +void vt_move(int8_t x, int8_t y) +{ + vt_move_x(x); + vt_move_y(y); +} + + +void vt_move_x(int8_t x) +{ + if (x < 0) { + vt_left(-x); + } else { + vt_right(x); + } +} + + +void vt_move_y(int8_t y) +{ + if (y < 0) { + vt_up(-y); + } else { + vt_down(y); + } +} + + +void vt_up(uint8_t y) +{ + if (y == 0) return; + uart_putc(27); + uart_putc('['); + uart_putu(y); + uart_putc('A'); +} + + +void vt_down(uint8_t y) +{ + if (y == 0) return; + uart_putc(27); + uart_putc('['); + uart_putu(y); + uart_putc('B'); +} + + +void vt_left(uint8_t x) +{ + if (x == 0) return; + uart_putc(27); + uart_putc('['); + uart_putu(x); + uart_putc('D'); +} + + +void vt_right(uint8_t x) +{ + if (x == 0) return; + uart_putc(27); + uart_putc('['); + uart_putu(x); + uart_putc('C'); +} + + +void vt_scroll(int8_t y) +{ + while (y < 0) { + uart_putc(27); + uart_putc('D'); // up + y++; + } + + while (y > 0) { + uart_putc(27); + uart_putc('M'); // down + y--; + } +} + + +void vt_scroll_set(uint8_t from, uint8_t to) +{ + uart_putc(27); + uart_putc('['); + uart_putu(from); + uart_putc(';'); + uart_putu(to); + uart_putc('r'); +} + + +void vt_scroll_reset() +{ + uart_putc(27); + uart_putc('['); + uart_putc('r'); +} + + + +typedef struct { + uint8_t flags; + uint8_t fg; + uint8_t bg; +} vt_style_t; + +vt_style_t saved_style; +vt_style_t current_style; + +void vt_save() +{ + uart_puts_pgm(PSTR("\x1B[s")); + + saved_style = current_style; +} + + +void vt_restore() +{ + uart_puts_pgm(PSTR("\x1B[u")); + + current_style = saved_style; +} + + +/** Disable all text attributes (excluding color) */ +void vt_attr_reset() +{ + current_style.flags = 0; + + _vt_reset_attribs_do(); + _vt_apply_style(); +} + + +/** Set color to white on black */ +void vt_color_reset() +{ + current_style.fg = VT_WHITE; + current_style.bg = VT_BLACK; + + _vt_color_do(); +} + + +/** Enable or disable a text attribute */ +void vt_attr(uint8_t attribute, bool on) +{ + // flags are powers of two + // so this can handle multiple OR'd flags + for(uint8_t c = 1; c <= VT_FAINT; c *= 2) { + if (attribute & c) { + if (on) { + current_style.flags |= c; + } else { + current_style.flags &= ~c; + } + } + } + + _vt_apply_style(); +} + + +/** Send style and color commands */ +void _vt_apply_style() +{ + _vt_reset_attribs_do(); + _vt_style_do(); + _vt_color_do(); +} + + +/** Set color 0..7 */ +void vt_color(uint8_t fg, uint8_t bg) +{ + current_style.fg = fg; + current_style.bg = bg; + _vt_color_do(); +} + + +/** Set FG color 0..7 */ +void vt_color_fg(uint8_t fg) +{ + current_style.fg = fg; + _vt_color_do(); +} + + +/** Set BG color 0..7 */ +void vt_color_bg(uint8_t bg) +{ + current_style.bg = bg; + _vt_color_do(); +} + + +/** Send reset command */ +inline void _vt_reset_attribs_do() +{ + uart_puts_pgm(PSTR("\x1B[m")); // reset +} + + +/** Send commands for text attribs */ +void _vt_style_do() +{ + if (current_style.flags & VT_BOLD) { + uart_puts_pgm(PSTR("\x1B[1m")); + } + + if (current_style.flags & VT_FAINT) { + uart_puts_pgm(PSTR("\x1B[2m")); + } + + if (current_style.flags & VT_ITALIC) { + uart_puts_pgm(PSTR("\x1B[3m")); + } + + if (current_style.flags & VT_UNDERLINE) { + uart_puts_pgm(PSTR("\x1B[4m")); + } + + if (current_style.flags & VT_BLINK) { + uart_puts_pgm(PSTR("\x1B[5m")); + } + + if (current_style.flags & VT_REVERSE) { + uart_puts_pgm(PSTR("\x1B[7m")); + } +} + + +/** Send commands for xolor */ +void _vt_color_do() +{ + uart_putc(27); + uart_putc('['); + uart_putu(30 + current_style.fg); + uart_putc(';'); + uart_putu(40 + current_style.bg); + uart_putc('m'); +} + + +/** Insert blank lines febore the current line */ +void vt_insert_lines(uint8_t count) +{ + uart_putc(27); + uart_putc('['); + uart_putu(count); + uart_putc('L'); +} + + +/** Delete lines from the current line down */ +void vt_delete_lines(uint8_t count) +{ + uart_putc(27); + uart_putc('['); + uart_putu(count); + uart_putc('M'); +} + + +/** Insert empty characters at cursor */ +void vt_insert_chars(uint8_t count) +{ + uart_putc(27); + uart_putc('['); + uart_putu(count); + uart_putc('@'); +} + + +/** Delete characters at cursor */ +void vt_delete_chars(uint8_t count) +{ + uart_putc(27); + uart_putc('['); + uart_putu(count); + uart_putc('P'); +} + + +void vt_clear() +{ + uart_puts_pgm(PSTR("\x1B[2J")); +} + + +void vt_erase_forth() +{ + uart_puts_pgm(PSTR("\x1B[K")); +} + + +void vt_erase_back() +{ + uart_puts_pgm(PSTR("\x1B[1K")); +} + + +void vt_erase_line() +{ + uart_puts_pgm(PSTR("\x1B[2K")); +} + + +void vt_erase_above() +{ + uart_puts_pgm(PSTR("\x1B[1J")); +} + +void vt_erase_below() +{ + uart_puts_pgm(PSTR("\x1B[J")); +} + + +void vt_home() +{ + uart_puts_pgm(PSTR("\x1B[H")); +} + + +/** Initialize helper variables */ +void vt_init() +{ + vt_reset(); +} + + +/** Reset state and clear screen */ +void vt_reset() +{ + // reset color and attributes + vt_color_reset(); + vt_attr_reset(); + vt_scroll_reset(); + + // clear screen + vt_clear(); + + // go to top left + vt_home(); + + // overwrite saved state + vt_save(); +} + + + +// Assigned keyhandler +void (*_vt_kh)(uint8_t, bool) = NULL; + +/** Assign a key handler (later used with vt_handle_key) */ +void vt_set_key_handler(void (*handler)(uint8_t, bool)) +{ + _vt_kh = handler; +} + + +// state machine states +typedef enum { + GROUND = 0, + ESC = 1, + BR = 2, + O = 3, + WAITING_TILDE = 4 +} KSTATE; + +// code received before started to wait for a tilde +uint8_t _before_wtilde; +// current state +KSTATE _kstate = GROUND; + + + +void _vt_kh_abort() +{ + switch (_kstate) { + case ESC: + _vt_kh(VK_ESC, true); + break; + + case BR: + _vt_kh(VK_ESC, true); + _vt_kh('[', false); + break; + + case O: + _vt_kh(VK_ESC, true); + _vt_kh('O', false); + break; + + case WAITING_TILDE: + _vt_kh(VK_ESC, true); + _vt_kh('[', false); + vt_handle_key(_before_wtilde); + break; + + case GROUND: + // nop + break; + } + + _kstate = GROUND; +} + + +/** + * Handle a key received over UART + * Takes care of multi-byte keys and translates them to special + * constants. + */ +void vt_handle_key(uint8_t c) +{ + if (_vt_kh == NULL) return; + + switch (_kstate) { + case GROUND: + switch (c) { + case 27: + _kstate = ESC; + break; + + case VK_ENTER: + case VK_TAB: + case VK_BACKSPACE: + _vt_kh(c, true); + return; + + default: + _vt_kh(c, false); + return; + } + + break; // continue to next char + + case ESC: + switch (c) { + case '[': + _kstate = BR; + break; // continue to next char + + case 'O': + _kstate = O; + break; // continue to next char + + default: + // bad code + _vt_kh_abort(); + vt_handle_key(c); + return; + } + break; + + case BR: + switch (c) { + // arrows + case 65: + case 66: + case 67: + case 68: + _vt_kh(c, true); + _kstate = GROUND; + return; + + // ins del pgup pgdn + case 50: + case 51: + case 53: + case 54: + // wait for terminating tilde + _before_wtilde = c; + _kstate = WAITING_TILDE; + break; // continue to next char + + // bad key + default: + _vt_kh_abort(); + vt_handle_key(c); + return; + } + break; + + case O: + switch (c) { + // F keys + case 80: + case 81: + case 82: + case 83: + // home, end + case 72: + case 70: + _vt_kh(c, true); + _kstate = GROUND; + return; + + // bad key + default: + _vt_kh_abort(); + vt_handle_key(c); + return; + } + + case WAITING_TILDE: + if (c != '~') { + _vt_kh_abort(); + vt_handle_key(c); + return; + } else { + _vt_kh(_before_wtilde, true); + _kstate = GROUND; + return; + } + } + + // wait for next key + if (_kstate != GROUND) { + _delay_ms(2); + if (!uart_rx_ready()) { + // abort receiving + _vt_kh_abort(); + + } else { + vt_handle_key(uart_rx()); + } + } +} diff --git a/projects/uart_keyhandler_test/lib/uart_ansi.h b/projects/uart_keyhandler_test/lib/uart_ansi.h new file mode 100644 index 0000000..f780758 --- /dev/null +++ b/projects/uart_keyhandler_test/lib/uart_ansi.h @@ -0,0 +1,192 @@ +#pragma once + +// +// ANSI / VT100 utilities for UART +// +// To use this, first call uart_init(baud) and vt_init() +// To print stuff on the screen, use uart_puts() etc from uart.h +// + +#include +#include +#include +#include +#include "uart.h" + + + +// INIT + +/** Initialize helper variables */ +void vt_init(); + +/** Reset state and clear screen */ +void vt_reset(); + + + +// CURSOR MOVE + +/** Move cursor to top left corner */ +void vt_home(); + +/** Jump to a location on the screen */ +void vt_goto(uint8_t x, uint8_t y); + +/** Jump to given X, keep Y */ +void vt_goto_x(uint8_t x); + +/** Jump to given Y, keep X */ +void vt_goto_y(uint8_t y); + +/** Move cursor relative to current location */ +void vt_move(int8_t x, int8_t y); + +/** Move cursor horizontally */ +void vt_move_x(int8_t x); + +/** Move cursor vertically */ +void vt_move_y(int8_t y); + +/** Move cursor up y cells */ +void vt_up(uint8_t y); + +/** Move cursor down y cells */ +void vt_down(uint8_t y); + +/** Move cursor left x cells */ +void vt_left(uint8_t x); + +/** Move cursor right x cells */ +void vt_right(uint8_t x); + + + +// SCROLLING + +/** Scroll y lines down (like up/down, but moves window if needed) */ +void vt_scroll(int8_t down); + +/** Set scrolling region (lines) */ +void vt_scroll_set(uint8_t from, uint8_t to); + + +/** Sets scrolling region to the entire screen. */ +void vt_scroll_reset(); + + +// COLOR + +#define VT_BLACK 0 +#define VT_RED 1 +#define VT_GREEN 2 +#define VT_YELLOW 3 +#define VT_BLUE 4 +#define VT_MAGENTA 5 +#define VT_CYAN 6 +#define VT_WHITE 7 + +/** Set color 0..7 */ +void vt_color(uint8_t fg, uint8_t bg); + +/** Set FG color 0..7 */ +void vt_color_fg(uint8_t fg); + +/** Set BG color 0..7 */ +void vt_color_bg(uint8_t bg); + +/** Set color to white on black */ +void vt_color_reset(); + + + +// STYLES + +#define VT_BOLD 1 +#define VT_UNDERLINE 2 +#define VT_BLINK 4 +#define VT_REVERSE 8 +#define VT_ITALIC 16 +#define VT_FAINT 32 + +/** Enable or disable a text attribute */ +void vt_attr(uint8_t attribute, bool on); + +/** Disable all text attributes (excluding color) */ +void vt_attr_reset(); + + + +// SAVE & RESTORE + +/** Save cursor position & text attributes */ +void vt_save(); + +/** Restore cursor to saved values */ +void vt_restore(); + + + +// MODIFY + + +/** Insert blank lines febore the current line */ +void vt_insert_lines(uint8_t count); + +/** Delete lines from the current line down */ +void vt_delete_lines(uint8_t count); + +/** Insert empty characters at cursor */ +void vt_insert_chars(uint8_t count); + +/** Delete characters at cursor */ +void vt_delete_chars(uint8_t count); + + + +// ERASING + +/** Clear the screen */ +void vt_clear(); + +/** Erase to the end of line */ +void vt_erase_forth(); + +/** Erase line to cursor */ +void vt_erase_back(); + +/** Erase entire line */ +void vt_erase_line(); + +/** Erase screen below the line */ +void vt_erase_above(); + +/** Erase screen above the line */ +void vt_erase_below(); + + + +// KEY HANDLER + +// Special keys from key handler +#define VK_LEFT 68 +#define VK_RIGHT 67 +#define VK_UP 65 +#define VK_DOWN 66 +#define VK_DELETE 51 +#define VK_INSERT 50 +#define VK_PGUP 53 +#define VK_PGDN 54 +#define VK_HOME 72 +#define VK_END 70 +#define VK_F1 80 +#define VK_F2 81 +#define VK_F3 82 +#define VK_F4 83 +#define VK_BACKSPACE 8 +#define VK_TAB 9 +#define VK_ENTER 13 +#define VK_ESC 27 + +void vt_handle_key(uint8_t c); +void vt_set_key_handler(void (*handler)(uint8_t, bool)); diff --git a/projects/uart_keyhandler_test/main.c b/projects/uart_keyhandler_test/main.c new file mode 100644 index 0000000..023d423 --- /dev/null +++ b/projects/uart_keyhandler_test/main.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "lib/uart.h" +#include "lib/uart_ansi.h" + +ISR(USART_RX_vect) +{ + vt_handle_key(uart_rx()); +} + + +void key_handler(uint8_t code, bool special) +{ + if (special) { + uart_puts_pgm(PSTR("Special key: ")); + } else { + uart_puts_pgm(PSTR("Character: ")); + } + + uart_putc(code); + uart_putc(32); + uart_putu(code); + uart_nl(); +} + + +void main() +{ + uart_init(9600); + uart_isr_rx(1); + vt_init(); + + vt_set_key_handler(&key_handler); + + sei(); + + uart_puts_pgm(PSTR("UART key handler test!\r\n")); + + while(1); +}