kopia lustrzana https://github.com/cariboulabs/cariboulite
1076 wiersze
36 KiB
C
1076 wiersze
36 KiB
C
#ifndef ZF_LOG_LEVEL
|
|
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
|
#endif
|
|
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
|
#define ZF_LOG_TAG "CARIBOULITE Prod"
|
|
#include "zf_log/zf_log.h"
|
|
|
|
//=================================================
|
|
// INTERNAL INCLUDES
|
|
#include "production_utils/production_testing.h"
|
|
#include "cariboulite_setup.h"
|
|
#include "cariboulite_radio.h"
|
|
#include "cariboulite_events.h"
|
|
#include "cariboulite.h"
|
|
#include "hat/hat.h"
|
|
#include "cariboulite_dtbo.h"
|
|
#include "production_utils/production_utils.h"
|
|
#include "io_utils/io_utils_sys_info.h"
|
|
|
|
//=================================================
|
|
// EXTERNAL INCLUDES
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
//=================================================
|
|
// FLOW MANAGEMENT
|
|
CARIBOULITE_CONFIG_DEFAULT(cariboulite_sys);
|
|
|
|
//=================================================
|
|
hat_st hat =
|
|
{
|
|
.vendor_name = "CaribouLabs LTD",
|
|
.product_name = "CaribouLite RPI Hat",
|
|
.product_id = system_type_cariboulite_full,
|
|
.product_version = 0x01,
|
|
.device_tree_buffer = cariboulite_dtbo,
|
|
.device_tree_buffer_size = sizeof(cariboulite_dtbo),
|
|
|
|
.dev = {
|
|
.i2c_address = 0x50, // the i2c address of the eeprom chip
|
|
.eeprom_type = eeprom_type_24c32,
|
|
},
|
|
};
|
|
|
|
//=================================================
|
|
int stop_program ()
|
|
{
|
|
ZF_LOGD("program termination requested");
|
|
return 0;
|
|
}
|
|
|
|
//=================================================
|
|
void sighandler( struct sys_st_t *sys,
|
|
void* context,
|
|
int signal_number,
|
|
siginfo_t *si)
|
|
{
|
|
switch (signal_number)
|
|
{
|
|
case SIGINT:
|
|
case SIGTERM:
|
|
case SIGABRT:
|
|
case SIGILL:
|
|
case SIGSEGV:
|
|
case SIGFPE: stop_program (); break;
|
|
default: return; break;
|
|
}
|
|
}
|
|
|
|
//================================================================
|
|
//================================================================
|
|
//================================================================
|
|
// TEST TYPES
|
|
typedef enum
|
|
{
|
|
cariboulite_test_en_current_system = 0,
|
|
cariboulite_test_en_fpga_programming,
|
|
cariboulite_test_en_fpga_communication,
|
|
cariboulite_test_en_fpga_id_resistors,
|
|
cariboulite_test_en_fpga_reset,
|
|
cariboulite_test_en_fpga_switch,
|
|
cariboulite_test_en_fpga_leds,
|
|
cariboulite_test_en_fpga_smi,
|
|
cariboulite_test_en_rpi_id_eeprom,
|
|
cariboulite_test_en_mixer_communication,
|
|
cariboulite_test_en_mixer_versions,
|
|
cariboulite_test_en_modem_communication,
|
|
cariboulite_test_en_modem_versions,
|
|
cariboulite_test_en_modem_leds,
|
|
cariboulite_test_en_modem_interrupt,
|
|
cariboulite_test_en_current_modem_rx,
|
|
cariboulite_test_en_current_modem_tx,
|
|
cariboulite_test_en_system_smi_data,
|
|
cariboulite_test_en_system_rf_loopback,
|
|
cariboulite_test_en_system_rf_tx_power,
|
|
cariboulite_test_en_max,
|
|
} cariboulite_test_en;
|
|
|
|
//================================================================
|
|
// TEST FUNCTIONS
|
|
static int cariboulite_test_hat_eeprom(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_fpga_programming(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_fpga_communication(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_fpga_id_resistors(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_fpga_soft_reset(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_fpga_switch(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_fpga_leds(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_fpga_smi(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_mixer_communication(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_mixer_versions(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_modem_communication(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_modem_version(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_modem_leds(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_modem_interrupt(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_current_system(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_current_modem_rx(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_current_modem_tx(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_smi_data(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_rf_loopback(void* context, void* test_context, int test_num);
|
|
static int cariboulite_test_rf_tx_power(void* context, void* test_context, int test_num);
|
|
|
|
//================================================================
|
|
// TEST DEFINITIONS
|
|
production_test_st tests[] =
|
|
{
|
|
{.name_short = "CURR. SYS", .test_name = "current_system", .group = 4, .test_number = cariboulite_test_en_current_system, .func = cariboulite_test_current_system, },
|
|
{.name_short = "FPGA PROG", .test_name = "fpga_programming", .group = 1, .test_number = cariboulite_test_en_fpga_programming, .func = cariboulite_test_fpga_programming, },
|
|
{.name_short = "FPGA COMM", .test_name = "fpga_communication", .group = 1, .test_number = cariboulite_test_en_fpga_communication, .func = cariboulite_test_fpga_communication, },
|
|
{.name_short = "FPGA IDRES", .test_name = "fpga_id_resistors", .group = 1, .test_number = cariboulite_test_en_fpga_id_resistors, .func = cariboulite_test_fpga_id_resistors, },
|
|
{.name_short = "FPGA SFTRST", .test_name = "fpga_soft_reset", .group = 1, .test_number = cariboulite_test_en_fpga_reset, .func = cariboulite_test_fpga_soft_reset, },
|
|
{.name_short = "FPGA SWTCH", .test_name = "fpga_switch", .group = 1, .test_number = cariboulite_test_en_fpga_switch, .func = cariboulite_test_fpga_switch, },
|
|
{.name_short = "FPGA LEDS", .test_name = "fpga_leds", .group = 1, .test_number = cariboulite_test_en_fpga_leds, .func = cariboulite_test_fpga_leds, },
|
|
{.name_short = "FPGA SMI", .test_name = "fpga_smi", .group = 1, .test_number = cariboulite_test_en_fpga_smi, .func = cariboulite_test_fpga_smi, },
|
|
{.name_short = "EEPROM", .test_name = "hat_eeprom", .group = 0, .test_number = cariboulite_test_en_rpi_id_eeprom, .func = cariboulite_test_hat_eeprom, },
|
|
{.name_short = "MXR COMM", .test_name = "mixer_communication", .group = 2, .test_number = cariboulite_test_en_mixer_communication, .func = cariboulite_test_mixer_communication, },
|
|
{.name_short = "MXR VER", .test_name = "mixer_version_id", .group = 2, .test_number = cariboulite_test_en_mixer_versions, .func = cariboulite_test_mixer_versions, },
|
|
{.name_short = "MDM COMM", .test_name = "modem_communication", .group = 3, .test_number = cariboulite_test_en_modem_communication, .func = cariboulite_test_modem_communication, },
|
|
{.name_short = "MDM VER", .test_name = "modem_version", .group = 3, .test_number = cariboulite_test_en_modem_versions, .func = cariboulite_test_modem_version, },
|
|
{.name_short = "MDM LED", .test_name = "modem_leds", .group = 3, .test_number = cariboulite_test_en_modem_leds, .func = cariboulite_test_modem_leds, },
|
|
{.name_short = "MDM INT", .test_name = "modem_interrupt", .group = 3, .test_number = cariboulite_test_en_modem_interrupt, .func = cariboulite_test_modem_interrupt, },
|
|
{.name_short = "CURR. RX", .test_name = "current_modem_rx", .group = 4, .test_number = cariboulite_test_en_current_modem_rx, .func = cariboulite_test_current_modem_rx, },
|
|
{.name_short = "CURR. TX", .test_name = "current_modem_tx", .group = 4, .test_number = cariboulite_test_en_current_modem_tx, .func = cariboulite_test_current_modem_tx, },
|
|
{.name_short = "SMI DATA", .test_name = "system_smi_data", .group = 5, .test_number = cariboulite_test_en_system_smi_data, .func = cariboulite_test_smi_data, },
|
|
{.name_short = "RF LB", .test_name = "system_rf_loopback", .group = 5, .test_number = cariboulite_test_en_system_rf_loopback, .func = cariboulite_test_rf_loopback, },
|
|
{.name_short = "RF TXPWR", .test_name = "system_rf_tx_power", .group = 5, .test_number = cariboulite_test_en_system_rf_tx_power, .func = cariboulite_test_rf_tx_power, },
|
|
};
|
|
|
|
#define NUM_OF_TESTS (sizeof(tests)/sizeof(production_test_st))
|
|
|
|
//=================================================
|
|
int cariboulite_test_current_system(void *context, void* test_context, int test_num)
|
|
{
|
|
int k;
|
|
bool fault = false;
|
|
float current_ma = 0.0f, voltage_mv = 0.0f, power_mw = 0.0f;
|
|
float average_current = 0.0;
|
|
bool pass = true;
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
caribou_fpga_hard_reset_keep(&sys->fpga, true);
|
|
|
|
//lcd_writeln(&prod.lcd, "Power on...", "", true);
|
|
hat_powermon_set_power_state(&prod->powermon, true);
|
|
io_utils_usleep(400000);
|
|
|
|
for (k = 0; k < 20; k++)
|
|
{
|
|
io_utils_usleep(100000);
|
|
production_monitor_power_fault(prod, &fault, ¤t_ma, &voltage_mv, &power_mw);
|
|
|
|
average_current += current_ma;
|
|
}
|
|
|
|
average_current /= (float)(k);
|
|
|
|
if (fault || average_current > 220.0f || voltage_mv < 2500.0f || current_ma < 10.0f)
|
|
{
|
|
tests[test_num].test_result_float = average_current;
|
|
sprintf(tests[test_num].test_result_textual, "Wrong current %.1f mA, low voltage (%.1f mV), fault: %d", average_current, voltage_mv, fault);
|
|
pass = false;
|
|
hat_powermon_set_power_state(&prod->powermon, false);
|
|
}
|
|
else
|
|
{
|
|
tests[test_num].test_result_float = average_current;
|
|
sprintf(tests[test_num].test_result_textual, "Pass - idle current - %.1f mA", average_current);
|
|
pass = true;
|
|
}
|
|
|
|
return pass;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_fpga_programming(void *context, void* test_context, int test_num)
|
|
{
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
ZF_LOGD("Programming FPGA");
|
|
if (cariboulite_configure_fpga (sys, cariboulite_firmware_source_blob, NULL/*sys->firmware_path_operational*/) < 0)
|
|
{
|
|
ZF_LOGE("FPGA programming failed");
|
|
caribou_fpga_close(&sys->fpga);
|
|
|
|
sprintf(tests[test_num].test_result_textual, "FPGA programming failed");
|
|
tests[test_num].test_result_float = -1;
|
|
return false;
|
|
}
|
|
|
|
sys->system_status = sys_status_minimal_init;
|
|
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass");
|
|
tests[test_num].test_pass = true;
|
|
|
|
return tests[test_num].test_pass;
|
|
}
|
|
|
|
|
|
//=================================================
|
|
int cariboulite_test_fpga_communication(void *context, void* test_context, int test_num)
|
|
{
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
caribou_fpga_versions_st vers = {0};
|
|
caribou_fpga_get_versions (&sys->fpga, &vers);
|
|
|
|
if (vers.sys_ver == 1 && vers.sys_manu_id == 1 && vers.sys_ctrl_mod_ver == 1
|
|
&& vers.io_ctrl_mod_ver == 1 && vers.smi_ctrl_mod_ver == 1)
|
|
{
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass");
|
|
tests[test_num].test_pass = true;
|
|
}
|
|
else
|
|
{
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual,
|
|
"Fail - sys_ver: %02X, sys_manu_id: %02X, sys_ctrl_mod_ver: %02X, io_ctrl_mod_ver: %02X, smi_ctrl_mod_ver: %02X",
|
|
vers.sys_ver, vers.sys_manu_id, vers.sys_ctrl_mod_ver, vers.io_ctrl_mod_ver, vers.smi_ctrl_mod_ver);
|
|
tests[test_num].test_pass = true;
|
|
}
|
|
|
|
return tests[test_num].test_pass;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_fpga_id_resistors(void *context, void* test_context, int test_num)
|
|
{
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
|
|
// Reading the configuration from the FPGA (resistor set)
|
|
int led0 = 0, led1 = 0, btn = 0, cfg = 0;
|
|
caribou_fpga_get_io_ctrl_dig (&sys->fpga, &led0, &led1, &btn, &cfg);
|
|
|
|
ZF_LOGD("FPGA Digital Values: led0: %d, led1: %d, btn: %d, CFG[0..3]: [%d,%d,%d,%d]",
|
|
led0, led1, btn, (cfg >> 0) & 0x1, (cfg >> 1) & 0x1, (cfg >> 2) & 0x1, (cfg >> 3) & 0x1);
|
|
sys->fpga_config_resistor_state = cfg;
|
|
|
|
// 0xf = full, 0xe = ism
|
|
if (sys->fpga_config_resistor_state != 0xF && sys->fpga_config_resistor_state != 0xE)
|
|
{
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Failed - unrecognized fpga id resistor config %01X (check R38, R39, R40, R41)", cfg);
|
|
tests[test_num].test_pass = false;
|
|
}
|
|
else
|
|
{
|
|
sys->sys_type = (cfg == 0xF) ? system_type_cariboulite_full : system_type_cariboulite_ism;
|
|
|
|
ZF_LOGD("System detected: %s, Operator set: %s", (sys->sys_type == system_type_cariboulite_full) ? "CaribouFull" : "CaribouISM",
|
|
(prod->operator_set_version == production_sys_version_ism) ? "CaribouISM" : "CaribouFull");
|
|
|
|
// mismatch
|
|
if (((prod->operator_set_version == production_sys_version_ism) && (sys->sys_type == system_type_cariboulite_full)) ||
|
|
((prod->operator_set_version == production_sys_version_full) && (sys->sys_type == system_type_cariboulite_ism)))
|
|
{
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Failed - version mismatch - detected %s, but operator set %s",
|
|
(cfg == 0xF) ? "CaribouFull" : "CaribouISM",
|
|
(prod->operator_set_version == production_sys_version_ism) ? "CaribouISM" : "CaribouFull");
|
|
tests[test_num].test_pass = false;
|
|
}
|
|
else
|
|
{
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass - detected %s", (cfg == 0xF) ? "CaribouFull" : "CaribouISM");
|
|
tests[test_num].test_pass = true;
|
|
|
|
prod->system_type_valid = true;
|
|
sprintf(prod->product_name, "%s", (cfg == 0xF) ? "CaribouFull" : "CaribouISM");
|
|
}
|
|
}
|
|
|
|
return tests[test_num].test_pass;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_fpga_soft_reset(void *context, void* test_context, int test_num)
|
|
{
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
caribou_fpga_soft_reset(&sys->fpga);
|
|
|
|
return cariboulite_test_fpga_communication(context, test_context, cariboulite_test_en_fpga_reset);
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_fpga_switch(void *context, void* test_context, int test_num)
|
|
{
|
|
bool pass = false;
|
|
int key1 = 0;
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
lcd_writeln(&prod->lcd, "PRESS CARIBU SW", "<= OR CLICK FAIL", true);
|
|
|
|
int led0 = 0, led1 = 0, btn = 0, cfg = 0;
|
|
while (1)
|
|
{
|
|
io_utils_usleep(300000);
|
|
caribou_fpga_get_io_ctrl_dig (&sys->fpga, &led0, &led1, &btn, &cfg);
|
|
if (btn == 0)
|
|
{
|
|
pass = true;
|
|
break;
|
|
}
|
|
|
|
lcd_get_keys(&prod->lcd, &key1, NULL);
|
|
|
|
if (key1)
|
|
{
|
|
pass = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pass)
|
|
{
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass");
|
|
tests[test_num].test_pass = true;
|
|
}
|
|
else
|
|
{
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Fail - didn't detect switch press - aborted by operator. Check switch assembly (S1)");
|
|
tests[test_num].test_pass = false;
|
|
}
|
|
|
|
return tests[test_num].test_pass;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_fpga_leds(void *context, void* test_context, int test_num)
|
|
{
|
|
bool pass = false;
|
|
int led = 0;
|
|
int key1 = 0, key2 = 0;
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
lcd_writeln(&prod->lcd, "<= YES BLINKING?", "<= NO", true);
|
|
|
|
int led0 = 0, led1 = 0, btn = 0, cfg = 0;
|
|
while (1)
|
|
{
|
|
io_utils_usleep(200000);
|
|
caribou_fpga_set_io_ctrl_dig (&sys->fpga, led, led);
|
|
|
|
led = !led;
|
|
|
|
lcd_get_keys(&prod->lcd, &key1, &key2);
|
|
if (key1)
|
|
{
|
|
pass = false;
|
|
break;
|
|
}
|
|
|
|
if (key2)
|
|
{
|
|
pass = true;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (pass)
|
|
{
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass");
|
|
tests[test_num].test_pass = true;
|
|
}
|
|
else
|
|
{
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Fail - CaribouLite LEDs didn't blink - check D11, D12");
|
|
tests[test_num].test_pass = false;
|
|
}
|
|
|
|
return tests[test_num].test_pass;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_fpga_smi(void *context, void* test_context, int test_num)
|
|
{
|
|
caribou_fpga_smi_fifo_status_st status = {0};
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
caribou_fpga_get_smi_ctrl_fifo_status (&sys->fpga, &status);
|
|
|
|
ZF_LOGI("FPGA SMI FIFO: 09Empty: %d, 09Full: %d, 24Empty: %d, 24Full:%d",
|
|
status.rx_fifo_09_empty, status.rx_fifo_09_full,
|
|
status.rx_fifo_24_empty, status.rx_fifo_24_full);
|
|
|
|
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass - 09Empty: %d, 09Full: %d, 24Empty: %d, 24Full:%d",
|
|
status.rx_fifo_09_empty, status.rx_fifo_09_full,
|
|
status.rx_fifo_24_empty, status.rx_fifo_24_full);
|
|
tests[test_num].test_pass = true;
|
|
|
|
return tests[test_num].test_pass;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_prod_eeprom_programming(sys_st* sys)
|
|
{
|
|
int led0 = 0, led1 = 0, btn = 0, cfg = 0;
|
|
|
|
// get the configuration resistors
|
|
caribou_fpga_get_io_ctrl_dig (&sys->fpga, &led0, &led1, &btn, &cfg);
|
|
|
|
sys->sys_type = (cfg & 0x1) ? system_type_cariboulite_full : system_type_cariboulite_ism;
|
|
if (sys->sys_type == system_type_cariboulite_full) ZF_LOGI("Detected CaribouLite FULL Version");
|
|
else if (sys->sys_type == system_type_cariboulite_ism) ZF_LOGI("Detected CaribouLite ISM Version");
|
|
hat.product_id = sys->sys_type;
|
|
|
|
hat_generate_write_config(&hat);
|
|
|
|
sleep(1);
|
|
caribou_fpga_set_io_ctrl_dig (&sys->fpga, 0, 0);
|
|
return 0;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_hat_eeprom(void *context, void* test_context, int test_num)
|
|
{
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
// EEPROM programming
|
|
ZF_LOGI("Starting EEPROM programming sequence");
|
|
hat_init(&hat);
|
|
cariboulite_prod_eeprom_programming(&cariboulite_sys);
|
|
hat_close(&hat);
|
|
|
|
ZF_LOGI("Verifying EEPROM");
|
|
hat_init(&hat);
|
|
|
|
if (hat.eeprom_initialized == 0)
|
|
{
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Fail - hat eeprom programming failed, check U26, R43, R44");
|
|
tests[test_num].test_pass = false;
|
|
}
|
|
else
|
|
{
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass");
|
|
tests[test_num].test_pass = true;
|
|
hat_print(&hat);
|
|
|
|
prod->serial_number_written_and_valid = true;
|
|
prod->serial_number = hat.generated_serial;
|
|
}
|
|
|
|
hat_close(&hat);
|
|
|
|
return tests[test_num].test_pass;
|
|
}
|
|
|
|
|
|
//=================================================
|
|
int cariboulite_test_mixer_communication(void *context, void* test_context, int test_num)
|
|
{
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
if (sys->sys_type != system_type_cariboulite_full)
|
|
{
|
|
ZF_LOGI("MIXER testing not applicable");
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "N/A - ISM");
|
|
tests[test_num].test_pass = true;
|
|
return true;
|
|
}
|
|
|
|
// initialize
|
|
ZF_LOGD("INIT MIXER - RFFC5072");
|
|
int res = rffc507x_init(&sys->mixer, &sys->spi_dev);
|
|
if (res < 0)
|
|
{
|
|
ZF_LOGE("Error initializing mixer 'rffc5072'");
|
|
rffc507x_release(&sys->mixer);
|
|
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Fail");
|
|
tests[test_num].test_pass = false;
|
|
|
|
return false;
|
|
}
|
|
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass");
|
|
tests[test_num].test_pass = true;
|
|
|
|
// calibrate
|
|
rffc507x_calibrate(&sys->mixer);
|
|
return true;
|
|
}
|
|
|
|
|
|
//=================================================
|
|
int cariboulite_test_mixer_versions(void *context, void* test_context, int test_num)
|
|
{
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
if (sys->sys_type != system_type_cariboulite_full)
|
|
{
|
|
ZF_LOGI("MIXER testing not applicable");
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "N/A - ISM");
|
|
tests[test_num].test_pass = true;
|
|
return true;
|
|
}
|
|
|
|
rffc507x_device_id_st dev_id;
|
|
rffc507x_device_status_st stat;
|
|
rffc507x_readback_status(&sys->mixer, &dev_id, &stat);
|
|
rffc507x_print_dev_id(&dev_id);
|
|
rffc507x_print_stat(&stat);
|
|
|
|
if (dev_id.device_rev == 1 || dev_id.device_id == 0x8A01)
|
|
{
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass");
|
|
tests[test_num].test_pass = true;
|
|
}
|
|
else
|
|
{
|
|
rffc507x_release(&sys->mixer);
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Fail: dev-id = %04x, dev-rev = %04x", dev_id.device_id, dev_id.device_rev);
|
|
tests[test_num].test_pass = false;
|
|
}
|
|
|
|
return tests[test_num].test_pass;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_modem_communication(void *context, void* test_context, int test_num)
|
|
{
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
int res = at86rf215_init(&sys->modem, &sys->spi_dev);
|
|
if (res < 0)
|
|
{
|
|
ZF_LOGE("Error initializing modem 'at86rf215'");
|
|
at86rf215_close(&sys->modem);
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Fail");
|
|
tests[test_num].test_pass = false;
|
|
return false;
|
|
}
|
|
|
|
at86rf215_setup_rf_irq(&sys->modem, 0, 1, at86rf215_drive_current_2ma);
|
|
at86rf215_radio_set_state(&sys->modem, at86rf215_rf_channel_900mhz, at86rf215_radio_state_cmd_trx_off);
|
|
at86rf215_radio_set_state(&sys->modem, at86rf215_rf_channel_2400mhz, at86rf215_radio_state_cmd_trx_off);
|
|
|
|
at86rf215_radio_irq_st int_mask = {
|
|
.wake_up_por = 1,
|
|
.trx_ready = 1,
|
|
.energy_detection_complete = 1,
|
|
.battery_low = 1,
|
|
.trx_error = 1,
|
|
.IQ_if_sync_fail = 1,
|
|
.res = 0,
|
|
};
|
|
at86rf215_radio_setup_interrupt_mask(&sys->modem, at86rf215_rf_channel_900mhz, &int_mask);
|
|
at86rf215_radio_setup_interrupt_mask(&sys->modem, at86rf215_rf_channel_2400mhz, &int_mask);
|
|
|
|
at86rf215_iq_interface_config_st modem_iq_config = {
|
|
.loopback_enable = 0,
|
|
.drv_strength = at86rf215_iq_drive_current_4ma,
|
|
.common_mode_voltage = at86rf215_iq_common_mode_v_ieee1596_1v2,
|
|
.tx_control_with_iq_if = false,
|
|
.radio09_mode = at86rf215_iq_if_mode,
|
|
.radio24_mode = at86rf215_iq_if_mode,
|
|
.clock_skew = at86rf215_iq_clock_data_skew_4_906ns,
|
|
};
|
|
at86rf215_setup_iq_if(&sys->modem, &modem_iq_config);
|
|
|
|
at86rf215_radio_external_ctrl_st ext_ctrl = {
|
|
.ext_lna_bypass_available = 0,
|
|
.agc_backoff = 0,
|
|
.analog_voltage_external = 0,
|
|
.analog_voltage_enable_in_off = 1,
|
|
.int_power_amplifier_voltage = 2,
|
|
.fe_pad_configuration = 1,
|
|
};
|
|
at86rf215_radio_setup_external_settings(&sys->modem, at86rf215_rf_channel_900mhz, &ext_ctrl);
|
|
at86rf215_radio_setup_external_settings(&sys->modem, at86rf215_rf_channel_2400mhz, &ext_ctrl);
|
|
|
|
cariboulite_radio_ext_ref (sys, cariboulite_ext_ref_32mhz);
|
|
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass");
|
|
tests[test_num].test_pass = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_modem_version(void *context, void* test_context, int test_num)
|
|
{
|
|
uint8_t pn, vn;
|
|
char pn_st[15] = {0};
|
|
bool pass = false;
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
at86rf215_get_versions(&sys->modem, &pn, &vn);
|
|
|
|
if ((pn == 0x34 || pn == 0x35) && vn > 0 && vn < 5) pass = true;
|
|
if (!pass)
|
|
{
|
|
at86rf215_close(&sys->modem);
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Fail, PN: 0x%02X, VER: %d, wrong P/N", pn, vn);
|
|
tests[test_num].test_pass = false;
|
|
return pass;
|
|
}
|
|
|
|
if (pn==0x34)
|
|
sprintf(pn_st, "AT86RF215");
|
|
else if (pn==0x35)
|
|
sprintf(pn_st, "AT86RF215IQ");
|
|
else if (pn==0x36)
|
|
sprintf(pn_st, "AT86RF215M");
|
|
else
|
|
sprintf(pn_st, "UNKNOWN");
|
|
|
|
printf("TEST:AT86RF215:VERSIONS:PN=0x%02X\n", pn);
|
|
printf("TEST:AT86RF215:VERSIONS:VN=%d\n", vn);
|
|
printf("TEST:AT86RF215:VERSIONS:PASS=%d\n", pass);
|
|
printf("TEST:AT86RF215:VERSIONS:INFO=The component PN is %s (0x%02X), Version %d\n", pn_st, pn, vn);
|
|
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass, PN: %s, VER: %d", pn_st, vn);
|
|
tests[test_num].test_pass = true;
|
|
|
|
return tests[test_num].test_pass;
|
|
}
|
|
|
|
|
|
//=================================================
|
|
int cariboulite_prod_set_modems_state(sys_st* sys, int state) // state = 0 = off, 1 = rx, 2 = tx
|
|
{
|
|
double freq_lo = 900e6;
|
|
double freq_hi = 2400e6;
|
|
float power_lo = 14;
|
|
float power_hi = 14;
|
|
|
|
cariboulite_channel_dir_en chan = state == 2?cariboulite_channel_dir_tx:cariboulite_channel_dir_rx;
|
|
|
|
cariboulite_radio_state_st *radio_low = &sys->radio_low;
|
|
cariboulite_radio_state_st *radio_hi = &sys->radio_high;
|
|
|
|
// frequency
|
|
cariboulite_radio_set_frequency(radio_low, true, &freq_lo);
|
|
cariboulite_radio_set_frequency(radio_hi, true, &freq_hi);
|
|
|
|
// deactivate - just to be sure
|
|
cariboulite_radio_activate_channel(radio_low, chan, false);
|
|
cariboulite_radio_activate_channel(radio_hi, chan, false);
|
|
|
|
if (state == 0)
|
|
{
|
|
cariboulite_radio_set_cw_outputs(radio_low, false, false);
|
|
cariboulite_radio_set_cw_outputs(radio_hi, false, false);
|
|
|
|
// deactivate
|
|
cariboulite_radio_activate_channel(radio_low, chan, false);
|
|
cariboulite_radio_activate_channel(radio_hi, chan, false);
|
|
}
|
|
else if (state == 1)
|
|
{
|
|
cariboulite_radio_set_cw_outputs(radio_low, false, false);
|
|
cariboulite_radio_set_cw_outputs(radio_hi, false, false);
|
|
|
|
// synchronize
|
|
cariboulite_radio_sync_information(radio_low);
|
|
cariboulite_radio_sync_information(radio_hi);
|
|
|
|
// activate rx
|
|
cariboulite_radio_activate_channel(radio_low, chan, true);
|
|
cariboulite_radio_activate_channel(radio_hi, chan, true);
|
|
}
|
|
if (state == 2)
|
|
{
|
|
// output power
|
|
cariboulite_radio_set_tx_power(radio_low, power_lo);
|
|
cariboulite_radio_set_tx_power(radio_hi, power_hi);
|
|
|
|
// setup cw outputs from modem
|
|
cariboulite_radio_set_cw_outputs(radio_low, false, true);
|
|
cariboulite_radio_set_cw_outputs(radio_hi, false, true);
|
|
|
|
// synchronize
|
|
cariboulite_radio_sync_information(radio_low);
|
|
cariboulite_radio_sync_information(radio_hi);
|
|
|
|
// activate tx
|
|
cariboulite_radio_activate_channel(radio_low, chan, true);
|
|
cariboulite_radio_activate_channel(radio_hi, chan, true);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_modem_leds(void *context, void* test_context, int test_num)
|
|
{
|
|
bool pass = false;
|
|
int key1, key2;
|
|
int state = 0;
|
|
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
lcd_writeln(&prod->lcd, "<= YES BLINKING?", "<= NO", true);
|
|
|
|
while (1)
|
|
{
|
|
io_utils_usleep(500000);
|
|
cariboulite_prod_set_modems_state(sys, state);
|
|
state = (state + 1) % 3;
|
|
|
|
lcd_get_keys(&prod->lcd, &key1, &key2);
|
|
if (key1)
|
|
{
|
|
pass = false;
|
|
break;
|
|
}
|
|
|
|
if (key2)
|
|
{
|
|
pass = true;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (pass)
|
|
{
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass");
|
|
tests[test_num].test_pass = true;
|
|
}
|
|
else
|
|
{
|
|
at86rf215_close(&sys->modem);
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Fail - Modem LEDs didn't blink");
|
|
tests[test_num].test_pass = false;
|
|
}
|
|
|
|
// deactivate both
|
|
cariboulite_prod_set_modems_state(sys, 0);
|
|
|
|
return tests[test_num].test_pass;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_modem_interrupt(void *context, void* test_context, int test_num)
|
|
{
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
if (sys->modem.num_interrupts == 0)
|
|
{
|
|
at86rf215_close(&sys->modem);
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Fail - didn't get modem interrupts");
|
|
tests[test_num].test_pass = false;
|
|
}
|
|
else
|
|
{
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass");
|
|
tests[test_num].test_pass = true;
|
|
}
|
|
return tests[test_num].test_pass;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_current_modem_rx(void *context, void* test_context, int test_num)
|
|
{
|
|
bool fault = false;
|
|
float current_ma = 0.0f, voltage_mv = 0.0f, power_mw = 0.0f;
|
|
float current_ma_before = 0.0f;
|
|
float current_diff = 0.0;
|
|
float current_diff_avg = 0.0;
|
|
bool pass = true;
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
int k = 0;
|
|
|
|
// deactivate
|
|
cariboulite_prod_set_modems_state(sys, 0);
|
|
|
|
io_utils_usleep(100000);
|
|
production_monitor_power_fault(prod, &fault, ¤t_ma, &voltage_mv, &power_mw);
|
|
current_ma_before = current_ma;
|
|
|
|
// activate rx
|
|
cariboulite_prod_set_modems_state(sys, 1);
|
|
io_utils_usleep(300000);
|
|
|
|
for (k = 0; k < 20; k ++)
|
|
{
|
|
io_utils_usleep(50000);
|
|
production_monitor_power_fault(prod, &fault, ¤t_ma, &voltage_mv, &power_mw);
|
|
current_diff = current_ma - current_ma_before;
|
|
|
|
current_diff_avg += current_diff;
|
|
}
|
|
current_diff_avg /= (float)(k);
|
|
|
|
if (fault || current_diff_avg > 150.0f)
|
|
{
|
|
at86rf215_close(&sys->modem);
|
|
tests[test_num].test_result_float = current_diff_avg;
|
|
sprintf(tests[test_num].test_result_textual, "High modem RX extra current %.1f mA, fault: %d", current_diff_avg, fault);
|
|
tests[test_num].test_pass = false;
|
|
pass = false;
|
|
}
|
|
else
|
|
{
|
|
tests[test_num].test_result_float = current_diff_avg;
|
|
sprintf(tests[test_num].test_result_textual, "Pass, RX extra current %.1f mA", current_diff_avg);
|
|
tests[test_num].test_pass = true;
|
|
pass = true;
|
|
}
|
|
|
|
// deactivate
|
|
cariboulite_prod_set_modems_state(sys, 0);
|
|
|
|
return pass;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_current_modem_tx(void *context, void* test_context, int test_num)
|
|
{
|
|
bool fault = false;
|
|
float current_ma = 0.0f, voltage_mv = 0.0f, power_mw = 0.0f;
|
|
float current_ma_before = 0.0f;
|
|
float current_diff = 0.0;
|
|
float current_diff_avg = 0.0;
|
|
bool pass = true;
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
int k = 0;
|
|
|
|
// deactivate
|
|
cariboulite_prod_set_modems_state(sys, 0);
|
|
|
|
io_utils_usleep(100000);
|
|
|
|
production_monitor_power_fault(prod, &fault, ¤t_ma_before, &voltage_mv, &power_mw);
|
|
|
|
// activate tx
|
|
cariboulite_prod_set_modems_state(sys, 2);
|
|
io_utils_usleep(300000);
|
|
|
|
// test the current
|
|
|
|
for (k = 0; k < 20; k++)
|
|
{
|
|
io_utils_usleep(100000);
|
|
production_monitor_power_fault(prod, &fault, ¤t_ma, &voltage_mv, &power_mw);
|
|
|
|
current_diff = current_ma - current_ma_before;
|
|
current_diff_avg += current_diff;
|
|
}
|
|
|
|
current_diff_avg /= (float)(k);
|
|
|
|
if (fault || current_diff_avg > 230.0f)
|
|
{
|
|
at86rf215_close(&sys->modem);
|
|
tests[test_num].test_result_float = current_diff_avg;
|
|
sprintf(tests[test_num].test_result_textual, "High modem TX extra current %.1f mA, fault: %d", current_diff_avg, fault);
|
|
tests[test_num].test_pass = false;
|
|
pass = false;
|
|
hat_powermon_set_power_state(&prod->powermon, false);
|
|
}
|
|
else
|
|
{
|
|
tests[test_num].test_result_float = current_diff_avg;
|
|
sprintf(tests[test_num].test_result_textual, "Pass, TX extra current %.1f mA", current_diff_avg);
|
|
tests[test_num].test_pass = true;
|
|
pass = true;
|
|
}
|
|
|
|
// deactivate
|
|
cariboulite_prod_set_modems_state(sys, 0);
|
|
|
|
return pass;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_smi_data(void *context, void* test_context, int test_num)
|
|
{
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass");
|
|
tests[test_num].test_pass = true;
|
|
return true;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_rf_loopback(void *context, void* test_context, int test_num)
|
|
{
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass");
|
|
tests[test_num].test_pass = true;
|
|
return true;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_test_rf_tx_power(void *context, void* test_context, int test_num)
|
|
{
|
|
sys_st* sys = (sys_st*)context;
|
|
production_sequence_st* prod = (production_sequence_st*)test_context;
|
|
|
|
tests[test_num].test_result_float = -1;
|
|
sprintf(tests[test_num].test_result_textual, "Pass");
|
|
tests[test_num].test_pass = true;
|
|
return true;
|
|
}
|
|
|
|
|
|
// GIT REPO
|
|
#define PRODUCTION_GIT_DIR "/home/pi/cariboulite_production_results"
|
|
#define PRODUCTION_PAT_PATH "/home/pi/manufacturing_PAT.txt"
|
|
//#define PRODUCTION_GIT_URI "github.com/cariboulabs/cariboulite_production_results.git"
|
|
#define PRODUCTION_GIT_URI "gitee.com/meexmachina/cariboulite_production_results.git"
|
|
|
|
//=================================================
|
|
int cariboulite_production_app_close(production_sequence_st* prod)
|
|
{
|
|
ZF_LOGI("CLOSING...");
|
|
production_close(prod);
|
|
return 0;
|
|
}
|
|
|
|
//=================================================
|
|
int cariboulite_production_clear_drivers(production_sequence_st* prod)
|
|
{
|
|
sys_st* sys = (sys_st*)prod->context;
|
|
|
|
rffc507x_release(&sys->mixer);
|
|
caribou_fpga_hard_reset_keep(&sys->fpga, true);
|
|
at86rf215_close(&sys->modem);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=================================================
|
|
int main(int argc, char *argv[])
|
|
{
|
|
bool fault = false;
|
|
char report_file_path[2048] = {0};
|
|
float i, v, p;
|
|
production_sequence_st prod = {};
|
|
|
|
cariboulite_init_system_production(&cariboulite_sys);
|
|
|
|
if (production_init(&prod, tests, NUM_OF_TESTS, &cariboulite_sys) != 0)
|
|
{
|
|
ZF_LOGE("Couldn't init production testing");
|
|
return 0;
|
|
}
|
|
|
|
production_generate_event_file(&prod, PRODUCTION_GIT_DIR, "tester started", prod.tester.rpi_info.serial_number);
|
|
production_set_git_repo(&prod, PRODUCTION_PAT_PATH, PRODUCTION_GIT_URI, PRODUCTION_GIT_DIR);
|
|
production_git_sync_sequence(&prod, "auto commit");
|
|
|
|
ZF_LOGI("WELLCOME!!");
|
|
lcd_writeln(&prod.lcd, "CaribouLite Tst", "WELLCOME! (3)", true);
|
|
sleep(1);
|
|
lcd_writeln(&prod.lcd, "CaribouLite Tst", "WELLCOME! (2)", true);
|
|
sleep(1);
|
|
lcd_writeln(&prod.lcd, "CaribouLite Tst", "WELLCOME! (1)", true);
|
|
sleep(1);
|
|
|
|
while (1)
|
|
{
|
|
int ret = 0;
|
|
char msg_cache[32];
|
|
lcd_button_en input_button = lcd_button_bottom;
|
|
|
|
production_wait_for_button(&prod, lcd_button_bottom, "MOUNT, START", "<== CLICK HERE");
|
|
lcd_writeln(&prod.lcd, "Starting Tests...", "", true);
|
|
sleep(2);
|
|
|
|
production_wait_input(&prod, &input_button, "<== FULL CHOOSE", "<== ISM");
|
|
|
|
prod.operator_set_version = input_button == lcd_button_bottom ? production_sys_version_ism : production_sys_version_full;
|
|
sprintf(msg_cache, "VER: %s", prod.operator_set_version == production_sys_version_ism? "ISM" : "FULL");
|
|
|
|
prod.serial_number_written_and_valid = false;
|
|
prod.system_type_valid = false;
|
|
|
|
// start the tests
|
|
ret = production_start_tests(&prod);
|
|
|
|
sleep(1);
|
|
hat_powermon_set_power_state(&prod.powermon, false);
|
|
|
|
// close the driver and release resources
|
|
production_utils_rpi_leds_init(0);
|
|
|
|
if (ret == false)
|
|
{
|
|
production_wait_for_button(&prod, lcd_button_bottom, "F A I L! UNMOUNT", "<== CLICK HERE");
|
|
production_generate_event_file(&prod, PRODUCTION_GIT_DIR, "board testing failed", prod.tester.rpi_info.serial_number);
|
|
}
|
|
else
|
|
{
|
|
production_wait_for_button(&prod, lcd_button_bottom, "P A S S! UNMOUNT", "<== CLICK HERE");
|
|
}
|
|
|
|
hat_powermon_set_power_state(&prod.powermon, false);
|
|
|
|
//sprintf(report_file_path, "%s/%08x_%s.yml", PRODUCTION_GIT_DIR, prod.prod->serial_number, ret?"PASS":"FAIL");
|
|
production_generate_report(&prod, PRODUCTION_GIT_DIR, prod.serial_number);
|
|
|
|
production_git_sync_sequence(&prod, "auto commit");
|
|
cariboulite_production_clear_drivers(&prod);
|
|
production_rewind(&prod);
|
|
}
|
|
|
|
production_generate_event_file(&prod, PRODUCTION_GIT_DIR, "tester stopped", prod.tester.rpi_info.serial_number);
|
|
cariboulite_production_app_close(&prod);
|
|
cariboulite_deinit_system_production(&cariboulite_sys);
|
|
|
|
return 0;
|
|
}
|