diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 749285d29a3cefd..dde5a9abe8b4e0a 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -58,6 +58,7 @@ add_subdirectory_ifdef(CONFIG_MM_DRV mm) add_subdirectory_ifdef(CONFIG_MODEM modem) add_subdirectory_ifdef(CONFIG_NET_DRIVERS net) add_subdirectory_ifdef(CONFIG_NET_L2_ETHERNET ethernet) +add_subdirectory_ifdef(CONFIG_NEOM8 gnss) add_subdirectory_ifdef(CONFIG_NEURAL_NET_ACCEL neural_net) add_subdirectory_ifdef(CONFIG_PECI peci) add_subdirectory_ifdef(CONFIG_PINCTRL pinctrl) diff --git a/drivers/Kconfig b/drivers/Kconfig index 0a369875f6f37ee..1e95954187b541f 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -30,6 +30,7 @@ source "drivers/ethernet/Kconfig" source "drivers/flash/Kconfig" source "drivers/fpga/Kconfig" source "drivers/fuel_gauge/Kconfig" +source "drivers/gnss/Kconfig" source "drivers/gpio/Kconfig" source "drivers/hwinfo/Kconfig" source "drivers/i2c/Kconfig" diff --git a/drivers/gnss/CMakeLists.txt b/drivers/gnss/CMakeLists.txt new file mode 100644 index 000000000000000..5742e30fb18a1da --- /dev/null +++ b/drivers/gnss/CMakeLists.txt @@ -0,0 +1,4 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +add_subdirectory_ifdef(CONFIG_NEOM8 ublox-neo-m8) diff --git a/drivers/gnss/Kconfig b/drivers/gnss/Kconfig new file mode 100644 index 000000000000000..457102ce12e128e --- /dev/null +++ b/drivers/gnss/Kconfig @@ -0,0 +1,6 @@ +# GNSS configuration options + +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +rsource "ublox-neo-m8/Kconfig" diff --git a/drivers/gnss/ublox-neo-m8/CMakeLists.txt b/drivers/gnss/ublox-neo-m8/CMakeLists.txt new file mode 100644 index 000000000000000..0977e5eb0703344 --- /dev/null +++ b/drivers/gnss/ublox-neo-m8/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(ublox_neo_m8.c) diff --git a/drivers/gnss/ublox-neo-m8/Kconfig b/drivers/gnss/ublox-neo-m8/Kconfig new file mode 100644 index 000000000000000..354ff956c1d6ff9 --- /dev/null +++ b/drivers/gnss/ublox-neo-m8/Kconfig @@ -0,0 +1,23 @@ +# ublox NEO-M8 GNSS module configuration options + +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +menuconfig NEOM8 + bool "u-blox NEO-M8 GNSS Module" + help + Enable the driver for u-blox NEO-M8 GNSS Module. + +if NEOM8 + +module = NEOM8 +module-str = neom8 +source "subsys/logging/Kconfig.template.log_config" + +config NEOM8_INIT_PRIORITY + int "Counter init priority" + default 80 + help + NEO-M8 driver device initialization priority. + +endif # NEOM8 diff --git a/drivers/gnss/ublox-neo-m8/ublox_neo_m8.c b/drivers/gnss/ublox-neo-m8/ublox_neo_m8.c new file mode 100644 index 000000000000000..3009fc1d79c0a11 --- /dev/null +++ b/drivers/gnss/ublox-neo-m8/ublox_neo_m8.c @@ -0,0 +1,852 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT u_blox_neom8 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +LOG_MODULE_REGISTER(neom8, CONFIG_NEOM8_LOG_LEVEL); + +#define TO_LITTLE_ENDIAN(data, b) \ + for (int j = 0; j < sizeof(data); j++) { \ + b[j] = (data >> (j * 8)) & 0xFF; \ + } + +#define TIMEOUT_COUNT_UBX 10000000 +#define TIMEOUT_COUNT_NMEA 10000000 + +#define NEOM8_DEVICE_LABEL neom8 +#define NEOM8_NODE_LABEL DT_NODELABEL(NEOM8_DEVICE_LABEL) +#define BUS DT_BUS(NEOM8_NODE_LABEL) + +static void neom8_get_time(const struct device *dev, struct time *time) +{ + struct neom8_data *data = dev->data; + *time = data->time; +} + +static void neom8_get_latitude(const struct device *dev, float *latitude) +{ + struct neom8_data *data = dev->data; + *latitude = data->latitude_deg; +} + +static void neom8_get_ns(const struct device *dev, char *ns) +{ + struct neom8_data *data = dev->data; + *ns = data->ind_latitude; +} + +static void neom8_get_longitude(const struct device *dev, float *longitude) +{ + struct neom8_data *data = dev->data; + *longitude = data->longitude_deg; +} + +static void neom8_get_ew(const struct device *dev, char *ew) +{ + struct neom8_data *data = dev->data; + *ew = data->ind_longitude; +} + +static void neom8_get_altitude(const struct device *dev, float *altitude) +{ + struct neom8_data *data = dev->data; + *altitude = data->altitude; +} + +static void neom8_get_satellites(const struct device *dev, int *satellites) +{ + struct neom8_data *data = dev->data; + *satellites = data->satellites; +} + +static int hex2int(uint8_t c) +{ + int a_offset = 10; + + if (c >= '0' && c <= '9') { + return c - '0'; + } + + if (c >= 'A' && c <= 'F') { + return c - 'A' + a_offset; + } + + if (c >= 'a' && c <= 'f') { + return c - 'a' + a_offset; + } + + return -EINVAL; +} + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(uart) + + #if CONFIG_NEOM8_INIT_PRIORITY <= CONFIG_SERIAL_INIT_PRIORITY + #error CONFIG_NEOM8_INIT_PRIORITY must be greater than UART SERIAL_INIT_PRIORITY + #endif + +/* + * @brief Read RX register on UART bus if available. + * + * @details the rate at which the gps module sends bytes is very low. + * this is why this function returns -1 most of the times. + * 4 unsuccessful attempts to poll UART when "RX data reg is not full" + * causes the UART device to set it's RxOverrunFlag and stop receiving data. + * performing error check on UART resets it's error flags and ensures that + * it doesn't stop receiving data even after multiple unsuccessful attempts. + * + * @param dev Pointer to UART bus. + * @param buffer Buffer where read byte will be stored. + * + * @return 0 if success or -1 if error. + */ +static int read_register(const struct device *dev, char *buffer) +{ + const struct neom8_config *cfg = dev->config; + int rc; + + rc = uart_poll_in(cfg->bus_dev, buffer); + uart_err_check(cfg->bus_dev); + + return rc; +} + +static int write_register(const struct device *dev, char *buffer, uint16_t length) +{ + const struct neom8_config *cfg = dev->config; + uint8_t data[length + 1]; + int rc = 0; + + data[0] = 0xFF; + memcpy(data + 1, buffer, length); + + for (int i = 0; i < length + 1; ++i) { + uart_poll_out(cfg->bus_dev, data[i]); + k_busy_wait(1000); + } + + return rc; +} + +static int set_bus_baudrate(const struct device *dev, uint32_t baudrate) +{ + const struct uart_driver_api *uart_api = dev->api; + + struct uart_config config; + + uart_api->config_get(dev, &config); + config.baudrate = baudrate; + + int ret = uart_api->configure(dev, &config); + + return ret; +} + +#else + + #if CONFIG_NEOM8_INIT_PRIORITY <= CONFIG_I2C_INIT_PRIORITY + #error CONFIG_NEOM8_INIT_PRIORITY must be greater than I2C_INIT_PRIORITY + #endif + +#endif + +char *strsep(char **stringp, const char *delim) +{ + char *rv = *stringp; + + if (rv) { + *stringp += strcspn(*stringp, delim); + if (**stringp) { + *(*stringp)++ = '\0'; + } else { + *stringp = NULL; + } + } + + return rv; +} + +static void neom8_parse_comma_del(char *buffer, char *fields[20]) +{ + int i = 0; + const char delim = ','; + char *string = buffer; + char *found; + + while ((found = strsep(&string, &delim))) { + if (found[0] == '\0') { + found = "-"; + } + fields[i++] = found; + } +} + +static int message_check(const char *buffer, bool strict) +{ + uint8_t checksum = 0x00; + uint8_t upper, lower, expected; + + if (strlen(buffer) > 83) { + return 0; + } + + if (*buffer++ != '$') { + return 0; + } + + while (*buffer && *buffer != '*' && isprint((uint8_t)*buffer)) { + checksum ^= *buffer++; + } + + if (*buffer == '*') { + buffer++; + upper = hex2int(*buffer++); + + if (upper == -1) { + return 0; + } + + lower = hex2int(*buffer++); + + if (lower == -1) { + return 0; + } + + expected = upper << 4 | lower; + + if (checksum != expected) { + return 0; + } + } else if (strict) { + return 0; + } + + if (*buffer && strcmp(buffer, "\n") && strcmp(buffer, "\r\n")) { + return 0; + } + + return 1; +} + +static enum message_id get_message_id(const char *buffer, bool strict) +{ + const char *temp = buffer + 3; + + if (!message_check(buffer, strict)) { + return INVALID; + } + + if (!strncmp(temp, "RMC", 3)) { + return MESSAGE_RMC; + } + + if (!strncmp(temp, "GGA", 3)) { + return MESSAGE_GGA; + } + + if (!strncmp(temp, "GSA", 3)) { + return MESSAGE_GSA; + } + + if (!strncmp(temp, "GLL", 3)) { + return MESSAGE_GLL; + } + + if (!strncmp(temp, "GST", 3)) { + return MESSAGE_GST; + } + + if (!strncmp(temp, "GSV", 3)) { + return MESSAGE_GSV; + } + + if (!strncmp(temp, "VTG", 3)) { + return MESSAGE_VTG; + } + + if (!strncmp(temp, "ZDA", 3)) { + return MESSAGE_ZDA; + } + + return UNKNOWN; +} + +static float to_degrees(float deg_min) +{ + float degrees = (int)(deg_min / 100.0); + float minutes = deg_min - (100.0 * degrees); + + return (degrees + (minutes / 60.0)); +} + +void neom8_parse_gga(const struct device *dev, char *fields[20]) +{ + struct neom8_data *data = dev->data; + uint8_t hour[3], min[3], sec[3]; + uint8_t degrees_minutes[6], decimal_minutes[6]; + uint32_t wholeNum; + uint32_t decimal; + uint8_t meter[4], meter_decimal[2]; + + if (strcmp(fields[1], "-")) { + strncpy(hour, &fields[1][0], 2); + hour[2] = '\0'; + strncpy(min, &fields[1][2], 2); + min[2] = '\0'; + strncpy(sec, &fields[1][4], 2); + sec[2] = '\0'; + data->time.hour = atoi(hour); + data->time.min = atoi(min); + data->time.sec = atoi(sec); + } + + if (strcmp(fields[2], "-")) { + strncpy(degrees_minutes, &fields[2][0], 4); + degrees_minutes[4] = '\0'; + strncpy(decimal_minutes, &fields[2][5], 5); + decimal_minutes[5] = '\0'; + wholeNum = atoi(degrees_minutes); + decimal = atoi(decimal_minutes); + data->latitude_min = (float)((float)wholeNum + ((float)decimal / 100000)); + data->latitude_deg = to_degrees(data->latitude_min); + } + + if (strcmp(fields[3], "-")) { + strncpy(&data->ind_latitude, fields[3], 1); + } + + if (strcmp(fields[4], "-")) { + strncpy(degrees_minutes, &fields[4][0], 5); + degrees_minutes[5] = '\0'; + strncpy(decimal_minutes, &fields[4][6], 5); + decimal_minutes[5] = '\0'; + wholeNum = atoi(degrees_minutes); + decimal = atoi(decimal_minutes); + data->longitude_min = (float)((float)wholeNum + ((float)decimal / 100000)); + data->longitude_deg = to_degrees(data->longitude_min); + } + + if (strcmp(fields[5], "-")) { + strncpy(&data->ind_longitude, fields[5], 1); + } + + if (strcmp(fields[7], "-")) { + data->satellites = atoi(fields[7]); + } + + if (strcmp(fields[9], "-")) { + strncpy(meter, &fields[9][0], 3); + meter[3] = '\0'; + strncpy(meter_decimal, &fields[9][4], 1); + meter_decimal[1] = '\0'; + wholeNum = atoi(meter); + decimal = atoi(meter_decimal); + data->altitude = (float)((float)wholeNum + ((float)decimal / 10)); + } +} + +static int neom8_parse_data(const struct device *dev) +{ + struct neom8_data *data = dev->data; + char tmp[MAX_NMEA_SIZE]; + char *fields[20] = { 0 }; + + strcpy(tmp, data->_buffer); + + int message_id = get_message_id(tmp, false); + + neom8_parse_comma_del(tmp, fields); + + switch (message_id) { + case MESSAGE_RMC: { + break; + } + case MESSAGE_GGA: { + neom8_parse_gga(dev, fields); + break; + } + case MESSAGE_GSA: { + break; + } + case MESSAGE_GLL: { + break; + } + case MESSAGE_GST: { + break; + } + case MESSAGE_GSV: { + break; + } + case MESSAGE_VTG: { + break; + } + case MESSAGE_ZDA: { + break; + } + default: { + break; + } + } + + free(tmp); + + return 0; +} + +static int neom8_fetch_data(const struct device *dev) +{ + struct neom8_data *data = dev->data; + uint8_t c = 0; + int rc = 0; + + k_sem_take(&data->lock, K_FOREVER); + + int underrun_count = 0; /* Count of the number of times read_register returns -1. */ + + data->_buffer[0] = 0; /* Buffer where the received NMEA message will be stored. */ + data->_index = 0; /* Position of next byte to be stored in _buffer. */ + while ((c != '\n' || data->_buffer[0] != '$') + /* Break when both the first ($) & last character (\n) has been received. */ + && underrun_count < TIMEOUT_COUNT_NMEA + && data->_index < MAX_NMEA_SIZE - 1) /* _index < MAX message size. */ { + rc = read_register(dev, &c); /* Read the next byte from the RX register from bus. */ + + if (rc) { /* Underrun: there was no byte available to be read. */ + ++underrun_count; + continue; + } else if (c == '$' || data->_buffer[0] == '$') { + /* Read byte will be appended to _buffer if it's either '$' (first char), + * or first char ($) has already been received & set at 0 index in _buffer. + */ + underrun_count = 0; + data->_buffer[data->_index++] = c; + } + } + + if (c == '\n') { + data->_buffer[data->_index++] = '\0'; + neom8_parse_data(dev); + } else { + rc = -1; + } + + k_sem_give(&data->lock); + + return rc; +} + +static int neom8_send_ubx(const struct device *dev, uint8_t class, uint8_t id, + uint8_t payload[], uint16_t length) +{ + struct neom8_data *data = dev->data; + uint8_t ckA = 0; + uint8_t ckB = 0; + uint8_t c = 0; + int response_size = 10; + uint8_t response[response_size]; + /* Buffer where the received UBX message will be stored. */ + int rc = 0; + + const unsigned int cmdLength = 8 + length; + uint8_t cmd[cmdLength]; + + cmd[0] = 0xb5; + cmd[1] = 0x62; + cmd[2] = class; + cmd[3] = id; + cmd[4] = (length & 0xff); + cmd[5] = (length >> 8); + memcpy(&cmd[6], payload, length); + + for (unsigned int i = 2; i < (cmdLength - 2); i++) { + ckA += cmd[i]; + ckB += ckA; + } + + cmd[cmdLength - 2] = ckA; + cmd[cmdLength - 1] = ckB; + + k_sem_take(&data->lock, K_FOREVER); + + rc = write_register(dev, cmd, cmdLength); + + int underrun_count = 0; /* Count of the number of times read_register returns -1. */ + + response[0] = 0; /* Initializing the received message to NULL. */ + int index = 0; /* Position of next byte to be stored in response. */ + + while (index < response_size /* Break when the complete message has been received. */ + && underrun_count < TIMEOUT_COUNT_UBX) { + rc = read_register(dev, &c); + + if (rc) { /* Underrun: there was no byte available to be read. */ + ++underrun_count; + continue; + } else if (c == 0xB5 || response[0] == 0xB5) { + /* Read byte will be appended to response if it's either 0xB5 (first char), + * or first char (0xB5) has already been received & set at response[0]. + */ + underrun_count = 0; + response[index++] = c; + } + } + + if (underrun_count < TIMEOUT_COUNT_UBX) { /* Underrun condition didn't occur. */ + if (response[3] == 0x00) { + rc = NACK; + } else if (response[3] == 0x01) { + rc = ACK; + } + } + + k_sem_give(&data->lock); + + return rc; +} + +static int neom8_cfg_nav5(const struct device *dev, enum gnss_mode g_mode, + enum fix_mode f_mode, int32_t fixed_alt, uint32_t fixed_alt_var, + int8_t min_elev, uint16_t p_dop, uint16_t t_dop, uint16_t p_acc, + uint16_t t_acc, uint8_t static_hold_thresh, uint8_t dgnss_timeout, + uint8_t cno_thresh_num_svs, uint8_t cno_thresh, + uint16_t static_hold_max_dist, enum utc_standard utc_strd) +{ + uint8_t payload[MAX_PAYLOAD_SIZE]; + int8_t fixed_alt_le[4] = { 0 }; + uint8_t fixed_alt_var_le[4] = { 0 }; + uint8_t p_dop_le[2] = { 0 }; + uint8_t t_dop_le[2] = { 0 }; + uint8_t p_acc_le[2] = { 0 }; + uint8_t t_acc_le[2] = { 0 }; + uint8_t static_hold_max_dist_le[2] = { 0 }; + int rc; + + TO_LITTLE_ENDIAN(fixed_alt, fixed_alt_le); + TO_LITTLE_ENDIAN(fixed_alt_var, fixed_alt_var_le); + TO_LITTLE_ENDIAN(p_dop, p_dop_le); + TO_LITTLE_ENDIAN(t_dop, t_dop_le); + TO_LITTLE_ENDIAN(p_acc, p_acc_le); + TO_LITTLE_ENDIAN(t_acc, t_acc_le); + TO_LITTLE_ENDIAN(static_hold_max_dist, static_hold_max_dist_le); + + payload[0] = 0xff; + payload[1] = 0x05; + payload[2] = g_mode; + payload[3] = f_mode; + payload[4] = fixed_alt_le[0]; + payload[5] = fixed_alt_le[1]; + payload[6] = fixed_alt_le[2]; + payload[7] = fixed_alt_le[3]; + payload[8] = fixed_alt_var_le[0]; + payload[9] = fixed_alt_var_le[1]; + payload[10] = fixed_alt_var_le[2]; + payload[11] = fixed_alt_var_le[3]; + payload[12] = min_elev; + payload[14] = p_dop_le[0]; + payload[15] = p_dop_le[1]; + payload[16] = t_dop_le[0]; + payload[17] = t_dop_le[1]; + payload[18] = p_acc_le[0]; + payload[19] = p_acc_le[1]; + payload[20] = t_acc_le[0]; + payload[21] = t_acc_le[1]; + payload[22] = static_hold_thresh; + payload[23] = dgnss_timeout; + payload[24] = cno_thresh_num_svs; + payload[25] = cno_thresh; + payload[28] = static_hold_max_dist_le[0]; + payload[29] = static_hold_max_dist_le[1]; + payload[30] = utc_strd; + + rc = neom8_send_ubx(dev, UBX_CLASS_CFG, UBX_CFG_NAV5, payload, 36); + if (rc == NACK) { + LOG_ERR("Config NAV5 not acknowledged %s", dev->name); + } else if (rc == ACK) { + LOG_INF("Config NAV5 acknowledged %s", dev->name); + } else if (rc) { + LOG_ERR("Error %d config NAV5 for %s", rc, dev->name); + } else { + LOG_ERR("Error %d: API didn't return valid UBX message.", rc); + } + + return rc; +} + +static int neom8_cfg_gnss(const struct device *dev, uint8_t msg_ver, + uint8_t num_trk_ch_use, uint8_t num_config_blocks, ...) +{ + uint8_t payload[MAX_PAYLOAD_SIZE]; + uint8_t flags_le[4] = { 0 }; + uint32_t flags; + va_list ap; + int rc; + + va_start(ap, num_config_blocks); + + payload[0] = msg_ver; + payload[2] = num_trk_ch_use; + payload[3] = num_config_blocks; + + for (int i = 0; i < num_config_blocks; i++) { + payload[4 + (8 * i)] = (uint8_t)va_arg(ap, int); + payload[5 + (8 * i)] = (uint8_t)va_arg(ap, int); + payload[6 + (8 * i)] = (uint8_t)va_arg(ap, int); + payload[7 + (8 * i)] = (uint8_t)va_arg(ap, int); + + flags = (uint32_t)va_arg(ap, int); + TO_LITTLE_ENDIAN(flags, flags_le); + payload[8 + (8 * i)] = flags_le[0]; + payload[9 + (8 * i)] = flags_le[1]; + payload[10 + (8 * i)] = flags_le[2]; + payload[11 + (8 * i)] = flags_le[3]; + } + + va_end(ap); + + int length = (4 + (8 * num_config_blocks)); + + rc = neom8_send_ubx(dev, UBX_CLASS_CFG, UBX_CFG_GNSS, payload, length); + if (rc == NACK) { + LOG_ERR("Config GNSS not acknowledged %s", dev->name); + } else if (rc == ACK) { + LOG_INF("Config GNSS acknowledged %s", dev->name); + } else if (rc) { + LOG_ERR("Error %d config GNSS for %s", rc, dev->name); + } else { + LOG_ERR("Error %d: API didn't return valid UBX message.", rc); + } + + return rc; +} + +static int neom8_cfg_msg(const struct device *dev, uint8_t msg_id, uint8_t rate) +{ + uint8_t payload[MAX_PAYLOAD_SIZE]; + int rc; + + payload[0] = UBX_CLASS_NMEA; + payload[1] = msg_id; + payload[2] = rate; + + rc = neom8_send_ubx(dev, UBX_CLASS_CFG, UBX_CFG_MSG, payload, 3); + if (rc == NACK) { + LOG_ERR("Config MSG not acknowledged %s", dev->name); + } else if (rc == ACK) { + LOG_INF("Config MSG acknowledged %s", dev->name); + } else if (rc) { + LOG_ERR("Error %d config MSG for %s", rc, dev->name); + } else { + LOG_ERR("Error %d: API didn't return valid UBX message.", rc); + } + + return rc; +} + +static int neom8_cfg_prt(const struct device *dev, uint8_t port_id, uint32_t baudrate) +{ + uint8_t payload[MAX_PAYLOAD_SIZE]; + int rc; + + payload[0] = port_id; + payload[1] = 0x0; + + payload[2] = 0x0; + payload[3] = 0x0; + /* Mode = 0x8D0 */ + payload[4] = 0xD0; + payload[5] = 0x08; + payload[6] = 0x0; + payload[7] = 0x0; + /* Baud Rate*/ + payload[8] = baudrate; + payload[9] = baudrate >> 8; + payload[10] = baudrate >> 16; + payload[11] = baudrate >> 24; + /* in_proto_mask = All proto enable */ + payload[12] = 0x7; + payload[13] = 0x0; + /* Out Proto Mask = All proto enable */ + payload[14] = 0x23; + payload[15] = 0x0; + /* flags */ + payload[16] = 0x0; + payload[17] = 0x0; + + payload[18] = 0x0; + payload[19] = 0x0; + + rc = neom8_send_ubx(dev, UBX_CLASS_CFG, UBX_CFG_PRT, payload, 20); + if (rc == NACK) { + LOG_ERR("Config PRT not acknowledged %s", dev->name); + } else if (rc == ACK) { + LOG_INF("Config PRT acknowledged %s", dev->name); + } else if (rc) { + LOG_ERR("Error %d config PRT for %s", rc, dev->name); + } else { + LOG_ERR("Error %d: API didn't return valid UBX message.", rc); + } + + return rc; +} + +int neo_m8_set_baudrate(const struct device *dev, uint32_t baudrate) +{ + int ret = neom8_cfg_prt(dev, 0x01, baudrate); + + k_sleep(K_MSEC(100)); + + return ret; +} + +/*! + * @brief Sets baudrate of NEO-M8 and BUS as the baudrate in the parameter. + * + * @details + * the current setted baudrate of neo-m8 could only be determined by sending + * a UBX message at it's current baudrate and checking for acknowledgment. + * we attempt to set neo-m8 baudrate by sending CFG_PRT in neo_m8_set_baudrate + * with the bus baudrate as every possible baudrate of neo-m8, + * which ensures at least one neo_m8_set_baudrate call will be successful. + * after a successful call to neo_m8_set_baudrate, we change bus baudrate + * to the target baudrate as well. + * + * @param dev Pointer to sensor. + * @param baudrate Target baudrate for both NEO-M8 and BUS. + * + * @return 0 if success or 1 if error. + */ +static int neom8_configure_baudrate(const struct device *dev, uint32_t baudrate) +{ + const struct neom8_config *cfg = dev->config; + + int baudrates[8] = { + 4800, + 9600, + 19200, + 38400, + 57600, + 115200, + 230400, + 460800, + }; + + int i; + /* Retry if we are unable to set the baudrate for either gps or uart/i2c module. */ + for (i = 0; i < RETRY_COUNT; ++i) { + int ret = 0; + /* Try to comply the baudrate of bus with the current baudrate of gps module + * before setting the target baudrate on both the gps module and the bus. + */ + for (int j = 0; j < 8; ++j) { + if (set_bus_baudrate(cfg->bus_dev, baudrates[j])) + break; + + ret = neo_m8_set_baudrate(dev, baudrate); + if (ret == ACK) + break; + } + + if (set_bus_baudrate(cfg->bus_dev, baudrate)) + continue; + + if (ret == ACK) + break; + } + + return (i == RETRY_COUNT) ? 1 : 0; +} + +static int neom8_init(const struct device *dev) +{ + struct neom8_data *data = dev->data; + const struct neom8_config *cfg = dev->config; + + int rc = 0; + + k_sem_init(&data->lock, 0, 1); + + k_sem_take(&data->lock, K_FOREVER); + + if (!device_is_ready(cfg->bus_dev)) { + LOG_ERR("Bus device %s is not ready", cfg->bus_dev->name); + rc = -ENODEV; + goto out; + } + + data->neom8 = dev; + data->_index = 0; + for (int i = 0; i < MAX_NMEA_SIZE; ++i) + data->_buffer[i] = 0; + + data->time.hour = 0; + data->time.min = 0; + data->time.sec = 0; + data->longitude_min = 0; + data->latitude_min = 0; + data->longitude_deg = 0; + data->latitude_deg = 0; + data->altitude = 0; + data->ind_latitude = 'A'; + data->ind_longitude = 'A'; + data->satellites = 0; + +out: + k_sem_give(&data->lock); + + return rc; +}; + +static const struct neom8_api neom8_api = { + .fetch_data = neom8_fetch_data, + .send_ubx = neom8_send_ubx, + .cfg_nav5 = neom8_cfg_nav5, + .cfg_gnss = neom8_cfg_gnss, + .cfg_msg = neom8_cfg_msg, + .cfg_prt = neom8_cfg_prt, + + .get_altitude = neom8_get_altitude, + .get_latitude = neom8_get_latitude, + .get_ns = neom8_get_ns, + .get_longitude = neom8_get_longitude, + .get_ew = neom8_get_ew, + .get_time = neom8_get_time, + .get_satellites = neom8_get_satellites, + + .configure_baudrate = neom8_configure_baudrate, +}; + +#define NEOM8_CONFIG_UART .bus_dev = DEVICE_DT_GET(BUS) +#define NEOM8_CONFIG_I2C .bus_dev = DEVICE_DT_GET(BUS), \ + .i2c_addr = DT_REG_ADDR(BUS) + +#define NEOM8_INIT(n) \ + static struct neom8_data neom8_data_##n; \ + static const struct neom8_config neom8_config_##n = { \ + COND_CODE_1(DT_INST_ON_BUS(n, uart), \ + (NEOM8_CONFIG_UART), \ + (NEOM8_CONFIG_I2C) \ + ) \ + }; \ + DEVICE_DT_INST_DEFINE(n, neom8_init, NULL, &neom8_data_##n, &neom8_config_##n, \ + POST_KERNEL, CONFIG_NEOM8_INIT_PRIORITY, &neom8_api); + +DT_INST_FOREACH_STATUS_OKAY(NEOM8_INIT); diff --git a/dts/bindings/gnss/u-blox,neom8-i2c.yaml b/dts/bindings/gnss/u-blox,neom8-i2c.yaml new file mode 100644 index 000000000000000..b70dfcba7d8e33c --- /dev/null +++ b/dts/bindings/gnss/u-blox,neom8-i2c.yaml @@ -0,0 +1,8 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: U-blox NEO-M8 GNSS Module + +compatible: "u-blox,neom8" + +include: i2c-device.yaml diff --git a/dts/bindings/gnss/u-blox,neom8-uart.yaml b/dts/bindings/gnss/u-blox,neom8-uart.yaml new file mode 100644 index 000000000000000..8b5a56495bf193d --- /dev/null +++ b/dts/bindings/gnss/u-blox,neom8-uart.yaml @@ -0,0 +1,8 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: U-blox NEO-M8 GNSS Module + +compatible: "u-blox,neom8" + +include: uart-device.yaml diff --git a/include/zephyr/drivers/gnss/ublox_neo_m8.h b/include/zephyr/drivers/gnss/ublox_neo_m8.h new file mode 100644 index 000000000000000..522772368bed963 --- /dev/null +++ b/include/zephyr/drivers/gnss/ublox_neo_m8.h @@ -0,0 +1,141 @@ +/* + * @brief NEO-M8 gnss module public API header file. + * + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_GNSS_UBLOX_NEO_M8_H_ +#define ZEPHYR_INCLUDE_DRIVERS_GNSS_UBLOX_NEO_M8_H_ + +#include +#include +#include +#include + +enum gnss_mode { + Portable = 0, + Stationary = 2, + Pedestrian, + Automotiv, + Sea, + Airbone1G, + Airbone2G, + Airbone4G, + Wirst, + Bike, + LawnMower, + KickScooter, +}; + +enum fix_mode { + P_2D = 1, + P_3D, + AutoFix, +}; + +enum utc_standard { + AutoUTC = 0, + GPS = 3, + GALILEO = 5, + GLONASS, + BEIDOU, + NAVIC, +}; + +enum message_id { + INVALID = -1, + UNKNOWN, + MESSAGE_RMC, + MESSAGE_GGA, + MESSAGE_GSA, + MESSAGE_GLL, + MESSAGE_GST, + MESSAGE_GSV, + MESSAGE_VTG, + MESSAGE_ZDA, +}; + +enum ack_nack_return_codes { + NACK = 150, + ACK = 151, +}; + +struct neom8_config { + const struct device *bus_dev; + uint16_t i2c_addr; +}; + +struct neom8_data { + const struct device *neom8; + + struct k_sem lock; + + uint8_t _index; + uint8_t _buffer[MAX_NMEA_SIZE]; + + struct time { + uint8_t hour; + uint8_t min; + uint8_t sec; + } time; + + float longitude_min; /* Longitude minutes. */ + float latitude_min; /* Latitude minutes. */ + float longitude_deg; /* Longitude degrees. */ + float latitude_deg; /* Latitude degrees. */ + + float altitude; + + char ind_longitude; /* Longitude indicator (East/West). */ + char ind_latitude; /* Latitude indicator (North/South). */ + + uint8_t satellites; +}; + +typedef int (*neom8_api_fetch_data)(const struct device *dev); +typedef int (*neom8_api_send_ubx)(const struct device *dev, uint8_t class, uint8_t id, + uint8_t payload[], uint16_t length); +typedef int (*neom8_api_cfg_nav5)(const struct device *dev, enum gnss_mode g_mode, + enum fix_mode f_mode, int32_t fixed_alt, uint32_t fixed_alt_var, int8_t min_elev, + uint16_t p_dop, uint16_t t_dop, uint16_t p_acc, uint16_t t_acc, + uint8_t static_hold_thresh, uint8_t dgnss_timeout, uint8_t cno_thresh_num_svs, + uint8_t cno_thresh, uint16_t static_hold_max_dist, enum utc_standard utc_strd); +typedef int (*neom8_api_cfg_gnss)(const struct device *dev, uint8_t msg_ver, + uint8_t num_trk_ch_use, uint8_t num_config_blocks, ...); +typedef int (*neom8_api_cfg_msg)(const struct device *dev, uint8_t msg_id, uint8_t rate); + +typedef int (*neom8_api_cfg_prt)(const struct device *dev, uint8_t port_id, + uint32_t baudrate); + +typedef void (*neom8_api_get_time)(const struct device *dev, struct time *time); +typedef void (*neom8_api_get_latitude)(const struct device *dev, float *latitude); +typedef void (*neom8_api_get_ns)(const struct device *dev, char *ns); +typedef void (*neom8_api_get_longitude)(const struct device *dev, float *longitude); +typedef void (*neom8_api_get_ew)(const struct device *dev, char *ew); +typedef void (*neom8_api_get_altitude)(const struct device *dev, float *altitude); +typedef void (*neom8_api_get_satellites)(const struct device *dev, int *satellites); + +typedef int (*neom8_api_configure_baudrate)(const struct device *dev, uint32_t baudrate); + +__subsystem struct neom8_api { + neom8_api_fetch_data fetch_data; + neom8_api_send_ubx send_ubx; + neom8_api_cfg_nav5 cfg_nav5; + neom8_api_cfg_gnss cfg_gnss; + neom8_api_cfg_msg cfg_msg; + neom8_api_cfg_prt cfg_prt; + + neom8_api_get_time get_time; + neom8_api_get_latitude get_latitude; + neom8_api_get_ns get_ns; + neom8_api_get_longitude get_longitude; + neom8_api_get_ew get_ew; + neom8_api_get_altitude get_altitude; + neom8_api_get_satellites get_satellites; + + neom8_api_configure_baudrate configure_baudrate; +}; + +#endif /*ZEPHYR_INCLUDE_DRIVERS_GNSS_UBLOX_NEO_M8_H_*/ diff --git a/include/zephyr/drivers/gnss/ublox_neo_m8_defines.h b/include/zephyr/drivers/gnss/ublox_neo_m8_defines.h new file mode 100644 index 000000000000000..47ffe9ec0aacaed --- /dev/null +++ b/include/zephyr/drivers/gnss/ublox_neo_m8_defines.h @@ -0,0 +1,248 @@ +/* + * @brief NEO-M8 gnss module public API definitions header file. + * + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_GNSS_UBLOX_NEO_M8_DEFINES_H_ +#define ZEPHYR_INCLUDE_DRIVERS_GNSS_UBLOX_NEO_M8_DEFINES_H_ + +#define RETRY_COUNT 10 + +enum interface_description { + NBYTES_HIGH_ADDR = 0xFD, + NBYTES_LOW_ADDR = 0xFE, + DATA_STREAM_ADDR = 0xFF, + + MAX_NMEA_SIZE = 83, + MAX_PAYLOAD_SIZE = 256, + + UBX_SEC_UNIQID = 0x03, + + UBX_UPD_SOS = 0x14, +}; + +enum ubx_class_ids { + UBX_CLASS_NAV = 0x01, + UBX_CLASS_RXM = 0x02, + UBX_CLASS_INF = 0x04, + UBX_CLASS_ACK = 0x05, + UBX_CLASS_CFG = 0x06, + UBX_CLASS_UPD = 0x09, + UBX_CLASS_MON = 0x0A, + UBX_CLASS_AID = 0x0B, + UBX_CLASS_TIM = 0x0D, + UBX_CLASS_ESF = 0x10, + UBX_CLASS_MGA = 0x13, + UBX_CLASS_LOG = 0x21, + UBX_CLASS_SEC = 0x27, + UBX_CLASS_HNR = 0x28, + UBX_CLASS_NMEA = 0xF0, +}; + +enum ubx_ack_nack_messages { + UBX_ACK_ACK = 0x01, + UBX_ACK_NAK = 0x00, +}; + +enum ubx_assistnow_aiding_messages { + UBX_AID_ALM = 0x30, + UBX_AID_AOP = 0x33, + UBX_AID_EPH = 0x31, + UBX_AID_HUI = 0x02, + UBX_AID_INI = 0x01, +}; + +enum ubx_configuration_input_messages { + UBX_CFG_ANT = 0x13, + UBX_CFG_BATCH = 0x93, + UBX_CFG_CFG = 0x09, + UBX_CFG_DAT = 0x06, + UBX_CFG_DGNSS = 0x70, + UBX_CFG_DOSC = 0x61, + UBX_CFG_ESFALG = 0x56, + UBX_CFG_ESFAE = 0x4C, + UBX_CFG_ESFGE = 0x4D, + UBX_CFG_ESFWTE = 0x82, + UBX_CFG_ESRCE = 0x60, + UBX_CFG_GEOFENCE = 0x69, + UBX_CFG_GNSS = 0x3E, + UBX_CFG_HNR = 0x5C, + UBX_CFG_INF = 0x02, + UBX_CFG_ITFM = 0x39, + UBX_CFG_LOGFILTER = 0x47, + UBX_CFG_MSG = 0x01, + UBX_CFG_NAV5 = 0x24, + UBX_CFG_NAVX5 = 0x23, + UBX_CFG_NMEA = 0x17, + UBX_CFG_ODO = 0x1E, + UBX_CFG_PM2 = 0x3B, + UBX_CFG_PMS = 0x86, + UBX_CFG_PRT = 0x00, + UBX_CFG_PWR = 0x57, + UBX_CFG_RATE = 0x08, + UBX_CFG_RINV = 0x34, + UBX_CFG_RST = 0x04, + UBX_CFG_RXM = 0x11, + UBX_CFG_SBAS = 0x16, + UBX_CFG_SENIF = 0x88, + UBX_CFG_SLAS = 0x8D, + UBX_CFG_SMGR = 0x62, + UBX_CFG_SPT = 0x64, + UBX_CFG_TMODE2 = 0x3D, + UBX_CFG_TMODE3 = 0x71, + UBX_CFG_TP5 = 0x31, + UBX_CFG_TXSLOT = 0x53, + UBX_CFG_USB = 0x1B, +}; + +enum ubx_external_sensor_fusion_messages { + UBX_ESF_ALG = 0x14, + UBX_ESF_INS = 0x15, + UBX_ESF_MEAS = 0x02, + UBX_ESF_RAW = 0x03, + UBX_ESF_STATUS = 0x10, +}; + +enum ubx_high_rate_navigation_results_messages { + UBX_HNR_ATT = 0x01, + UBX_HNR_INS = 0x02, + UBX_HNR_PVT = 0x00, +}; + +enum ubx_information_messages { + UBX_INF_DEBUG = 0x04, + UBX_INF_ERROR = 0x00, + UBX_INF_NOTICE = 0x02, + UBX_INF_TEST = 0x03, + UBX_INF_WARNING = 0x01, +}; + +enum ubx_logging_messages { + UBX_LOG_BATCH = 0x11, + UBX_LOG_CREATE = 0x07, + UBX_LOG_ERASE = 0x03, + UBX_LOG_FINDTIME = 0x0E, + UBX_LOG_INFO = 0x08, + UBX_LOG_RETRIEVEBATCH = 0x10, + UBX_LOG_RETRIEVEPOSEXTRA = 0x0f, + UBX_LOG_RETRIEVEPOS = 0x0b, + UBX_LOG_RETRIEVESTRING = 0x0d, + UBX_LOG_RETRIEVE = 0x09, + UBX_LOG_STRING = 0x04, +}; + +enum ubx_multiple_gnss_assistance_messages { + UBX_MGA_ACK = 0x60, + UBX_MGA_ANO = 0x20, + UBX_MGA_BDS = 0x03, + UBX_MGA_DBD = 0x80, + UBX_MGA_FLASH = 0x21, + UBX_MGA_GAL = 0x02, + UBX_MGA_GLO = 0x06, + UBX_MGA_GPS = 0x00, + UBX_MGA_INI = 0x40, + UBX_MGA_QZSS = 0x05, +}; + +enum ubx_monitoring_messages { + UBX_MON_BATCH = 0x32, + UBX_MON_GNSS = 0x28, + UBX_MON_HW2 = 0x0B, + UBX_MON_HW = 0x09, + UBX_MON_IO = 0x02, + UBX_MON_MSGPP = 0x06, + UBX_MON_PATCH = 0x27, + UBX_MON_RXBUF = 0x07, + UBX_MON_RXR = 0x21, + UBX_MON_SMGR = 0x2E, + UBX_MON_SPT = 0x2F, + UBX_MON_TXBUF = 0x08, + UBX_MON_VER = 0x04, +}; + +enum ubx_nagivation_results_messages { + UBX_NAV_AOPSTATUS = 0x60, + UBX_NAV_ATT = 0x05, + UBX_NAV_CLOCK = 0x22, + UBX_NAV_COV = 0x36, + UBX_NAV_DGPS = 0x31, + UBX_NAV_DOP = 0x04, + UBX_NAV_EELL = 0x3d, + UBX_NAV_EOE = 0x61, + UBX_NAV_GEOFENCE = 0x39, + UBX_NAV_HPPOSECEF = 0x13, + UBX_NAV_HPPOSLLH = 0x14, + UBX_NAV_NMI = 0x28, + UBX_NAV_ODO = 0x09, + UBX_NAV_ORB = 0x34, + UBX_NAV_POSECEF = 0x01, + UBX_NAV_POSLLH = 0x02, + UBX_NAV_PVT = 0x07, + UBX_NAV_RELPOSNED = 0x3C, + UBX_NAV_RESETODO = 0x10, + UBX_NAV_SAT = 0x35, + UBX_NAV_SBAS = 0x32, + UBX_NAV_SLAS = 0x42, + UBX_NAV_SOL = 0x06, + UBX_NAV_STATUS = 0x03, + UBX_NAV_SVINFO = 0x30, + UBX_NAV_SVIN = 0x3B, + UBX_NAV_TIMEBDS = 0x24, + UBX_NAV_TIMEGAL = 0x25, + UBX_NAV_TIMEGLO = 0x23, + UBX_NAV_TIMEGPS = 0x20, + UBX_NAV_TIMELS = 0x26, + UBX_NAV_TIMEUTC = 0x21, + UBX_NAV_VELECEF = 0x11, + UBX_NAV_VELNED = 0x12, +}; + +enum ubx_receiver_manager_messages { + UBX_RXM_IMES = 0x61, + UBX_RXM_MEASX = 0x14, + UBX_RXM_PMREQ = 0x41, + UBX_RXM_RAWX = 0x15, + UBX_RXM_RLM = 0x59, + UBX_RXM_RTCM = 0x32, + UBX_RXM_SFRBX = 0x13, +}; + +enum ubx_timing_messages { + UBX_TIM_DOSC = 0x11, + UBX_TIM_FCHG = 0x16, + UBX_TIM_HOC = 0x17, + UBX_TIM_SMEAS = 0x13, + UBX_TIM_SVIN = 0x04, + UBX_TIM_TM2 = 0x03, + UBX_TIM_TOS = 0x12, + UBX_TIM_TP = 0x01, + UBX_TIM_VCOCAL = 0x15, + UBX_TIM_VRFY = 0x06, +}; + +enum nmea_message_ids { + NMEA_DTM = 0x0A, + NMEA_GBQ = 0x44, + NMEA_GBS = 0x09, + NMEA_GGA = 0x00, + NMEA_GLL = 0x01, + NMEA_GLQ = 0x43, + NMEA_GNQ = 0x42, + NMEA_GNS = 0x0D, + NMEA_GPQ = 0x40, + NMEA_GRS = 0x06, + NMEA_GSA = 0x02, + NMEA_GST = 0x07, + NMEA_GSV = 0x03, + NMEA_RMC = 0x04, + NMEA_THS = 0x0E, + NMEA_TXT = 0x41, + NMEA_VLW = 0x0F, + NMEA_VTG = 0x05, + NMEA_ZDA = 0x08, +}; + +#endif /*ZEPHYR_INCLUDE_DRIVERS_GNSS_UBLOX_NEO_M8_DEFINES_H_*/ diff --git a/samples/drivers/neo_m8/CMakeLists.txt b/samples/drivers/neo_m8/CMakeLists.txt new file mode 100644 index 000000000000000..5c17bdf6ae61aff --- /dev/null +++ b/samples/drivers/neo_m8/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(neom8) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/samples/drivers/neo_m8/boards/mimxrt1062_fmurt6.overlay b/samples/drivers/neo_m8/boards/mimxrt1062_fmurt6.overlay new file mode 100644 index 000000000000000..edf709cae6ec8f6 --- /dev/null +++ b/samples/drivers/neo_m8/boards/mimxrt1062_fmurt6.overlay @@ -0,0 +1,18 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&lpuart2 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&pinmux_lpuart2>; + pinctrl-1 = <&pinmux_lpuart2_sleep>; + pinctrl-names = "default", "sleep"; + + neom8: neom8 { + status = "okay"; + compatible = "u-blox,neom8"; + }; +}; diff --git a/samples/drivers/neo_m8/prj.conf b/samples/drivers/neo_m8/prj.conf new file mode 100644 index 000000000000000..b96571bac7d9bf3 --- /dev/null +++ b/samples/drivers/neo_m8/prj.conf @@ -0,0 +1,7 @@ +CONFIG_NEOM8=y +CONFIG_CBPRINTF_FP_SUPPORT=y +CONFIG_LOG=y +CONFIG_NEOM8_LOG_LEVEL_DBG=y +CONFIG_LOG_BUFFER_SIZE=65536 +CONFIG_SERIAL=y +CONFIG_UART_USE_RUNTIME_CONFIGURE=y diff --git a/samples/drivers/neo_m8/sample.yaml b/samples/drivers/neo_m8/sample.yaml new file mode 100644 index 000000000000000..796358f916eccc2 --- /dev/null +++ b/samples/drivers/neo_m8/sample.yaml @@ -0,0 +1,11 @@ +# Copyright 2023 NXP +# +# SPDX-License-Identifier: Apache-2.0 + +sample: + description: Demonstration of Neo-M8 GNSS module + name: Neo-M8 Sample +tests: + sample.driver.neom8: + build_only: true + platform_allow: mimxrt1062_fmurt6 diff --git a/samples/drivers/neo_m8/src/main.c b/samples/drivers/neo_m8/src/main.c new file mode 100644 index 000000000000000..56ecb5e58b1a2d8 --- /dev/null +++ b/samples/drivers/neo_m8/src/main.c @@ -0,0 +1,141 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include + +#include +LOG_MODULE_DECLARE(neom8, CONFIG_NEOM8_LOG_LEVEL); + +#define TARGET_BAUDRATE 38400 + +static const struct device *neo_dev; +static const struct neom8_api *neo_api; + +int configure_messages(void) +{ + int ret = ACK, i = 0; + + for (i = 0; i < RETRY_COUNT; ++i) { + ret &= neo_api->cfg_nav5(neo_dev, Stationary, P_2D, 0, 1, 5, 100, 100, 100, + 350, 0, 60, 0, 0, 0, AutoUTC); + /* Configure navigation engine: set 'stationary' dynamic platform model, + * '2d' position fix mode, 'auto utc' as utc standard. + */ + ret &= neo_api->cfg_gnss(neo_dev, 0, 32, 5, 0, 8, 16, 0, 0x01010001, 1, 1, + 3, 0, 0x01010001, 3, 8, 16, 0, 0x01010000, 5, 0, 3, 0, 0x01010001, 6, 8, + 14, 0, 0x01010001); + /* Configure GNSS system: set '0x00' as message version, '32' as number + * of tracking channels to use, '5' as number of configuration blocks; + * providing values for the subsequent configuration blocks. + */ + + /* Enabling various supported NMEA messages. */ + ret &= neo_api->cfg_msg(neo_dev, NMEA_DTM, 0); + ret &= neo_api->cfg_msg(neo_dev, NMEA_GBS, 0); + ret &= neo_api->cfg_msg(neo_dev, NMEA_GLL, 0); + ret &= neo_api->cfg_msg(neo_dev, NMEA_GNS, 0); + ret &= neo_api->cfg_msg(neo_dev, NMEA_GRS, 0); + ret &= neo_api->cfg_msg(neo_dev, NMEA_GSA, 0); + ret &= neo_api->cfg_msg(neo_dev, NMEA_GST, 0); + ret &= neo_api->cfg_msg(neo_dev, NMEA_GSV, 0); + ret &= neo_api->cfg_msg(neo_dev, NMEA_RMC, 0); + ret &= neo_api->cfg_msg(neo_dev, NMEA_VLW, 0); + ret &= neo_api->cfg_msg(neo_dev, NMEA_VTG, 0); + ret &= neo_api->cfg_msg(neo_dev, NMEA_ZDA, 0); + + if (ret == ACK) + break; + } + + k_sleep(K_MSEC(100)); + + return (i == RETRY_COUNT) ? 1 : 0; +} + +int configure_neo_m8(void) +{ + int ret; + + LOG_INF("configuring baudrate."); + ret = neo_api->configure_baudrate(neo_dev, TARGET_BAUDRATE); + if (ret) { + LOG_ERR("error %d while configuration baudrate. returning.\n", ret); + return 1; + } + + LOG_INF("configuring messages."); + ret = configure_messages(); + if (ret) { + LOG_ERR("error %d while configuring neo-m8. returning.\n", ret); + return 1; + } + + return 0; +} + +void display_gnss_data(void) +{ + struct time neotime; + float lat; + char ns; + float lon; + char ew; + float alt; + int sat; + + neo_api->get_time(neo_dev, &neotime); + neo_api->get_latitude(neo_dev, &lat); + neo_api->get_ns(neo_dev, &ns); + neo_api->get_longitude(neo_dev, &lon); + neo_api->get_ew(neo_dev, &ew); + neo_api->get_altitude(neo_dev, &alt); + neo_api->get_satellites(neo_dev, &sat); + + LOG_INF("Hour: %d", neotime.hour); + LOG_INF("Minute: %d", neotime.min); + LOG_INF("Second: %d", neotime.sec); + LOG_INF("Latitude: %.5f", lat); + LOG_INF("North/South: %c", ns); + LOG_INF("Longitude: %.5f", lon); + LOG_INF("East/West: %c", ew); + LOG_INF("Altitude: %.2f", alt); + LOG_INF("Satellites: %d", sat); + + printk("\n"); +} + +void main(void) +{ + int rc; + + neo_dev = DEVICE_DT_GET(DT_NODELABEL(neom8)); + if (!device_is_ready(neo_dev)) { + LOG_ERR("%s device is not ready. returning.", neo_dev->name); + return; + } + + neo_api = neo_dev->api; + + rc = configure_neo_m8(); + if (rc) + return; + + while (true) { + rc = neo_api->fetch_data(neo_dev); + if (rc) { + LOG_ERR("error %d while reading data. returning.\n", rc); + return; + } + + display_gnss_data(); + + k_sleep(K_MSEC(100)); + } +}