icE1usb fw: Import GPS module driver code

This only initializes the GPS module and keeps track of the
antenna and fix status

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Change-Id: I24811f872fafefc7f8dfaa3028c4288001a87d2f
This commit is contained in:
Sylvain Munaut 2022-01-12 11:55:44 +01:00
parent 7bed9039e4
commit ef5fe385fe
4 changed files with 318 additions and 0 deletions

View File

@ -48,6 +48,7 @@ SOURCES_common += $(SOURCES_no2usb)
HEADERS_app=\
config.h \
e1.h \
gps.h \
ice1usb_proto.h \
misc.h \
usb_desc_ids.h \
@ -59,6 +60,7 @@ HEADERS_app=\
SOURCES_app=\
e1.c \
fw_app.c \
gps.c \
misc.c \
usb_desc_app.c \
usb_dev.c \

View File

@ -14,6 +14,7 @@
#include "console.h"
#include "e1.h"
#include "gps.h"
#include "led.h"
#include "misc.h"
#include "mini-printf.h"
@ -96,6 +97,9 @@ void main()
pdm_set(PDM_CLK_HI, true, 2048, false);
pdm_set(PDM_CLK_LO, false, 0, false);
/* GPS init */
gps_init();
/* Enable USB directly */
usb_init(&app_stack_desc);
usb_dev_init();
@ -161,5 +165,8 @@ void main()
/* E1 poll */
usb_e1_poll();
/* GPS poll */
gps_poll();
}
}

View File

@ -0,0 +1,286 @@
/*
* gps.c
*
* Copyright (C) 2019-2022 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "console.h"
#include "gps.h"
#include "misc.h"
#include "utils.h"
#include "config.h"
struct gps_uart {
uint32_t data;
uint32_t csr;
} __attribute__((packed,aligned(4)));
#define GPS_UART_CSR_DIV(baud) ((SYS_CLK_FREQ+(baud)/2)/(baud)-2)
#define GPS_UART_CSR_TX_EMPTY (1 << 29)
#define GPS_UART_DATA_EMPTY (1 << 31)
static volatile struct gps_uart * const gps_uart_regs = (void*)(GPS_UART_BASE);
static struct {
/* NMEA rx state */
struct {
enum {
GNS_WAIT = 0,
GNS_READ = 1,
GNS_CK_HI = 2,
GNS_CK_LO = 3,
GNS_END_CR = 4,
GNS_END_LF = 5,
} state;
int len;
uint8_t cksum;
char buf[95]; /* Max length is actually 82 + 1 (\0) */
} nmea;
/* Current lock state */
struct {
bool valid;
} fix;
/* Reported antenna state */
enum gps_antenna_state antenna;
} g_gps;
static void
_gps_empty(bool wait_eol)
{
bool eol = false;
uint32_t c;
while (1) {
c = gps_uart_regs->data;
if (c & GPS_UART_DATA_EMPTY) {
if (!wait_eol || eol)
break;
} else {
eol = (c == '\n');
}
}
}
static void
_gps_send(const char *s)
{
char cksum = 0;
/* Start sentence */
gps_uart_regs->data = '$';
/* Send payload */
while (*s)
cksum ^= (gps_uart_regs->data = *s++);
/* Send checksum */
gps_uart_regs->data = '*';
s = hexstr(&cksum, 1, false);
gps_uart_regs->data = *s++;
gps_uart_regs->data = *s++;
gps_uart_regs->data = '\r';
gps_uart_regs->data = '\n';
}
static const char *
_gps_query(void)
{
uint32_t c;
while (1) {
/* Get next char */
c = gps_uart_regs->data;
if (c & GPS_UART_DATA_EMPTY)
break;
/* Always store it */
if (g_gps.nmea.len == sizeof(g_gps.nmea.buf))
g_gps.nmea.state = GNS_WAIT;
else
g_gps.nmea.buf[g_gps.nmea.len++] = c;
/* State */
if (c == '$') {
/* '$' always triggers reset */
g_gps.nmea.state = GNS_READ;
g_gps.nmea.cksum = 0;
g_gps.nmea.len = 1;
g_gps.nmea.buf[0] = '$';
} else {
switch (g_gps.nmea.state) {
case GNS_READ:
if (c == '*')
g_gps.nmea.state = GNS_CK_HI;
else
g_gps.nmea.cksum ^= c;
break;
case GNS_CK_HI:
g_gps.nmea.cksum ^= hexval(c) << 4;
g_gps.nmea.state = GNS_CK_LO;
break;
case GNS_CK_LO:
g_gps.nmea.cksum ^= hexval(c);
g_gps.nmea.state = GNS_END_CR;
break;
case GNS_END_CR:
g_gps.nmea.state = (c == '\r') ? GNS_END_LF : GNS_WAIT;
break;
case GNS_END_LF:
g_gps.nmea.state = GNS_WAIT;
g_gps.nmea.buf[g_gps.nmea.len] = 0x00;
if (c == '\n')
return g_gps.nmea.buf;
break;
default:
g_gps.nmea.state = GNS_WAIT;
g_gps.nmea.len = 0;
break;
}
}
}
return NULL;
}
void
_gps_parse_nmea(const char *nmea)
{
/* Very basic parsing, we just look at GSA messages and consider
* that if we have a 3D fix with PDOP < 5, the timing data should
* be usable
*/
if (!strncmp(nmea, "$GPGSA", 6))
{
/* Check for Autonomous 3D fix (fixed field positions) */
if ((nmea[7] != 'A') || (nmea[9] != '3')) {
g_gps.fix.valid = false;
return;
}
/* Find PDOP */
const char *p = nmea;
for (int i=0; (i < 15) && (*p != '*'); i += (*(p++)==',') );
/* Is it low enough ? */
g_gps.fix.valid = (p[1] < '5');
}
/* Parse TXT ANTENNA Status */
if (!strncmp(nmea, "$GPTXT", 6) && nmea[13] == '0' && nmea[14] == '1')
{
if (!strncmp(&nmea[16], "ANTENNA OK", 10))
g_gps.antenna = ANT_OK;
else if (!strncmp(&nmea[16], "ANTENNA OPEN", 12))
g_gps.antenna = ANT_OPEN;
else if (!strncmp(&nmea[16], "ANTENNA SHORT", 13))
g_gps.antenna = ANT_SHORT;
else
g_gps.antenna = ANT_UNKNOWN;
}
}
bool
gps_has_valid_fix(void)
{
return g_gps.fix.valid;
}
enum gps_antenna_state
gps_antenna_status(void)
{
return g_gps.antenna;
}
void
gps_poll(void)
{
const char *nmea;
/* Check if we have anything from the module */
nmea = _gps_query();
if (!nmea)
return;
/* If we do, process it locally to update our state */
_gps_parse_nmea(nmea);
}
void
gps_init(void)
{
int i;
/* State init */
memset(&g_gps, 0x00, sizeof(g_gps));
/* Configure reset gpio */
gpio_out(3, false);
gpio_dir(3, true);
/* Attempt reset sequence at 9600 baud and then 115200 baud */
for (i=0; i<2; i++)
{
uint32_t start_time = time_now_read();
bool init_ok;
/* Assert reset */
gpio_out(3, false);
/* Configure uart and empty buffer */
gps_uart_regs->csr = i ? GPS_UART_CSR_DIV(115200) : GPS_UART_CSR_DIV(9600);
_gps_empty(false);
/* Wait 100 ms */
delay(100);
/* Release reset line */
gpio_out(3, true);
/* Wait for first line of output as sign it's ready, timeout after 1s */
while (!time_elapsed(start_time, SYS_CLK_FREQ))
if ((init_ok = (_gps_query() != NULL)))
break;
if (init_ok) {
printf("[+] GPS ok at %d baud\n", i ? 115200 : 9600);
break;
}
}
/* Failed ? */
if (i == 2) {
printf("[!] GPS init failed\n");
return;
}
/* If success was at 9600 baud, need to speed up */
if (i == 0) {
/* Configure GPS to use serial at 115200 baud */
_gps_send("PCAS01,5");
/* Add dummy byte which will be mangled during baudrate switch ... */
gps_uart_regs->data = 0x00;
while (!(gps_uart_regs->csr & GPS_UART_CSR_TX_EMPTY));
/* Set uart to 115200 and empty uart buffer, line aligned */
gps_uart_regs->csr = GPS_UART_CSR_DIV(115200) ;
_gps_empty(true);
}
/* Configure GPS to be GPS-only (no GLONASS/BEIDOU) */
_gps_send("PCAS04,1");
}

View File

@ -0,0 +1,23 @@
/*
* gps.h
*
* Copyright (C) 2019-2022 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <stdbool.h>
enum gps_antenna_state {
ANT_UNKNOWN = 0,
ANT_OK,
ANT_OPEN,
ANT_SHORT
};
bool gps_has_valid_fix(void);
enum gps_antenna_state gps_antenna_status(void);
void gps_poll(void);
void gps_init(void);