From 35c504703c103ad27ca0ed6f56522e41dccf6788 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Thu, 18 Dec 2014 23:45:36 +0100 Subject: [PATCH] Working build --- Makefile | 2 +- device.h | 36 +- hardware/AFSK.c | 21 +- main.c | 108 ++++- protocol/AX25.c | 85 +++- protocol/AX25.h | 42 +- protocol/SimpleSerial.c | 856 ++++++++++++++++++++++++++++++++++++++++ protocol/SimpleSerial.h | 25 ++ util/constants.h | 9 + util/time.h | 9 + 10 files changed, 1159 insertions(+), 34 deletions(-) create mode 100644 protocol/SimpleSerial.c create mode 100644 protocol/SimpleSerial.h create mode 100644 util/constants.h diff --git a/Makefile b/Makefile index 61b84c8..f2ccb83 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ FORMAT = ihex # List C source files here. (C dependencies are automatically generated.) #SRC = $(TARGET).c -SRC = main.c hardware/Serial.c hardware/AFSK.c util/CRC-CCIT.c protocol/AX25.c protocol/KISS.c +SRC = main.c hardware/Serial.c hardware/AFSK.c util/CRC-CCIT.c protocol/AX25.c protocol/KISS.c protocol/SimpleSerial.c # If there is more than one source file, append them above, or modify and # uncomment the following: diff --git a/device.h b/device.h index d7dd2cb..84f5331 100644 --- a/device.h +++ b/device.h @@ -1,22 +1,44 @@ +#include "util/constants.h" + #ifndef DEVICE_CONFIGURATION #define DEVICE_CONFIGURATION // CPU settings +#define TARGET_CPU m328p #define F_CPU 16000000 #define FREQUENCY_CORRECTION 0 +// ADC Reference settings +#define ADC_REFERENCE REF_3V3 +// OR +//#define ADC_REFERENCE REF_5V + // Sampling & timer setup #define CONFIG_AFSK_DAC_SAMPLERATE 9600 +// Serial protocol settings +//#define SERIAL_PROTOCOL PROTOCOL_KISS +// OR +#define SERIAL_PROTOCOL PROTOCOL_SIMPLE_SERIAL + +// AX25 settings +#if SERIAL_PROTOCOL == PROTOCOL_SIMPLE_SERIAL + #define CUSTOM_FRAME_SIZE 330 +#endif + // Serial settings -#define BAUD 115200 +#define BAUD 9600 +#define SERIAL_DEBUG false +#define TX_MAXWAIT 2UL // Port settings -#define DAC_PORT PORTD -#define DAC_DDR DDRD -#define LED_PORT PORTB -#define LED_DDR DDRB -#define ADC_PORT PORTC -#define ADC_DDR DDRC +#if TARGET_CPU == m328p + #define DAC_PORT PORTD + #define DAC_DDR DDRD + #define LED_PORT PORTB + #define LED_DDR DDRB + #define ADC_PORT PORTC + #define ADC_DDR DDRC +#endif #endif \ No newline at end of file diff --git a/hardware/AFSK.c b/hardware/AFSK.c index def4db1..4e6d448 100644 --- a/hardware/AFSK.c +++ b/hardware/AFSK.c @@ -7,20 +7,37 @@ extern unsigned long custom_preamble; extern unsigned long custom_tail; bool hw_afsk_dac_isr = false; +bool hw_5v_ref = false; Afsk *AFSK_modem; // Forward declerations int afsk_getchar(void); void afsk_putchar(char c); +void AFSK_hw_refDetect(void) { + // This is manual for now + #if ADC_REFERENCE == REF_5V + hw_5v_ref = true; + #else + hw_5v_ref = false; + #endif +} + void AFSK_hw_init(void) { // Set up ADC + + AFSK_hw_refDetect(); + TCCR1A = 0; TCCR1B = _BV(CS10) | _BV(WGM13) | _BV(WGM12); ICR1 = (((CPU_FREQ+FREQUENCY_CORRECTION)) / 9600) - 1; - // TODO: Implement reference detection - ADMUX = _BV(REFS0) | 0; + if (hw_5v_ref) { + ADMUX = _BV(REFS0) | 0; + } else { + ADMUX = 0; + } + ADC_DDR &= ~_BV(0); ADC_PORT &= ~_BV(0); DIDR0 |= _BV(0); diff --git a/main.c b/main.c index c3bf965..15abf3f 100644 --- a/main.c +++ b/main.c @@ -3,46 +3,114 @@ #include "device.h" #include "util/FIFO.h" +#include "util/time.h" #include "hardware/AFSK.h" #include "hardware/Serial.h" #include "protocol/AX25.h" -#include "protocol/KISS.h" -#define FEND 0xC0 -#define FESC 0xDB -#define TFEND 0xDC -#define TFESC 0xDD +#if SERIAL_PROTOCOL == PROTOCOL_KISS + #include "protocol/KISS.h" +#endif + +#if SERIAL_PROTOCOL == PROTOCOL_SIMPLE_SERIAL + #include "protocol/SimpleSerial.h" +#endif Serial serial; Afsk modem; AX25Ctx AX25; -static void ax25_callback(struct AX25Ctx *ctx) { - kiss_messageCallback(ctx); -} +#if SERIAL_PROTOCOL == PROTOCOL_KISS + static void ax25_callback(struct AX25Ctx *ctx) { + kiss_messageCallback(ctx); + } +#endif + +#if SERIAL_PROTOCOL == PROTOCOL_SIMPLE_SERIAL + static uint8_t serialBuffer[AX25_MAX_FRAME_LEN+1]; + static int sbyte; + static size_t serialLen = 0; + static bool sertx = false; + + static void ax25_callback(struct AX25Msg *msg) { + ss_messageCallback(msg); + } +#endif void init(void) { sei(); - AFSK_init(&modem); serial_init(&serial); - ax25_init(&AX25, &modem.fd, ax25_callback); - kiss_init(&AX25, &modem, &serial); - stdout = &serial.uart0; stdin = &serial.uart0; + + + AFSK_init(&modem); + ax25_init(&AX25, &modem.fd, ax25_callback); + + #if SERIAL_PROTOCOL == PROTOCOL_KISS + kiss_init(&AX25, &modem, &serial); + #endif + + #if SERIAL_PROTOCOL == PROTOCOL_SIMPLE_SERIAL + ss_init(&AX25); + #endif } int main (void) { init(); - while (true) { - ax25_poll(&AX25); - - if (serial_available(0)) { - char sbyte = uart0_getchar_nowait(); - kiss_serialCallback(sbyte); - } - } + #if SERIAL_PROTOCOL == PROTOCOL_KISS + while (true) { + ax25_poll(&AX25); + + if (serial_available(0)) { + char sbyte = uart0_getchar_nowait(); + kiss_serialCallback(sbyte); + } + } + #endif + + #if SERIAL_PROTOCOL == PROTOCOL_SIMPLE_SERIAL + ticks_t start = timer_clock(); + while (1) { + ax25_poll(&AX25); + + if (!sertx && serial_available(0)) { + sbyte = uart0_getchar_nowait(); + + #if SERIAL_DEBUG + if ((serialLen < AX25_MAX_FRAME_LEN) && (sbyte != 10)) { + serialBuffer[serialLen] = sbyte; + serialLen++; + } else { + sertx = true; + } + #else + if (serialLen < AX25_MAX_FRAME_LEN-1) { + serialBuffer[serialLen] = sbyte; + serialLen++; + } else { + serialBuffer[serialLen] = sbyte; + serialLen++; + sertx = true; + } + + start = timer_clock(); + #endif + } else { + if (!SERIAL_DEBUG && serialLen > 0 && timer_clock() - start > ms_to_ticks(TX_MAXWAIT)) { + sertx = true; + } + } + + if (sertx) { + ss_serialCallback(serialBuffer, serialLen, &AX25); + sertx = false; + serialLen = 0; + } + + } + #endif return(0); } \ No newline at end of file diff --git a/protocol/AX25.c b/protocol/AX25.c index eff1bed..92fc8aa 100644 --- a/protocol/AX25.c +++ b/protocol/AX25.c @@ -1,9 +1,17 @@ +// Based on work by Francesco Sacchi + #include +#include #include "AX25.h" #include "protocol/HDLC.h" #include "util/CRC-CCIT.h" #include "../hardware/AFSK.h" +#define countof(a) sizeof(a)/sizeof(a[0]) +#define MIN(a,b) ({ typeof(a) _a = (a); typeof(b) _b = (b); ((typeof(_a))((_a < _b) ? _a : _b)); }) +#define DECODE_CALL(buf, addr) for (unsigned i = 0; i < sizeof((addr)); i++) { char c = (*(buf)++ >> 1); (addr)[i] = (c == ' ') ? '\x0' : c; } +#define AX25_SET_REPEATED(msg, idx, val) do { if (val) { (msg)->rpt_flags |= _BV(idx); } else { (msg)->rpt_flags &= ~_BV(idx) ; } } while(0) + void ax25_init(AX25Ctx *ctx, FILE *channel, ax25_callback_t hook) { memset(ctx, 0, sizeof(*ctx)); ctx->ch = channel; @@ -15,6 +23,35 @@ static void ax25_decode(AX25Ctx *ctx) { #if SERIAL_PROTOCOL == PROTOCOL_KISS if (ctx->hook) ctx->hook(ctx); #endif + + #if SERIAL_PROTOCOL == PROTOCOL_SIMPLE_SERIAL + AX25Msg msg; + uint8_t *buf = ctx->buf; + + DECODE_CALL(buf, msg.dst.call); + msg.dst.ssid = (*buf++ >> 1) & 0x0F; + + DECODE_CALL(buf, msg.src.call); + msg.src.ssid = (*buf >> 1) & 0x0F; + + for (msg.rpt_count = 0; !(*buf++ & 0x01) && (msg.rpt_count < countof(msg.rpt_list)); msg.rpt_count++) { + DECODE_CALL(buf, msg.rpt_list[msg.rpt_count].call); + msg.rpt_list[msg.rpt_count].ssid = (*buf >> 1) & 0x0F; + AX25_SET_REPEATED(&msg, msg.rpt_count, (*buf & 0x80)); + } + + msg.ctrl = *buf++; + if (msg.ctrl != AX25_CTRL_UI) { return; } + + msg.pid = *buf++; + if (msg.pid != AX25_PID_NOLAYER3) { return; } + + msg.len = ctx->frame_len - 2 - (buf - ctx->buf); + msg.info = buf; + + if (ctx->hook) ctx->hook(&msg); + + #endif } void ax25_poll(AX25Ctx *ctx) { @@ -74,4 +111,50 @@ void ax25_sendRaw(AX25Ctx *ctx, void *_buf, size_t len) { ax25_putchar(ctx, crch); fputc(HDLC_FLAG, ctx->ch); -} \ No newline at end of file +} + +#if SERIAL_PROTOCOL == PROTOCOL_SIMPLE_SERIAL + static void ax25_sendCall(AX25Ctx *ctx, const AX25Call *addr, bool last){ + unsigned len = MIN(sizeof(addr->call), strlen(addr->call)); + + for (unsigned i = 0; i < len; i++) { + uint8_t c = addr->call[i]; + c = toupper(c); + ax25_putchar(ctx, c << 1); + } + + if (len < sizeof(addr->call)) { + for (unsigned i = 0; i < sizeof(addr->call) - len; i++) { + ax25_putchar(ctx, ' ' << 1); + } + } + + uint8_t ssid = 0x60 | (addr->ssid << 1) | (last ? 0x01 : 0); + ax25_putchar(ctx, ssid); + } + + void ax25_sendVia(AX25Ctx *ctx, const AX25Call *path, size_t path_len, const void *_buf, size_t len) { + const uint8_t *buf = (const uint8_t *)_buf; + + ctx->crc_out = CRC_CCIT_INIT_VAL; + fputc(HDLC_FLAG, ctx->ch); + + for (size_t i = 0; i < path_len; i++) { + ax25_sendCall(ctx, &path[i], (i == path_len - 1)); + } + + ax25_putchar(ctx, AX25_CTRL_UI); + ax25_putchar(ctx, AX25_PID_NOLAYER3); + + while (len--) { + ax25_putchar(ctx, *buf++); + } + + uint8_t crcl = (ctx->crc_out & 0xff) ^ 0xff; + uint8_t crch = (ctx->crc_out >> 8) ^ 0xff; + ax25_putchar(ctx, crcl); + ax25_putchar(ctx, crch); + + fputc(HDLC_FLAG, ctx->ch); + } +#endif \ No newline at end of file diff --git a/protocol/AX25.h b/protocol/AX25.h index 6f2eaa4..2142419 100644 --- a/protocol/AX25.h +++ b/protocol/AX25.h @@ -3,9 +3,14 @@ #include #include +#include "device.h" #define AX25_MIN_FRAME_LEN 18 -#define AX25_MAX_FRAME_LEN 620 +#ifndef CUSTOM_FRAME_SIZE + #define AX25_MAX_FRAME_LEN 620 +#else + #define AX25_MAX_FRAME_LEN CUSTOM_FRAME_SIZE +#endif #define AX25_CRC_CORRECT 0xF0B8 @@ -13,13 +18,17 @@ #define AX25_PID_NOLAYER3 0xF0 struct AX25Ctx; // Forward declaration +struct AX25Msg; #if SERIAL_PROTOCOL == PROTOCOL_KISS typedef void (*ax25_callback_t)(struct AX25Ctx *ctx); #endif -typedef struct AX25Ctx -{ +#if SERIAL_PROTOCOL == PROTOCOL_SIMPLE_SERIAL + typedef void (*ax25_callback_t)(struct AX25Msg *msg); +#endif + +typedef struct AX25Ctx { uint8_t buf[AX25_MAX_FRAME_LEN]; FILE *ch; size_t frame_len; @@ -30,6 +39,33 @@ typedef struct AX25Ctx bool escape; } AX25Ctx; +#if SERIAL_PROTOCOL == PROTOCOL_SIMPLE_SERIAL + #define AX25_CALL(str, id) {.call = (str), .ssid = (id) } + #define AX25_MAX_RPT 8 + #define AX25_REPEATED(msg, n) ((msg)->rpt_flags & BV(n)) + + typedef struct AX25Call { + char call[6]; + uint8_t ssid; + } AX25Call; + + typedef struct AX25Msg { + AX25Call src; + AX25Call dst; + AX25Call rpt_list[AX25_MAX_RPT]; + uint8_t rpt_count; + uint8_t rpt_flags; + uint16_t ctrl; + uint8_t pid; + const uint8_t *info; + size_t len; + } AX25Msg; + + void ax25_sendVia(AX25Ctx *ctx, const AX25Call *path, size_t path_len, const void *_buf, size_t len); + #define ax25_send(ctx, dst, src, buf, len) ax25_sendVia(ctx, ({static AX25Call __path[]={dst, src}; __path;}), 2, buf, len) + +#endif + void ax25_poll(AX25Ctx *ctx); void ax25_sendRaw(AX25Ctx *ctx, void *_buf, size_t len); void ax25_init(AX25Ctx *ctx, FILE *channel, ax25_callback_t hook); diff --git a/protocol/SimpleSerial.c b/protocol/SimpleSerial.c new file mode 100644 index 0000000..e264f93 --- /dev/null +++ b/protocol/SimpleSerial.c @@ -0,0 +1,856 @@ +#include "device.h" + +#if SERIAL_PROTOCOL == PROTOCOL_SIMPLE_SERIAL + +#define ENABLE_HELP true + +#include +#include +#include +#include +#include "hardware/Serial.h" +#include "SimpleSerial.h" +#include "util/time.h" + +#define countof(a) sizeof(a)/sizeof(a[0]) + +bool PRINT_SRC = true; +bool PRINT_DST = true; +bool PRINT_PATH = true; +bool PRINT_DATA = true; +bool PRINT_INFO = true; +bool VERBOSE = true; +bool SILENT = false; +bool SS_INIT = false; +bool SS_DEFAULT_CONF = false; + +AX25Call src; +AX25Call dst; +AX25Call path1; +AX25Call path2; + +char CALL[6] = DEFAULT_CALLSIGN; +int CALL_SSID = 0; +char DST[6] = DEFAULT_DESTINATION_CALL; +int DST_SSID = 0; +char PATH1[6] = "WIDE1"; +int PATH1_SSID = 1; +char PATH2[6] = "WIDE2"; +int PATH2_SSID = 2; + +AX25Call path[4]; +AX25Ctx *ax25ctx; + +#define NV_MAGIC_BYTE 0x69 +uint8_t EEMEM nvMagicByte; +uint8_t EEMEM nvCALL[6]; +uint8_t EEMEM nvDST[6]; +uint8_t EEMEM nvPATH1[6]; +uint8_t EEMEM nvPATH2[6]; +uint8_t EEMEM nvCALL_SSID; +uint8_t EEMEM nvDST_SSID; +uint8_t EEMEM nvPATH1_SSID; +uint8_t EEMEM nvPATH2_SSID; +bool EEMEM nvPRINT_SRC; +bool EEMEM nvPRINT_DST; +bool EEMEM nvPRINT_PATH; +bool EEMEM nvPRINT_DATA; +bool EEMEM nvPRINT_INFO; +bool EEMEM nvVERBOSE; +bool EEMEM nvSILENT; +uint8_t EEMEM nvPOWER; +uint8_t EEMEM nvHEIGHT; +uint8_t EEMEM nvGAIN; +uint8_t EEMEM nvDIRECTIVITY; +uint8_t EEMEM nvSYMBOL_TABLE; +uint8_t EEMEM nvSYMBOL; +uint8_t EEMEM nvAUTOACK; +int EEMEM nvPREAMBLE; +int EEMEM nvTAIL; + +// Location packet assembly fields +char latitude[8]; +char longtitude[9]; +char symbolTable = '/'; +char symbol = 'n'; + +uint8_t power = 10; +uint8_t height = 10; +uint8_t gain = 10; +uint8_t directivity = 10; +///////////////////////// + +// Message packet assembly fields +char message_recip[6]; +int message_recip_ssid = -1; + +int message_seq = 0; +char lastMessage[67]; +size_t lastMessageLen; +bool message_autoAck = false; +///////////////////////// + +extern unsigned long custom_preamble; +extern unsigned long custom_tail; + +void ss_init(AX25Ctx *ax25) { + ax25ctx = ax25; + ss_loadSettings(); + SS_INIT = true; + if (VERBOSE) { + delay_ms(300); + printf_P(PSTR("---------------\n")); + printf_P(PSTR("MicroAPRS v1.0b\n")); + printf_P(PSTR("unsigned.io/microaprs\n")); + if (SS_DEFAULT_CONF) printf_P(PSTR("Default configuration loaded!\n")); + printf_P(PSTR("Modem ready\n")); + printf_P(PSTR("---------------\n")); + } +} + +void ss_clearSettings(void) { + eeprom_update_byte((void*)&nvMagicByte, 0xFF); + if (VERBOSE) printf_P(PSTR("Configuration cleared. Restart to load defaults.\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); +} + +void ss_loadSettings(void) { + uint8_t verification = eeprom_read_byte((void*)&nvMagicByte); + if (verification == NV_MAGIC_BYTE) { + eeprom_read_block((void*)CALL, (void*)nvCALL, 6); + eeprom_read_block((void*)DST, (void*)nvDST, 6); + eeprom_read_block((void*)PATH1, (void*)nvPATH1, 6); + eeprom_read_block((void*)PATH2, (void*)nvPATH2, 6); + + CALL_SSID = eeprom_read_byte((void*)&nvCALL_SSID); + DST_SSID = eeprom_read_byte((void*)&nvDST_SSID); + PATH1_SSID = eeprom_read_byte((void*)&nvPATH1_SSID); + PATH2_SSID = eeprom_read_byte((void*)&nvPATH2_SSID); + + PRINT_SRC = eeprom_read_byte((void*)&nvPRINT_SRC); + PRINT_DST = eeprom_read_byte((void*)&nvPRINT_DST); + PRINT_PATH = eeprom_read_byte((void*)&nvPRINT_PATH); + PRINT_DATA = eeprom_read_byte((void*)&nvPRINT_DATA); + PRINT_INFO = eeprom_read_byte((void*)&nvPRINT_INFO); + VERBOSE = eeprom_read_byte((void*)&nvVERBOSE); + SILENT = eeprom_read_byte((void*)&nvSILENT); + + power = eeprom_read_byte((void*)&nvPOWER); + height = eeprom_read_byte((void*)&nvHEIGHT); + gain = eeprom_read_byte((void*)&nvGAIN); + directivity = eeprom_read_byte((void*)&nvDIRECTIVITY); + symbolTable = eeprom_read_byte((void*)&nvSYMBOL_TABLE); + symbol = eeprom_read_byte((void*)&nvSYMBOL); + message_autoAck = eeprom_read_byte((void*)&nvAUTOACK); + + custom_preamble = eeprom_read_word((void*)&nvPREAMBLE); + custom_tail = eeprom_read_word((void*)&nvTAIL); + + if (VERBOSE && SS_INIT) printf_P(PSTR("Configuration loaded\n")); + } else { + if (SS_INIT && !SILENT && VERBOSE) printf_P(PSTR("Error: No stored configuration to load!\n")); + if (SS_INIT && !SILENT && !VERBOSE) printf_P(PSTR("0\n")); + SS_DEFAULT_CONF = true; + } +} + +void ss_saveSettings(void) { + eeprom_update_block((void*)CALL, (void*)nvCALL, 6); + eeprom_update_block((void*)DST, (void*)nvDST, 6); + eeprom_update_block((void*)PATH1, (void*)nvPATH1, 6); + eeprom_update_block((void*)PATH2, (void*)nvPATH2, 6); + + eeprom_update_byte((void*)&nvCALL_SSID, CALL_SSID); + eeprom_update_byte((void*)&nvDST_SSID, DST_SSID); + eeprom_update_byte((void*)&nvPATH1_SSID, PATH1_SSID); + eeprom_update_byte((void*)&nvPATH2_SSID, PATH2_SSID); + + eeprom_update_byte((void*)&nvPRINT_SRC, PRINT_SRC); + eeprom_update_byte((void*)&nvPRINT_DST, PRINT_DST); + eeprom_update_byte((void*)&nvPRINT_PATH, PRINT_PATH); + eeprom_update_byte((void*)&nvPRINT_DATA, PRINT_DATA); + eeprom_update_byte((void*)&nvPRINT_INFO, PRINT_INFO); + eeprom_update_byte((void*)&nvVERBOSE, VERBOSE); + eeprom_update_byte((void*)&nvSILENT, SILENT); + + eeprom_update_byte((void*)&nvPOWER, power); + eeprom_update_byte((void*)&nvHEIGHT, height); + eeprom_update_byte((void*)&nvGAIN, gain); + eeprom_update_byte((void*)&nvDIRECTIVITY, directivity); + eeprom_update_byte((void*)&nvSYMBOL_TABLE, symbolTable); + eeprom_update_byte((void*)&nvSYMBOL, symbol); + eeprom_update_byte((void*)&nvAUTOACK, message_autoAck); + + eeprom_update_word((void*)&nvPREAMBLE, custom_preamble); + eeprom_update_word((void*)&nvTAIL, custom_tail); + + eeprom_update_byte((void*)&nvMagicByte, NV_MAGIC_BYTE); + + if (VERBOSE) printf_P(PSTR("Configuration saved\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); +} + +void ss_messageCallback(struct AX25Msg *msg) { + if (PRINT_SRC) { + if (PRINT_INFO) printf_P(PSTR("SRC: ")); + printf_P(PSTR("[%.6s-%d] "), msg->src.call, msg->src.ssid); + } + if (PRINT_DST) { + if (PRINT_INFO) printf_P(PSTR("DST: ")); + printf_P(PSTR("[%.6s-%d] "), msg->dst.call, msg->dst.ssid); + } + + if (PRINT_PATH) { + if (PRINT_INFO) printf_P(PSTR("PATH: ")); + for (int i = 0; i < msg->rpt_count; i++) + printf_P(PSTR("[%.6s-%d] "), msg->rpt_list[i].call, msg->rpt_list[i].ssid); + } + + if (PRINT_DATA) { + if (PRINT_INFO) printf_P(PSTR("DATA: ")); + printf_P(PSTR("%.*s"), msg->len, msg->info); + } + printf_P(PSTR("\r\n")); + + if (message_autoAck && msg->len > 11) { + char mseq[6]; + bool shouldAck = true; + int msl = 0; + int loc = msg->len - 1; + size_t i = 0; + + while (i<7 && i < msg->len) { + if (msg->info[loc-i] == '{') { + size_t p; + for (p = 0; p <= i; p++) { + mseq[p] = msg->info[loc-i+p]; + msl = i; + } + } + i++; + } + + if (msl != 0) { + int pos = 1; + int ssidPos = 0; + while (pos < 7) { + if (msg->info[pos] != CALL[pos-1]) { + shouldAck = false; + pos = 7; + } + pos++; + } + while (pos < 10) { + if (msg->info[pos] == '-') ssidPos = pos; + pos++; + } + if (ssidPos != 0) { + if (msg->info[ssidPos+2] == ' ') { + if (msg->info[ssidPos+1]-48 != CALL_SSID) { + shouldAck = false; + } + } else { + int assid = 10+(msg->info[ssidPos+2]-48); + if (assid != CALL_SSID) { + shouldAck = false; + } + } + } + + if (msl != 0 && shouldAck) { + int ii = 0; + char *ack = malloc(14+msl); + + for (ii = 0; ii < 9; ii++) { + ack[1+ii] = ' '; + } + int calllen = 0; + for (ii = 0; ii < 6; ii++) { + if (msg->src.call[ii] != 0) { + ack[1+ii] = msg->src.call[ii]; + calllen++; + } + } + + if (msg->src.ssid != 0) { + ack[1+calllen] = '-'; + if (msg->src.ssid < 10) { + ack[2+calllen] = msg->src.ssid+48; + } else { + ack[2+calllen] = 49; + ack[3+calllen] = msg->src.ssid-10+48; + } + } + + ack[0] = ':'; + ack[10] = ':'; + ack[11] = 'a'; + ack[12] = 'c'; + ack[13] = 'k'; + + for (ii = 0; ii < msl; ii++) { + ack[14+ii] = mseq[ii+1]; + } + + delay_ms(1750); + ss_sendPkt(ack, 14+msl, ax25ctx); + + free(ack); + } + } + } +} + +void ss_serialCallback(void *_buffer, size_t length, AX25Ctx *ctx) { + uint8_t *buffer = (uint8_t *)_buffer; + if (length > 0) { + // ! as first char to send packet + if (buffer[0] == '!' && length > 1) { + buffer++; length--; + ss_sendPkt(buffer, length, ctx); + if (VERBOSE) printf_P(PSTR("Packet sent\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else if (buffer[0] == '@') { + buffer++; length--; + ss_sendLoc(buffer, length, ctx); + if (VERBOSE) printf_P(PSTR("Location update sent\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else if (buffer[0] == '#') { + buffer++; length--; + ss_sendMsg(buffer, length, ctx); + if (VERBOSE) printf_P(PSTR("Message sent\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } + #if ENABLE_HELP + else if (buffer[0] == 'h') { + ss_printHelp(); + } + #endif + else if (buffer[0] == 'H') { + ss_printSettings(); + } else if (buffer[0] == 'S') { + ss_saveSettings(); + } else if (buffer[0] == 'C') { + ss_clearSettings(); + } else if (buffer[0] == 'L') { + ss_loadSettings(); + } else if (buffer[0] == 'c' && length > 3) { + buffer++; length--; + int count = 0; + while (length-- && count < 6) { + char c = buffer[count]; + if (c != 0 && c != 10 && c != 13) { + CALL[count] = c; + } else { + CALL[count] = 0x00; + } + count++; + } + while (count < 6) { + CALL[count] = 0x00; + count++; + } + if (VERBOSE) printf_P(PSTR("Callsign: %.6s-%d\n"), CALL, CALL_SSID); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + + } else if (buffer[0] == 'd' && length > 3) { + buffer++; length--; + int count = 0; + while (length-- && count < 6) { + char c = buffer[count]; + if (c != 0 && c != 10 && c != 13) { + DST[count] = c; + } else { + DST[count] = 0; + } + count++; + } + while (count < 6) { + DST[count] = 0x00; + count++; + } + if (VERBOSE) printf_P(PSTR("Destination: %.6s-%d\n"), DST, DST_SSID); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + + + } else if (buffer[0] == '1' && length > 1) { + buffer++; length--; + int count = 0; + while (length-- && count < 6) { + char c = buffer[count]; + if (c != 0 && c != 10 && c != 13) { + PATH1[count] = c; + } else { + PATH1[count] = 0; + } + count++; + } + while (count < 6) { + PATH1[count] = 0x00; + count++; + } + if (VERBOSE) printf_P(PSTR("Path1: %.6s-%d\n"), PATH1, PATH1_SSID); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + + + } else if (buffer[0] == '2' && length > 1) { + buffer++; length--; + int count = 0; + while (length-- && count < 6) { + char c = buffer[count]; + if (c != 0 && c != 10 && c != 13) { + PATH2[count] = c; + } else { + PATH2[count] = 0; + } + count++; + } + while (count < 6) { + PATH2[count] = 0x00; + count++; + } + if (VERBOSE) printf_P(PSTR("Path2: %.6s-%d\n"), PATH2, PATH2_SSID); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + + + } else if (buffer[0] == 's' && length > 2) { + buffer++; length--; + if (buffer[0] == 'c') { + if (length > 2 && buffer[2] > 48 && buffer[2] < 58) { + CALL_SSID = 10+buffer[2]-48; + } else { + CALL_SSID = buffer[1]-48; + } + if (VERBOSE) printf_P(PSTR("Callsign: %.6s-%d\n"), CALL, CALL_SSID); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } + if (buffer[0] == 'd') { + if (length > 2 && buffer[2] > 48 && buffer[2] < 58) { + DST_SSID = 10+buffer[2]-48; + } else { + DST_SSID = buffer[1]-48; + } + if (VERBOSE) printf_P(PSTR("Destination: %.6s-%d\n"), DST, DST_SSID); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } + if (buffer[0] == '1' && buffer[2] > 48 && buffer[2] < 58) { + if (length > 2) { + PATH1_SSID = 10+buffer[2]-48; + } else { + PATH1_SSID = buffer[1]-48; + } + if (VERBOSE) printf_P(PSTR("Path1: %.6s-%d\n"), PATH1, PATH1_SSID); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } + if (buffer[0] == '2' && buffer[2] > 48 && buffer[2] < 58) { + if (length > 2) { + PATH2_SSID = 10+buffer[2]-48; + } else { + PATH2_SSID = buffer[1]-48; + } + if (VERBOSE) printf_P(PSTR("Path2: %.6s-%d\n"), PATH2, PATH2_SSID); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } + + } else if (buffer[0] == 'p' && length > 2) { + buffer++; length--; + if (buffer[0] == 's') { + if (buffer[1] == 49) { + PRINT_SRC = true; + if (VERBOSE) printf_P(PSTR("Print SRC enabled\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else { + PRINT_SRC = false; + if (VERBOSE) printf_P(PSTR("Print SRC disabled\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } + } + if (buffer[0] == 'd') { + if (buffer[1] == 49) { + PRINT_DST = true; + if (VERBOSE) printf_P(PSTR("Print DST enabled\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else { + PRINT_DST = false; + if (VERBOSE) printf_P(PSTR("Print DST disabled\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } + } + if (buffer[0] == 'p') { + if (buffer[1] == 49) { + PRINT_PATH = true; + if (VERBOSE) printf_P(PSTR("Print PATH enabled\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else { + PRINT_PATH = false; + if (VERBOSE) printf_P(PSTR("Print PATH disabled\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } + } + if (buffer[0] == 'm') { + if (buffer[1] == 49) { + PRINT_DATA = true; + if (VERBOSE) printf_P(PSTR("Print DATA enabled\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else { + PRINT_DATA = false; + if (VERBOSE) printf_P(PSTR("Print DATA disabled\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } + } + if (buffer[0] == 'i') { + if (buffer[1] == 49) { + PRINT_INFO = true; + if (VERBOSE) printf_P(PSTR("Print INFO enabled\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else { + PRINT_INFO = false; + if (VERBOSE) printf_P(PSTR("Print INFO disabled\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } + } + } else if (buffer[0] == 'v') { + if (buffer[1] == 49) { + VERBOSE = true; + printf_P(PSTR("Verbose mode enabled\n")); + } else { + VERBOSE = false; + printf_P(PSTR("Verbose mode disabled\n")); + } + } else if (buffer[0] == 'V') { + if (buffer[1] == 49) { + SILENT = true; + VERBOSE = false; + printf_P(PSTR("Silent mode enabled\n")); + } else { + SILENT = false; + printf_P(PSTR("Silent mode disabled\n")); + } + } else if (buffer[0] == 'l' && length > 2) { + buffer++; length--; + if (buffer[0] == 'l' && buffer[1] == 'a' && length >= 10) { + buffer += 2; + memcpy(latitude, (void *)buffer, 8); + if (VERBOSE) printf_P(PSTR("Latitude set to %.8s\n"), latitude); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else if (buffer[0] == 'l' && buffer[1] == 'o' && length >= 11) { + buffer += 2; + memcpy(longtitude, (void *)buffer, 9); + if (VERBOSE) printf_P(PSTR("Longtitude set to %.9s\n"), longtitude); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else if (buffer[0] == 'p' && length >= 2 && buffer[1] >= 48 && buffer[1] <= 57) { + power = buffer[1] - 48; + if (VERBOSE) printf_P(PSTR("Power set to %dw\n"), power*power); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else if (buffer[0] == 'h' && length >= 2 && buffer[1] >= 48 && buffer[1] <= 57) { + height = buffer[1] - 48; + if (VERBOSE) printf_P(PSTR("Antenna height set to %ldm AAT\n"), (long)(_BV(height)*1000L)/328L); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else if (buffer[0] == 'g' && length >= 2 && buffer[1] >= 48 && buffer[1] <= 57) { + gain = buffer[1] - 48; + if (VERBOSE) printf_P(PSTR("Gain set to %ddB\n"), gain); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else if (buffer[0] == 'd' && length >= 2 && buffer[1] >= 48 && buffer[1] <= 57) { + directivity = buffer[1] - 48; + if (directivity == 9) directivity = 8; + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + if (VERBOSE) { + if (directivity == 0) printf_P(PSTR("Directivity set to omni\n")); + if (directivity != 0) printf_P(PSTR("Directivity set to %ddeg\n"), directivity*45); + } + } else if (buffer[0] == 's' && length >= 2) { + symbol = buffer[1]; + if (VERBOSE) printf_P(PSTR("Symbol set to %c\n"), symbol); + } else if (buffer[0] == 't' && length >= 2) { + if (buffer[1] == 'a') { + symbolTable = '\\'; + if (VERBOSE) printf_P(PSTR("Selected alternate symbol table\n")); + } else { + symbolTable = '/'; + if (VERBOSE) printf_P(PSTR("Selected standard symbol table\n")); + } + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } + + } else if (buffer[0] == 'm' && length > 1) { + buffer++; length--; + if (buffer[0] == 'c' && length > 1) { + buffer++; length--; + int count = 0; + while (length-- && count < 6) { + char c = buffer[count]; + if (c != 0 && c != 10 && c != 13) { + message_recip[count] = c; + } else { + message_recip[count] = 0x00; + } + count++; + } + while (count < 6) { + message_recip[count] = 0x00; + count++; + } + if (VERBOSE) { + printf_P(PSTR("Message recipient: %.6s"), message_recip); + if (message_recip_ssid != -1) { + printf_P(PSTR("-%d\n"), message_recip_ssid); + } else { + printf_P(PSTR("\n")); + } + } + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else if (buffer[0] == 's' && length > 1) { + if (length > 2) { + message_recip_ssid = 10+buffer[2]-48; + } else { + message_recip_ssid = buffer[1]-48; + } + if (message_recip_ssid < 0 || message_recip_ssid > 15) message_recip_ssid = -1; + if (VERBOSE) { + printf_P(PSTR("Message recipient: %.6s"), message_recip); + if (message_recip_ssid != -1) { + printf_P(PSTR("-%d\n"), message_recip_ssid); + } else { + printf_P(PSTR("\n")); + } + } + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else if (buffer[0] == 'r') { + ss_msgRetry(ctx); + if (VERBOSE) printf_P(PSTR("Retried last message\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else if (buffer[0] == 'a') { + if (buffer[1] == 49) { + message_autoAck = true; + if (VERBOSE) printf_P(PSTR("Message auto-ack enabled\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } else { + message_autoAck = false; + if (VERBOSE) printf_P(PSTR("Message auto-ack disabled\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("1\n")); + } + } + + } else if (buffer[0] == 'w' && length >= 2) { + char str[4]; buffer++; + memcpy(str, buffer, length-1); + int preamble = atoi(str); + if (preamble >= 0 && preamble <= 9999) { + custom_preamble = preamble; + printf_P(PSTR("Preamble set to %lums\n"), custom_preamble); + } else { + printf_P(PSTR("Error: Invalid value for preamble\n")); + } + } else if (buffer[0] == 'W' && length >= 2) { + char str[4]; buffer++; + memcpy(str, buffer, length-1); + int tail = atoi(str); + if (tail >= 0 && tail <= 9999) { + custom_tail = tail; + printf_P(PSTR("TX Tail set to %lums\n"), custom_tail); + } else { + printf_P(PSTR("Error: Invalid value for TX tail\n")); + } + } else { + if (VERBOSE) printf_P(PSTR("Error: Invalid command\n")); + if (!VERBOSE && !SILENT) printf_P(PSTR("0\n")); + } + + } + +} + +void ss_sendPkt(void *_buffer, size_t length, AX25Ctx *ax25) { + + uint8_t *buffer = (uint8_t *)_buffer; + + memcpy(dst.call, DST, 6); + dst.ssid = DST_SSID; + + memcpy(src.call, CALL, 6); + src.ssid = CALL_SSID; + + memcpy(path1.call, PATH1, 6); + path1.ssid = PATH1_SSID; + + memcpy(path2.call, PATH2, 6); + path2.ssid = PATH2_SSID; + + path[0] = dst; + path[1] = src; + path[2] = path1; + path[3] = path2; + + ax25_sendVia(ax25, path, countof(path), buffer, length); +} + +void ss_sendLoc(void *_buffer, size_t length, AX25Ctx *ax25) { + size_t payloadLength = 20+length; + bool usePHG = false; + if (power < 10 && height < 10 && gain < 10 && directivity < 9) { + usePHG = true; + payloadLength += 7; + } + uint8_t *packet = malloc(payloadLength); + uint8_t *ptr = packet; + packet[0] = '='; + packet[9] = symbolTable; + packet[19] = symbol; + ptr++; + memcpy(ptr, latitude, 8); + ptr += 9; + memcpy(ptr, longtitude, 9); + ptr += 10; + if (usePHG) { + packet[20] = 'P'; + packet[21] = 'H'; + packet[22] = 'G'; + packet[23] = power+48; + packet[24] = height+48; + packet[25] = gain+48; + packet[26] = directivity+48; + ptr+=7; + } + if (length > 0) { + uint8_t *buffer = (uint8_t *)_buffer; + memcpy(ptr, buffer, length); + } + + //printf_P(PSTR("Assembled packet:\n%.*s\n", payloadLength, packet); + ss_sendPkt(packet, payloadLength, ax25); + free(packet); +} + +void ss_sendMsg(void *_buffer, size_t length, AX25Ctx *ax25) { + if (length > 67) length = 67; + size_t payloadLength = 11+length+4; + + uint8_t *packet = malloc(payloadLength); + uint8_t *ptr = packet; + packet[0] = ':'; + int callSize = 6; + int count = 0; + while (callSize--) { + if (message_recip[count] != 0) { + packet[1+count] = message_recip[count]; + count++; + } + } + if (message_recip_ssid != -1) { + packet[1+count] = '-'; count++; + if (message_recip_ssid < 10) { + packet[1+count] = message_recip_ssid+48; count++; + } else { + packet[1+count] = 49; count++; + packet[1+count] = message_recip_ssid-10+48; count++; + } + } + while (count < 9) { + packet[1+count] = ' '; count++; + } + packet[1+count] = ':'; + ptr += 11; + if (length > 0) { + uint8_t *buffer = (uint8_t *)_buffer; + memcpy(ptr, buffer, length); + memcpy(lastMessage, buffer, length); + lastMessageLen = length; + } + + message_seq++; + if (message_seq > 999) message_seq = 0; + + packet[11+length] = '{'; + int n = message_seq % 10; + int d = ((message_seq % 100) - n)/10; + int h = (message_seq - d - n) / 100; + + packet[12+length] = h+48; + packet[13+length] = d+48; + packet[14+length] = n+48; + + //printf_P(PSTR("Assembled packet:\n%.*s\n", payloadLength, packet); + ss_sendPkt(packet, payloadLength, ax25); + + free(packet); +} + +void ss_msgRetry(AX25Ctx *ax25) { + message_seq--; + ss_sendMsg(lastMessage, lastMessageLen, ax25); +} + +void ss_printSettings(void) { + printf_P(PSTR("Configuration:\n")); + printf_P(PSTR("Callsign: %.6s-%d\n"), CALL, CALL_SSID); + printf_P(PSTR("Destination: %.6s-%d\n"), DST, DST_SSID); + printf_P(PSTR("Path1: %.6s-%d\n"), PATH1, PATH1_SSID); + printf_P(PSTR("Path2: %.6s-%d\n"), PATH2, PATH2_SSID); + if (message_autoAck) { + printf_P(PSTR("Auto-ack messages: On\n")); + } else { + printf_P(PSTR("Auto-ack messages: Off\n")); + } + if (power != 10) printf_P(PSTR("Power: %d\n"), power); + if (height != 10) printf_P(PSTR("Height: %d\n"), height); + if (gain != 10) printf_P(PSTR("Gain: %d\n"), gain); + if (directivity != 10) printf_P(PSTR("Directivity: %d\n"), directivity); + if (symbolTable == '\\') printf_P(PSTR("Symbol table: alternate\n")); + if (symbolTable == '/') printf_P(PSTR("Symbol table: standard\n")); + printf_P(PSTR("Symbol: %c\n"), symbol); + printf_P(PSTR("TX Preamble: %lu\n"), custom_preamble); + printf_P(PSTR("TX Tail: %lu\n"), custom_tail); +} + +#if ENABLE_HELP + void ss_printHelp(void) { + printf_P(PSTR("----------------------------------\n")); + printf_P(PSTR("Serial commands:\n")); + printf_P(PSTR("! Send raw packet\n")); + printf_P(PSTR("@ Send location update (cmt = optional comment)\n")); + printf_P(PSTR("# Send APRS message\n\n")); + + printf_P(PSTR("c Set your callsign\n")); + printf_P(PSTR("d Set destination callsign\n")); + printf_P(PSTR("1 Set PATH1 callsign\n")); + printf_P(PSTR("2 Set PATH2 callsign\n\n")); + + printf_P(PSTR("sc Set your SSID\n")); + printf_P(PSTR("sd Set destination SSID\n")); + printf_P(PSTR("s1 Set PATH1 SSID\n")); + printf_P(PSTR("s2 Set PATH2 SSID\n\n")); + + printf_P(PSTR("lla Set latitude (NMEA-format, eg 4903.50N)\n")); + printf_P(PSTR("llo Set latitude (NMEA-format, eg 07201.75W)\n")); + printf_P(PSTR("lp<0-9> Set TX power info\n")); + printf_P(PSTR("lh<0-9> Set antenna height info\n")); + printf_P(PSTR("lg<0-9> Set antenna gain info\n")); + printf_P(PSTR("ld<0-9> Set antenna directivity info\n")); + printf_P(PSTR("ls Select symbol\n")); + printf_P(PSTR("lt Select symbol table (standard/alternate)\n\n")); + + printf_P(PSTR("mc Set message recipient callsign\n")); + printf_P(PSTR("ms Set message recipient SSID\n")); + printf_P(PSTR("mr Retry last message\n")); + printf_P(PSTR("ma<1/0> Automatic message ACK on/off\n\n")); + + printf_P(PSTR("ps<1/0> Print SRC on/off\n")); + printf_P(PSTR("pd<1/0> Print DST on/off\n")); + printf_P(PSTR("pp<1/0> Print PATH on/off\n")); + printf_P(PSTR("pm<1/0> Print DATA on/off\n")); + printf_P(PSTR("pi<1/0> Print INFO on/off\n\n")); + printf_P(PSTR("v<1/0> Verbose mode on/off\n")); + printf_P(PSTR("V<1/0> Silent mode on/off\n\n")); + + printf_P(PSTR("w Set preamble time in ms\n")); + printf_P(PSTR("W Set transmission tail time in ms\n")); + + printf_P(PSTR("S Save configuration\n")); + printf_P(PSTR("L Load configuration\n")); + printf_P(PSTR("C Clear configuration\n")); + printf_P(PSTR("H Print configuration\n")); + printf_P(PSTR("----------------------------------\n")); + } +#endif + +#endif \ No newline at end of file diff --git a/protocol/SimpleSerial.h b/protocol/SimpleSerial.h new file mode 100644 index 0000000..d71546d --- /dev/null +++ b/protocol/SimpleSerial.h @@ -0,0 +1,25 @@ +#ifndef _PROTOCOL_SIMPLE_SERIAL +#define _PROTOCOL_SIMPLE_SERIAL 0x01 +#include "AX25.h" + +#define DEFAULT_CALLSIGN "NOCALL" +#define DEFAULT_DESTINATION_CALL "APZMDM" + +void ss_init(AX25Ctx *ax25); + +void ss_messageCallback(struct AX25Msg *msg); +void ss_serialCallback(void *_buffer, size_t length, AX25Ctx *ctx); + +void ss_sendPkt(void *_buffer, size_t length, AX25Ctx *ax25); +void ss_sendLoc(void *_buffer, size_t length, AX25Ctx *ax25); +void ss_sendMsg(void *_buffer, size_t length, AX25Ctx *ax25); +void ss_msgRetry(AX25Ctx *ax25); + +void ss_clearSettings(void); +void ss_loadSettings(void); +void ss_saveSettings(void); +void ss_printSettings(void); + +void ss_printHelp(void); + +#endif \ No newline at end of file diff --git a/util/constants.h b/util/constants.h new file mode 100644 index 0000000..6a16a68 --- /dev/null +++ b/util/constants.h @@ -0,0 +1,9 @@ +#define PROTOCOL_KISS 0x01 +#define PROTOCOL_SIMPLE_SERIAL 0x02 + +#define m328p 0x01 +#define m1284p 0x02 +#define m644p 0x03 + +#define REF_3V3 0x01 +#define REF_5V 0x02 \ No newline at end of file diff --git a/util/time.h b/util/time.h index 1f9f276..eb79970 100644 --- a/util/time.h +++ b/util/time.h @@ -1,6 +1,7 @@ #ifndef UTIL_TIME_H #define UTIL_TIME_H +#include #include "device.h" #define DIV_ROUND(dividend, divisor) (((dividend) + (divisor) / 2) / (divisor)) @@ -30,5 +31,13 @@ inline void cpu_relax(void) { // Do nothing! } +inline void delay_ms(unsigned long ms) { + ticks_t start = timer_clock(); + unsigned long n_ticks = ms_to_ticks(ms); + while (timer_clock() - start < n_ticks) { + cpu_relax(); + } +} + #endif \ No newline at end of file