feat(factory): add GNSS test and LED status modes

This commit is contained in:
chenjintao
2026-09-24 17:49:04 +08:00
parent c011f5c491
commit a4d6f79838
16 changed files with 361 additions and 76 deletions
@@ -11,6 +11,7 @@ vpath %.c ../custom_app ../custom_app/lib/libnfc
SRC_FILES+= \
factory_test.c \
factory_protocol.c \
factory_gnss.c \
factory_audio.c \
hal_i2c.c \
DP1312EA.c \
@@ -0,0 +1,118 @@
#include <stdio.h>
#include <string.h>
#include "ql_gnss.h"
#include "ql_rtos.h"
#include "factory_gnss.h"
#define FT_GNSS_NMEA_GGA_GSV ((1 << 0) | (1 << 2))
typedef struct {
volatile int open_ret;
volatile u32 nmea_count;
volatile int sat_count;
volatile int fix_status;
volatile int started;
u32 start_ms;
} factory_gnss_context_t;
static factory_gnss_context_t g_factory_gnss;
static u32 factory_gnss_now_ms(void)
{
return ql_rtos_get_systicks() * 5U;
}
static int nmea_field_int(const char *line, int line_len, int wanted_field,
int *value)
{
int field = 0;
int i;
int parsed = 0;
int number = 0;
for (i = 0; i < line_len; ++i) {
char ch = line[i];
if (ch == ',') {
if (field == wanted_field) break;
++field;
continue;
}
if (field == wanted_field) {
if (ch < '0' || ch > '9') break;
number = number * 10 + (ch - '0');
parsed = 1;
}
}
if (!parsed) return 0;
*value = number;
return 1;
}
static void factory_gnss_callback(char *data, int len)
{
int i;
if (!data || len <= 0) return;
for (i = 0; i + 5 < len; ++i) {
int line_end;
int value;
if (data[i] != '$') continue;
++g_factory_gnss.nmea_count;
for (line_end = i; line_end < len; ++line_end) {
if (data[line_end] == '\r' || data[line_end] == '\n') break;
}
if (data[i + 3] == 'G' && data[i + 4] == 'S' && data[i + 5] == 'V' &&
nmea_field_int(data + i, line_end - i, 3, &value) &&
value > g_factory_gnss.sat_count) {
g_factory_gnss.sat_count = value;
}
if (data[i + 3] == 'G' && data[i + 4] == 'G' && data[i + 5] == 'A' &&
nmea_field_int(data + i, line_end - i, 6, &value) &&
value > g_factory_gnss.fix_status) {
g_factory_gnss.fix_status = value;
}
i = line_end;
}
}
void factory_gnss_start(void)
{
int cfgsys_ret;
int cfgmsg_ret;
int mode_ret;
memset(&g_factory_gnss, 0, sizeof(g_factory_gnss));
g_factory_gnss.open_ret = -1;
g_factory_gnss.start_ms = factory_gnss_now_ms();
cfgsys_ret = ql_gnss_cfgsys_set(1);
cfgmsg_ret = ql_gnss_cfgmsg_set(FT_GNSS_NMEA_GGA_GSV);
mode_ret = ql_set_gnss_nmea_mode(PACKET_MODE);
g_factory_gnss.open_ret = ql_gnss_open(factory_gnss_callback);
g_factory_gnss.started = (g_factory_gnss.open_ret == 0);
printf("[FT_GNSS] start open_ret=%d cfgsys_ret=%d cfgmsg_ret=%d mode_ret=%d\r\n",
g_factory_gnss.open_ret, cfgsys_ret, cfgmsg_ret, mode_ret);
}
void factory_gnss_snapshot(factory_gnss_result_t *result)
{
if (!result) return;
result->open_ret = g_factory_gnss.open_ret;
result->nmea_count = g_factory_gnss.nmea_count;
result->sat_count = g_factory_gnss.sat_count;
result->fix_status = g_factory_gnss.fix_status;
result->search_ms = factory_gnss_now_ms() - g_factory_gnss.start_ms;
}
void factory_gnss_stop(void)
{
int ret;
if (!g_factory_gnss.started) return;
ret = ql_gnss_close();
g_factory_gnss.started = 0;
printf("[FT_GNSS] stop ret=%d nmea=%lu sat=%d fix=%d\r\n", ret,
(unsigned long)g_factory_gnss.nmea_count,
g_factory_gnss.sat_count, g_factory_gnss.fix_status);
}
@@ -0,0 +1,18 @@
#ifndef BANBAN_FACTORY_GNSS_H
#define BANBAN_FACTORY_GNSS_H
#include "ql_type.h"
typedef struct {
int open_ret;
u32 nmea_count;
int sat_count;
int fix_status;
u32 search_ms;
} factory_gnss_result_t;
void factory_gnss_start(void);
void factory_gnss_snapshot(factory_gnss_result_t *result);
void factory_gnss_stop(void);
#endif
@@ -42,7 +42,7 @@ const char *ft_node_name(ft_node_t node)
{
static const char *names[FT_NODE_COUNT] = {
"BOOT_AUDIO", "POWER_ADC", "AUDIO_LOOPBACK",
"ESIM", "DATA_CALL", "NFC"
"ESIM", "DATA_CALL", "NFC", "GNSS"
};
if (node < 0 || node >= FT_NODE_COUNT) {
@@ -249,7 +249,7 @@ void ft_protocol_session_result(int passed, ft_error_t code)
"\"duration_ms\":%lu,\"nodes\":{"
"\"BOOT_AUDIO\":\"%s\",\"POWER_ADC\":\"%s\","
"\"AUDIO_LOOPBACK\":\"%s\",\"ESIM\":\"%s\","
"\"DATA_CALL\":\"%s\",\"NFC\":\"%s\"}}",
"\"DATA_CALL\":\"%s\",\"NFC\":\"%s\",\"GNSS\":\"%s\"}}",
FT_PROTOCOL_VERSION, (unsigned long)++g_protocol.seq,
g_protocol.session, (unsigned long)ft_now_ms(), passed ? "PASS" : "FAIL",
code, g_protocol.imei, g_protocol.iccid, g_protocol.nfc_uid,
@@ -258,7 +258,8 @@ void ft_protocol_session_result(int passed, ft_error_t code)
(unsigned long)(ft_now_ms() - g_protocol.start_ms),
ft_state_name(g_protocol.nodes[0]), ft_state_name(g_protocol.nodes[1]),
ft_state_name(g_protocol.nodes[2]), ft_state_name(g_protocol.nodes[3]),
ft_state_name(g_protocol.nodes[4]), ft_state_name(g_protocol.nodes[5]));
ft_state_name(g_protocol.nodes[4]), ft_state_name(g_protocol.nodes[5]),
ft_state_name(g_protocol.nodes[6]));
}
void ft_protocol_power_off(void)
@@ -9,12 +9,14 @@
#include "ql_iic.h"
#include "ql_nw.h"
#include "ql_power.h"
#include "ql_pwm.h"
#include "ql_rtos.h"
#include "ql_sim.h"
#include "sockets.h"
#include "DP1312EA.h"
#include "factory_audio.h"
#include "factory_gnss.h"
#include "factory_protocol.h"
#include "factory_test.h"
#include "nfc.h"
@@ -34,24 +36,96 @@ static char g_ip_address[64];
static volatile int g_led_mode;
static ql_task_t g_led_task;
enum {
FT_LED_OFF = 0,
FT_LED_ON,
FT_LED_LOOPBACK,
FT_LED_NFC,
FT_LED_GNSS
};
static int factory_led_pwm_set(unsigned short duty, int *enabled)
{
QL_PWM_CONFIG_EX config;
config.pwm_psc = 1;
config.high_one_cycle_duration = duty;
config.total_one_cycle_duration = 320;
config.clk_src = QL_PWM_CLK_SRC_32K;
if (ql_pwm_config_plus_ex(FT_STATUS_LED_PIN, &config) != 0) return -1;
if (!*enabled) {
if (ql_pwm_enable(FT_STATUS_LED_PIN) != 0) {
ql_pwm_disable(FT_STATUS_LED_PIN);
return -1;
}
*enabled = 1;
}
return 0;
}
static void factory_led_pwm_stop(int *enabled)
{
if (*enabled) ql_pwm_disable(FT_STATUS_LED_PIN);
*enabled = 0;
}
static void factory_led_task(void *arg)
{
int phase = 0;
int last_mode = -1;
int pwm_enabled = 0;
int pwm_failed = 0;
int breath_duty = 16;
int breath_step = 16;
(void)arg;
ql_gpio_init(FT_STATUS_LED_PIN, PIN_DIRECTION_OUT, PIN_PULL_DISABLE,
PIN_LEVEL_HIGH);
while (1) {
if (g_led_mode == 0) {
int mode = g_led_mode;
if (mode != last_mode) {
phase = 0;
breath_duty = 16;
breath_step = 16;
pwm_failed = 0;
last_mode = mode;
}
if (mode == FT_LED_GNSS && !pwm_failed) {
if (factory_led_pwm_set((unsigned short)breath_duty,
&pwm_enabled) == 0) {
breath_duty += breath_step;
if (breath_duty >= 304 || breath_duty <= 16) {
breath_step = -breath_step;
}
ql_rtos_task_sleep_ms(40);
continue;
}
pwm_failed = 1;
factory_led_pwm_stop(&pwm_enabled);
printf("[FT_LED] GPIO70 PWM unavailable, use GNSS double-flash\r\n");
}
if (pwm_enabled) {
factory_led_pwm_stop(&pwm_enabled);
ql_gpio_init(FT_STATUS_LED_PIN, PIN_DIRECTION_OUT,
PIN_PULL_DISABLE, PIN_LEVEL_HIGH);
}
if (mode == FT_LED_OFF) {
ql_gpio_set_level(FT_STATUS_LED_PIN, PIN_LEVEL_LOW);
ql_rtos_task_sleep_ms(100);
} else if (g_led_mode == 1) {
} else if (mode == FT_LED_ON) {
ql_gpio_set_level(FT_STATUS_LED_PIN, PIN_LEVEL_HIGH);
ql_rtos_task_sleep_ms(100);
} else if (mode == FT_LED_GNSS) {
static const int levels[] = {1, 0, 1, 0};
static const int delays[] = {80, 80, 80, 520};
ql_gpio_set_level(FT_STATUS_LED_PIN,
levels[phase] ? PIN_LEVEL_HIGH : PIN_LEVEL_LOW);
ql_rtos_task_sleep_ms(delays[phase]);
phase = (phase + 1) % 4;
} else {
phase = !phase;
ql_gpio_set_level(FT_STATUS_LED_PIN,
phase ? PIN_LEVEL_HIGH : PIN_LEVEL_LOW);
ql_rtos_task_sleep_ms(g_led_mode == 2 ? 250 : 100);
ql_rtos_task_sleep_ms(mode == FT_LED_LOOPBACK ? 250 : 100);
}
}
}
@@ -123,9 +197,9 @@ static ft_error_t run_audio_loopback(char *data, unsigned int data_size)
if (ret != 0) {
return FT_ERR_AUDIO_PLAY;
}
factory_led_mode(2);
factory_led_mode(FT_LED_LOOPBACK);
ret = factory_audio_wait_and_loopback(FT_DIALOG_KEY_TIMEOUT_MS, &recorded_ms);
factory_led_mode(1);
factory_led_mode(FT_LED_ON);
snprintf(data, data_size,
"{\"recorded_ms\":%lu,\"max_ms\":%d,\"volume_percent\":100}",
(unsigned long)recorded_ms, FT_AUDIO_MAX_MS);
@@ -211,12 +285,12 @@ static ft_error_t run_nfc(char *data, unsigned int data_size)
if (audio_ret != 0) {
return FT_ERR_AUDIO_PLAY;
}
factory_led_mode(3);
factory_led_mode(FT_LED_NFC);
DP1312EA_Reset();
version = DP1312EA_ReadRawRC(VersionReg);
if (version != FT_NFC_VERSION_EXPECTED) {
snprintf(data, data_size, "{\"chip_version\":\"0x%02X\"}", version);
factory_led_mode(1);
factory_led_mode(FT_LED_ON);
return FT_ERR_NFC_CHIP;
}
@@ -227,16 +301,54 @@ static ft_error_t run_nfc(char *data, unsigned int data_size)
snprintf(data, data_size,
"{\"chip_version\":\"0x%02X\",\"uid\":\"%s\"}",
version, (char *)output_uuid);
factory_led_mode(1);
factory_led_mode(FT_LED_ON);
return FT_OK;
}
ql_rtos_task_sleep_ms(200);
} while (ft_now_ms() - start < FT_NFC_TIMEOUT_MS);
factory_led_mode(1);
factory_led_mode(FT_LED_ON);
return FT_ERR_NFC_TIMEOUT;
}
static ft_error_t run_gnss(char *data, unsigned int data_size)
{
factory_gnss_result_t result;
int audio_ret;
factory_led_mode(FT_LED_GNSS);
audio_ret = factory_audio_play_prompt(FT_AUDIO_GNSS_PATH);
if (audio_ret != 0) {
factory_gnss_stop();
factory_led_mode(FT_LED_ON);
return audio_ret == -2 ? FT_ERR_AUDIO_FILE_MISSING : FT_ERR_AUDIO_PLAY;
}
while (1) {
factory_gnss_snapshot(&result);
if (result.open_ret == 0 && result.nmea_count >= 1 &&
(result.sat_count >= 1 || result.fix_status > 0)) {
snprintf(data, data_size,
"{\"open_ret\":%d,\"nmea_count\":%lu,\"sat_count\":%d,"
"\"fix_status\":%d,\"search_ms\":%lu,"
"\"required_nmea_count\":1,\"required_sat_count\":1}",
result.open_ret, (unsigned long)result.nmea_count,
result.sat_count, result.fix_status,
(unsigned long)result.search_ms);
factory_gnss_stop();
factory_led_mode(FT_LED_ON);
return FT_OK;
}
if (result.open_ret != 0) {
ql_rtos_task_sleep_s(2);
factory_gnss_start();
} else {
ql_rtos_task_sleep_ms(500);
}
}
}
static ft_error_t run_node(ft_node_t node, char *data, unsigned int data_size)
{
data[0] = '\0';
@@ -247,6 +359,7 @@ static ft_error_t run_node(ft_node_t node, char *data, unsigned int data_size)
case FT_NODE_ESIM: return run_esim(data, data_size);
case FT_NODE_DATA_CALL: return run_data_call(data, data_size);
case FT_NODE_NFC: return run_nfc(data, data_size);
case FT_NODE_GNSS: return run_gnss(data, data_size);
default: return FT_ERR_NFC_LOCKED;
}
}
@@ -261,10 +374,11 @@ static void factory_test_task(void *arg)
int audio_ret;
(void)arg;
factory_gnss_start();
ql_rtos_task_sleep_s(2);
ft_protocol_init();
ql_rtos_task_create(&g_led_task, 2048, 15, "ft_led", factory_led_task, NULL);
factory_led_mode(1);
factory_led_mode(FT_LED_ON);
if (ql_dev_get_imei(imei, sizeof(imei)) == 0) {
ft_protocol_set_imei(imei);
}
@@ -4,8 +4,8 @@
#include "ql_type.h"
#include "ql_gpio.h"
#define FT_PROTOCOL_VERSION 1
#define FT_FIRMWARE_VERSION "FACTORY_TEST_1.0.0"
#define FT_PROTOCOL_VERSION 2
#define FT_FIRMWARE_VERSION "FACTORY_TEST_1.1.0"
#define FT_ADC_MIN_MV 3500
#define FT_ADC_MAX_MV 4200
@@ -37,6 +37,7 @@ typedef enum {
FT_NODE_ESIM,
FT_NODE_DATA_CALL,
FT_NODE_NFC,
FT_NODE_GNSS,
FT_NODE_COUNT
} ft_node_t;
@@ -58,6 +59,9 @@ typedef enum {
FT_ERR_NETWORK_REGISTER = 1501,
FT_ERR_PDP_ACTIVATE = 1502,
FT_ERR_IP_INVALID = 1503,
FT_ERR_GNSS_OPEN = 1601,
FT_ERR_GNSS_NO_NMEA = 1602,
FT_ERR_GNSS_NO_SATELLITE = 1603,
FT_ERR_NFC_LOCKED = 1701,
FT_ERR_NFC_CHIP = 1702,
FT_ERR_NFC_TIMEOUT = 1703,