From bbe4080e94308149b74dd9ccefddf95878eec5d0 Mon Sep 17 00:00:00 2001 From: Maximilien Cuony Date: Thu, 2 Jun 2016 22:00:22 +0200 Subject: Simulator: UART, Leds, begining of GPS --- src/common/includes/Core/usart.h | 57 ---- src/common/includes/GPIO/leds.h | 9 + src/common/includes/GPIO/usart.h | 64 +++++ src/common/includes/GPS/gps.h | 45 +++ src/common/includes/GPS/minmea.h | 234 ++++++++++++++++ src/common/src/Core/common.c | 4 +- src/common/src/Core/main.c | 230 +++++++++------- src/common/src/GPIO/leds.c | 1 + src/common/src/GPIO/usart.c | 136 ++++++++++ src/common/src/GPS/gps.c | 129 +++++++++ src/common/src/GPS/minmea.c | 572 +++++++++++++++++++++++++++++++++++++++ src/fsm/gps.c | 138 ---------- src/fsm/gps.h | 46 ---- src/fsm/minmea.c | 572 --------------------------------------- src/fsm/minmea.h | 234 ---------------- src/simulator/Makefile | 2 + src/simulator/src/Core/main.c | 19 +- src/simulator/src/Core/usart.c | 190 ------------- src/simulator/src/GPIO/leds.c | 44 +++ src/simulator/src/GPIO/usart.c | 97 +++++++ src/simulator/src/GPS/gps.c | 11 + src/simulator/src/GPS/minema.c | 1 + src/simulator/src/Gui/gui.c | 402 +++++++++++++++++++++++++++ src/simulator/src/Gui/test.c | 285 ------------------- src/simulator/vc.h | 2 +- src/stm32f/includes/GPIO/leds.h | 4 + src/stm32f/src/Core/main.c | 7 +- src/stm32f/src/Core/usart.c | 280 ------------------- src/stm32f/src/GPIO/leds.c | 42 +++ src/stm32f/src/GPIO/usart.c | 156 +++++++++++ src/stm32f/src/GPS/gps.c | 4 + src/stm32f/src/GPS/minema.c | 1 + 32 files changed, 2111 insertions(+), 1907 deletions(-) delete mode 100644 src/common/includes/Core/usart.h create mode 100644 src/common/includes/GPIO/leds.h create mode 100644 src/common/includes/GPIO/usart.h create mode 100644 src/common/includes/GPS/gps.h create mode 100644 src/common/includes/GPS/minmea.h create mode 100644 src/common/src/GPIO/leds.c create mode 100644 src/common/src/GPIO/usart.c create mode 100644 src/common/src/GPS/gps.c create mode 100644 src/common/src/GPS/minmea.c delete mode 100644 src/fsm/gps.c delete mode 100644 src/fsm/gps.h delete mode 100644 src/fsm/minmea.c delete mode 100644 src/fsm/minmea.h delete mode 100644 src/simulator/src/Core/usart.c create mode 100644 src/simulator/src/GPIO/leds.c create mode 100644 src/simulator/src/GPIO/usart.c create mode 100644 src/simulator/src/GPS/gps.c create mode 100644 src/simulator/src/GPS/minema.c create mode 100644 src/simulator/src/Gui/gui.c delete mode 100644 src/simulator/src/Gui/test.c create mode 100644 src/stm32f/includes/GPIO/leds.h delete mode 100644 src/stm32f/src/Core/usart.c create mode 100644 src/stm32f/src/GPIO/leds.c create mode 100644 src/stm32f/src/GPIO/usart.c create mode 100644 src/stm32f/src/GPS/gps.c create mode 100644 src/stm32f/src/GPS/minema.c (limited to 'src') diff --git a/src/common/includes/Core/usart.h b/src/common/includes/Core/usart.h deleted file mode 100644 index 9d9b59c..0000000 --- a/src/common/includes/Core/usart.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016 Matthias P. Braendli - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. -*/ - -/* This handles the USART 3 to the GPS receiver, and fills a queue of - * NMEA messages. - * - * It also handles the debug USART 2 and allows sending messages to the PC. - */ - -#ifndef __USART_H_ -#define __USART_H_ - -#define MAX_NMEA_SENTENCE_LEN 256 - -// Initialise USART2 for PC debugging -void usart_init(void); - -// Initialise USART3 for GPS and NMEA queue -// Needs running scheduler -void usart_gps_init(void); - -// Send the str to the GPS receiver -void usart_gps_puts(const char* str); - -// a printf to send data to the PC -void usart_debug(const char *format, ...); - -// Send a string to the PC -void usart_debug_puts(const char* str); - -// Get a MAX_NMEA_SENTENCE_LEN sized NMEA sentence -// Return 1 on success -int usart_get_nmea_sentence(char* nmea); - -#endif //__USART_H_ - diff --git a/src/common/includes/GPIO/leds.h b/src/common/includes/GPIO/leds.h new file mode 100644 index 0000000..70c57d2 --- /dev/null +++ b/src/common/includes/GPIO/leds.h @@ -0,0 +1,9 @@ +#pragma once + +#define LED_GREEN 1 +#define LED_ORANGE 2 +#define LED_RED 3 +#define LED_BLUE 4 + +void leds_turn_off(int); +void leds_turn_on(int); diff --git a/src/common/includes/GPIO/usart.h b/src/common/includes/GPIO/usart.h new file mode 100644 index 0000000..62c86c9 --- /dev/null +++ b/src/common/includes/GPIO/usart.h @@ -0,0 +1,64 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Matthias P. Braendli, Maximilien Cuony + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +/* This handles the USART 3 to the GPS receiver, and fills a queue of + * NMEA messages. + * + * It also handles the debug USART 2 and allows sending messages to the PC. + */ + +#ifndef __USART_H_ +#define __USART_H_ + +#define MAX_NMEA_SENTENCE_LEN 256 + +// Initialise USART2 for PC debugging +void usart_init(void); + +// Initialise USART3 for GPS and NMEA queue +// Needs running scheduler +void usart_gps_init(void); + +// Send the str to the GPS receiver +void usart_gps_puts(const char* str); + +// a printf to send data to the PC +void usart_debug(const char *format, ...); + +// Send a string to the PC +void usart_debug_puts(const char* str); + +// Get a MAX_NMEA_SENTENCE_LEN sized NMEA sentence +// Return 1 on success +int usart_get_nmea_sentence(char* nmea); + +void usart_debug_timestamp(); + +void usart_gps_specific_init(); + +void usart_process_char(char); +void usart_gps_process_char(char); + +#endif //__USART_H_ + diff --git a/src/common/includes/GPS/gps.h b/src/common/includes/GPS/gps.h new file mode 100644 index 0000000..768ade1 --- /dev/null +++ b/src/common/includes/GPS/gps.h @@ -0,0 +1,45 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Matthias P. Braendli + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +#pragma once + +#include +#include + +/* Setup GPS receiver over USART and parse time */ + +/* USART connections: + * board TX to GPS RX on PD8 + * board RX to GPS TX on PD9 + */ + +// Setup communication and GPS receiver +void gps_init(); + +// Return 1 of the GPS is receiving time +int gps_locked(); + +// Get current time from GPS +// Returns 1 if time is valid, 0 otherwise +int gps_utctime(struct tm *timeutc); diff --git a/src/common/includes/GPS/minmea.h b/src/common/includes/GPS/minmea.h new file mode 100644 index 0000000..6d7fe18 --- /dev/null +++ b/src/common/includes/GPS/minmea.h @@ -0,0 +1,234 @@ +/* + * Copyright © 2014 Kosma Moczek + * This program is free software. It comes without any warranty, to the extent + * permitted by applicable law. You can redistribute it and/or modify it under + * the terms of the Do What The Fuck You Want To Public License, Version 2, as + * published by Sam Hocevar. See the COPYING file for more details. + */ + +#ifndef MINMEA_H +#define MINMEA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include + +#define MINMEA_MAX_LENGTH 80 + +enum minmea_sentence_id { + MINMEA_INVALID = -1, + MINMEA_UNKNOWN = 0, + MINMEA_SENTENCE_RMC, + MINMEA_SENTENCE_GGA, + MINMEA_SENTENCE_GSA, + MINMEA_SENTENCE_GLL, + MINMEA_SENTENCE_GST, + MINMEA_SENTENCE_GSV, +}; + +struct minmea_float { + int_least32_t value; + int_least32_t scale; +}; + +struct minmea_date { + int day; + int month; + int year; +}; + +struct minmea_time { + int hours; + int minutes; + int seconds; + int microseconds; +}; + +struct minmea_sentence_rmc { + struct minmea_time time; + bool valid; + struct minmea_float latitude; + struct minmea_float longitude; + struct minmea_float speed; + struct minmea_float course; + struct minmea_date date; + struct minmea_float variation; +}; + +struct minmea_sentence_gga { + struct minmea_time time; + struct minmea_float latitude; + struct minmea_float longitude; + int fix_quality; + int satellites_tracked; + struct minmea_float hdop; + struct minmea_float altitude; char altitude_units; + struct minmea_float height; char height_units; + int dgps_age; +}; + +enum minmea_gll_status { + MINMEA_GLL_STATUS_DATA_VALID = 'A', + MINMEA_GLL_STATUS_DATA_NOT_VALID = 'V', +}; + +enum minmea_gll_mode { + MINMEA_GLL_MODE_AUTONOMOUS = 'A', + MINMEA_GLL_MODE_DPGS = 'D', + MINMEA_GLL_MODE_DR = 'E', +}; + +struct minmea_sentence_gll { + struct minmea_float latitude; + struct minmea_float longitude; + struct minmea_time time; + char status; + char mode; +}; + +struct minmea_sentence_gst { + struct minmea_time time; + struct minmea_float rms_deviation; + struct minmea_float semi_major_deviation; + struct minmea_float semi_minor_deviation; + struct minmea_float semi_major_orientation; + struct minmea_float latitude_error_deviation; + struct minmea_float longitude_error_deviation; + struct minmea_float altitude_error_deviation; +}; + +enum minmea_gsa_mode { + MINMEA_GPGSA_MODE_AUTO = 'A', + MINMEA_GPGSA_MODE_FORCED = 'M', +}; + +enum minmea_gsa_fix_type { + MINMEA_GPGSA_FIX_NONE = 1, + MINMEA_GPGSA_FIX_2D = 2, + MINMEA_GPGSA_FIX_3D = 3, +}; + +struct minmea_sentence_gsa { + char mode; + int fix_type; + int sats[12]; + struct minmea_float pdop; + struct minmea_float hdop; + struct minmea_float vdop; +}; + +struct minmea_sat_info { + int nr; + int elevation; + int azimuth; + int snr; +}; + +struct minmea_sentence_gsv { + int total_msgs; + int msg_nr; + int total_sats; + struct minmea_sat_info sats[4]; +}; + +/** + * Calculate raw sentence checksum. Does not check sentence integrity. + */ +uint8_t minmea_checksum(const char *sentence); + +/** + * Check sentence validity and checksum. Returns true for valid sentences. + */ +bool minmea_check(const char *sentence, bool strict); + +/** + * Determine talker identifier. + */ +bool minmea_talker_id(char talker[3], const char *sentence); + +/** + * Determine sentence identifier. + */ +enum minmea_sentence_id minmea_sentence_id(const char *sentence, bool strict); + +/** + * Scanf-like processor for NMEA sentences. Supports the following formats: + * c - single character (char *) + * d - direction, returned as 1/-1, default 0 (int *) + * f - fractional, returned as value + scale (int *, int *) + * i - decimal, default zero (int *) + * s - string (char *) + * t - talker identifier and type (char *) + * T - date/time stamp (int *, int *, int *) + * Returns true on success. See library source code for details. + */ +bool minmea_scan(const char *sentence, const char *format, ...); + +/* + * Parse a specific type of sentence. Return true on success. + */ +bool minmea_parse_rmc(struct minmea_sentence_rmc *frame, const char *sentence); +bool minmea_parse_gga(struct minmea_sentence_gga *frame, const char *sentence); +bool minmea_parse_gsa(struct minmea_sentence_gsa *frame, const char *sentence); +bool minmea_parse_gll(struct minmea_sentence_gll *frame, const char *sentence); +bool minmea_parse_gst(struct minmea_sentence_gst *frame, const char *sentence); +bool minmea_parse_gsv(struct minmea_sentence_gsv *frame, const char *sentence); + +/** + * Convert GPS UTC date/time representation to a UNIX timestamp. + */ +int minmea_gettime(struct timespec *ts, const struct minmea_date *date, const struct minmea_time *time_); + +/** + * Rescale a fixed-point value to a different scale. Rounds towards zero. + */ +static inline int_least32_t minmea_rescale(struct minmea_float *f, int_least32_t new_scale) +{ + if (f->scale == 0) + return 0; + if (f->scale == new_scale) + return f->value; + if (f->scale > new_scale) + return (f->value + ((f->value > 0) - (f->value < 0)) * f->scale/new_scale/2) / (f->scale/new_scale); + else + return f->value * (new_scale/f->scale); +} + +/** + * Convert a fixed-point value to a floating-point value. + * Returns NaN for "unknown" values. + */ +static inline float minmea_tofloat(struct minmea_float *f) +{ + if (f->scale == 0) + return NAN; + return (float) f->value / (float) f->scale; +} + +/** + * Convert a raw coordinate to a floating point DD.DDD... value. + * Returns NaN for "unknown" values. + */ +static inline float minmea_tocoord(struct minmea_float *f) +{ + if (f->scale == 0) + return NAN; + int_least32_t degrees = f->value / (f->scale * 100); + int_least32_t minutes = f->value % (f->scale * 100); + return (float) degrees + (float) minutes / (60 * f->scale); +} + +#ifdef __cplusplus +} +#endif + +#endif /* MINMEA_H */ + +/* vim: set ts=4 sw=4 et: */ diff --git a/src/common/src/Core/common.c b/src/common/src/Core/common.c index 2eee88f..8c63917 100644 --- a/src/common/src/Core/common.c +++ b/src/common/src/Core/common.c @@ -23,7 +23,7 @@ */ #include "Core/common.h" -#include "Core/usart.h" +#include "GPIO/usart.h" #include "FreeRTOS.h" #include "timers.h" /* #include "Core/gps.h" */ @@ -118,7 +118,7 @@ int local_time(struct tm *time) { const int local_time_offset=1; // hours - int valid = 0; // TODO gps_utctime(time); + int valid = gps_utctime(time); if (valid) { time->tm_hour += local_time_offset; diff --git a/src/common/src/Core/main.c b/src/common/src/Core/main.c index ac41355..feac593 100644 --- a/src/common/src/Core/main.c +++ b/src/common/src/Core/main.c @@ -38,12 +38,13 @@ /* #include "cw.h" */ /* #include "pio.h" */ /* #include "i2c.h" */ -/* #include "gps.h" */ +#include "GPS/gps.h" /* #include "fsm.h" */ -/* #include "common.h" */ -#include "Core/usart.h" +#include "Core/common.h" +#include "GPIO/usart.h" /* #include "delay.h" */ /* #include "temperature.h" */ +#include "GPIO/leds.h" #include "vc.h" @@ -70,10 +71,15 @@ void vApplicationStackOverflowHook( TaskHandle_t xTask, while (1) {}; } +void * threadscheduler(void * arg) { + /* Start the RTOS Scheduler */ + vTaskStartScheduler(); +} + int main(void) { init(); /* delay_init(); */ - /* usart_init(); */ + usart_init(); usart_debug_puts("\r\n******* glutt-o-matique version " GIT_VERSION " *******\r\n"); /* */ /* if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET) */ @@ -82,26 +88,54 @@ int main(void) { /* } */ /* RCC_ClearFlag(); */ /* */ - /* TaskHandle_t task_handle; */ - /* xTaskCreate( */ - /* launcher_task, */ - /* "Launcher", */ - /* configMINIMAL_STACK_SIZE, */ - /* (void*) NULL, */ - /* tskIDLE_PRIORITY + 2UL, */ - /* &task_handle); */ - /* */ - /* if (!task_handle) { */ - /* trigger_fault(FAULT_SOURCE_MAIN); */ - /* } */ - /* */ - /* #<{(| Start the RTOS Scheduler |)}># */ - /* vTaskStartScheduler(); */ - /* */ - /* #<{(| HALT |)}># */ + TaskHandle_t task_handle; + xTaskCreate( + launcher_task, + "Launcher", + configMINIMAL_STACK_SIZE, + (void*) NULL, + tskIDLE_PRIORITY + 2UL, + &task_handle); + + if (!task_handle) { + trigger_fault(FAULT_SOURCE_MAIN); + } + + vTaskStartScheduler(); + /* pthread_t pth; */ + /* pthread_create(&pth, NULL, threadscheduler, "processing..."); */ + + /* HALT */ while(1); } + +static void test_task(void *pvParameters) { + + int i = 0; + + while(1) { + vTaskDelay(1000 / portTICK_RATE_MS); + + if (i == 0) { + i = 1; + leds_turn_on(LED_RED); + + } else { + i = 0; + leds_turn_off(LED_RED); + } + + char * poney = "$GNRMC,202140.00,A,4719.59789,N,00830.92084,E,0.012,,310516,,,A*65\n"; + gps_usart_send(poney); + /* char * poney2 = "$GNRMC,202141.00,A,4719.59788,N,00830.92085,E,0.012,,310516,,,A*64"; */ + /* gps_usart_send(poney2); */ + /* char * poney3 = "$GNRMC,202142.00,A,4719.59785,N,00830.92087,E,0.010,,310516,,,A*6A"; */ + /* gps_usart_send(poney3); */ + } + +} + // Launcher task is here to make sure the scheduler is // already running when calling the init functions. static void launcher_task(void *pvParameters) @@ -118,15 +152,15 @@ static void launcher_task(void *pvParameters) /* usart_debug_puts("common init\r\n"); */ /* common_init(); */ /* */ - /* usart_debug_puts("GPS init\r\n"); */ - /* gps_init(); */ + usart_debug_puts("GPS init\r\n"); + gps_init(); /* */ /* usart_debug_puts("DS18B20 init\r\n"); */ /* temperature_init(); */ /* */ /* usart_debug_puts("TaskButton init\r\n"); */ /* */ - /* TaskHandle_t task_handle; */ + TaskHandle_t task_handle; /* xTaskCreate( */ /* detect_button_press, */ /* "TaskButton", */ @@ -153,20 +187,20 @@ static void launcher_task(void *pvParameters) /* trigger_fault(FAULT_SOURCE_MAIN); */ /* } */ /* */ - /* usart_debug_puts("TaskGPS init\r\n"); */ - /* */ - /* xTaskCreate( */ - /* gps_monit_task, */ - /* "TaskGPSMonit", */ - /* 4*configMINIMAL_STACK_SIZE, */ - /* (void*) NULL, */ - /* tskIDLE_PRIORITY + 2UL, */ - /* &task_handle); */ - /* */ - /* if (!task_handle) { */ - /* trigger_fault(FAULT_SOURCE_MAIN); */ - /* } */ - /* */ + usart_debug_puts("TaskGPS init\r\n"); + + xTaskCreate( + gps_monit_task, + "TaskGPSMonit", + 4*configMINIMAL_STACK_SIZE, + (void*) NULL, + tskIDLE_PRIORITY + 2UL, + &task_handle); + + if (!task_handle) { + trigger_fault(FAULT_SOURCE_MAIN); + } + /* usart_debug_puts("Audio init\r\n"); */ /* */ /* InitializeAudio(Audio16000HzSettings); */ @@ -179,19 +213,29 @@ static void launcher_task(void *pvParameters) /* */ /* // By default, let's the audio off to save power */ /* AudioOff(); */ - /* */ - /* usart_debug_puts("Init done.\r\n"); */ - /* */ - /* #<{(| We are done now, suspend this task */ - /* * With FreeDOS' heap_1.c, we cannot delete it. */ - /* * See freertos.org -> More Advanced... -> Memory Management */ - /* * for more info. */ - /* |)}># */ - /* while (1) { */ - /* vTaskSuspend(NULL); */ - /* } */ + + usart_debug_puts("Init done.\r\n"); + + xTaskCreate( + test_task, + "TaskFSM", + 4*configMINIMAL_STACK_SIZE, + (void*) NULL, + tskIDLE_PRIORITY + 2UL, + &task_handle); + + /* We are done now, suspend this task + * With FreeDOS' heap_1.c, we cannot delete it. + * See freertos.org -> More Advanced... -> Memory Management + * for more info. + */ + + while (1) { + vTaskSuspend(NULL); + } } + static void detect_button_press(void *pvParameters) { /* int pin_high_count = 0; */ @@ -264,50 +308,52 @@ static void audio_callback(void* context, int select_buffer) /* ProvideAudioBufferWithoutBlocking(samples, samples_len); */ } -/* static struct tm gps_time; */ +static struct tm gps_time; static void gps_monit_task(void *pvParameters) { -/* GPIO_SetBits(GPIOD, GPIOD_BOARD_LED_BLUE); */ -/* */ -/* int t_gps_print_latch = 0; */ -/* */ -/* while (1) { */ -/* struct tm time; */ -/* int time_valid = local_time(&time); */ -/* */ -/* if (time_valid) { */ -/* if (time.tm_sec % 4 >= 2) { */ -/* GPIO_SetBits(GPIOD, GPIOD_BOARD_LED_BLUE); */ -/* } */ -/* else { */ -/* GPIO_ResetBits(GPIOD, GPIOD_BOARD_LED_BLUE); */ -/* } */ -/* */ -/* // Even hours: tm_trigger=1, odd hours: tm_trigger=0 */ -/* tm_trigger = (time.tm_hour + 1) % 2; */ -/* } */ -/* */ -/* gps_utctime(&gps_time); */ -/* */ -/* if (gps_time.tm_sec % 30 == 0 && t_gps_print_latch == 0) { */ -/* usart_debug("T_GPS %04d-%02d-%02d %02d:%02d:%02d\r\n", */ -/* gps_time.tm_year, gps_time.tm_mon, gps_time.tm_mday, */ -/* gps_time.tm_hour, gps_time.tm_min, gps_time.tm_sec); */ -/* */ -/* usart_debug("TIME %04d-%02d-%02d %02d:%02d:%02d\r\n", */ -/* time.tm_year, time.tm_mon, time.tm_mday, */ -/* time.tm_hour, time.tm_min, time.tm_sec); */ -/* */ -/* t_gps_print_latch = 1; */ -/* } */ -/* if (gps_time.tm_sec % 30 > 0) { */ -/* t_gps_print_latch = 0; */ -/* } */ -/* */ -/* vTaskDelay(100 / portTICK_RATE_MS); */ -/* */ -/* // Reload watchdog */ -/* IWDG_ReloadCounter(); */ -/* } */ + + leds_turn_on(LED_BLUE); + + int t_gps_print_latch = 0; + + while (1) { + struct tm time; + int time_valid = local_time(&time); + + if (time_valid) { + printf("Valid\n"); + if (time.tm_sec % 4 >= 2) { + leds_turn_on(LED_BLUE); + } + else { + leds_turn_off(LED_BLUE); + } + + // Even hours: tm_trigger=1, odd hours: tm_trigger=0 + tm_trigger = (time.tm_hour + 1) % 2; + } + + gps_utctime(&gps_time); + + if (gps_time.tm_sec % 30 == 0 && t_gps_print_latch == 0) { + usart_debug("T_GPS %04d-%02d-%02d %02d:%02d:%02d\r\n", + gps_time.tm_year, gps_time.tm_mon, gps_time.tm_mday, + gps_time.tm_hour, gps_time.tm_min, gps_time.tm_sec); + + usart_debug("TIME %04d-%02d-%02d %02d:%02d:%02d\r\n", + time.tm_year, time.tm_mon, time.tm_mday, + time.tm_hour, time.tm_min, time.tm_sec); + + t_gps_print_latch = 1; + } + if (gps_time.tm_sec % 30 > 0) { + t_gps_print_latch = 0; + } + + vTaskDelay(100 / portTICK_RATE_MS); + + // Reload watchdog //TODO + /* IWDG_ReloadCounter(); */ + } } /* static struct fsm_input_signals_t fsm_input; */ diff --git a/src/common/src/GPIO/leds.c b/src/common/src/GPIO/leds.c new file mode 100644 index 0000000..df2adea --- /dev/null +++ b/src/common/src/GPIO/leds.c @@ -0,0 +1 @@ +#include "../../../common/includes/GPIO/leds.h" diff --git a/src/common/src/GPIO/usart.c b/src/common/src/GPIO/usart.c new file mode 100644 index 0000000..77272f8 --- /dev/null +++ b/src/common/src/GPIO/usart.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include "Core/common.h" +#include "GPIO/usart.h" +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +// The ISR writes into this buffer +static char nmea_sentence[MAX_NMEA_SENTENCE_LEN]; +static int nmea_sentence_last_written = 0; + + +// Once a completed NMEA sentence is received in the ISR, +// it is appended to this queue +static QueueHandle_t usart_nmea_queue; + +void usart_gps_init() { + usart_nmea_queue = xQueueCreate(15, MAX_NMEA_SENTENCE_LEN); + if (usart_nmea_queue == 0) { + while(1); /* fatal error */ + } + + usart_gps_specific_init(); + +} + +void usart_gps_puts(const char* str) { + vTaskSuspendAll(); + return usart_puts(USART3, str); + xTaskResumeAll(); +} + +#define MAX_MSG_LEN 80 +static char usart_debug_message[MAX_MSG_LEN]; + +void usart_debug_timestamp() { + // Don't call printf here, to reduce stack usage + uint64_t now = timestamp_now(); + if (now == 0) { + usart_puts(USART2, "[0] "); + } + else { + char ts_str[64]; + int i = 63; + + ts_str[i--] = '\0'; + ts_str[i--] = ' '; + ts_str[i--] = ']'; + + while (now > 0 && i >= 0) { + ts_str[i--] = '0' + (now % 10); + now /= 10; + } + ts_str[i] = '['; + + usart_puts(USART2, &ts_str[i]); + } +} + +void usart_debug(const char *format, ...) { + va_list list; + va_start(list, format); + vsnprintf(usart_debug_message, MAX_MSG_LEN-1, format, list); + + vTaskSuspendAll(); + usart_debug_timestamp(); + usart_puts(USART2, usart_debug_message); + xTaskResumeAll(); + + va_end(list); +} + +void usart_debug_puts(const char* str) { + vTaskSuspendAll(); + usart_debug_timestamp(); + usart_puts(USART2, str); + xTaskResumeAll(); +} + +int usart_get_nmea_sentence(char* nmea) { + return xQueueReceive(usart_nmea_queue, nmea, portMAX_DELAY); +} + + +static void usart_clear_nmea_buffer(void) { + for (int i = 0; i < MAX_NMEA_SENTENCE_LEN; i++) { + nmea_sentence[i] = '\0'; + } + nmea_sentence_last_written = 0; +} + +void usart_process_char(char c) { + + if (c == 'h') { + usart_debug_puts("help: no commands supported yet!\r\n"); + } else { + usart_debug("Unknown command %c\r\n", c); + } + +} + +void usart_gps_process_char(char c) { + + if (nmea_sentence_last_written == 0) { + if (c == '$') { + // Likely new start of sentence + nmea_sentence[nmea_sentence_last_written] = c; + nmea_sentence_last_written++; + } + } + else if (nmea_sentence_last_written < MAX_NMEA_SENTENCE_LEN) { + nmea_sentence[nmea_sentence_last_written] = c; + nmea_sentence_last_written++; + + if (c == '\n') { + int success = xQueueSendToBackFromISR( + usart_nmea_queue, + nmea_sentence, + NULL); + + if (success == pdFALSE) { + trigger_fault(FAULT_SOURCE_USART); + } + + usart_clear_nmea_buffer(); + } + } + else { + // Buffer overrun without a meaningful NMEA message. + usart_clear_nmea_buffer(); + } + +} diff --git a/src/common/src/GPS/gps.c b/src/common/src/GPS/gps.c new file mode 100644 index 0000000..61c8c48 --- /dev/null +++ b/src/common/src/GPS/gps.c @@ -0,0 +1,129 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Matthias P. Braendli + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +#include "FreeRTOS.h" +#include "FreeRTOSConfig.h" +#include "task.h" +#include "semphr.h" +#include "Core/common.h" +#include "GPS/gps.h" +#include "GPS/minmea.h" +#include "GPIO/usart.h" + + +TickType_t gps_timeutc_last_updated = 0; +static struct tm gps_timeutc; +static int gps_timeutc_valid; + +const TickType_t gps_data_validity_timeout = 10000ul / portTICK_PERIOD_MS; + +static void gps_task(void *pvParameters); + +SemaphoreHandle_t timeutc_semaphore; + +// Get current time from GPS +int gps_utctime(struct tm *timeutc) { + int valid = 0; + + xSemaphoreTake(timeutc_semaphore, portMAX_DELAY); + if (xTaskGetTickCount() - gps_timeutc_last_updated < gps_data_validity_timeout) { + timeutc->tm_year = gps_timeutc.tm_year; + timeutc->tm_mon = gps_timeutc.tm_mon; + timeutc->tm_mday = gps_timeutc.tm_mday; + timeutc->tm_hour = gps_timeutc.tm_hour; + timeutc->tm_min = gps_timeutc.tm_min; + timeutc->tm_sec = gps_timeutc.tm_sec; + valid = gps_timeutc_valid; + } + xSemaphoreGive(timeutc_semaphore); + + return valid; +} + +#define RXBUF_LEN MAX_NMEA_SENTENCE_LEN +static char rxbuf[RXBUF_LEN]; + +static void gps_task(void *pvParameters) { + // Periodically reinit the GPS + while (1) { + taskYIELD(); + + int success = usart_get_nmea_sentence(rxbuf); + + if (success) { + const int strict = 1; + switch (minmea_sentence_id(rxbuf, strict)) { + case MINMEA_SENTENCE_RMC: + { + struct minmea_sentence_rmc frame; + if (minmea_parse_rmc(&frame, rxbuf)) { + xSemaphoreTake(timeutc_semaphore, portMAX_DELAY); + gps_timeutc.tm_year = 2000 + frame.date.year; + gps_timeutc.tm_mon = frame.date.month; + gps_timeutc.tm_mday = frame.date.day; + gps_timeutc.tm_hour = frame.time.hours; + gps_timeutc.tm_min = frame.time.minutes; + gps_timeutc.tm_sec = frame.time.seconds; + gps_timeutc_valid = frame.valid; + gps_timeutc_last_updated = xTaskGetTickCount(); + xSemaphoreGive(timeutc_semaphore); + } + } break; + default: + break; + } + } + } +} + +void gps_init() { + gps_timeutc_valid = 0; + + usart_gps_init(); + + timeutc_semaphore = xSemaphoreCreateBinary(); + + if (timeutc_semaphore == NULL) { + trigger_fault(FAULT_SOURCE_GPS); + } else { + xSemaphoreGive(timeutc_semaphore); + } + + xTaskCreate( + gps_task, + "TaskGPS", + 4*configMINIMAL_STACK_SIZE, + (void*) NULL, + tskIDLE_PRIORITY + 2UL, + NULL); +} + +// Return 1 of the GPS is receiving time +int gps_locked() { + if (xTaskGetTickCount() - gps_timeutc_last_updated < gps_data_validity_timeout) { + return gps_timeutc_valid; + } else { + return 0; + } +} diff --git a/src/common/src/GPS/minmea.c b/src/common/src/GPS/minmea.c new file mode 100644 index 0000000..8700725 --- /dev/null +++ b/src/common/src/GPS/minmea.c @@ -0,0 +1,572 @@ +/* + * Copyright © 2014 Kosma Moczek + * This program is free software. It comes without any warranty, to the extent + * permitted by applicable law. You can redistribute it and/or modify it under + * the terms of the Do What The Fuck You Want To Public License, Version 2, as + * published by Sam Hocevar. See the COPYING file for more details. + */ + +// We are lacking timegm, but we only handle UTC and use mktime instead. See +// https://github.com/cloudyourcar/minmea-t/ README.md +#define timegm mktime + +#include "GPS/minmea.h" + +#include +#include +#include +#include + +#define boolstr(s) ((s) ? "true" : "false") + +static int hex2int(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + return -1; +} + +uint8_t minmea_checksum(const char *sentence) +{ + // Support senteces with or without the starting dollar sign. + if (*sentence == '$') + sentence++; + + uint8_t checksum = 0x00; + + // The optional checksum is an XOR of all bytes between "$" and "*". + while (*sentence && *sentence != '*') + checksum ^= *sentence++; + + return checksum; +} + +bool minmea_check(const char *sentence, bool strict) +{ + uint8_t checksum = 0x00; + + // Sequence length is limited. + if (strlen(sentence) > MINMEA_MAX_LENGTH + 3) + return false; + + // A valid sentence starts with "$". + if (*sentence++ != '$') + return false; + + // The optional checksum is an XOR of all bytes between "$" and "*". + while (*sentence && *sentence != '*' && isprint((unsigned char) *sentence)) + checksum ^= *sentence++; + + // If checksum is present... + if (*sentence == '*') { + // Extract checksum. + sentence++; + int upper = hex2int(*sentence++); + if (upper == -1) + return false; + int lower = hex2int(*sentence++); + if (lower == -1) + return false; + int expected = upper << 4 | lower; + + // Check for checksum mismatch. + if (checksum != expected) + return false; + } else if (strict) { + // Discard non-checksummed frames in strict mode. + return false; + } + + // The only stuff allowed at this point is a newline. + if (*sentence && strcmp(sentence, "\n") && strcmp(sentence, "\r\n")) + return false; + + return true; +} + +static inline bool minmea_isfield(char c) { + return isprint((unsigned char) c) && c != ',' && c != '*'; +} + +bool minmea_scan(const char *sentence, const char *format, ...) +{ + bool result = false; + bool optional = false; + va_list ap; + va_start(ap, format); + + const char *field = sentence; +#define next_field() \ + do { \ + /* Progress to the next field. */ \ + while (minmea_isfield(*sentence)) \ + sentence++; \ + /* Make sure there is a field there. */ \ + if (*sentence == ',') { \ + sentence++; \ + field = sentence; \ + } else { \ + field = NULL; \ + } \ + } while (0) + + while (*format) { + char type = *format++; + + if (type == ';') { + // All further fields are optional. + optional = true; + continue; + } + + if (!field && !optional) { + // Field requested but we ran out if input. Bail out. + goto parse_error; + } + + switch (type) { + case 'c': { // Single character field (char). + char value = '\0'; + + if (field && minmea_isfield(*field)) + value = *field; + + *va_arg(ap, char *) = value; + } break; + + case 'd': { // Single character direction field (int). + int value = 0; + + if (field && minmea_isfield(*field)) { + switch (*field) { + case 'N': + case 'E': + value = 1; + break; + case 'S': + case 'W': + value = -1; + break; + default: + goto parse_error; + } + } + + *va_arg(ap, int *) = value; + } break; + + case 'f': { // Fractional value with scale (struct minmea_float). + int sign = 0; + int_least32_t value = -1; + int_least32_t scale = 0; + + if (field) { + while (minmea_isfield(*field)) { + if (*field == '+' && !sign && value == -1) { + sign = 1; + } else if (*field == '-' && !sign && value == -1) { + sign = -1; + } else if (isdigit((unsigned char) *field)) { + int digit = *field - '0'; + if (value == -1) + value = 0; + if (value > (INT_LEAST32_MAX-digit) / 10) { + /* we ran out of bits, what do we do? */ + if (scale) { + /* truncate extra precision */ + break; + } else { + /* integer overflow. bail out. */ + goto parse_error; + } + } + value = (10 * value) + digit; + if (scale) + scale *= 10; + } else if (*field == '.' && scale == 0) { + scale = 1; + } else if (*field == ' ') { + /* Allow spaces at the start of the field. Not NMEA + * conformant, but some modules do this. */ + if (sign != 0 || value != -1 || scale != 0) + goto parse_error; + } else { + goto parse_error; + } + field++; + } + } + + if ((sign || scale) && value == -1) + goto parse_error; + + if (value == -1) { + /* No digits were scanned. */ + value = 0; + scale = 0; + } else if (scale == 0) { + /* No decimal point. */ + scale = 1; + } + if (sign) + value *= sign; + + *va_arg(ap, struct minmea_float *) = (struct minmea_float) {value, scale}; + } break; + + case 'i': { // Integer value, default 0 (int). + int value = 0; + + if (field) { + char *endptr; + value = strtol(field, &endptr, 10); + if (minmea_isfield(*endptr)) + goto parse_error; + } + + *va_arg(ap, int *) = value; + } break; + + case 's': { // String value (char *). + char *buf = va_arg(ap, char *); + + if (field) { + while (minmea_isfield(*field)) + *buf++ = *field++; + } + + *buf = '\0'; + } break; + + case 't': { // NMEA talker+sentence identifier (char *). + // This field is always mandatory. + if (!field) + goto parse_error; + + if (field[0] != '$') + goto parse_error; + for (int f=0; f<5; f++) + if (!minmea_isfield(field[1+f])) + goto parse_error; + + char *buf = va_arg(ap, char *); + memcpy(buf, field+1, 5); + buf[5] = '\0'; + } break; + + case 'D': { // Date (int, int, int), -1 if empty. + struct minmea_date *date = va_arg(ap, struct minmea_date *); + + int d = -1, m = -1, y = -1; + + if (field && minmea_isfield(*field)) { + // Always six digits. + for (int f=0; f<6; f++) + if (!isdigit((unsigned char) field[f])) + goto parse_error; + + d = strtol((char[]) {field[0], field[1], '\0'}, NULL, 10); + m = strtol((char[]) {field[2], field[3], '\0'}, NULL, 10); + y = strtol((char[]) {field[4], field[5], '\0'}, NULL, 10); + } + + date->day = d; + date->month = m; + date->year = y; + } break; + + case 'T': { // Time (int, int, int, int), -1 if empty. + struct minmea_time *time_ = va_arg(ap, struct minmea_time *); + + int h = -1, i = -1, s = -1, u = -1; + + if (field && minmea_isfield(*field)) { + // Minimum required: integer time. + for (int f=0; f<6; f++) + if (!isdigit((unsigned char) field[f])) + goto parse_error; + + h = strtol((char[]) {field[0], field[1], '\0'}, NULL, 10); + i = strtol((char[]) {field[2], field[3], '\0'}, NULL, 10); + s = strtol((char[]) {field[4], field[5], '\0'}, NULL, 10); + field += 6; + + // Extra: fractional time. Saved as microseconds. + if (*field++ == '.') { + int value = 0; + int scale = 1000000; + while (isdigit((unsigned char) *field) && scale > 1) { + value = (value * 10) + (*field++ - '0'); + scale /= 10; + } + u = value * scale; + } else { + u = 0; + } + } + + time_->hours = h; + time_->minutes = i; + time_->seconds = s; + time_->microseconds = u; + } break; + + case '_': { // Ignore the field. + } break; + + default: { // Unknown. + goto parse_error; + } break; + } + + next_field(); + } + + result = true; + +parse_error: + va_end(ap); + return result; +} + +bool minmea_talker_id(char talker[3], const char *sentence) +{ + char type[6]; + if (!minmea_scan(sentence, "t", type)) + return false; + + talker[0] = type[0]; + talker[1] = type[1]; + talker[2] = '\0'; + + return true; +} + +enum minmea_sentence_id minmea_sentence_id(const char *sentence, bool strict) +{ + if (!minmea_check(sentence, strict)) + return MINMEA_INVALID; + + char type[6]; + if (!minmea_scan(sentence, "t", type)) + return MINMEA_INVALID; + + if (!strcmp(type+2, "RMC")) + return MINMEA_SENTENCE_RMC; + if (!strcmp(type+2, "GGA")) + return MINMEA_SENTENCE_GGA; + if (!strcmp(type+2, "GSA")) + return MINMEA_SENTENCE_GSA; + if (!strcmp(type+2, "GLL")) + return MINMEA_SENTENCE_GLL; + if (!strcmp(type+2, "GST")) + return MINMEA_SENTENCE_GST; + if (!strcmp(type+2, "GSV")) + return MINMEA_SENTENCE_GSV; + + return MINMEA_UNKNOWN; +} + +bool minmea_parse_rmc(struct minmea_sentence_rmc *frame, const char *sentence) +{ + // $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62 + char type[6]; + char validity; + int latitude_direction; + int longitude_direction; + int variation_direction; + if (!minmea_scan(sentence, "tTcfdfdffDfd", + type, + &frame->time, + &validity, + &frame->latitude, &latitude_direction, + &frame->longitude, &longitude_direction, + &frame->speed, + &frame->course, + &frame->date, + &frame->variation, &variation_direction)) + return false; + if (strcmp(type+2, "RMC")) + return false; + + frame->valid = (validity == 'A'); + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + frame->variation.value *= variation_direction; + + return true; +} + +bool minmea_parse_gga(struct minmea_sentence_gga *frame, const char *sentence) +{ + // $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 + char type[6]; + int latitude_direction; + int longitude_direction; + + if (!minmea_scan(sentence, "tTfdfdiiffcfci_", + type, + &frame->time, + &frame->latitude, &latitude_direction, + &frame->longitude, &longitude_direction, + &frame->fix_quality, + &frame->satellites_tracked, + &frame->hdop, + &frame->altitude, &frame->altitude_units, + &frame->height, &frame->height_units, + &frame->dgps_age)) + return false; + if (strcmp(type+2, "GGA")) + return false; + + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + + return true; +} + +bool minmea_parse_gsa(struct minmea_sentence_gsa *frame, const char *sentence) +{ + // $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39 + char type[6]; + + if (!minmea_scan(sentence, "tciiiiiiiiiiiiifff", + type, + &frame->mode, + &frame->fix_type, + &frame->sats[0], + &frame->sats[1], + &frame->sats[2], + &frame->sats[3], + &frame->sats[4], + &frame->sats[5], + &frame->sats[6], + &frame->sats[7], + &frame->sats[8], + &frame->sats[9], + &frame->sats[10], + &frame->sats[11], + &frame->pdop, + &frame->hdop, + &frame->vdop)) + return false; + if (strcmp(type+2, "GSA")) + return false; + + return true; +} + +bool minmea_parse_gll(struct minmea_sentence_gll *frame, const char *sentence) +{ + // $GPGLL,3723.2475,N,12158.3416,W,161229.487,A,A*41$; + char type[6]; + int latitude_direction; + int longitude_direction; + + if (!minmea_scan(sentence, "tfdfdTc;c", + type, + &frame->latitude, &latitude_direction, + &frame->longitude, &longitude_direction, + &frame->time, + &frame->status, + &frame->mode)) + return false; + if (strcmp(type+2, "GLL")) + return false; + + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + + return true; +} + +bool minmea_parse_gst(struct minmea_sentence_gst *frame, const char *sentence) +{ + // $GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58 + char type[6]; + + if (!minmea_scan(sentence, "tTfffffff", + type, + &frame->time, + &frame->rms_deviation, + &frame->semi_major_deviation, + &frame->semi_minor_deviation, + &frame->semi_major_orientation, + &frame->latitude_error_deviation, + &frame->longitude_error_deviation, + &frame->altitude_error_deviation)) + return false; + if (strcmp(type+2, "GST")) + return false; + + return true; +} + +bool minmea_parse_gsv(struct minmea_sentence_gsv *frame, const char *sentence) +{ + // $GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74 + // $GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D + // $GPGSV,4,2,11,08,51,203,30,09,45,215,28*75 + // $GPGSV,4,4,13,39,31,170,27*40 + // $GPGSV,4,4,13*7B + char type[6]; + + if (!minmea_scan(sentence, "tiii;iiiiiiiiiiiiiiii", + type, + &frame->total_msgs, + &frame->msg_nr, + &frame->total_sats, + &frame->sats[0].nr, + &frame->sats[0].elevation, + &frame->sats[0].azimuth, + &frame->sats[0].snr, + &frame->sats[1].nr, + &frame->sats[1].elevation, + &frame->sats[1].azimuth, + &frame->sats[1].snr, + &frame->sats[2].nr, + &frame->sats[2].elevation, + &frame->sats[2].azimuth, + &frame->sats[2].snr, + &frame->sats[3].nr, + &frame->sats[3].elevation, + &frame->sats[3].azimuth, + &frame->sats[3].snr + )) { + return false; + } + if (strcmp(type+2, "GSV")) + return false; + + return true; +} + +int minmea_gettime(struct timespec *ts, const struct minmea_date *date, const struct minmea_time *time_) +{ + if (date->year == -1 || time_->hours == -1) + return -1; + + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_year = 2000 + date->year - 1900; + tm.tm_mon = date->month - 1; + tm.tm_mday = date->day; + tm.tm_hour = time_->hours; + tm.tm_min = time_->minutes; + tm.tm_sec = time_->seconds; + + time_t timestamp = timegm(&tm); /* See README.md if your system lacks timegm(). */ + if (timestamp != -1) { + ts->tv_sec = timestamp; + ts->tv_nsec = time_->microseconds * 1000; + return 0; + } else { + return -1; + } +} + +/* vim: set ts=4 sw=4 et: */ diff --git a/src/fsm/gps.c b/src/fsm/gps.c deleted file mode 100644 index ea3e597..0000000 --- a/src/fsm/gps.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Matthias P. Braendli - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. -*/ - -#include "stm32f4xx_conf.h" -#include "stm32f4xx.h" -#include "FreeRTOS.h" -#include "FreeRTOSConfig.h" -#include "task.h" -#include "semphr.h" -#include "common.h" -#include "gps.h" -#include "usart.h" -#include "minmea.h" - - -TickType_t gps_timeutc_last_updated = 0; -static struct tm gps_timeutc; -static int gps_timeutc_valid; - -const TickType_t gps_data_validity_timeout = 10000ul / portTICK_PERIOD_MS; - -static void gps_task(void *pvParameters); - -SemaphoreHandle_t timeutc_semaphore; - -// Get current time from GPS -int gps_utctime(struct tm *timeutc) -{ - int valid = 0; - - xSemaphoreTake(timeutc_semaphore, portMAX_DELAY); - if (xTaskGetTickCount() - gps_timeutc_last_updated < gps_data_validity_timeout) { - timeutc->tm_year = gps_timeutc.tm_year; - timeutc->tm_mon = gps_timeutc.tm_mon; - timeutc->tm_mday = gps_timeutc.tm_mday; - timeutc->tm_hour = gps_timeutc.tm_hour; - timeutc->tm_min = gps_timeutc.tm_min; - timeutc->tm_sec = gps_timeutc.tm_sec; - valid = gps_timeutc_valid; - } - xSemaphoreGive(timeutc_semaphore); - - return valid; -} - -#define RXBUF_LEN MAX_NMEA_SENTENCE_LEN -static char rxbuf[RXBUF_LEN]; - -static void gps_task(void *pvParameters) -{ - // Periodically reinit the GPS - while (1) { - taskYIELD(); - - int success = usart_get_nmea_sentence(rxbuf); - - if (success) { - const int strict = 1; - switch (minmea_sentence_id(rxbuf, strict)) { - case MINMEA_SENTENCE_RMC: - { - struct minmea_sentence_rmc frame; - if (minmea_parse_rmc(&frame, rxbuf)) { - xSemaphoreTake(timeutc_semaphore, portMAX_DELAY); - gps_timeutc.tm_year = 2000 + frame.date.year; - gps_timeutc.tm_mon = frame.date.month; - gps_timeutc.tm_mday = frame.date.day; - gps_timeutc.tm_hour = frame.time.hours; - gps_timeutc.tm_min = frame.time.minutes; - gps_timeutc.tm_sec = frame.time.seconds; - gps_timeutc_valid = frame.valid; - gps_timeutc_last_updated = xTaskGetTickCount(); - xSemaphoreGive(timeutc_semaphore); - } - } break; - default: - break; - } - } - } -} - -void gps_init() -{ - gps_timeutc_valid = 0; - - usart_gps_init(); - - timeutc_semaphore = xSemaphoreCreateBinary(); - - if( timeutc_semaphore == NULL ) { - trigger_fault(FAULT_SOURCE_GPS); - } - else { - xSemaphoreGive(timeutc_semaphore); - } - - xTaskCreate( - gps_task, - "TaskGPS", - 4*configMINIMAL_STACK_SIZE, - (void*) NULL, - tskIDLE_PRIORITY + 2UL, - NULL); -} - -// Return 1 of the GPS is receiving time -int gps_locked() -{ - if (xTaskGetTickCount() - gps_timeutc_last_updated < gps_data_validity_timeout) { - return gps_timeutc_valid; - } - else { - return 0; - } -} - diff --git a/src/fsm/gps.h b/src/fsm/gps.h deleted file mode 100644 index 3472237..0000000 --- a/src/fsm/gps.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2015 Matthias P. Braendli - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. -*/ - -#pragma once - -#include -#include - -/* Setup GPS receiver over USART and parse time */ - -/* USART connections: - * board TX to GPS RX on PD8 - * board RX to GPS TX on PD9 - */ - -// Setup communication and GPS receiver -void gps_init(); - -// Return 1 of the GPS is receiving time -int gps_locked(); - -// Get current time from GPS -// Returns 1 if time is valid, 0 otherwise -int gps_utctime(struct tm *timeutc); - diff --git a/src/fsm/minmea.c b/src/fsm/minmea.c deleted file mode 100644 index fc6aa43..0000000 --- a/src/fsm/minmea.c +++ /dev/null @@ -1,572 +0,0 @@ -/* - * Copyright © 2014 Kosma Moczek - * This program is free software. It comes without any warranty, to the extent - * permitted by applicable law. You can redistribute it and/or modify it under - * the terms of the Do What The Fuck You Want To Public License, Version 2, as - * published by Sam Hocevar. See the COPYING file for more details. - */ - -// We are lacking timegm, but we only handle UTC and use mktime instead. See -// https://github.com/cloudyourcar/minmea-t/ README.md -#define timegm mktime - -#include "minmea.h" - -#include -#include -#include -#include - -#define boolstr(s) ((s) ? "true" : "false") - -static int hex2int(char c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - return -1; -} - -uint8_t minmea_checksum(const char *sentence) -{ - // Support senteces with or without the starting dollar sign. - if (*sentence == '$') - sentence++; - - uint8_t checksum = 0x00; - - // The optional checksum is an XOR of all bytes between "$" and "*". - while (*sentence && *sentence != '*') - checksum ^= *sentence++; - - return checksum; -} - -bool minmea_check(const char *sentence, bool strict) -{ - uint8_t checksum = 0x00; - - // Sequence length is limited. - if (strlen(sentence) > MINMEA_MAX_LENGTH + 3) - return false; - - // A valid sentence starts with "$". - if (*sentence++ != '$') - return false; - - // The optional checksum is an XOR of all bytes between "$" and "*". - while (*sentence && *sentence != '*' && isprint((unsigned char) *sentence)) - checksum ^= *sentence++; - - // If checksum is present... - if (*sentence == '*') { - // Extract checksum. - sentence++; - int upper = hex2int(*sentence++); - if (upper == -1) - return false; - int lower = hex2int(*sentence++); - if (lower == -1) - return false; - int expected = upper << 4 | lower; - - // Check for checksum mismatch. - if (checksum != expected) - return false; - } else if (strict) { - // Discard non-checksummed frames in strict mode. - return false; - } - - // The only stuff allowed at this point is a newline. - if (*sentence && strcmp(sentence, "\n") && strcmp(sentence, "\r\n")) - return false; - - return true; -} - -static inline bool minmea_isfield(char c) { - return isprint((unsigned char) c) && c != ',' && c != '*'; -} - -bool minmea_scan(const char *sentence, const char *format, ...) -{ - bool result = false; - bool optional = false; - va_list ap; - va_start(ap, format); - - const char *field = sentence; -#define next_field() \ - do { \ - /* Progress to the next field. */ \ - while (minmea_isfield(*sentence)) \ - sentence++; \ - /* Make sure there is a field there. */ \ - if (*sentence == ',') { \ - sentence++; \ - field = sentence; \ - } else { \ - field = NULL; \ - } \ - } while (0) - - while (*format) { - char type = *format++; - - if (type == ';') { - // All further fields are optional. - optional = true; - continue; - } - - if (!field && !optional) { - // Field requested but we ran out if input. Bail out. - goto parse_error; - } - - switch (type) { - case 'c': { // Single character field (char). - char value = '\0'; - - if (field && minmea_isfield(*field)) - value = *field; - - *va_arg(ap, char *) = value; - } break; - - case 'd': { // Single character direction field (int). - int value = 0; - - if (field && minmea_isfield(*field)) { - switch (*field) { - case 'N': - case 'E': - value = 1; - break; - case 'S': - case 'W': - value = -1; - break; - default: - goto parse_error; - } - } - - *va_arg(ap, int *) = value; - } break; - - case 'f': { // Fractional value with scale (struct minmea_float). - int sign = 0; - int_least32_t value = -1; - int_least32_t scale = 0; - - if (field) { - while (minmea_isfield(*field)) { - if (*field == '+' && !sign && value == -1) { - sign = 1; - } else if (*field == '-' && !sign && value == -1) { - sign = -1; - } else if (isdigit((unsigned char) *field)) { - int digit = *field - '0'; - if (value == -1) - value = 0; - if (value > (INT_LEAST32_MAX-digit) / 10) { - /* we ran out of bits, what do we do? */ - if (scale) { - /* truncate extra precision */ - break; - } else { - /* integer overflow. bail out. */ - goto parse_error; - } - } - value = (10 * value) + digit; - if (scale) - scale *= 10; - } else if (*field == '.' && scale == 0) { - scale = 1; - } else if (*field == ' ') { - /* Allow spaces at the start of the field. Not NMEA - * conformant, but some modules do this. */ - if (sign != 0 || value != -1 || scale != 0) - goto parse_error; - } else { - goto parse_error; - } - field++; - } - } - - if ((sign || scale) && value == -1) - goto parse_error; - - if (value == -1) { - /* No digits were scanned. */ - value = 0; - scale = 0; - } else if (scale == 0) { - /* No decimal point. */ - scale = 1; - } - if (sign) - value *= sign; - - *va_arg(ap, struct minmea_float *) = (struct minmea_float) {value, scale}; - } break; - - case 'i': { // Integer value, default 0 (int). - int value = 0; - - if (field) { - char *endptr; - value = strtol(field, &endptr, 10); - if (minmea_isfield(*endptr)) - goto parse_error; - } - - *va_arg(ap, int *) = value; - } break; - - case 's': { // String value (char *). - char *buf = va_arg(ap, char *); - - if (field) { - while (minmea_isfield(*field)) - *buf++ = *field++; - } - - *buf = '\0'; - } break; - - case 't': { // NMEA talker+sentence identifier (char *). - // This field is always mandatory. - if (!field) - goto parse_error; - - if (field[0] != '$') - goto parse_error; - for (int f=0; f<5; f++) - if (!minmea_isfield(field[1+f])) - goto parse_error; - - char *buf = va_arg(ap, char *); - memcpy(buf, field+1, 5); - buf[5] = '\0'; - } break; - - case 'D': { // Date (int, int, int), -1 if empty. - struct minmea_date *date = va_arg(ap, struct minmea_date *); - - int d = -1, m = -1, y = -1; - - if (field && minmea_isfield(*field)) { - // Always six digits. - for (int f=0; f<6; f++) - if (!isdigit((unsigned char) field[f])) - goto parse_error; - - d = strtol((char[]) {field[0], field[1], '\0'}, NULL, 10); - m = strtol((char[]) {field[2], field[3], '\0'}, NULL, 10); - y = strtol((char[]) {field[4], field[5], '\0'}, NULL, 10); - } - - date->day = d; - date->month = m; - date->year = y; - } break; - - case 'T': { // Time (int, int, int, int), -1 if empty. - struct minmea_time *time_ = va_arg(ap, struct minmea_time *); - - int h = -1, i = -1, s = -1, u = -1; - - if (field && minmea_isfield(*field)) { - // Minimum required: integer time. - for (int f=0; f<6; f++) - if (!isdigit((unsigned char) field[f])) - goto parse_error; - - h = strtol((char[]) {field[0], field[1], '\0'}, NULL, 10); - i = strtol((char[]) {field[2], field[3], '\0'}, NULL, 10); - s = strtol((char[]) {field[4], field[5], '\0'}, NULL, 10); - field += 6; - - // Extra: fractional time. Saved as microseconds. - if (*field++ == '.') { - int value = 0; - int scale = 1000000; - while (isdigit((unsigned char) *field) && scale > 1) { - value = (value * 10) + (*field++ - '0'); - scale /= 10; - } - u = value * scale; - } else { - u = 0; - } - } - - time_->hours = h; - time_->minutes = i; - time_->seconds = s; - time_->microseconds = u; - } break; - - case '_': { // Ignore the field. - } break; - - default: { // Unknown. - goto parse_error; - } break; - } - - next_field(); - } - - result = true; - -parse_error: - va_end(ap); - return result; -} - -bool minmea_talker_id(char talker[3], const char *sentence) -{ - char type[6]; - if (!minmea_scan(sentence, "t", type)) - return false; - - talker[0] = type[0]; - talker[1] = type[1]; - talker[2] = '\0'; - - return true; -} - -enum minmea_sentence_id minmea_sentence_id(const char *sentence, bool strict) -{ - if (!minmea_check(sentence, strict)) - return MINMEA_INVALID; - - char type[6]; - if (!minmea_scan(sentence, "t", type)) - return MINMEA_INVALID; - - if (!strcmp(type+2, "RMC")) - return MINMEA_SENTENCE_RMC; - if (!strcmp(type+2, "GGA")) - return MINMEA_SENTENCE_GGA; - if (!strcmp(type+2, "GSA")) - return MINMEA_SENTENCE_GSA; - if (!strcmp(type+2, "GLL")) - return MINMEA_SENTENCE_GLL; - if (!strcmp(type+2, "GST")) - return MINMEA_SENTENCE_GST; - if (!strcmp(type+2, "GSV")) - return MINMEA_SENTENCE_GSV; - - return MINMEA_UNKNOWN; -} - -bool minmea_parse_rmc(struct minmea_sentence_rmc *frame, const char *sentence) -{ - // $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62 - char type[6]; - char validity; - int latitude_direction; - int longitude_direction; - int variation_direction; - if (!minmea_scan(sentence, "tTcfdfdffDfd", - type, - &frame->time, - &validity, - &frame->latitude, &latitude_direction, - &frame->longitude, &longitude_direction, - &frame->speed, - &frame->course, - &frame->date, - &frame->variation, &variation_direction)) - return false; - if (strcmp(type+2, "RMC")) - return false; - - frame->valid = (validity == 'A'); - frame->latitude.value *= latitude_direction; - frame->longitude.value *= longitude_direction; - frame->variation.value *= variation_direction; - - return true; -} - -bool minmea_parse_gga(struct minmea_sentence_gga *frame, const char *sentence) -{ - // $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 - char type[6]; - int latitude_direction; - int longitude_direction; - - if (!minmea_scan(sentence, "tTfdfdiiffcfci_", - type, - &frame->time, - &frame->latitude, &latitude_direction, - &frame->longitude, &longitude_direction, - &frame->fix_quality, - &frame->satellites_tracked, - &frame->hdop, - &frame->altitude, &frame->altitude_units, - &frame->height, &frame->height_units, - &frame->dgps_age)) - return false; - if (strcmp(type+2, "GGA")) - return false; - - frame->latitude.value *= latitude_direction; - frame->longitude.value *= longitude_direction; - - return true; -} - -bool minmea_parse_gsa(struct minmea_sentence_gsa *frame, const char *sentence) -{ - // $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39 - char type[6]; - - if (!minmea_scan(sentence, "tciiiiiiiiiiiiifff", - type, - &frame->mode, - &frame->fix_type, - &frame->sats[0], - &frame->sats[1], - &frame->sats[2], - &frame->sats[3], - &frame->sats[4], - &frame->sats[5], - &frame->sats[6], - &frame->sats[7], - &frame->sats[8], - &frame->sats[9], - &frame->sats[10], - &frame->sats[11], - &frame->pdop, - &frame->hdop, - &frame->vdop)) - return false; - if (strcmp(type+2, "GSA")) - return false; - - return true; -} - -bool minmea_parse_gll(struct minmea_sentence_gll *frame, const char *sentence) -{ - // $GPGLL,3723.2475,N,12158.3416,W,161229.487,A,A*41$; - char type[6]; - int latitude_direction; - int longitude_direction; - - if (!minmea_scan(sentence, "tfdfdTc;c", - type, - &frame->latitude, &latitude_direction, - &frame->longitude, &longitude_direction, - &frame->time, - &frame->status, - &frame->mode)) - return false; - if (strcmp(type+2, "GLL")) - return false; - - frame->latitude.value *= latitude_direction; - frame->longitude.value *= longitude_direction; - - return true; -} - -bool minmea_parse_gst(struct minmea_sentence_gst *frame, const char *sentence) -{ - // $GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58 - char type[6]; - - if (!minmea_scan(sentence, "tTfffffff", - type, - &frame->time, - &frame->rms_deviation, - &frame->semi_major_deviation, - &frame->semi_minor_deviation, - &frame->semi_major_orientation, - &frame->latitude_error_deviation, - &frame->longitude_error_deviation, - &frame->altitude_error_deviation)) - return false; - if (strcmp(type+2, "GST")) - return false; - - return true; -} - -bool minmea_parse_gsv(struct minmea_sentence_gsv *frame, const char *sentence) -{ - // $GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74 - // $GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D - // $GPGSV,4,2,11,08,51,203,30,09,45,215,28*75 - // $GPGSV,4,4,13,39,31,170,27*40 - // $GPGSV,4,4,13*7B - char type[6]; - - if (!minmea_scan(sentence, "tiii;iiiiiiiiiiiiiiii", - type, - &frame->total_msgs, - &frame->msg_nr, - &frame->total_sats, - &frame->sats[0].nr, - &frame->sats[0].elevation, - &frame->sats[0].azimuth, - &frame->sats[0].snr, - &frame->sats[1].nr, - &frame->sats[1].elevation, - &frame->sats[1].azimuth, - &frame->sats[1].snr, - &frame->sats[2].nr, - &frame->sats[2].elevation, - &frame->sats[2].azimuth, - &frame->sats[2].snr, - &frame->sats[3].nr, - &frame->sats[3].elevation, - &frame->sats[3].azimuth, - &frame->sats[3].snr - )) { - return false; - } - if (strcmp(type+2, "GSV")) - return false; - - return true; -} - -int minmea_gettime(struct timespec *ts, const struct minmea_date *date, const struct minmea_time *time_) -{ - if (date->year == -1 || time_->hours == -1) - return -1; - - struct tm tm; - memset(&tm, 0, sizeof(tm)); - tm.tm_year = 2000 + date->year - 1900; - tm.tm_mon = date->month - 1; - tm.tm_mday = date->day; - tm.tm_hour = time_->hours; - tm.tm_min = time_->minutes; - tm.tm_sec = time_->seconds; - - time_t timestamp = timegm(&tm); /* See README.md if your system lacks timegm(). */ - if (timestamp != -1) { - ts->tv_sec = timestamp; - ts->tv_nsec = time_->microseconds * 1000; - return 0; - } else { - return -1; - } -} - -/* vim: set ts=4 sw=4 et: */ diff --git a/src/fsm/minmea.h b/src/fsm/minmea.h deleted file mode 100644 index 6d7fe18..0000000 --- a/src/fsm/minmea.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright © 2014 Kosma Moczek - * This program is free software. It comes without any warranty, to the extent - * permitted by applicable law. You can redistribute it and/or modify it under - * the terms of the Do What The Fuck You Want To Public License, Version 2, as - * published by Sam Hocevar. See the COPYING file for more details. - */ - -#ifndef MINMEA_H -#define MINMEA_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include -#include -#include - -#define MINMEA_MAX_LENGTH 80 - -enum minmea_sentence_id { - MINMEA_INVALID = -1, - MINMEA_UNKNOWN = 0, - MINMEA_SENTENCE_RMC, - MINMEA_SENTENCE_GGA, - MINMEA_SENTENCE_GSA, - MINMEA_SENTENCE_GLL, - MINMEA_SENTENCE_GST, - MINMEA_SENTENCE_GSV, -}; - -struct minmea_float { - int_least32_t value; - int_least32_t scale; -}; - -struct minmea_date { - int day; - int month; - int year; -}; - -struct minmea_time { - int hours; - int minutes; - int seconds; - int microseconds; -}; - -struct minmea_sentence_rmc { - struct minmea_time time; - bool valid; - struct minmea_float latitude; - struct minmea_float longitude; - struct minmea_float speed; - struct minmea_float course; - struct minmea_date date; - struct minmea_float variation; -}; - -struct minmea_sentence_gga { - struct minmea_time time; - struct minmea_float latitude; - struct minmea_float longitude; - int fix_quality; - int satellites_tracked; - struct minmea_float hdop; - struct minmea_float altitude; char altitude_units; - struct minmea_float height; char height_units; - int dgps_age; -}; - -enum minmea_gll_status { - MINMEA_GLL_STATUS_DATA_VALID = 'A', - MINMEA_GLL_STATUS_DATA_NOT_VALID = 'V', -}; - -enum minmea_gll_mode { - MINMEA_GLL_MODE_AUTONOMOUS = 'A', - MINMEA_GLL_MODE_DPGS = 'D', - MINMEA_GLL_MODE_DR = 'E', -}; - -struct minmea_sentence_gll { - struct minmea_float latitude; - struct minmea_float longitude; - struct minmea_time time; - char status; - char mode; -}; - -struct minmea_sentence_gst { - struct minmea_time time; - struct minmea_float rms_deviation; - struct minmea_float semi_major_deviation; - struct minmea_float semi_minor_deviation; - struct minmea_float semi_major_orientation; - struct minmea_float latitude_error_deviation; - struct minmea_float longitude_error_deviation; - struct minmea_float altitude_error_deviation; -}; - -enum minmea_gsa_mode { - MINMEA_GPGSA_MODE_AUTO = 'A', - MINMEA_GPGSA_MODE_FORCED = 'M', -}; - -enum minmea_gsa_fix_type { - MINMEA_GPGSA_FIX_NONE = 1, - MINMEA_GPGSA_FIX_2D = 2, - MINMEA_GPGSA_FIX_3D = 3, -}; - -struct minmea_sentence_gsa { - char mode; - int fix_type; - int sats[12]; - struct minmea_float pdop; - struct minmea_float hdop; - struct minmea_float vdop; -}; - -struct minmea_sat_info { - int nr; - int elevation; - int azimuth; - int snr; -}; - -struct minmea_sentence_gsv { - int total_msgs; - int msg_nr; - int total_sats; - struct minmea_sat_info sats[4]; -}; - -/** - * Calculate raw sentence checksum. Does not check sentence integrity. - */ -uint8_t minmea_checksum(const char *sentence); - -/** - * Check sentence validity and checksum. Returns true for valid sentences. - */ -bool minmea_check(const char *sentence, bool strict); - -/** - * Determine talker identifier. - */ -bool minmea_talker_id(char talker[3], const char *sentence); - -/** - * Determine sentence identifier. - */ -enum minmea_sentence_id minmea_sentence_id(const char *sentence, bool strict); - -/** - * Scanf-like processor for NMEA sentences. Supports the following formats: - * c - single character (char *) - * d - direction, returned as 1/-1, default 0 (int *) - * f - fractional, returned as value + scale (int *, int *) - * i - decimal, default zero (int *) - * s - string (char *) - * t - talker identifier and type (char *) - * T - date/time stamp (int *, int *, int *) - * Returns true on success. See library source code for details. - */ -bool minmea_scan(const char *sentence, const char *format, ...); - -/* - * Parse a specific type of sentence. Return true on success. - */ -bool minmea_parse_rmc(struct minmea_sentence_rmc *frame, const char *sentence); -bool minmea_parse_gga(struct minmea_sentence_gga *frame, const char *sentence); -bool minmea_parse_gsa(struct minmea_sentence_gsa *frame, const char *sentence); -bool minmea_parse_gll(struct minmea_sentence_gll *frame, const char *sentence); -bool minmea_parse_gst(struct minmea_sentence_gst *frame, const char *sentence); -bool minmea_parse_gsv(struct minmea_sentence_gsv *frame, const char *sentence); - -/** - * Convert GPS UTC date/time representation to a UNIX timestamp. - */ -int minmea_gettime(struct timespec *ts, const struct minmea_date *date, const struct minmea_time *time_); - -/** - * Rescale a fixed-point value to a different scale. Rounds towards zero. - */ -static inline int_least32_t minmea_rescale(struct minmea_float *f, int_least32_t new_scale) -{ - if (f->scale == 0) - return 0; - if (f->scale == new_scale) - return f->value; - if (f->scale > new_scale) - return (f->value + ((f->value > 0) - (f->value < 0)) * f->scale/new_scale/2) / (f->scale/new_scale); - else - return f->value * (new_scale/f->scale); -} - -/** - * Convert a fixed-point value to a floating-point value. - * Returns NaN for "unknown" values. - */ -static inline float minmea_tofloat(struct minmea_float *f) -{ - if (f->scale == 0) - return NAN; - return (float) f->value / (float) f->scale; -} - -/** - * Convert a raw coordinate to a floating point DD.DDD... value. - * Returns NaN for "unknown" values. - */ -static inline float minmea_tocoord(struct minmea_float *f) -{ - if (f->scale == 0) - return NAN; - int_least32_t degrees = f->value / (f->scale * 100); - int_least32_t minutes = f->value % (f->scale * 100); - return (float) degrees + (float) minutes / (60 * f->scale); -} - -#ifdef __cplusplus -} -#endif - -#endif /* MINMEA_H */ - -/* vim: set ts=4 sw=4 et: */ diff --git a/src/simulator/Makefile b/src/simulator/Makefile index 4e74498..511b6e6 100644 --- a/src/simulator/Makefile +++ b/src/simulator/Makefile @@ -16,6 +16,8 @@ VPATH += $(SRCROOT)/Source VPATH += $(SRCROOT)/Source/portable/MemMang VPATH += $(SRCROOT)/Source/portable/GCC/POSIX VPATH += $(SRCROOT)/src/Core +VPATH += $(SRCROOT)/src/GPIO +VPATH += $(SRCROOT)/src/GPS # FreeRTOS Objects C_FILES += croutine.c diff --git a/src/simulator/src/Core/main.c b/src/simulator/src/Core/main.c index 7e93176..a326dbf 100644 --- a/src/simulator/src/Core/main.c +++ b/src/simulator/src/Core/main.c @@ -27,11 +27,22 @@ #include -void *threadFunc(void *arg) { - main2(); +static void thread_gui(void *arg) { + main_gui(); } void init() { - pthread_t pth; - pthread_create(&pth, NULL, threadFunc, "processing..."); + + /* pthread_t pth; */ + /* pthread_create(&pth, NULL, thread_gui, "processing..."); */ + + TaskHandle_t task_handle; + xTaskCreate( + thread_gui, + "Thread GUI", + configMINIMAL_STACK_SIZE, + (void*) NULL, + tskIDLE_PRIORITY + 2UL, + &task_handle); + } diff --git a/src/simulator/src/Core/usart.c b/src/simulator/src/Core/usart.c deleted file mode 100644 index bd91428..0000000 --- a/src/simulator/src/Core/usart.c +++ /dev/null @@ -1,190 +0,0 @@ - -/* - * The MIT License (MIT) - * - * Copyright (c) 2016 Matthias P. Braendli - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. -*/ - -#include -#include -#include -#include -#include "Core/common.h" -#include "Core/usart.h" -#include "FreeRTOS.h" -#include "task.h" -#include "queue.h" - -// The ISR writes into this buffer -static char nmea_sentence[MAX_NMEA_SENTENCE_LEN]; -static int nmea_sentence_last_written = 0; - -// Once a completed NMEA sentence is received in the ISR, -// it is appended to this queue -static QueueHandle_t usart_nmea_queue; - -#define USART_TypeDef void - -int _USART2 = 2; -int _USART3 = 3; - -#define USART2 &_USART2 -#define USART3 &_USART3 - -void usart_init() { -} - -void usart_gps_init() { - usart_nmea_queue = xQueueCreate(15, MAX_NMEA_SENTENCE_LEN); - if (usart_nmea_queue == 0) { - while(1); /* fatal error */ - } - -} - -// Make sure Tasks are suspended when this is called! -static void usart_puts(USART_TypeDef* USART, const char* str) -{ - while(*str) { - // TODO - putchar(*str); - str++; - } -} - -void usart_gps_puts(const char* str) -{ - vTaskSuspendAll(); - return usart_puts(USART3, str); - xTaskResumeAll(); -} - -#define MAX_MSG_LEN 80 -static char usart_debug_message[MAX_MSG_LEN]; - -void usart_debug_timestamp() { - // Don't call printf here, to reduce stack usage - uint64_t now = timestamp_now(); - if (now == 0) { - usart_puts(USART2, "[0] "); - } - else { - char ts_str[64]; - int i = 63; - - ts_str[i--] = '\0'; - ts_str[i--] = ' '; - ts_str[i--] = ']'; - - while (now > 0 && i >= 0) { - ts_str[i--] = '0' + (now % 10); - now /= 10; - } - ts_str[i] = '['; - - usart_puts(USART2, &ts_str[i]); - } -} - -void usart_debug(const char *format, ...) -{ - va_list list; - va_start(list, format); - vsnprintf(usart_debug_message, MAX_MSG_LEN-1, format, list); - - vTaskSuspendAll(); - usart_debug_timestamp(); - usart_puts(USART2, usart_debug_message); - xTaskResumeAll(); - - va_end(list); -} - -void usart_debug_puts(const char* str) -{ - vTaskSuspendAll(); - usart_debug_timestamp(); - usart_puts(USART2, str); - xTaskResumeAll(); -} - -int usart_get_nmea_sentence(char* nmea) -{ - return xQueueReceive(usart_nmea_queue, nmea, portMAX_DELAY); -} - - -static void usart_clear_nmea_buffer(void) -{ - for (int i = 0; i < MAX_NMEA_SENTENCE_LEN; i++) { - nmea_sentence[i] = '\0'; - } - nmea_sentence_last_written = 0; -} - -/* void USART3_IRQHandler(void) */ -/* { */ -/* if (USART_GetITStatus(USART3, USART_IT_RXNE)) { */ -/* char t = USART3->DR; */ -/* */ -/* if (nmea_sentence_last_written == 0) { */ -/* if (t == '$') { */ -/* // Likely new start of sentence */ -/* nmea_sentence[nmea_sentence_last_written] = t; */ -/* nmea_sentence_last_written++; */ -/* } */ -/* } */ -/* else if (nmea_sentence_last_written < MAX_NMEA_SENTENCE_LEN) { */ -/* nmea_sentence[nmea_sentence_last_written] = t; */ -/* nmea_sentence_last_written++; */ -/* */ -/* if (t == '\n') { */ -/* int success = xQueueSendToBackFromISR( */ -/* usart_nmea_queue, */ -/* nmea_sentence, */ -/* NULL); */ -/* */ -/* if (success == pdFALSE) { */ -/* trigger_fault(FAULT_SOURCE_USART); */ -/* } */ -/* */ -/* usart_clear_nmea_buffer(); */ -/* } */ -/* } */ -/* else { */ -/* // Buffer overrun without a meaningful NMEA message. */ -/* usart_clear_nmea_buffer(); */ -/* } */ -/* } */ -/* } */ -/* */ -/* void USART2_IRQHandler(void) */ -/* { */ -/* if (USART_GetITStatus(USART2, USART_IT_RXNE)) { */ -/* char t = USART2->DR; */ -/* if (t == 'h') { */ -/* usart_debug_puts("help: no commands supported yet!\r\n"); */ -/* } */ -/* else { */ -/* usart_debug("Unknown command %c\r\n", t); */ -/* } */ -/* } */ -/* } */ diff --git a/src/simulator/src/GPIO/leds.c b/src/simulator/src/GPIO/leds.c new file mode 100644 index 0000000..874cc19 --- /dev/null +++ b/src/simulator/src/GPIO/leds.c @@ -0,0 +1,44 @@ +#include "../../../common/src/GPIO/leds.c" + + +extern char led_blue; +extern char led_green; +extern char led_orange; +extern char led_red; + +void leds_turn_on(int l) { + + switch (l) { + case LED_GREEN: + led_green = 1; + break; + case LED_ORANGE: + led_orange = 1; + break; + case LED_RED: + led_red = 1; + break; + case LED_BLUE: + led_blue = 1; + break; + + } +} + +void leds_turn_off(int l) { + + switch (l) { + case LED_GREEN: + led_green = 0; + break; + case LED_ORANGE: + led_orange = 0; + break; + case LED_RED: + led_red = 0; + break; + case LED_BLUE: + led_blue = 0; + break; + } +} diff --git a/src/simulator/src/GPIO/usart.c b/src/simulator/src/GPIO/usart.c new file mode 100644 index 0000000..650602a --- /dev/null +++ b/src/simulator/src/GPIO/usart.c @@ -0,0 +1,97 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Maximilien Cuony + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + + +#define USART_TypeDef int + +int _USART2 = 2; +int _USART3 = 3; + +#define USART2 &_USART2 +#define USART3 &_USART3 + +static void usart_puts(USART_TypeDef*, const char*); + +#include "../../../common/includes/GPIO/usart.h" +#include "../../../common/src/GPIO/usart.c" + + +extern char uart_recv_txt[4096]; +int uart_recv_pointer = 0; + + +void usart_move_buffer_up(); + + +void usart_init() { + + // Zero buffer + for (int i = 0; i < 4096; i++) { + uart_recv_txt[i] = '\0'; + } + +} + +void usart_gps_specific_init() { +} + +void usart_move_buffer_up() { + + for (int i = 0; i <= 4010; i++) { + uart_recv_txt[i] = uart_recv_txt[i + 10]; + uart_recv_txt[i + 1] = '\0'; + } + + uart_recv_pointer -= 10; + +} + +// Make sure Tasks are suspended when this is called! +static void usart_puts(USART_TypeDef* USART, const char* str) { + + if (*USART == _USART2) { + while(*str) { + if (*str != '\r') { + uart_recv_txt[uart_recv_pointer+1] = '\0'; + uart_recv_txt[uart_recv_pointer] = *str; + uart_recv_pointer++; + + if (uart_recv_pointer >= 4000) { + usart_move_buffer_up(); + } + } + str++; + } + } +} + +void gui_usart_send(char * string) { + + while(*string) { + usart_process_char(*string); + + string++; + } + +} diff --git a/src/simulator/src/GPS/gps.c b/src/simulator/src/GPS/gps.c new file mode 100644 index 0000000..0ca20f3 --- /dev/null +++ b/src/simulator/src/GPS/gps.c @@ -0,0 +1,11 @@ +#include "../../../common/src/GPS/gps.c" + +void gps_usart_send(char * string) { + + while(*string) { + usart_gps_process_char(*string); + + string++; + } + +} diff --git a/src/simulator/src/GPS/minema.c b/src/simulator/src/GPS/minema.c new file mode 100644 index 0000000..10df198 --- /dev/null +++ b/src/simulator/src/GPS/minema.c @@ -0,0 +1 @@ +#include "../../../common/src/GPS/minmea.c" diff --git a/src/simulator/src/Gui/gui.c b/src/simulator/src/Gui/gui.c new file mode 100644 index 0000000..81c80b6 --- /dev/null +++ b/src/simulator/src/Gui/gui.c @@ -0,0 +1,402 @@ +#include "FreeRTOS.h" +#include "FreeRTOSConfig.h" +#include "task.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#define NK_XLIB_GL3_IMPLEMENTATION +#define NK_XLIB_LOAD_OPENGL_EXTENSIONS +#include "nuklear.h" +#include "nuklear_xlib_gl3.h" + +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +#define MAX_VERTEX_BUFFER 512 * 1024 +#define MAX_ELEMENT_BUFFER 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + + +/** + * USART + **/ +char uart_recv_txt[4096]; +static char uart_send_txt[512]; +static int uart_send_txt_len; +void gui_usart_send(char*); + +/** + * Leds + **/ +char led_blue = 0; +char led_green = 0; +char led_orange = 0; +char led_red = 0; + +struct XWindow { + Display *dpy; + Window win; + XVisualInfo *vis; + Colormap cmap; + XSetWindowAttributes swa; + XWindowAttributes attr; + GLXFBConfig fbc; + int width, height; +}; + +static int gl_err = FALSE; +static int gl_error_handler(Display *dpy, XErrorEvent *ev) { + UNUSED((dpy, ev)); + gl_err = TRUE; + return 0; +} + +static void die(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputs("\n", stderr); + exit(EXIT_FAILURE); +} + +static int has_extension(const char *string, const char *ext) { + + const char *start, *where, *term; + where = strchr(ext, ' '); + + if (where || *ext == '\0') + return FALSE; + + for (start = string;;) { + where = strstr((const char*)start, ext); + if (!where) break; + term = where + strlen(ext); + if (where == start || *(where - 1) == ' ') { + if (*term == ' ' || *term == '\0') + return TRUE; + } + start = term; + } + return FALSE; +} + + +int main_gui() { + /* Platform */ + int running = 1; + struct XWindow win; + GLXContext glContext; + struct nk_context *ctx; + struct nk_color background; + + memset(&win, 0, sizeof(win)); + win.dpy = XOpenDisplay(NULL); + + if (!win.dpy) { + die("Failed to open X display\n"); + } + + { + /* check glx version */ + int glx_major, glx_minor; + if (!glXQueryVersion(win.dpy, &glx_major, &glx_minor)) + die("[X11]: Error: Failed to query OpenGL version\n"); + if ((glx_major == 1 && glx_minor < 3) || (glx_major < 1)) + die("[X11]: Error: Invalid GLX version!\n"); + } + + { + /* find and pick matching framebuffer visual */ + int fb_count; + static GLint attr[] = { + GLX_X_RENDERABLE, True, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_DEPTH_SIZE, 24, + GLX_STENCIL_SIZE, 8, + GLX_DOUBLEBUFFER, True, + None + }; + GLXFBConfig *fbc; + fbc = glXChooseFBConfig(win.dpy, DefaultScreen(win.dpy), attr, &fb_count); + + if (!fbc) { + die("[X11]: Error: failed to retrieve framebuffer configuration\n"); + } + + { + /* pick framebuffer with most samples per pixel */ + int i; + int fb_best = -1; + int best_num_samples = -1; + + for (i = 0; i < fb_count; ++i) { + XVisualInfo *vi = glXGetVisualFromFBConfig(win.dpy, fbc[i]); + if (vi) { + int sample_buffer, samples; + glXGetFBConfigAttrib(win.dpy, fbc[i], GLX_SAMPLE_BUFFERS, &sample_buffer); + glXGetFBConfigAttrib(win.dpy, fbc[i], GLX_SAMPLES, &samples); + if ((fb_best < 0) || (sample_buffer && samples > best_num_samples)) + fb_best = i; best_num_samples = samples; + } + } + win.fbc = fbc[fb_best]; + XFree(fbc); + win.vis = glXGetVisualFromFBConfig(win.dpy, win.fbc); + } + } + { + /* create window */ + win.cmap = XCreateColormap(win.dpy, RootWindow(win.dpy, win.vis->screen), win.vis->visual, AllocNone); + win.swa.colormap = win.cmap; + win.swa.background_pixmap = None; + win.swa.border_pixel = 0; + win.swa.event_mask = + ExposureMask | KeyPressMask | KeyReleaseMask | + ButtonPress | ButtonReleaseMask| ButtonMotionMask | + Button1MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask| + PointerMotionMask| StructureNotifyMask; + win.win = XCreateWindow(win.dpy, RootWindow(win.dpy, win.vis->screen), 0, 0, + WINDOW_WIDTH, WINDOW_HEIGHT, 0, win.vis->depth, InputOutput, + win.vis->visual, CWBorderPixel|CWColormap|CWEventMask, &win.swa); + if (!win.win) die("[X11]: Failed to create window\n"); + XFree(win.vis); + XStoreName(win.dpy, win.win, "glutt-o-matique simulator 3000"); + XMapWindow(win.dpy, win.win); + } + { + /* create opengl context */ + typedef GLXContext(*glxCreateContext)(Display*, GLXFBConfig, GLXContext, Bool, const int*); + int(*old_handler)(Display*, XErrorEvent*) = XSetErrorHandler(gl_error_handler); + const char *extensions_str = glXQueryExtensionsString(win.dpy, DefaultScreen(win.dpy)); + glxCreateContext create_context = (glxCreateContext) + glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB"); + + gl_err = FALSE; + if (!has_extension(extensions_str, "GLX_ARB_create_context") || !create_context) { + fprintf(stdout, "[X11]: glXCreateContextAttribARB() not found...\n"); + fprintf(stdout, "[X11]: ... using old-style GLX context\n"); + glContext = glXCreateNewContext(win.dpy, win.fbc, GLX_RGBA_TYPE, 0, True); + } else { + GLint attr[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + None + }; + glContext = create_context(win.dpy, win.fbc, 0, True, attr); + XSync(win.dpy, False); + if (gl_err || !glContext) { + /* Could not create GL 3.0 context. Fallback to old 2.x context. + * If a version below 3.0 is requested, implementations will + * return the newest context version compatible with OpenGL + * version less than version 3.0.*/ + attr[1] = 1; attr[3] = 0; + gl_err = FALSE; + fprintf(stdout, "[X11] Failed to create OpenGL 3.0 context\n"); + fprintf(stdout, "[X11] ... using old-style GLX context!\n"); + glContext = create_context(win.dpy, win.fbc, 0, True, attr); + } + } + XSync(win.dpy, False); + XSetErrorHandler(old_handler); + if (gl_err || !glContext) + die("[X11]: Failed to create an OpenGL context\n"); + glXMakeCurrent(win.dpy, win.win, glContext); + } + + ctx = nk_x11_init(win.dpy, win.win); + /* Load Fonts: if none of these are loaded a default font will be used */ + {struct nk_font_atlas *atlas; + nk_x11_font_stash_begin(&atlas); + nk_x11_font_stash_end(); + } + + + background = nk_rgb(28,48,62); + + while (running) + { + taskYIELD(); + + vTaskSuspendAll(); + + /* Input */ + XEvent evt; + nk_input_begin(ctx); + while (XCheckWindowEvent(win.dpy, win.win, win.swa.event_mask, &evt)){ + if (XFilterEvent(&evt, win.win)) continue; + nk_x11_handle_event(&evt); + } + nk_input_end(ctx); + + /* GUI */ + { + + struct nk_panel layout; + + if (nk_begin(ctx, &layout, "UART", nk_rect(50, 50, 400, 300), NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) { + + + nk_menubar_begin(ctx); + nk_layout_row_begin(ctx, NK_STATIC, 25, 2); + nk_layout_row_push(ctx, 280); + + int active = nk_edit_string(ctx, NK_EDIT_FIELD|NK_EDIT_SIG_ENTER, uart_send_txt, &uart_send_txt_len, 512, nk_filter_default); + + nk_layout_row_push(ctx, 70); + if (nk_button_label(ctx, "Send", NK_BUTTON_DEFAULT) || (active & NK_EDIT_COMMITED)) { + + uart_send_txt[uart_send_txt_len] = '\0'; + + gui_usart_send(uart_send_txt); + + uart_send_txt[0] = '\0'; + uart_send_txt_len = 0; + } + + nk_menubar_end(ctx); + + nk_layout_row_dynamic(ctx, 25, 1); + + nk_label(ctx, "UART Output:", NK_TEXT_LEFT); + + nk_layout_row_dynamic(ctx, 16, 1); + + char * current_pointer = uart_recv_txt; + int l = 0; + + while (*current_pointer != 0) { + + if (*current_pointer == '\n') { + if (l > 1) { + nk_text(ctx, current_pointer - l, l, NK_TEXT_LEFT); + } + current_pointer++; + l = 0; + } + current_pointer++; + l++; + } + + if (l > 1) { + nk_text(ctx, current_pointer - l, l, NK_TEXT_LEFT); + } + + /* nk_layout_row_end(ctx); */ + + + nk_end(ctx); + } + + + if (nk_begin(ctx, &layout, "LEDs", nk_rect(460, 50, 100, 155), NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) { + + nk_layout_row_static(ctx, 20, 20, 3); + + struct nk_color color; + + + nk_text(ctx, "", 0, NK_TEXT_LEFT); + + color.r = 255; color.g = 0; color.b = 0; + + if (led_red == 1) { + color.a = 255; + } else { + color.a = 30; + } + nk_button_color(ctx, color, NK_BUTTON_DEFAULT); + + nk_text(ctx, "", 0, NK_TEXT_LEFT); + + color.r = 0; color.g = 255; color.b = 0; + + if (led_green == 1) { + color.a = 255; + } else { + color.a = 30; + } + nk_button_color(ctx, color, NK_BUTTON_DEFAULT); + + nk_text(ctx, "", 0, NK_TEXT_LEFT); + + color.r = 255; color.g = 165; color.b = 0; + + if (led_orange == 1) { + color.a = 255; + } else { + color.a = 30; + } + nk_button_color(ctx, color, NK_BUTTON_DEFAULT); + + nk_text(ctx, "", 0, NK_TEXT_LEFT); + + color.r = 0; color.g = 0; color.b = 255; + + if (led_blue == 1) { + color.a = 255; + } else { + color.a = 30; + } + nk_button_color(ctx, color, NK_BUTTON_DEFAULT); + + nk_text(ctx, "", 0, NK_TEXT_LEFT); + + nk_end(ctx); + + } + + + } + /* if (nk_window_is_closed(ctx, "Demo")) break; */ + + { + float bg[4]; + nk_color_fv(bg, background); + XGetWindowAttributes(win.dpy, win.win, &win.attr); + glViewport(0, 0, win.width, win.height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(bg[0], bg[1], bg[2], bg[3]); + nk_x11_render(NK_ANTI_ALIASING_ON, MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER); + glXSwapBuffers(win.dpy, win.win); + } + + xTaskResumeAll(); + } + + nk_x11_shutdown(); + glXMakeCurrent(win.dpy, 0, 0); + glXDestroyContext(win.dpy, glContext); + XUnmapWindow(win.dpy, win.win); + XFreeColormap(win.dpy, win.cmap); + XDestroyWindow(win.dpy, win.win); + XCloseDisplay(win.dpy); + return 0; + +} diff --git a/src/simulator/src/Gui/test.c b/src/simulator/src/Gui/test.c deleted file mode 100644 index f742fe8..0000000 --- a/src/simulator/src/Gui/test.c +++ /dev/null @@ -1,285 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define NK_INCLUDE_FIXED_TYPES -#define NK_INCLUDE_STANDARD_IO -#define NK_INCLUDE_DEFAULT_ALLOCATOR -#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT -#define NK_INCLUDE_FONT_BAKING -#define NK_INCLUDE_DEFAULT_FONT -#define NK_IMPLEMENTATION -#define NK_XLIB_GL3_IMPLEMENTATION -#define NK_XLIB_LOAD_OPENGL_EXTENSIONS -#include "nuklear.h" -#include "nuklear_xlib_gl3.h" - -#define WINDOW_WIDTH 1200 -#define WINDOW_HEIGHT 800 - -#define MAX_VERTEX_BUFFER 512 * 1024 -#define MAX_ELEMENT_BUFFER 128 * 1024 - -#define UNUSED(a) (void)a -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#define MAX(a,b) ((a) < (b) ? (b) : (a)) -#define LEN(a) (sizeof(a)/sizeof(a)[0]) - - -struct XWindow { - Display *dpy; - Window win; - XVisualInfo *vis; - Colormap cmap; - XSetWindowAttributes swa; - XWindowAttributes attr; - GLXFBConfig fbc; - int width, height; -}; - -static int gl_err = FALSE; -static int gl_error_handler(Display *dpy, XErrorEvent *ev) { - UNUSED((dpy, ev)); - gl_err = TRUE; - return 0; -} - -static void die(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fputs("\n", stderr); - exit(EXIT_FAILURE); -} - -static int has_extension(const char *string, const char *ext) { - - const char *start, *where, *term; - where = strchr(ext, ' '); - - if (where || *ext == '\0') - return FALSE; - - for (start = string;;) { - where = strstr((const char*)start, ext); - if (!where) break; - term = where + strlen(ext); - if (where == start || *(where - 1) == ' ') { - if (*term == ' ' || *term == '\0') - return TRUE; - } - start = term; - } - return FALSE; -} - -int main2() { - /* Platform */ - int running = 1; - struct XWindow win; - GLXContext glContext; - struct nk_context *ctx; - struct nk_color background; - - memset(&win, 0, sizeof(win)); - win.dpy = XOpenDisplay(NULL); - - if (!win.dpy) { - die("Failed to open X display\n"); - } - - { - /* check glx version */ - int glx_major, glx_minor; - if (!glXQueryVersion(win.dpy, &glx_major, &glx_minor)) - die("[X11]: Error: Failed to query OpenGL version\n"); - if ((glx_major == 1 && glx_minor < 3) || (glx_major < 1)) - die("[X11]: Error: Invalid GLX version!\n"); - } - - { - /* find and pick matching framebuffer visual */ - int fb_count; - static GLint attr[] = { - GLX_X_RENDERABLE, True, - GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, - GLX_RENDER_TYPE, GLX_RGBA_BIT, - GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, - GLX_RED_SIZE, 8, - GLX_GREEN_SIZE, 8, - GLX_BLUE_SIZE, 8, - GLX_ALPHA_SIZE, 8, - GLX_DEPTH_SIZE, 24, - GLX_STENCIL_SIZE, 8, - GLX_DOUBLEBUFFER, True, - None - }; - GLXFBConfig *fbc; - fbc = glXChooseFBConfig(win.dpy, DefaultScreen(win.dpy), attr, &fb_count); - - if (!fbc) { - die("[X11]: Error: failed to retrieve framebuffer configuration\n"); - } - - { - /* pick framebuffer with most samples per pixel */ - int i; - int fb_best = -1, best_num_samples = -1; - for (i = 0; i < fb_count; ++i) { - XVisualInfo *vi = glXGetVisualFromFBConfig(win.dpy, fbc[i]); - if (vi) { - int sample_buffer, samples; - glXGetFBConfigAttrib(win.dpy, fbc[i], GLX_SAMPLE_BUFFERS, &sample_buffer); - glXGetFBConfigAttrib(win.dpy, fbc[i], GLX_SAMPLES, &samples); - if ((fb_best < 0) || (sample_buffer && samples > best_num_samples)) - fb_best = i; best_num_samples = samples; - } - } - win.fbc = fbc[fb_best]; - XFree(fbc); - win.vis = glXGetVisualFromFBConfig(win.dpy, win.fbc); - } - } - { - /* create window */ - win.cmap = XCreateColormap(win.dpy, RootWindow(win.dpy, win.vis->screen), win.vis->visual, AllocNone); - win.swa.colormap = win.cmap; - win.swa.background_pixmap = None; - win.swa.border_pixel = 0; - win.swa.event_mask = - ExposureMask | KeyPressMask | KeyReleaseMask | - ButtonPress | ButtonReleaseMask| ButtonMotionMask | - Button1MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask| - PointerMotionMask| StructureNotifyMask; - win.win = XCreateWindow(win.dpy, RootWindow(win.dpy, win.vis->screen), 0, 0, - WINDOW_WIDTH, WINDOW_HEIGHT, 0, win.vis->depth, InputOutput, - win.vis->visual, CWBorderPixel|CWColormap|CWEventMask, &win.swa); - if (!win.win) die("[X11]: Failed to create window\n"); - XFree(win.vis); - XStoreName(win.dpy, win.win, "glutt-o-matique simulator 3000"); - XMapWindow(win.dpy, win.win); - } - { - /* create opengl context */ - typedef GLXContext(*glxCreateContext)(Display*, GLXFBConfig, GLXContext, Bool, const int*); - int(*old_handler)(Display*, XErrorEvent*) = XSetErrorHandler(gl_error_handler); - const char *extensions_str = glXQueryExtensionsString(win.dpy, DefaultScreen(win.dpy)); - glxCreateContext create_context = (glxCreateContext) - glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB"); - - gl_err = FALSE; - if (!has_extension(extensions_str, "GLX_ARB_create_context") || !create_context) { - fprintf(stdout, "[X11]: glXCreateContextAttribARB() not found...\n"); - fprintf(stdout, "[X11]: ... using old-style GLX context\n"); - glContext = glXCreateNewContext(win.dpy, win.fbc, GLX_RGBA_TYPE, 0, True); - } else { - GLint attr[] = { - GLX_CONTEXT_MAJOR_VERSION_ARB, 3, - GLX_CONTEXT_MINOR_VERSION_ARB, 0, - None - }; - glContext = create_context(win.dpy, win.fbc, 0, True, attr); - XSync(win.dpy, False); - if (gl_err || !glContext) { - /* Could not create GL 3.0 context. Fallback to old 2.x context. - * If a version below 3.0 is requested, implementations will - * return the newest context version compatible with OpenGL - * version less than version 3.0.*/ - attr[1] = 1; attr[3] = 0; - gl_err = FALSE; - fprintf(stdout, "[X11] Failed to create OpenGL 3.0 context\n"); - fprintf(stdout, "[X11] ... using old-style GLX context!\n"); - glContext = create_context(win.dpy, win.fbc, 0, True, attr); - } - } - XSync(win.dpy, False); - XSetErrorHandler(old_handler); - if (gl_err || !glContext) - die("[X11]: Failed to create an OpenGL context\n"); - glXMakeCurrent(win.dpy, win.win, glContext); - } - - ctx = nk_x11_init(win.dpy, win.win); - /* Load Fonts: if none of these are loaded a default font will be used */ - {struct nk_font_atlas *atlas; - nk_x11_font_stash_begin(&atlas); - nk_x11_font_stash_end(); - } - - - background = nk_rgb(28,48,62); - - static char box_buffer[512]; - static int box_len; - - while (running) - { - /* Input */ - XEvent evt; - nk_input_begin(ctx); - while (XCheckWindowEvent(win.dpy, win.win, win.swa.event_mask, &evt)){ - if (XFilterEvent(&evt, win.win)) continue; - nk_x11_handle_event(&evt); - } - nk_input_end(ctx); - - /* GUI */ - {struct nk_panel layout; - if (nk_begin(ctx, &layout, "UART", nk_rect(50, 50, 400, 200), NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) { - - nk_layout_row_dynamic(ctx, 25, 1); - - nk_label(ctx, "UART Output:", NK_TEXT_LEFT); - - nk_layout_row_dynamic(ctx, 75, 1); - nk_edit_string(ctx, NK_EDIT_BOX, box_buffer, &box_len, 512, nk_filter_default); - - /* #<{(| nk_layout_row(ctx, NK_STATIC, 25, 2, ratio); |)}># */ - /* active = nk_edit_string(ctx, NK_EDIT_FIELD|NK_EDIT_SIG_ENTER, text[7], &text_len[7], 64, nk_filter_ascii); */ - /* if (nk_button_label(ctx, "Submit", NK_BUTTON_DEFAULT) || */ - /* (active & NK_EDIT_COMMITED)) */ - /* { */ - /* text[7][text_len[7]] = '\n'; */ - /* text_len[7]++; */ - /* memcpy(&box_buffer[box_len], &text[7], (nk_size)text_len[7]); */ - /* box_len += text_len[7]; */ - /* text_len[7] = 0; */ - /* } */ - nk_layout_row_end(ctx); - - /* nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); */ - - } - nk_end(ctx);} - /* if (nk_window_is_closed(ctx, "Demo")) break; */ - - { - float bg[4]; - nk_color_fv(bg, background); - XGetWindowAttributes(win.dpy, win.win, &win.attr); - glViewport(0, 0, win.width, win.height); - glClear(GL_COLOR_BUFFER_BIT); - glClearColor(bg[0], bg[1], bg[2], bg[3]); - nk_x11_render(NK_ANTI_ALIASING_ON, MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER); - glXSwapBuffers(win.dpy, win.win); - } - } - - nk_x11_shutdown(); - glXMakeCurrent(win.dpy, 0, 0); - glXDestroyContext(win.dpy, glContext); - XUnmapWindow(win.dpy, win.win); - XFreeColormap(win.dpy, win.cmap); - XDestroyWindow(win.dpy, win.win); - XCloseDisplay(win.dpy); - return 0; - -} diff --git a/src/simulator/vc.h b/src/simulator/vc.h index d8d7797..fa3f184 100644 --- a/src/simulator/vc.h +++ b/src/simulator/vc.h @@ -1,4 +1,4 @@ // This file is generated by Makefile. // Do not edit this file! -#define GIT_VERSION "4803231" +#define GIT_VERSION "9069fc1" diff --git a/src/stm32f/includes/GPIO/leds.h b/src/stm32f/includes/GPIO/leds.h new file mode 100644 index 0000000..869a032 --- /dev/null +++ b/src/stm32f/includes/GPIO/leds.h @@ -0,0 +1,4 @@ +#define GPIOD_BOARD_LED_GREEN GPIO_Pin_12 +#define GPIOD_BOARD_LED_ORANGE GPIO_Pin_13 +#define GPIOD_BOARD_LED_RED GPIO_Pin_14 +#define GPIOD_BOARD_LED_BLUE GPIO_Pin_15 diff --git a/src/stm32f/src/Core/main.c b/src/stm32f/src/Core/main.c index 65d535e..d63f540 100644 --- a/src/stm32f/src/Core/main.c +++ b/src/stm32f/src/Core/main.c @@ -24,12 +24,7 @@ #include "stm32f4xx_conf.h" - -#define GPIOD_BOARD_LED_GREEN GPIO_Pin_12 -#define GPIOD_BOARD_LED_ORANGE GPIO_Pin_13 -#define GPIOD_BOARD_LED_RED GPIO_Pin_14 -#define GPIOD_BOARD_LED_BLUE GPIO_Pin_15 - +#include "../../../stm32f/includes/GPIO/leds.h" #include "../../../common/src/Core/main.c" diff --git a/src/stm32f/src/Core/usart.c b/src/stm32f/src/Core/usart.c deleted file mode 100644 index 29c8a20..0000000 --- a/src/stm32f/src/Core/usart.c +++ /dev/null @@ -1,280 +0,0 @@ - -/* - * The MIT License (MIT) - * - * Copyright (c) 2016 Matthias P. Braendli - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. -*/ - -#include -#include -#include -#include -#include "Core/common.h" -#include "Core/usart.h" -#include -#include -#include -#include "FreeRTOS.h" -#include "task.h" -#include "queue.h" - -/* USART 3 on PD8 and PD9 - * See pio.txt for PIO allocation details */ -const uint16_t GPIOD_PIN_USART3_TX = GPIO_Pin_8; -const uint16_t GPIOD_PIN_USART3_RX = GPIO_Pin_9; - -/* USART 2 on PA2 and PA3 */ -const uint16_t GPIOA_PIN_USART2_RX = GPIO_Pin_3; -const uint16_t GPIOA_PIN_USART2_TX = GPIO_Pin_2; - -// The ISR writes into this buffer -static char nmea_sentence[MAX_NMEA_SENTENCE_LEN]; -static int nmea_sentence_last_written = 0; - -// Once a completed NMEA sentence is received in the ISR, -// it is appended to this queue -static QueueHandle_t usart_nmea_queue; - -void usart_init() -{ - // ============== PC DEBUG USART =========== - RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); - - GPIO_InitTypeDef GPIO_InitStruct; - GPIO_InitStruct.GPIO_Pin = GPIOA_PIN_USART2_RX | GPIOA_PIN_USART2_TX; - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; - GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_Init(GPIOA, &GPIO_InitStruct); - - GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); - GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); - - // Setup USART2 for 9600,8,N,1 - USART_InitTypeDef USART_InitStruct; - USART_InitStruct.USART_BaudRate = 9600; - USART_InitStruct.USART_WordLength = USART_WordLength_8b; - USART_InitStruct.USART_StopBits = USART_StopBits_1; - USART_InitStruct.USART_Parity = USART_Parity_No; - USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; - USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; - USART_Init(USART2, &USART_InitStruct); - -#if USART2_RECEIVE_ENABLE - // enable the USART2 receive interrupt - USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); - - NVIC_InitTypeDef NVIC_InitStructure; - NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - - NVIC_SetPriority(USART2_IRQn, 6); -#endif - - // finally this enables the complete USART2 peripheral - USART_Cmd(USART2, ENABLE); -} - -void usart_gps_init() -{ - usart_nmea_queue = xQueueCreate(15, MAX_NMEA_SENTENCE_LEN); - if (usart_nmea_queue == 0) { - while(1); /* fatal error */ - } - - // ============== GPS USART =========== - // Setup GPIO D and connect to USART 3 - RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); - - GPIO_InitTypeDef GPIO_InitStruct; - GPIO_InitStruct.GPIO_Pin = GPIOD_PIN_USART3_RX | GPIOD_PIN_USART3_TX; - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; - GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_Init(GPIOD, &GPIO_InitStruct); - - GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_USART3); - GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_USART3); - - // Setup USART3 for 9600,8,N,1 - USART_InitTypeDef USART_InitStruct; - USART_InitStruct.USART_BaudRate = 9600; - USART_InitStruct.USART_WordLength = USART_WordLength_8b; - USART_InitStruct.USART_StopBits = USART_StopBits_1; - USART_InitStruct.USART_Parity = USART_Parity_No; - USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; - USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; - USART_Init(USART3, &USART_InitStruct); - - - // enable the USART3 receive interrupt - USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); - - NVIC_InitTypeDef NVIC_InitStructure; - NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&NVIC_InitStructure); - - NVIC_SetPriority(USART3_IRQn, 6); - - // finally this enables the complete USART3 peripheral - USART_Cmd(USART3, ENABLE); -} - -// Make sure Tasks are suspended when this is called! -static void usart_puts(USART_TypeDef* USART, const char* str) -{ - while(*str) { - // wait until data register is empty - USART_SendData(USART, *str); - while(USART_GetFlagStatus(USART, USART_FLAG_TXE) == RESET) ; - str++; - } -} - -void usart_gps_puts(const char* str) -{ - vTaskSuspendAll(); - return usart_puts(USART3, str); - xTaskResumeAll(); -} - -#define MAX_MSG_LEN 80 -static char usart_debug_message[MAX_MSG_LEN]; - -void usart_debug_timestamp() -{ - // Don't call printf here, to reduce stack usage - uint64_t now = timestamp_now(); - if (now == 0) { - usart_puts(USART2, "[0] "); - } - else { - char ts_str[64]; - int i = 63; - - ts_str[i--] = '\0'; - ts_str[i--] = ' '; - ts_str[i--] = ']'; - - while (now > 0 && i >= 0) { - ts_str[i--] = '0' + (now % 10); - now /= 10; - } - ts_str[i] = '['; - - usart_puts(USART2, &ts_str[i]); - } -} - -void usart_debug(const char *format, ...) -{ - va_list list; - va_start(list, format); - vsnprintf(usart_debug_message, MAX_MSG_LEN-1, format, list); - - vTaskSuspendAll(); - usart_debug_timestamp(); - usart_puts(USART2, usart_debug_message); - xTaskResumeAll(); - - va_end(list); -} - -void usart_debug_puts(const char* str) -{ - vTaskSuspendAll(); - usart_debug_timestamp(); - usart_puts(USART2, str); - xTaskResumeAll(); -} - -int usart_get_nmea_sentence(char* nmea) -{ - return xQueueReceive(usart_nmea_queue, nmea, portMAX_DELAY); -} - - -static void usart_clear_nmea_buffer(void) -{ - for (int i = 0; i < MAX_NMEA_SENTENCE_LEN; i++) { - nmea_sentence[i] = '\0'; - } - nmea_sentence_last_written = 0; -} - -void USART3_IRQHandler(void) -{ - if (USART_GetITStatus(USART3, USART_IT_RXNE)) { - char t = USART3->DR; - - if (nmea_sentence_last_written == 0) { - if (t == '$') { - // Likely new start of sentence - nmea_sentence[nmea_sentence_last_written] = t; - nmea_sentence_last_written++; - } - } - else if (nmea_sentence_last_written < MAX_NMEA_SENTENCE_LEN) { - nmea_sentence[nmea_sentence_last_written] = t; - nmea_sentence_last_written++; - - if (t == '\n') { - int success = xQueueSendToBackFromISR( - usart_nmea_queue, - nmea_sentence, - NULL); - - if (success == pdFALSE) { - trigger_fault(FAULT_SOURCE_USART); - } - - usart_clear_nmea_buffer(); - } - } - else { - // Buffer overrun without a meaningful NMEA message. - usart_clear_nmea_buffer(); - } - } -} - -void USART2_IRQHandler(void) -{ - if (USART_GetITStatus(USART2, USART_IT_RXNE)) { - char t = USART2->DR; - if (t == 'h') { - usart_debug_puts("help: no commands supported yet!\r\n"); - } - else { - usart_debug("Unknown command %c\r\n", t); - } - } -} diff --git a/src/stm32f/src/GPIO/leds.c b/src/stm32f/src/GPIO/leds.c new file mode 100644 index 0000000..291b650 --- /dev/null +++ b/src/stm32f/src/GPIO/leds.c @@ -0,0 +1,42 @@ +#include "../../../common/src/GPIO/leds.c" + +#include "stm32f4xx_conf.h" + +#include "../../../stm32f/includes/GPIO/leds.h" + +void leds_turn_on(int l) { + + switch (l) { + case LED_GREEN: + GPIO_SetBits(GPIOD, GPIOD_BOARD_LED_GREEN); + break; + case LED_ORANGE: + GPIO_SetBits(GPIOD, GPIOD_BOARD_LED_ORANGE); + break; + case LED_RED: + GPIO_SetBits(GPIOD, GPIOD_BOARD_LED_RED); + break; + case LED_BLUE: + GPIO_SetBits(GPIOD, GPIOD_BOARD_LED_BLUE); + break; + + } +} + +void leds_turn_off(int l) { + + switch (l) { + case LED_GREEN: + GPIO_ResetBits(GPIOD, GPIOD_BOARD_LED_GREEN); + break; + case LED_ORANGE: + GPIO_ResetBits(GPIOD, GPIOD_BOARD_LED_ORANGE); + break; + case LED_RED: + GPIO_ResetBits(GPIOD, GPIOD_BOARD_LED_RED); + break; + case LED_BLUE: + GPIO_ResetBits(GPIOD, GPIOD_BOARD_LED_BLUE); + break; + } +} diff --git a/src/stm32f/src/GPIO/usart.c b/src/stm32f/src/GPIO/usart.c new file mode 100644 index 0000000..d174719 --- /dev/null +++ b/src/stm32f/src/GPIO/usart.c @@ -0,0 +1,156 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Matthias P. Braendli + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + + +#include +#include +#include + +/* USART 3 on PD8 and PD9 + * See pio.txt for PIO allocation details */ +const uint16_t GPIOD_PIN_USART3_TX = GPIO_Pin_8; +const uint16_t GPIOD_PIN_USART3_RX = GPIO_Pin_9; + +/* USART 2 on PA2 and PA3 */ +const uint16_t GPIOA_PIN_USART2_RX = GPIO_Pin_3; +const uint16_t GPIOA_PIN_USART2_TX = GPIO_Pin_2; + +static void usart_puts(USART_TypeDef*, const char*); + +#include "../../../common/includes/GPIO/usart.h" +#include "../../../common/src/GPIO/usart.c" + + +void usart_init() { + // ============== PC DEBUG USART =========== + RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + + GPIO_InitTypeDef GPIO_InitStruct; + GPIO_InitStruct.GPIO_Pin = GPIOA_PIN_USART2_RX | GPIOA_PIN_USART2_TX; + GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; + GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(GPIOA, &GPIO_InitStruct); + + GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); + GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); + + // Setup USART2 for 9600,8,N,1 + USART_InitTypeDef USART_InitStruct; + USART_InitStruct.USART_BaudRate = 9600; + USART_InitStruct.USART_WordLength = USART_WordLength_8b; + USART_InitStruct.USART_StopBits = USART_StopBits_1; + USART_InitStruct.USART_Parity = USART_Parity_No; + USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; + USART_Init(USART2, &USART_InitStruct); + +#if USART2_RECEIVE_ENABLE + // enable the USART2 receive interrupt + USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); + + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + NVIC_SetPriority(USART2_IRQn, 6); +#endif + + // finally this enables the complete USART2 peripheral + USART_Cmd(USART2, ENABLE); +} + +void usart_gps_specific_init() { + + // ============== GPS USART =========== + // Setup GPIO D and connect to USART 3 + RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); + + GPIO_InitTypeDef GPIO_InitStruct; + GPIO_InitStruct.GPIO_Pin = GPIOD_PIN_USART3_RX | GPIOD_PIN_USART3_TX; + GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; + GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(GPIOD, &GPIO_InitStruct); + + GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_USART3); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_USART3); + + // Setup USART3 for 9600,8,N,1 + USART_InitTypeDef USART_InitStruct; + USART_InitStruct.USART_BaudRate = 9600; + USART_InitStruct.USART_WordLength = USART_WordLength_8b; + USART_InitStruct.USART_StopBits = USART_StopBits_1; + USART_InitStruct.USART_Parity = USART_Parity_No; + USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; + USART_Init(USART3, &USART_InitStruct); + + + // enable the USART3 receive interrupt + USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); + + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + NVIC_SetPriority(USART3_IRQn, 6); + + // finally this enables the complete USART3 peripheral + USART_Cmd(USART3, ENABLE); +} + +// Make sure Tasks are suspended when this is called! +static void usart_puts(USART_TypeDef* USART, const char* str) { + while(*str) { + // wait until data register is empty + USART_SendData(USART, *str); + while(USART_GetFlagStatus(USART, USART_FLAG_TXE) == RESET) ; + str++; + } +} + +void USART3_IRQHandler(void) { + if (USART_GetITStatus(USART3, USART_IT_RXNE)) { + char t = USART3->DR; + usart_gps_process_char(t); + } +} + +void USART2_IRQHandler(void) { + if (USART_GetITStatus(USART2, USART_IT_RXNE)) { + char t = USART2->DR; + usart_process_char(t); + } +} diff --git a/src/stm32f/src/GPS/gps.c b/src/stm32f/src/GPS/gps.c new file mode 100644 index 0000000..10afe88 --- /dev/null +++ b/src/stm32f/src/GPS/gps.c @@ -0,0 +1,4 @@ +#include "stm32f4xx_conf.h" +#include "stm32f4xx.h" + +#include "../../../common/src/GPS/gps.c" diff --git a/src/stm32f/src/GPS/minema.c b/src/stm32f/src/GPS/minema.c new file mode 100644 index 0000000..10df198 --- /dev/null +++ b/src/stm32f/src/GPS/minema.c @@ -0,0 +1 @@ +#include "../../../common/src/GPS/minmea.c" -- cgit v1.2.3