aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Phillips <david@yeah.nah.nz>2020-12-13 16:35:58 +1300
committerDavid Phillips <david@yeah.nah.nz>2020-12-13 20:42:33 +1300
commit7e26f31e221665ee059b02fc6beda025d39d6e75 (patch)
tree1a5f66e12680778b001f3cb1d2c49b0f2ce0f04c
downloadaltimeter-7e26f31e221665ee059b02fc6beda025d39d6e75.tar.xz
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.
-rw-r--r--.gitignore3
-rw-r--r--Makefile54
-rw-r--r--altimeter.c100
-rw-r--r--barometer.h3
-rw-r--r--barometer_sim.c22
-rw-r--r--config.mk1
-rw-r--r--display.h14
-rw-r--r--display_sim.c118
-rw-r--r--util.c25
-rw-r--r--util.h7
10 files changed, 347 insertions, 0 deletions
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 <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <avr/sleep.h>
+#include <avr/interrupt.h>
+
+#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 <stdio.h>
+#include <avr/io.h>
+
+#define BAUD 57600
+#include <util/setbaud.h>
+
+#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 <math.h>
+#include <string.h>
+
+#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 <stddef.h>
+
+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);