From 7e26f31e221665ee059b02fc6beda025d39d6e75 Mon Sep 17 00:00:00 2001 From: David Phillips Date: Sun, 13 Dec 2020 16:35:58 +1300 Subject: Initial prototype This patch adds a skeleton of AVR code for a "simulator" target (atmega2560) and for the real intended hardware target (atmega32u4). The simulator target is one that is supported by the QEMU AVR emulator, while the 32u4 is currently not. --- .gitignore | 3 ++ Makefile | 54 ++++++++++++++++++++++++++ altimeter.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++ barometer.h | 3 ++ barometer_sim.c | 22 +++++++++++ config.mk | 1 + display.h | 14 +++++++ display_sim.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ util.c | 25 ++++++++++++ util.h | 7 ++++ 10 files changed, 347 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 altimeter.c create mode 100644 barometer.h create mode 100644 barometer_sim.c create mode 100644 config.mk create mode 100644 display.h create mode 100644 display_sim.c create mode 100644 util.c create mode 100644 util.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4b523e3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.hex +*.o +*.elf diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..879452f --- /dev/null +++ b/Makefile @@ -0,0 +1,54 @@ +CC ?= avr-gcc +OBJCOPY ?= avr-objcopy +AVRDUDE ?= avrdude +QEMU_AVR ?= qemu-system-avr + +MCU ?= atmega32u4 +SIM_MCU ?= atmega2560 +CFLAGS += -DF_CPU=16000000UL -mmcu=$(MCU) -O3 +SIM_CFLAGS += -DF_CPU=16000000UL -mmcu=$(SIM_MCU) -g3 -gdwarf-2 + +$(shell mkdir -p build/{real,sim}) + +all: build/altimeter_sim.elf build/altimeter.hex + +emu: build/altimeter_sim.elf + $(QEMU_AVR) -s -S -nographic -machine mega2560 -bios $< + +flash: build/altimeter.hex + $(AVRDUDE) -F -V -c avr109 -p $(MCU) -P $(PORT) -b 115200 -U flash:w:$< + +clean: + rm -rf build/ + +build/altimeter.elf: \ + build/real/barometer_sim.o \ + build/real/display_sim.o \ + build/real/altimeter.o \ + build/real/util.o + $(CC) -o $@ $^ $(CFLAGS) + +build/altimeter_sim.elf: \ + build/sim/barometer_sim.o \ + build/sim/display_sim.o \ + build/sim/altimeter.o \ + build/sim/util.o + $(CC) -o $@ $^ $(SIM_CFLAGS) + +include config.mk + + +build/sim/%.o: %.c + $(CC) -c -o $@ $^ $(SIM_CFLAGS) + +build/real/%.o: %.c + $(CC) -c -o $@ $^ $(CFLAGS) + +build/%.hex: build/%.elf + $(OBJCOPY) -O ihex -R .eeprom $< $@ + +build/%.elf: + $(CC) -o $@ $^ $(CFLAGS) + + +.PHONY: all emu flash clean diff --git a/altimeter.c b/altimeter.c new file mode 100644 index 0000000..2aef956 --- /dev/null +++ b/altimeter.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include + +#include +#include + +#include "barometer.h" +#include "display.h" +#include "util.h" + +/* ISR for collecting and displaying pressure, altitude data etc */ +ISR(TIMER1_COMPA_vect) +{ + PORTD ^= 1 << 5; + int tcnt1_start = TCNT1; + static volatile float setting = 1040.21; /* FIXME volatile for gdb hacks */ + static float last_height = 0; + float pressure = 0; + float height_m = 0; + float rate_m_s; + char rate_sym; + pressure = barometer_read(); + height_m = pressure_to_metres_asl(pressure, setting); + rate_m_s = height_m - last_height; + last_height = height_m; + + if (rate_m_s < 0) { + rate_sym = C_DESC; + } else if (rate_m_s == 0) { + rate_sym = C_IDLE; + } else { + rate_sym = C_ASC; + } + + char line[LCD_WIDTH+1]; + /* line 1 */ + int alt_m_int; + float alt_m_float; + split_float(height_m, &alt_m_int, &alt_m_float); + snprintf(line, sizeof(line), "%d.%d m %c%d m/s", alt_m_int, (int)(round(alt_m_float*10)), rate_sym, abs(rate_m_s)); + blank_to_eol(line, sizeof(line)); + display_set_cursor(0, 0); + display_write(line); + + + /* line 2 */ + int pressure_hpa_int; + float pressure_hpa_float; + split_float(pressure, &pressure_hpa_int, &pressure_hpa_float); + snprintf(line, sizeof(line), "%d.%d hPa", pressure_hpa_int, (int)(round(pressure_hpa_float*100))); + blank_to_eol(line, sizeof(line)); + display_set_cursor(0, 1); + display_write(line); + + /* line 3 */ + int setting_hpa_int; + float setting_hpa_float; + split_float(setting, &setting_hpa_int, &setting_hpa_float); + snprintf(line, sizeof(line), "SET %d.%d hPa", setting_hpa_int, (int)(round(setting_hpa_float*100))); + blank_to_eol(line, sizeof(line)); + display_set_cursor(0, 2); + display_write(line); + + int tcnt1_end = TCNT1; + /* line 4 */ + snprintf(line, sizeof(line), "TCNT1 delta %u", tcnt1_end - tcnt1_start); + blank_to_eol(line, sizeof(line)); + display_set_cursor(0, 3); + display_write(line); + return; +} + +int main(void) +{ + /* Initialise display before enabling interrupts */ + display_init(); + display_clear(); + + DDRB |= 1 << 0; + DDRD |= 1 << 5; + + /* Initialise timers for /1024 prescaler, 1 Hz comparator val */ + TCCR1B |= (1 << CS10) | (1 << CS12) | (1 << WGM12); + TIMSK1 |= (1 << OCIE1A); + OCR1A = 15624; + +#ifdef USBCON + /* Disable USB controller if one is present - this spams (latches?) USB_GEN + * interrupt which we're not handling for now */ + USBCON &= ~(1 << USBE); +#endif + + sei(); + while(1) { + set_sleep_mode(SLEEP_MODE_IDLE); + sleep_mode(); + } +} diff --git a/barometer.h b/barometer.h new file mode 100644 index 0000000..8277665 --- /dev/null +++ b/barometer.h @@ -0,0 +1,3 @@ +#pragma once + +float barometer_read(void); diff --git a/barometer_sim.c b/barometer_sim.c new file mode 100644 index 0000000..f2e9eac --- /dev/null +++ b/barometer_sim.c @@ -0,0 +1,22 @@ +/* Dummy pressures to return in sequence, each call to barometer_read */ +static const float pressures[] = { + 1019.5, + 1019.45, + 1019.41, + 1019.33, + 1019.2, + 1019.0, + 1018.99, + 1018.91, + 1018.88, + 1018.86, + 1018.83, + 1018.82, +}; + +float barometer_read(void) +{ + static int i = 0; + i %= sizeof(pressures)/sizeof(pressures[0]); + return pressures[i++]; +} diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..7eb0c2e --- /dev/null +++ b/config.mk @@ -0,0 +1 @@ +CC = avr-gcc diff --git a/display.h b/display.h new file mode 100644 index 0000000..771323c --- /dev/null +++ b/display.h @@ -0,0 +1,14 @@ +#pragma once + +#define LCD_WIDTH (20) +#define LCD_HEIGHT (4) + +#define C_BAT_LOW (1) +#define C_DESC (2) +#define C_IDLE (3) +#define C_ASC (4) + +void display_clear(void); +void display_init(void); +void display_write(const char *text); +void display_set_cursor(int x, int y); diff --git a/display_sim.c b/display_sim.c new file mode 100644 index 0000000..b17979f --- /dev/null +++ b/display_sim.c @@ -0,0 +1,118 @@ +#include +#include + +#define BAUD 57600 +#include + +#include "display.h" + +/* WIDTH+1 for string terminator */ +static char display[LCD_HEIGHT][LCD_WIDTH+1]; +static int cursor_x; +static int cursor_y; + +/* Slightly icky hack to use first avaialable USART */ +#ifdef UDR0 +# define UBRRx UBRR0 +# define UCSRxA UCSR0A +# define UCSRxB UCSR0B +# define UCSRxC UCSR0C +# define UCSZx0 UCSZ00 +# define UCSZx1 UCSZ01 +# define RXENx RXEN0 +# define TXENx TXEN0 +# define UDREx UDRE0 +# define UDRx UDR0 +# define U2Xx U2X0 +#else +# define UBRRx UBRR1 +# define UCSRxA UCSR1A +# define UCSRxB UCSR1B +# define UCSRxC UCSR1C +# define UCSZx0 UCSZ10 +# define UCSZx1 UCSZ11 +# define RXENx RXEN1 +# define TXENx TXEN1 +# define UDREx UDRE1 +# define UDRx UDR1 +# define U2Xx U2X1 +#endif + +static void uart_init(void) +{ + UBRRx = UBRR_VALUE; +#if USE_2X + UCSRxA |= (1 << U2Xx); +#else + UCSRxA &= ~(1 << U2Xx); +#endif + UCSRxB = _BV(RXENx) | _BV(TXENx); + UCSRxC = _BV(UCSZx1) | _BV(UCSZx0); +} + +static int uart_putc(char c, FILE *unused) +{ + UDRx = c; + loop_until_bit_is_set(UCSRxA, UDREx); + return 1; +} + +/* uart_out is plopped on stdout so that prints go to it by default */ +static FILE uart_out = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE); + +/* internal function to dump the display contents to stdout, since there + * is no background thread for doing this constantly */ +void display_display(void) +{ + int x = 0; + int y = 0; + printf("\033[6A"); // hack: raw ANSI escape sequence to move up 6 + printf(",--------------------.\n"); + for (y = 0; y < LCD_HEIGHT; y++) { + printf("|%s|\n", display[y]); + } + printf("`--------------------'\n"); +} + +void display_clear(void) +{ + int x = 0; + int y = 0; + for (y = 0; y < LCD_HEIGHT; y++) { + for (x = 0; x < LCD_WIDTH; x++) { + display[y][x] = ' '; + } + display[y][x] = '\0'; + } + + display_display(); +} + +void display_init(void) +{ + uart_init(); + /* FIXME hack: stdout not guaranteed to be assignable */ + stdout = &uart_out; + display_clear(); + cursor_x = cursor_y = 0; +} + +void display_write(const char *text) +{ + size_t text_x = 0; + char c = '\0'; + while (cursor_x < LCD_WIDTH && *text) { + c = *(text++); + /* 0-7 are custom chars in CGRAM on real LCD */ + if (c < 8) + c = '_'; + display[cursor_y][cursor_x++] = c; + } + display_display(); +} + +void display_set_cursor(int x, int y) +{ + cursor_x = x; + cursor_y = y; +} diff --git a/util.c b/util.c new file mode 100644 index 0000000..794eea7 --- /dev/null +++ b/util.c @@ -0,0 +1,25 @@ +#include +#include + +#include "util.h" + +float pressure_to_metres_asl(float real, float setting) +{ + return 44330.f*(1.f-pow(real/setting, 1/5.255)); +} + +void blank_to_eol(char *line, size_t len) +{ + size_t i = 0; + for (i = strlen(line); i < len - 1; i++) { + line[i] = ' '; + } + line[i] = '\0'; +} + +void split_float(float input, int *integer_part, float *float_part) +{ + *integer_part = (int)input; + *float_part = fabs(input - *integer_part); +} + diff --git a/util.h b/util.h new file mode 100644 index 0000000..49812b9 --- /dev/null +++ b/util.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +float pressure_to_metres_asl(float real, float setting); +void blank_to_eol(char *line, size_t len); +void split_float(float input, int *integer_part, float *float_part); -- cgit v1.1