diff options
Diffstat (limited to 'robot')
| -rw-r--r-- | robot/consts.h | 5 | ||||
| -rw-r--r-- | robot/errcatch.c | 58 | ||||
| -rw-r--r-- | robot/errcatch.h | 29 | ||||
| -rw-r--r-- | robot/hypervisor.c | 26 | ||||
| -rw-r--r-- | robot/makefile | 28 | ||||
| -rw-r--r-- | robot/orangutan_shim.h | 7 | ||||
| -rw-r--r-- | robot/readme.md | 29 | ||||
| -rw-r--r-- | robot/setup.c | 2 | ||||
| -rw-r--r-- | robot/sim.c | 64 | ||||
| -rw-r--r-- | robot/sim.h | 40 | 
10 files changed, 262 insertions, 26 deletions
| diff --git a/robot/consts.h b/robot/consts.h index 1195422..a81908d 100644 --- a/robot/consts.h +++ b/robot/consts.h @@ -1,17 +1,19 @@  #pragma once  #ifndef W2_BUILD_STR -// should be defined with -DBUILD_STR in makefile +// is defined by CFLAGS += -DW2_BUILD_STR in makefile  #define W2_BUILD_STR ("????????")  #endif  #define W2_MAX_MODULE_CYCLE_MS (20)  #define W2_SERIAL_BAUD (9600) +#define W2_ERROR_BUFFER_SIZE (16)  #define W2_ERR_TYPE_CRIT (0b00 << 6)  #define W2_ERR_TYPE_WARN (0b01 << 6)  #define W2_ERR_TYPE_INFO (0b10 << 6)  #define W2_ERR_TYPE_VERB (0b11 << 6) +#define W2_ERR_TYPE_MASK (0b11 << 6)  /**   * enum storing all error codes @@ -30,4 +32,5 @@ enum w2_e_errorcodes {  	W2_ERR_BATTERY_LOW		 = 0x00 | W2_ERR_TYPE_WARN,  	W2_ERR_OBSTACLE_DETECTED = 0x01 | W2_ERR_TYPE_WARN,  	W2_ERR_CYCLE_EXPIRED	 = 0x02 | W2_ERR_TYPE_WARN, +	W2_ERR_UNCAUGHT_ERROR	 = 0x03 | W2_ERR_TYPE_WARN,  }; diff --git a/robot/errcatch.c b/robot/errcatch.c index 5b42bb4..d5ad320 100644 --- a/robot/errcatch.c +++ b/robot/errcatch.c @@ -1,7 +1,61 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "consts.h"  #include "errcatch.h" +#include "halt.h" +#include "modes.h" +#include "orangutan_shim.h" + +w2_s_error g_w2_error_buffer[W2_ERROR_BUFFER_SIZE] = {}; +uint8_t g_w2_error_index						   = 0; +uint8_t g_w2_error_offset						   = 0; -void w2_errcatch_main() {} +void w2_errcatch_main() { +	while (g_w2_error_index != g_w2_error_offset) { +		w2_s_error *error = &g_w2_error_buffer[g_w2_error_offset]; +		w2_errcatch_handle_error(error); +		free(error); +		g_w2_error_offset = (g_w2_error_offset + 1) % W2_ERROR_BUFFER_SIZE; +	} +} -void w2_errcatch_throw_msg(enum w2_e_errorcodes code, uint16_t length, const char *message) {} +w2_s_error *w2_alloc_error(enum w2_e_errorcodes code, uint16_t length, const char *message) { +	w2_s_error *error = calloc(sizeof(w2_s_error) + length, 1); +	memcpy(error, &(w2_s_error const){.code = code, .message_length = length}, sizeof(w2_s_error)); +	strncpy(error->message, message, length); + +	return error; +}  void w2_errcatch_throw(enum w2_e_errorcodes code) { w2_errcatch_throw_msg(code, 0, ""); } +void w2_errcatch_throw_msg(enum w2_e_errorcodes code, uint16_t length, const char *message) { +	w2_s_error error					= *w2_alloc_error(code, length, message); +	g_w2_error_buffer[g_w2_error_index] = error; +	g_w2_error_index					= (g_w2_error_index + 1) % W2_ERROR_BUFFER_SIZE; +} + +void w2_errcatch_handle_error(w2_s_error *error) { +	uint8_t severity = error->code & W2_ERR_TYPE_MASK; + +	// trigger emergency mode for critical errors +	if ((severity ^ W2_ERR_TYPE_CRIT) == 0) g_w2_current_mode = &w2_mode_halt; + +	// TODO: handle more error types +	switch (error->code) { +		case W2_ERR_UNCAUGHT_ERROR: { +			break; +		} +		default: { +			w2_errcatch_throw(W2_ERR_UNCAUGHT_ERROR); +#ifdef W2_SIM +			simwarn("Uncaught/unhandled error found with code 0x%02x", error->code); +#endif +		} +	} + +	// TODO: forward error to sercomm + +	return; +} diff --git a/robot/errcatch.h b/robot/errcatch.h index 48e2a75..2297886 100644 --- a/robot/errcatch.h +++ b/robot/errcatch.h @@ -4,11 +4,40 @@  #include "consts.h" +/** + * error struct + * + * holds an error with type `code`, and an optional `message` with length + * `message_length` + */ +typedef struct { +	enum w2_e_errorcodes code; +	uint8_t message_length; +	char message[]; +} w2_s_error; + +/** error ring buffer */ +extern w2_s_error g_w2_error_buffer[W2_ERROR_BUFFER_SIZE]; +/** stores head of ring buffer */ +extern uint8_t g_w2_error_index; +/** stores start of ring buffer */ +extern uint8_t g_w2_error_offset; +  /** error-handler module main */  void w2_errcatch_main(); +/** handle error */ +void w2_errcatch_handle_error(w2_s_error *error); +  /** append error to error buffer */  void w2_errcatch_throw(enum w2_e_errorcodes code);  /** append error to error buffer (with debug message) */  void w2_errcatch_throw_msg(enum w2_e_errorcodes code, uint16_t length, const char *message); + +/** + * allocate and initialize error struct + * + * TODO: doesn't handle null pointers from calloc + */ +w2_s_error *w2_alloc_error(enum w2_e_errorcodes code, uint16_t length, const char *message); diff --git a/robot/hypervisor.c b/robot/hypervisor.c index 381d9af..6b32776 100644 --- a/robot/hypervisor.c +++ b/robot/hypervisor.c @@ -1,19 +1,31 @@ -#include <pololu/orangutan.h> - +#include "hypervisor.h"  #include "consts.h"  #include "errcatch.h" -#include "hypervisor.h"  #include "io.h"  #include "modes.h" +#include "orangutan_shim.h"  #include "sercomm.h"  void w2_hypervisor_main() { +	time_reset(); +  	w2_sercomm_main(); +	unsigned long sercomm_time = get_ms();  	w2_errcatch_main(); +	unsigned long errcatch_time = get_ms() - sercomm_time;  	w2_io_main(); - -	time_reset(); +	unsigned long io_time = get_ms() - errcatch_time;  	w2_modes_main(); -	unsigned long elapsed_ms = get_ms(); -	if (elapsed_ms > W2_MAX_MODULE_CYCLE_MS) w2_errcatch_throw(W2_ERR_CYCLE_EXPIRED); +	unsigned long mode_time = get_ms() - io_time; + +#ifdef W2_SIM +	siminfo("sercomm:  %lums\n", sercomm_time); +	siminfo("errcatch: %lums\n", errcatch_time); +	siminfo("io:       %lums\n", io_time); +	siminfo("mode:     %lums\n", mode_time); + +	usleep(100e3); +#endif + +	if (mode_time > W2_MAX_MODULE_CYCLE_MS) w2_errcatch_throw(W2_ERR_CYCLE_EXPIRED);  } diff --git a/robot/makefile b/robot/makefile index 598e0fa..6f50519 100644 --- a/robot/makefile +++ b/robot/makefile @@ -5,23 +5,33 @@ DEVICE ?= atmega168  MCU ?= atmega168  AVRDUDE_DEVICE ?= m168 -CFLAGS=-g -Wall -mcall-prologues -mmcu=$(MCU) $(DEVICE_SPECIFIC_CFLAGS) -Os -LDFLAGS=-Wl,-gc-sections -lpololu_$(DEVICE) -Wl,-relax -  PORT ?= /dev/ttyACM0 -SOURCES := $(wildcard *.c) -HEADERS := $(wildcard *.h) +CFLAGS=-g -Wall $(DEVICE_SPECIFIC_CFLAGS) -Os +LDFLAGS=-Wl,-gc-sections -Wl,-relax + +SOURCES := $(filter-out sim.c, $(wildcard *.c)) +HEADERS := $(filter-out sim.h, $(wildcard *.h)) + +# simulation +# SIM = true +CFLAGS += $(if $(SIM), -DW2_SIM, -mcall-prologues -mmcu=$(MCU)) +LDFLAGS += $(if $(SIM), , -lpololu_$(DEVICE)) +PREFIX := $(if $(SIM), , avr-) +SOURCES += $(if $(SIM), sim.c, ) +HEADERS += $(if $(SIM), sim.h, ) +  OBJECTS := $(patsubst %.c,%.o, $(SOURCES))  AVRDUDE=avrdude -CC=avr-gcc -OBJ2HEX=avr-objcopy +CC=$(PREFIX)gcc +OBJ2HEX=$(PREFIX)objcopy +# debug build info string  BUILD_STR=$(shell git update-index -q --refresh; git describe --tags --dirty='*' --broken='x' | cut -c1-20)  CFLAGS += -DW2_BUILD_STR="$(BUILD_STR)" -all: out.hex +all: $(if $(SIM), a.out, out.hex)  clean:  	rm -f *.o out.hex a.out compile_commands.json @@ -40,6 +50,8 @@ flash: out.hex  	$(AVRDUDE) -p $(AVRDUDE_DEVICE) -c avrisp2 -P $(PORT) -U flash:w:out.hex  format: +	$(eval SOURCES := $(filter-out sim.c, $(SOURCES))) +	$(eval HEADERS := $(filter-out sim.h, $(HEADERS)))  	clang-format -i $(SOURCES) $(HEADERS)  	clang-tidy --fix-errors $(SOURCES) $(HEADERS) diff --git a/robot/orangutan_shim.h b/robot/orangutan_shim.h new file mode 100644 index 0000000..de57c98 --- /dev/null +++ b/robot/orangutan_shim.h @@ -0,0 +1,7 @@ +#pragma once + +#ifdef W2_SIM +#include "sim.h" +#else +#include <pololu/orangutan.h> +#endif diff --git a/robot/readme.md b/robot/readme.md index 0f70f31..4a7aca3 100644 --- a/robot/readme.md +++ b/robot/readme.md @@ -17,6 +17,13 @@ device manager on windows, or by running `ls /dev/ttyACM*` on linux. once the  com port is configured, run `make flash` to upload the executable and  automatically reboot the robot. +another fun option in the makefile is the SIM mode. by uncommenting the line `# +SIM = true`, the robot code can be compiled for desktop debugging instead. all +used pololu functions must be manually implemented in sim.c for this to work, +but it allows easier debugging. *it's important that the `orangutan_shim.h` +header is used instead of including `<pololu/orangutan.h>` directly for this to +keep working!* +  ## module hierarchy  the software is divided into seperate 'modules' for organizational, @@ -86,6 +93,8 @@ this list will probably get updated from time to time:  - arbitrary numbers should be aliased to `#define` statements or `enum`s if    part of a series.  - general constants should be placed in `consts.h` +- don't import `<pololu/orangutan.h>` directly, instead use +  `"orangutan_shim.h"` to keep code compatible with the simulator  ## todo @@ -120,19 +129,25 @@ to act on accordingly.  the error handling module (a) provides functions for other modules to report  errors, and (b) handles errors accordingly. -- [ ] create an error `struct` that holds: -  - [ ] error code -  - [ ] message length -  - [ ] message contents -- [ ] create a global error ring buffer with an appropriate size that holds +- [x] create an error `struct` that holds: +  - [x] error code +  - [x] message length +  - [x] message contents +- [x] create a global error ring buffer with an appropriate size that holds    error messages  - [ ] handle errors in the error buffer, referencing the functional    specification for details on what the robot should do to resolve each kind of    error  - [ ] forward error codes to the pc-communication module -empty function declarations are in place for providing other modules an error -reporting function. +~empty function declarations are in place for providing other modules an error +reporting function.~ + +this module is as good as finished but full functionality is currently +dependent on: + +- [ ] pc communication +- [ ] other mode implementations  ### i/o read & write diff --git a/robot/setup.c b/robot/setup.c index 10001c7..c74cca9 100644 --- a/robot/setup.c +++ b/robot/setup.c @@ -1,9 +1,9 @@ -#include <pololu/orangutan.h>  #include <stdlib.h>  #include "consts.h"  #include "halt.h"  #include "modes.h" +#include "orangutan_shim.h"  #include "setup.h"  void w2_setup_main() { diff --git a/robot/sim.c b/robot/sim.c new file mode 100644 index 0000000..6bd5838 --- /dev/null +++ b/robot/sim.c @@ -0,0 +1,64 @@ +#include <stdio.h> +#include <time.h> +#include <string.h> + +#include "sim.h" + +struct timespec reference_time; // NOLINT + +void time_reset() { +	simprintfunc("time_reset", ""); +	clock_gettime(CLOCK_MONOTONIC, &reference_time); +	return; +} + +unsigned long get_ms() { +	simprintfunc("get_ms", ""); +	struct timespec elapsed; +	clock_gettime(CLOCK_MONOTONIC, &elapsed); +	return ((elapsed.tv_sec * 1000) + (elapsed.tv_nsec / 1000000)) - +		((reference_time.tv_sec * 1000) + (reference_time.tv_nsec / 1000000)); +} + +void red_led(unsigned char on) { +	simprintfunc("red_led", "%i", on); +	return; +} + +void green_led(unsigned char on) { +	simprintfunc("green_led", "%i", on); +	return; +} + +void clear() { +	simprintfunc("clear", ""); +	return; +} + +void play(const char* melody) { +	simprintfunc("play", "\"%s\"", melody); +	return; +} + +void serial_set_baud_rate(unsigned int rate) { +	simprintfunc("serial_set_baud_rate", "%u", rate); +	return; +} + +void serial_send(char* message, unsigned int length) { +	simprintfunc("serial_send", "<see below>, %u", length); +	unsigned int bytes = 0; +	simprintf(""); +	for (unsigned int byte = 0; byte < length; byte++) { +		if (bytes > DBG_BYTES_PER_LINE) { +			bytes = 0; +			printf("\n"); +			simprintf(""); +		} +		printf("%02x ", message[byte]); +		bytes++; +	} +	printf("\n"); +	return; +} + diff --git a/robot/sim.h b/robot/sim.h new file mode 100644 index 0000000..15a1b4b --- /dev/null +++ b/robot/sim.h @@ -0,0 +1,40 @@ +#pragma once + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +// debug print options +#define DBG_BYTES_PER_LINE 16 + +// debug colors +#define COL_BLK "\e[0;30m" +#define COL_RED "\e[0;31m" +#define COL_GRN "\e[0;32m" +#define COL_YEL "\e[0;33m" +#define COL_BLU "\e[0;34m" +#define COL_MAG "\e[0;35m" +#define COL_CYN "\e[0;36m" +#define COL_WHT "\e[0;37m" +#define COL_RST "\e[0m" + +// debug stdout print macros +#define simprintf(message, ...) printf(COL_RED "[SIM] " COL_RST message, ##__VA_ARGS__) +#define simprint(message) simprintf(message "\n") +#define simprintfunc(name, fmt, ...) simprintf(COL_BLU "[FUNC] " \ +		COL_CYN name COL_RST "(" COL_YEL fmt COL_RST ")\n", ##__VA_ARGS__) +#define simwarn(message, ...) simprintf(COL_YEL "[WARN] " COL_RST message, ##__VA_ARGS__) +#define siminfo(message, ...) simprintf(COL_MAG "[INFO] " COL_RST message, ##__VA_ARGS__) + +/** + * simulates pololu library functions for local testing + * NOLINT is so clang-tidy doesn't correct function names + */ +void time_reset(); // NOLINT +unsigned long get_ms(); // NOLINT +void red_led(unsigned char on); // NOLINT +void green_led(unsigned char on); // NOLINT +void clear(); // NOLINT +void play(const char* melody); // NOLINT +void serial_set_baud_rate(unsigned int rate); // NOLINT +void serial_send(char* message, unsigned int length); // NOLINT |