drivers: gnss: Air530z: Add new driver

Intial driver adding intial script to receive only GGA, RMC and GSV
NMEA messages, configuring fix rate, setting enabled system and adding
power management with the module on-off pin.

Signed-off-by: Jeronimo Agullo <jeronimoagullo97@gmail.com>
This commit is contained in:
Jeronimo Agullo 2024-04-09 21:48:05 +02:00 committed by Fabio Baltieri
commit 7ae9a16ca6
5 changed files with 407 additions and 0 deletions

View file

@ -11,3 +11,4 @@ zephyr_library_sources_ifdef(CONFIG_GNSS_NMEA_GENERIC gnss_nmea_generic.c)
zephyr_library_sources_ifdef(CONFIG_GNSS_QUECTEL_LCX6G gnss_quectel_lcx6g.c)
zephyr_library_sources_ifdef(CONFIG_GNSS_U_BLOX_M10 gnss_u_blox_m10.c)
zephyr_library_sources_ifdef(CONFIG_GNSS_U_BLOX_PROTOCOL gnss_u_blox_protocol/gnss_u_blox_protocol.c)
zephyr_library_sources_ifdef(CONFIG_GNSS_LUATOS_AIR530Z gnss_luatos_air530z.c)

View file

@ -73,5 +73,6 @@ source "subsys/logging/Kconfig.template.log_config"
rsource "Kconfig.generic"
rsource "Kconfig.quectel_lcx6g"
rsource "Kconfig.u_blox_m10"
rsource "Kconfig.luatos_air530z"
endif

View file

@ -0,0 +1,26 @@
# Copyright 2024 Jerónimo Agulló
# SPDX-License-Identifier: Apache-2.0
config GNSS_LUATOS_AIR530Z
bool "Luatos Air530z GNSS device"
default y
depends on GNSS
depends on DT_HAS_LUATOS_AIR530Z_ENABLED
select MODEM_MODULES
select MODEM_BACKEND_UART
select MODEM_CHAT
select GNSS_PARSE
select GNSS_NMEA0183
select GNSS_NMEA0183_MATCH
help
Enable Luatos Air530z GNSS driver.
config GNSS_LUATOS_AIR530Z_SATELLITES_COUNT
int "Maximum satellite count"
depends on GNSS_SATELLITES
default 24
help
Maximum number of satellites that can be decoded from the
GNSS device. This does not affect the number of devices that
the device is actually tracking, just how many of those can
be reported in the satellites callback.

View file

@ -0,0 +1,364 @@
/*
* Copyright (c) 2024 Jerónimo Agulló
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/gnss.h>
#include <zephyr/drivers/gnss/gnss_publish.h>
#include <zephyr/modem/chat.h>
#include <zephyr/modem/backend/uart.h>
#include <zephyr/kernel.h>
#include <zephyr/pm/device.h>
#include <zephyr/drivers/gpio.h>
#include <string.h>
#include "gnss_nmea0183.h"
#include "gnss_nmea0183_match.h"
#include "gnss_parse.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(luatos_air530z, CONFIG_GNSS_LOG_LEVEL);
#define DT_DRV_COMPAT luatos_air530z
#define UART_RECV_BUF_SZ 128
#define UART_TRANS_BUF_SZ 64
#define CHAT_RECV_BUF_SZ 256
#define CHAT_ARGV_SZ 32
MODEM_CHAT_SCRIPT_CMDS_DEFINE(init_script_cmds,
#if CONFIG_GNSS_SATELLITES
/* receive only GGA, RMC and GSV NMEA messages */
MODEM_CHAT_SCRIPT_CMD_RESP_NONE("$PCAS03,1,0,0,1,1,0,0,0,0,0,0,0,0*1F", 10),
#else
/* receive only GGA and RMC NMEA messages */
MODEM_CHAT_SCRIPT_CMD_RESP_NONE("$PCAS03,1,0,0,0,1,0,0,0,0,0,0,0,0*1E", 10),
#endif
);
MODEM_CHAT_SCRIPT_NO_ABORT_DEFINE(init_script, init_script_cmds, NULL, 5);
struct gnss_luatos_air530z_config {
const struct device *uart;
const struct gpio_dt_spec on_off_gpio;
const int uart_baudrate;
};
struct gnss_luatos_air530z_data {
struct gnss_nmea0183_match_data match_data;
#if CONFIG_GNSS_SATELLITES
struct gnss_satellite satellites[CONFIG_GNSS_LUATOS_AIR530Z_SATELLITES_COUNT];
#endif
/* UART backend */
struct modem_pipe *uart_pipe;
struct modem_backend_uart uart_backend;
uint8_t uart_backend_receive_buf[UART_RECV_BUF_SZ];
uint8_t uart_backend_transmit_buf[UART_TRANS_BUF_SZ];
/* Modem chat */
struct modem_chat chat;
uint8_t chat_receive_buf[CHAT_RECV_BUF_SZ];
uint8_t chat_delimiter[2];
uint8_t *chat_argv[CHAT_ARGV_SZ];
/* Dynamic chat script */
uint8_t dynamic_separators_buf[2];
uint8_t dynamic_request_buf[32];
struct modem_chat_script_chat dynamic_script_chat;
struct modem_chat_script dynamic_script;
struct k_sem lock;
};
MODEM_CHAT_MATCHES_DEFINE(unsol_matches,
MODEM_CHAT_MATCH_WILDCARD("$??GGA,", ",*", gnss_nmea0183_match_gga_callback),
MODEM_CHAT_MATCH_WILDCARD("$??RMC,", ",*", gnss_nmea0183_match_rmc_callback),
#if CONFIG_GNSS_SATELLITES
MODEM_CHAT_MATCH_WILDCARD("$??GSV,", ",*", gnss_nmea0183_match_gsv_callback),
#endif
);
static void luatos_air530z_lock(const struct device *dev)
{
struct gnss_luatos_air530z_data *data = dev->data;
(void)k_sem_take(&data->lock, K_FOREVER);
}
static void luatos_air530z_unlock(const struct device *dev)
{
struct gnss_luatos_air530z_data *data = dev->data;
k_sem_give(&data->lock);
}
static int gnss_luatos_air530z_init_nmea0183_match(const struct device *dev)
{
struct gnss_luatos_air530z_data *data = dev->data;
const struct gnss_nmea0183_match_config match_config = {
.gnss = dev,
#if CONFIG_GNSS_SATELLITES
.satellites = data->satellites,
.satellites_size = ARRAY_SIZE(data->satellites),
#endif
};
return gnss_nmea0183_match_init(&data->match_data, &match_config);
}
static void gnss_luatos_air530z_init_pipe(const struct device *dev)
{
const struct gnss_luatos_air530z_config *config = dev->config;
struct gnss_luatos_air530z_data *data = dev->data;
const struct modem_backend_uart_config uart_backend_config = {
.uart = config->uart,
.receive_buf = data->uart_backend_receive_buf,
.receive_buf_size = sizeof(data->uart_backend_receive_buf),
.transmit_buf = data->uart_backend_transmit_buf,
.transmit_buf_size = ARRAY_SIZE(data->uart_backend_transmit_buf),
};
data->uart_pipe = modem_backend_uart_init(&data->uart_backend, &uart_backend_config);
}
static int gnss_luatos_air530z_init_chat(const struct device *dev)
{
struct gnss_luatos_air530z_data *data = dev->data;
const struct modem_chat_config chat_config = {
.user_data = data,
.receive_buf = data->chat_receive_buf,
.receive_buf_size = sizeof(data->chat_receive_buf),
.delimiter = data->chat_delimiter,
.delimiter_size = ARRAY_SIZE(data->chat_delimiter),
.filter = NULL,
.filter_size = 0,
.argv = data->chat_argv,
.argv_size = ARRAY_SIZE(data->chat_argv),
.unsol_matches = unsol_matches,
.unsol_matches_size = ARRAY_SIZE(unsol_matches),
};
return modem_chat_init(&data->chat, &chat_config);
}
static void luatos_air530z_init_dynamic_script(const struct device *dev)
{
struct gnss_luatos_air530z_data *data = dev->data;
/* Air530z doesn't respond to commands. Thus, response_matches_size = 0; */
data->dynamic_script_chat.request = data->dynamic_request_buf;
data->dynamic_script_chat.response_matches = NULL;
data->dynamic_script_chat.response_matches_size = 0;
data->dynamic_script_chat.timeout = 0;
data->dynamic_script.name = "PCAS";
data->dynamic_script.script_chats = &data->dynamic_script_chat;
data->dynamic_script.script_chats_size = 1;
data->dynamic_script.abort_matches = NULL;
data->dynamic_script.abort_matches_size = 0;
data->dynamic_script.callback = NULL;
data->dynamic_script.timeout = 5;
}
static int gnss_luatos_air530z_init(const struct device *dev)
{
struct gnss_luatos_air530z_data *data = dev->data;
const struct gnss_luatos_air530z_config *config = dev->config;
int ret;
k_sem_init(&data->lock, 1, 1);
ret = gnss_luatos_air530z_init_nmea0183_match(dev);
if (ret < 0) {
return ret;
}
gnss_luatos_air530z_init_pipe(dev);
ret = gnss_luatos_air530z_init_chat(dev);
if (ret < 0) {
return ret;
}
luatos_air530z_init_dynamic_script(dev);
ret = modem_pipe_open(data->uart_pipe);
if (ret < 0) {
return ret;
}
ret = modem_chat_attach(&data->chat, data->uart_pipe);
if (ret < 0) {
modem_pipe_close(data->uart_pipe);
return ret;
}
ret = modem_chat_run_script(&data->chat, &init_script);
if (ret < 0) {
LOG_ERR("Failed to run init_script");
modem_pipe_close(data->uart_pipe);
return ret;
}
/* setup on-off gpio for power management */
if (!gpio_is_ready_dt(&config->on_off_gpio)) {
LOG_ERR("on-off GPIO device not ready");
return -ENODEV;
}
gpio_pin_configure_dt(&config->on_off_gpio, GPIO_OUTPUT_HIGH);
return 0;
}
static int luatos_air530z_pm_resume(const struct device *dev)
{
struct gnss_luatos_air530z_data *data = dev->data;
int ret;
ret = modem_pipe_open(data->uart_pipe);
if (ret < 0) {
return ret;
}
ret = modem_chat_attach(&data->chat, data->uart_pipe);
if (ret < 0) {
modem_pipe_close(data->uart_pipe);
return ret;
}
ret = modem_chat_run_script(&data->chat, &init_script);
if (ret < 0) {
modem_pipe_close(data->uart_pipe);
return ret;
}
return 0;
}
static int luatos_air530z_pm_action(const struct device *dev, enum pm_device_action action)
{
struct gnss_luatos_air530z_data *data = dev->data;
const struct gnss_luatos_air530z_config *config = dev->config;
int ret = -ENOTSUP;
switch (action) {
case PM_DEVICE_ACTION_SUSPEND:
gpio_pin_set_dt(&config->on_off_gpio, 0);
ret = modem_pipe_close(data->uart_pipe);
break;
case PM_DEVICE_ACTION_RESUME:
gpio_pin_set_dt(&config->on_off_gpio, 1);
ret = luatos_air530z_pm_resume(dev);
break;
default:
break;
}
return ret;
}
static int luatos_air530z_set_fix_rate(const struct device *dev, uint32_t fix_interval_ms)
{
struct gnss_luatos_air530z_data *data = dev->data;
int ret;
if (fix_interval_ms < 100 || fix_interval_ms > 1000) {
return -EINVAL;
}
luatos_air530z_lock(dev);
ret = gnss_nmea0183_snprintk(data->dynamic_request_buf, sizeof(data->dynamic_request_buf),
"PCAS02,%u", fix_interval_ms);
data->dynamic_script_chat.request_size = ret;
ret = modem_chat_run_script(&data->chat, &data->dynamic_script);
if (ret < 0) {
goto unlock_return;
}
unlock_return:
luatos_air530z_unlock(dev);
return ret;
}
static int luatos_air530z_set_enabled_systems(const struct device *dev, gnss_systems_t systems)
{
struct gnss_luatos_air530z_data *data = dev->data;
gnss_systems_t supported_systems;
uint8_t encoded_systems = 0;
int ret;
supported_systems = (GNSS_SYSTEM_GPS | GNSS_SYSTEM_GLONASS | GNSS_SYSTEM_BEIDOU);
if ((~supported_systems) & systems) {
return -EINVAL;
}
luatos_air530z_lock(dev);
WRITE_BIT(encoded_systems, 0, systems & GNSS_SYSTEM_GPS);
WRITE_BIT(encoded_systems, 1, systems & GNSS_SYSTEM_GLONASS);
WRITE_BIT(encoded_systems, 2, systems & GNSS_SYSTEM_BEIDOU);
ret = gnss_nmea0183_snprintk(data->dynamic_request_buf, sizeof(data->dynamic_request_buf),
"PCAS04,%u", encoded_systems);
if (ret < 0) {
goto unlock_return;
}
data->dynamic_script_chat.request_size = ret;
ret = modem_chat_run_script(&data->chat, &data->dynamic_script);
if (ret < 0) {
goto unlock_return;
}
unlock_return:
luatos_air530z_unlock(dev);
return ret;
}
static int luatos_air530z_get_supported_systems(const struct device *dev, gnss_systems_t *systems)
{
*systems = (GNSS_SYSTEM_GPS | GNSS_SYSTEM_GLONASS | GNSS_SYSTEM_BEIDOU);
return 0;
}
static struct gnss_driver_api gnss_api = {
.set_fix_rate = luatos_air530z_set_fix_rate,
.set_enabled_systems = luatos_air530z_set_enabled_systems,
.get_supported_systems = luatos_air530z_get_supported_systems,
};
#define LUATOS_AIR530Z(inst) \
static struct gnss_luatos_air530z_config gnss_luatos_air530z_cfg_##inst = { \
.uart = DEVICE_DT_GET(DT_INST_BUS(inst)), \
.on_off_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, on_off_gpios, { 0 }), \
}; \
\
static struct gnss_luatos_air530z_data gnss_luatos_air530z_data_##inst = { \
.chat_delimiter = {'\r', '\n'}, \
.dynamic_separators_buf = {',', '*'}, \
}; \
\
PM_DEVICE_DT_INST_DEFINE(inst, luatos_air530z_pm_action); \
\
DEVICE_DT_INST_DEFINE(inst, gnss_luatos_air530z_init, \
PM_DEVICE_DT_INST_GET(inst), \
&gnss_luatos_air530z_data_##inst, \
&gnss_luatos_air530z_cfg_##inst, \
POST_KERNEL, CONFIG_GNSS_INIT_PRIORITY, &gnss_api);
DT_INST_FOREACH_STATUS_OKAY(LUATOS_AIR530Z)

View file

@ -0,0 +1,15 @@
# Copyright 2024 Jerónimo Agulló
# SPDX-License-Identifier: Apache-2.0
description: AIR530Z Luatos GNSS modem
compatible: "luatos,air530z"
include:
- uart-device.yaml
properties:
on-off-gpios:
type: phandle-array
description: |
Identifies the On-Off pin for entering into low-power mode.