Factory test and a couple of bugfixes

This commit is contained in:
Renze Nicolai 2022-06-03 01:30:50 +02:00
parent 1412a86108
commit cf5f513f10
13 changed files with 422 additions and 122 deletions

View file

@ -20,6 +20,8 @@ idf_component_register(
"menus/wifi.c" "menus/wifi.c"
"uninstall.c" "uninstall.c"
"file_browser.c" "file_browser.c"
"test_common.c"
"factory_test.c"
INCLUDE_DIRS "." INCLUDE_DIRS "."
"include" "include"
"menus" "menus"

View file

@ -72,15 +72,24 @@ void appfs_store_app(pax_buf_t* pax_buffer, ILI9341* ili9341, char* path, char*
res = appfsCreateFile(label, app_size, &handle); res = appfsCreateFile(label, app_size, &handle);
if (res != ESP_OK) { if (res != ESP_OK) {
display_boot_screen(pax_buffer, ili9341, "Failed to create on AppFS"); display_boot_screen(pax_buffer, ili9341, "Failed to create file");
ESP_LOGE(TAG, "Failed to create file on AppFS (%d)", res); ESP_LOGE(TAG, "Failed to create file on AppFS (%d)", res);
vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelay(100 / portTICK_PERIOD_MS);
free(app); free(app);
return; return;
} }
int roundedSize=(app_size+(SPI_FLASH_MMU_PAGE_SIZE-1))&(~(SPI_FLASH_MMU_PAGE_SIZE-1));
res = appfsErase(handle, 0, roundedSize);
if (res != ESP_OK) {
display_boot_screen(pax_buffer, ili9341, "Failed to erase file");
ESP_LOGE(TAG, "Failed to erase file on AppFS (%d)", res);
vTaskDelay(100 / portTICK_PERIOD_MS);
free(app);
return;
}
res = appfsWrite(handle, 0, app, app_size); res = appfsWrite(handle, 0, app, app_size);
if (res != ESP_OK) { if (res != ESP_OK) {
display_boot_screen(pax_buffer, ili9341, "Failed to write to AppFS"); display_boot_screen(pax_buffer, ili9341, "Failed to write file");
ESP_LOGE(TAG, "Failed to write to file on AppFS (%d)", res); ESP_LOGE(TAG, "Failed to write to file on AppFS (%d)", res);
vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelay(100 / portTICK_PERIOD_MS);
free(app); free(app);

174
main/factory_test.c Normal file
View file

@ -0,0 +1,174 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include <driver/gpio.h>
#include "hardware.h"
#include "ili9341.h"
#include "ice40.h"
#include "rp2040.h"
#include "fpga_test.h"
#include "pax_gfx.h"
#include "test_common.h"
#include "settings.h"
#include "ws2812.h"
#include "audio.h"
static const char *TAG = "factory";
/* Test routines */
bool test_rp2040_init(uint32_t* rc) {
esp_err_t res = bsp_rp2040_init();
*rc = (uint32_t) res;
return (res == ESP_OK);
}
bool test_ice40_init(uint32_t* rc) {
esp_err_t res = bsp_ice40_init();
*rc = (uint32_t) res;
return (res == ESP_OK);
}
bool test_bno055_init(uint32_t* rc) {
esp_err_t res = bsp_bno055_init();
*rc = (uint32_t) res;
return (res == ESP_OK);
}
bool test_bme680_init(uint32_t* rc) {
esp_err_t res = bsp_bme680_init();
*rc = (uint32_t) res;
return (res == ESP_OK);
}
bool test_stuck_buttons(uint32_t* rc) {
RP2040* rp2040 = get_rp2040();
uint16_t state;
esp_err_t res = rp2040_read_buttons(rp2040, &state);
if (res != ESP_OK) {
*rc = 0xFFFFFFFF;
return false;
}
state &= ~(1 << RP2040_INPUT_FPGA_CDONE); // Ignore FPGA CDONE
*rc = state;
return (state == 0x0000);
}
bool test_sd_power(uint32_t* rc) {
*rc = 0x00000000;
// Init all GPIO pins for SD card and LED
if (gpio_reset_pin(GPIO_SD_PWR) != ESP_OK) return false;
if (gpio_set_direction(GPIO_SD_PWR, GPIO_MODE_INPUT) != ESP_OK) return false;
if (gpio_reset_pin(GPIO_SD_CMD) != ESP_OK) return false;
if (gpio_set_direction(GPIO_SD_CMD, GPIO_MODE_INPUT) != ESP_OK) return false;
if (gpio_reset_pin(GPIO_SD_CLK) != ESP_OK) return false;
if (gpio_set_direction(GPIO_SD_CLK, GPIO_MODE_INPUT) != ESP_OK) return false;
if (gpio_reset_pin(GPIO_SD_D0) != ESP_OK) return false;
if (gpio_set_direction(GPIO_SD_D0, GPIO_MODE_INPUT) != ESP_OK) return false;
if (gpio_reset_pin(GPIO_LED_DATA) != ESP_OK) return false;
if (gpio_set_direction(GPIO_LED_DATA, GPIO_MODE_INPUT) != ESP_OK) return false;
if (gpio_get_level(GPIO_SD_PWR)) {*rc = 0x01; return false;} // Check that power enable is pulled low
if (gpio_set_direction(GPIO_SD_PWR, GPIO_MODE_OUTPUT) != ESP_OK) return false;
if (gpio_set_level(GPIO_SD_PWR, 1) != ESP_OK) return false;
vTaskDelay(10 / portTICK_PERIOD_MS);
// SD pins should be pulled high
if (!gpio_get_level(GPIO_SD_CMD)) {*rc = 0x02; return false;}
if (!gpio_get_level(GPIO_SD_CLK)) {*rc = 0x04; return false;}
if (!gpio_get_level(GPIO_SD_D0)) {*rc = 0x08; return false;}
return true;
}
bool run_basic_tests(pax_buf_t* pax_buffer, ILI9341* ili9341) {
const pax_font_t *font;
int line = 0;
bool ok = true;
/* Screen init */
font = pax_get_font("sky mono");
pax_noclip(pax_buffer);
pax_background(pax_buffer, 0x8060f0);
ili9341_write(ili9341, pax_buffer->buf);
/* Run mandatory tests */
RUN_TEST_MANDATORY("RP2040", test_rp2040_init);
RUN_TEST_MANDATORY("ICE40", test_ice40_init);
RUN_TEST_MANDATORY("BNO055", test_bno055_init);
RUN_TEST_MANDATORY("BME680", test_bme680_init);
RUN_TEST_MANDATORY("STUCK BUTTONS", test_stuck_buttons);
RUN_TEST_MANDATORY("SD/LED POWER", test_sd_power);
error:
/* Fail result on screen */
if (!ok) pax_draw_text(pax_buffer, 0xffff0000, font, 36, 0, 20*line, "FAIL");
ili9341_write(ili9341, pax_buffer->buf);
return ok;
}
const uint8_t led_green[15] = {50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0};
const uint8_t led_red[15] = {0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0};
const uint8_t led_blue[15] = {0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50};
void factory_test(pax_buf_t* pax_buffer, ILI9341* ili9341) {
uint8_t factory_test_done = nvs_get_u8_default("system", "factory_test", 0);
if (!factory_test_done) {
bool result;
ESP_LOGI(TAG, "Factory test start");
result = run_basic_tests(pax_buffer, ili9341);
gpio_set_direction(GPIO_SD_PWR, GPIO_MODE_OUTPUT);
gpio_set_level(GPIO_SD_PWR, 1);
ws2812_init(GPIO_LED_DATA);
if (result) {
ws2812_send_data(led_blue, sizeof(led_blue));
} else {
ws2812_send_data(led_red, sizeof(led_red));
}
if (!result) goto test_end;
RP2040* rp2040 = get_rp2040();
result = run_fpga_tests(rp2040->queue, pax_buffer, ili9341);
if (!result) {
ws2812_send_data(led_red, sizeof(led_red));
goto test_end;
}
ws2812_send_data(led_green, sizeof(led_green));
// Wait for the operator to unplug the badge
test_end:
if (result) {
esp_err_t res = nvs_set_u8_fixed("system", "factory_test", 1);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Failed to store test result %d\n", res);
result = false;
ws2812_send_data(led_red, sizeof(led_red));
pax_noclip(pax_buffer);
pax_background(pax_buffer, 0xa85a32);
ili9341_write(ili9341, pax_buffer->buf);
}
}
while (true) {
if (result) play_bootsound();
vTaskDelay(3000 / portTICK_PERIOD_MS);
}
}
}

View file

@ -15,6 +15,7 @@
#include "menu.h" #include "menu.h"
#include "rp2040.h" #include "rp2040.h"
#include "appfs_wrapper.h" #include "appfs_wrapper.h"
#include "bootscreen.h"
static const char *TAG = "file browser"; static const char *TAG = "file browser";
@ -104,20 +105,24 @@ void find_parent_dir(char* path, char* parent) {
} }
void file_browser(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, const char* initial_path) { void file_browser(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341, const char* initial_path) {
display_boot_screen(pax_buffer, ili9341, "Please wait...");
char path[512] = {0}; char path[512] = {0};
strncpy(path, initial_path, sizeof(path)); strncpy(path, initial_path, sizeof(path));
while (true) { while (true) {
menu_t* menu = menu_alloc(path); menu_t* menu = menu_alloc(path);
DIR* dir = opendir(path); DIR* dir = opendir(path);
if (dir == NULL) { if (dir == NULL) {
ESP_LOGE(TAG, "Failed to open directory %s", path); if (path[0] != 0) {
ESP_LOGE(TAG, "Failed to open directory %s", path);
display_boot_screen(pax_buffer, ili9341, "Failed to open directory");
vTaskDelay(200 / portTICK_PERIOD_MS);
}
return; return;
} }
struct dirent *ent; struct dirent *ent;
file_browser_menu_args_t* pd_args = malloc(sizeof(file_browser_menu_args_t)); file_browser_menu_args_t* pd_args = malloc(sizeof(file_browser_menu_args_t));
pd_args->type = 'd'; pd_args->type = 'd';
find_parent_dir(path, pd_args->path); find_parent_dir(path, pd_args->path);
printf("Parent dir: %s\n", pd_args->path);
menu_insert_item(menu, "../", NULL, pd_args, -1); menu_insert_item(menu, "../", NULL, pd_args, -1);
while ((ent = readdir(dir)) != NULL) { while ((ent = readdir(dir)) != NULL) {
@ -134,8 +139,6 @@ void file_browser(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9
args->type = 'd'; args->type = 'd';
} }
printf("%c %s %s\r\n", args->type, ent->d_name, args->path);
snprintf(args->label, sizeof(args->label), "%s%s", ent->d_name, (args->type == 'd') ? "/" : ""); snprintf(args->label, sizeof(args->label), "%s%s", ent->d_name, (args->type == 'd') ? "/" : "");
menu_insert_item(menu, args->label, NULL, args, -1); menu_insert_item(menu, args->label, NULL, args, -1);
} }
@ -186,7 +189,8 @@ void file_browser(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9
if (renderbg) { if (renderbg) {
pax_background(pax_buffer, 0xFFFFFF); pax_background(pax_buffer, 0xFFFFFF);
pax_noclip(pax_buffer); pax_noclip(pax_buffer);
pax_draw_text(pax_buffer, 0xFF000000, NULL, 18, 5, 240 - 19, "[A] install [B] back"); const pax_font_t *font = pax_get_font("saira regular");
pax_draw_text(pax_buffer, 0xFF000000, font, 18, 5, 240 - 19, "[A] install [B] back");
renderbg = false; renderbg = false;
} }

View file

@ -11,6 +11,7 @@
#include "rp2040.h" #include "rp2040.h"
#include "fpga_test.h" #include "fpga_test.h"
#include "pax_gfx.h" #include "pax_gfx.h"
#include "test_common.h"
extern const uint8_t fpga_selftest_bin_start[] asm("_binary_fpga_selftest_bin_start"); extern const uint8_t fpga_selftest_bin_start[] asm("_binary_fpga_selftest_bin_start");
extern const uint8_t fpga_selftest_bin_end[] asm("_binary_fpga_selftest_bin_end"); extern const uint8_t fpga_selftest_bin_end[] asm("_binary_fpga_selftest_bin_end");
@ -114,7 +115,8 @@ static bool soc_message(ICE40* ice40, uint8_t cmd, uint32_t param, uint32_t *res
/* Test routines */ /* Test routines */
static bool test_bitstream_load(ICE40* ice40, uint32_t *rc) { static bool test_bitstream_load(uint32_t *rc) {
ICE40* ice40 = get_ice40();
esp_err_t res; esp_err_t res;
res = ice40_load_bitstream(ice40, fpga_selftest_bin_start, fpga_selftest_bin_end - fpga_selftest_bin_start); res = ice40_load_bitstream(ice40, fpga_selftest_bin_start, fpga_selftest_bin_end - fpga_selftest_bin_start);
@ -127,7 +129,7 @@ static bool test_bitstream_load(ICE40* ice40, uint32_t *rc) {
return true; return true;
} }
static bool test_spi_loopback_one(ICE40* ice40) { static bool _test_spi_loopback_one(ICE40* ice40) {
esp_err_t res; esp_err_t res;
uint8_t data_tx[257]; uint8_t data_tx[257];
uint8_t data_rx[258]; uint8_t data_rx[258];
@ -219,12 +221,14 @@ static bool test_spi_loopback_one(ICE40* ice40) {
return true; return true;
} }
static bool test_spi_loopback(ICE40* ice40, uint32_t *rc) { static bool test_spi_loopback(uint32_t *rc) {
int i; int i;
ICE40* ice40 = get_ice40();
/* Run test 256 times */ /* Run test 256 times */
for (i=0; i<256; i++) { for (i=0; i<256; i++) {
if (!test_spi_loopback_one(ice40)) if (!_test_spi_loopback_one(ice40))
break; break;
} }
@ -239,7 +243,9 @@ static bool test_spi_loopback(ICE40* ice40, uint32_t *rc) {
return true; return true;
} }
static bool test_soc_loopback(ICE40 *ice40, uint32_t *rc) { static bool test_soc_loopback(uint32_t *rc) {
ICE40* ice40 = get_ice40();
/* Execute command */ /* Execute command */
if (!soc_message(ice40, SOC_CMD_PING, SOC_CMD_PING_PARAM, rc, 0)) { if (!soc_message(ice40, SOC_CMD_PING, SOC_CMD_PING_PARAM, rc, 0)) {
*rc = -1; *rc = -1;
@ -255,7 +261,9 @@ static bool test_soc_loopback(ICE40 *ice40, uint32_t *rc) {
return true; return true;
} }
static bool test_uart_loopback(ICE40* ice40, uint32_t *rc) { static bool test_uart_loopback(uint32_t *rc) {
ICE40* ice40 = get_ice40();
/* Enable loopback mode of RP2040 */ /* Enable loopback mode of RP2040 */
rp2040_set_fpga_loopback(get_rp2040(), true, true); rp2040_set_fpga_loopback(get_rp2040(), true, true);
vTaskDelay(pdMS_TO_TICKS(10)); vTaskDelay(pdMS_TO_TICKS(10));
@ -273,7 +281,9 @@ static bool test_uart_loopback(ICE40* ice40, uint32_t *rc) {
return *rc == SOC_RESP_OK; return *rc == SOC_RESP_OK;
} }
static bool test_psram(ICE40* ice40, uint32_t *rc) { static bool test_psram(uint32_t *rc) {
ICE40* ice40 = get_ice40();
/* Execute command */ /* Execute command */
if (!soc_message(ice40, SOC_CMD_PSRAM_TEST, 0, rc, pdMS_TO_TICKS(1000))) { if (!soc_message(ice40, SOC_CMD_PSRAM_TEST, 0, rc, pdMS_TO_TICKS(1000))) {
*rc = -1; *rc = -1;
@ -284,7 +294,9 @@ static bool test_psram(ICE40* ice40, uint32_t *rc) {
return *rc == SOC_RESP_OK; return *rc == SOC_RESP_OK;
} }
static bool test_irq_n(ICE40* ice40, uint32_t *rc) { static bool test_irq_n(uint32_t *rc) {
ICE40* ice40 = get_ice40();
esp_err_t res; esp_err_t res;
/* Set pin as input */ /* Set pin as input */
@ -327,7 +339,8 @@ static bool test_irq_n(ICE40* ice40, uint32_t *rc) {
return true; return true;
} }
static bool test_lcd_mode(ICE40* ice40, uint32_t *rc) { static bool test_lcd_mode(uint32_t *rc) {
ICE40* ice40 = get_ice40();
esp_err_t res; esp_err_t res;
bool ok; bool ok;
@ -371,7 +384,8 @@ static bool test_lcd_mode(ICE40* ice40, uint32_t *rc) {
return ok; return ok;
} }
static bool test_pmod_open(ICE40* ice40, uint32_t *rc) { static bool test_pmod_open(uint32_t *rc) {
ICE40* ice40 = get_ice40();
/* Execute command */ /* Execute command */
if (!soc_message(ice40, SOC_CMD_PMOD_OPEN_TEST, 0, rc, 0)) { if (!soc_message(ice40, SOC_CMD_PMOD_OPEN_TEST, 0, rc, 0)) {
*rc = -1; *rc = -1;
@ -382,7 +396,9 @@ static bool test_pmod_open(ICE40* ice40, uint32_t *rc) {
return *rc == SOC_RESP_OK; return *rc == SOC_RESP_OK;
} }
static bool test_pmod_plug(ICE40* ice40, uint32_t *rc) { static bool test_pmod_plug(uint32_t *rc) {
ICE40* ice40 = get_ice40();
/* Execute command */ /* Execute command */
if (!soc_message(ice40, SOC_CMD_PMOD_PLUG_TEST, 0, rc, 0)) { if (!soc_message(ice40, SOC_CMD_PMOD_PLUG_TEST, 0, rc, 0)) {
*rc = -1; *rc = -1;
@ -393,7 +409,9 @@ static bool test_pmod_plug(ICE40* ice40, uint32_t *rc) {
return *rc == SOC_RESP_OK; return *rc == SOC_RESP_OK;
} }
static bool test_lcd_init(ICE40* ice40, uint32_t *rc) { static bool test_lcd_init(uint32_t *rc) {
ICE40* ice40 = get_ice40();
/* Execute command */ /* Execute command */
if (!soc_message(ice40, SOC_CMD_LCD_INIT_TEST, 0, rc, 0)) { if (!soc_message(ice40, SOC_CMD_LCD_INIT_TEST, 0, rc, 0)) {
*rc = -1; *rc = -1;
@ -404,83 +422,8 @@ static bool test_lcd_init(ICE40* ice40, uint32_t *rc) {
return *rc == SOC_RESP_OK; return *rc == SOC_RESP_OK;
} }
bool run_fpga_tests(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) {
typedef bool (*test_fn)(ICE40 *ice40, uint32_t *rc); ICE40* ice40 = get_ice40();
static bool wait_button(xQueueHandle buttonQueue) {
rp2040_input_message_t buttonMessage = {0};
while (1) {
if (xQueueReceive(buttonQueue, &buttonMessage, 0) == pdTRUE) {
if (buttonMessage.state) {
switch(buttonMessage.input) {
case RP2040_INPUT_BUTTON_HOME:
case RP2040_INPUT_BUTTON_MENU:
case RP2040_INPUT_BUTTON_BACK:
return false;
case RP2040_INPUT_BUTTON_ACCEPT:
return true;
default:
break;
}
}
} else {
vTaskDelay(pdMS_TO_TICKS(10));
}
}
}
static bool run_test(ICE40* ice40, pax_buf_t* pax_buffer, const pax_font_t *font, ILI9341* ili9341, int line,
const char *test_name, test_fn fn) {
bool rv;
uint32_t rc;
/* Test name */
pax_draw_text(pax_buffer, 0xffffffff, font, 18, 0, 20*line, test_name);
if (ili9341)
ili9341_write(ili9341, pax_buffer->buf);
/* Run the test */
rv = fn(ice40, &rc);
/* Display result */
if (!rv) {
/* Error */
char buf[10];
snprintf(buf, sizeof(buf), "%08x", rc);
pax_draw_text(pax_buffer, 0xffff0000, font, 18, 200, 20*line, buf);
} else {
/* OK ! */
pax_draw_text(pax_buffer, 0xff00ff00, font, 18, 200, 20*line, " OK");
}
if (ili9341)
ili9341_write(ili9341, pax_buffer->buf);
/* Pass through the 'OK' status */
return rv;
}
#define RUN_TEST(name, fn) do {\
ok &= run_test(ice40, pax_buffer, font, ili9341, line++, name, fn); \
} while (0)
#define RUN_TEST_MANDATORY(name, fn) do {\
if (!run_test(ice40, pax_buffer, font, ili9341, line++, name, fn)) { \
pax_draw_text(pax_buffer, 0xffff0000, font, 18, 0, 20*line, "Aborted"); \
ili9341_write(ili9341, pax_buffer->buf); \
ok = false; \
goto error; \
} \
} while (0)
#define RUN_TEST_BLIND(name, fn) do {\
ok &= run_test(ice40, pax_buffer, font, NULL, line++, name, fn); \
} while (0)
bool run_fpga_tests(xQueueHandle buttonQueue, ICE40* ice40, pax_buf_t* pax_buffer, ILI9341* ili9341) {
const pax_font_t *font; const pax_font_t *font;
int line = 0; int line = 0;
bool ok = true; bool ok = true;
@ -534,9 +477,7 @@ bool run_fpga_tests(xQueueHandle buttonQueue, ICE40* ice40, pax_buf_t* pax_buffe
soc_message(ice40, SOC_CMD_LCD_RGB_CYCLE_SET, 1, NULL, 0); soc_message(ice40, SOC_CMD_LCD_RGB_CYCLE_SET, 1, NULL, 0);
/* Wait for button */ /* Wait for button */
if (!wait_button(buttonQueue)) { RUN_TEST("LCD control", test_wait_for_response);
ok = false;
}
/* Stop LCD / RGB cycling */ /* Stop LCD / RGB cycling */
soc_message(ice40, SOC_CMD_LCD_RGB_CYCLE_SET, 0, NULL, 0); soc_message(ice40, SOC_CMD_LCD_RGB_CYCLE_SET, 0, NULL, 0);
@ -550,9 +491,9 @@ error:
/* Pass / Fail result on screen */ /* Pass / Fail result on screen */
if (ok) if (ok)
pax_draw_text(pax_buffer, 0xff00ff00, font, 36, 0, 20*line, "FPGA PASS"); pax_draw_text(pax_buffer, 0xff00ff00, font, 36, 0, 20*line, "PASS");
else else
pax_draw_text(pax_buffer, 0xffff0000, font, 36, 0, 20*line, "FPGA FAIL"); pax_draw_text(pax_buffer, 0xffff0000, font, 36, 0, 20*line, "FAIL");
ili9341_write(ili9341, pax_buffer->buf); ili9341_write(ili9341, pax_buffer->buf);
@ -562,7 +503,7 @@ error:
return ok; return ok;
} }
void fpga_test(xQueueHandle buttonQueue, ICE40* ice40, pax_buf_t* pax_buffer, ILI9341* ili9341) { void fpga_test(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341) {
run_fpga_tests(buttonQueue, ice40, pax_buffer, ili9341); run_fpga_tests(buttonQueue, pax_buffer, ili9341);
wait_button(buttonQueue); test_wait_for_response(NULL);
} }

View file

@ -0,0 +1,10 @@
#pragma once
#include <sdkconfig.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
#include "ili9341.h"
#include "pax_gfx.h"
void factory_test(pax_buf_t* pax_buffer, ILI9341* ili9341);

View file

@ -10,4 +10,5 @@
#include "ice40.h" #include "ice40.h"
#include "pax_gfx.h" #include "pax_gfx.h"
void fpga_test(xQueueHandle buttonQueue, ICE40* ice40, pax_buf_t* pax_buffer, ILI9341* ili9341); void fpga_test(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341);
bool run_fpga_tests(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341);

View file

@ -6,3 +6,6 @@
#include "ice40.h" #include "ice40.h"
esp_err_t nvs_init(); esp_err_t nvs_init();
esp_err_t nvs_get_str_fixed(const char* nvs_namespace, const char* key, char* target, size_t target_size, size_t* size);
uint8_t nvs_get_u8_default(const char* nvs_namespace, const char* key, uint8_t default_value);
esp_err_t nvs_set_u8_fixed(const char* nvs_namespace, const char* key, uint8_t value);

View file

@ -0,0 +1,28 @@
#pragma once
#include <stdio.h>
#include <string.h>
#include "ili9341.h"
#include "pax_gfx.h"
typedef bool (*test_fn)(uint32_t *rc);
bool test_wait_for_response(uint32_t *rc);
bool run_test(pax_buf_t* pax_buffer, const pax_font_t *font, ILI9341* ili9341, int line, const char *test_name, test_fn fn);
#define RUN_TEST(name, fn) do {\
ok &= run_test(pax_buffer, font, ili9341, line++, name, fn); \
} while (0)
#define RUN_TEST_MANDATORY(name, fn) do {\
if (!run_test(pax_buffer, font, ili9341, line++, name, fn)) { \
pax_draw_text(pax_buffer, 0xffff0000, font, 18, 0, 20*line, "Aborted"); \
ili9341_write(ili9341, pax_buffer->buf); \
ok = false; \
goto error; \
} \
} while (0)
#define RUN_TEST_BLIND(name, fn) do {\
ok &= run_test(pax_buffer, font, NULL, line++, name, fn); \
} while (0)

View file

@ -48,6 +48,8 @@
#include "menus/start.h" #include "menus/start.h"
#include "factory_test.h"
extern const uint8_t wallpaper_png_start[] asm("_binary_wallpaper_png_start"); extern const uint8_t wallpaper_png_start[] asm("_binary_wallpaper_png_start");
extern const uint8_t wallpaper_png_end[] asm("_binary_wallpaper_png_end"); extern const uint8_t wallpaper_png_end[] asm("_binary_wallpaper_png_end");
@ -102,10 +104,20 @@ void app_main(void) {
esp_restart(); esp_restart();
} }
display_boot_screen(pax_buffer, ili9341, "Starting..."); /* Start NVS */
res = nvs_init();
if (res != ESP_OK) {
ESP_LOGE(TAG, "NVS init failed: %d", res);
display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "NVS failed to initialize", "Flash may be corrupted", NULL);
esp_restart();
}
audio_init(); audio_init();
factory_test(pax_buffer, ili9341);
display_boot_screen(pax_buffer, ili9341, "Starting...");
if (bsp_rp2040_init() != ESP_OK) { if (bsp_rp2040_init() != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize the RP2040 co-processor"); ESP_LOGE(TAG, "Failed to initialize the RP2040 co-processor");
display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "RP2040 co-processor error", NULL, NULL); display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "RP2040 co-processor error", NULL, NULL);
@ -179,16 +191,6 @@ void app_main(void) {
esp_restart(); esp_restart();
} }
//display_boot_screen(pax_buffer, ili9341, "Initializing NVS...");
/* Start NVS */
res = nvs_init();
if (res != ESP_OK) {
ESP_LOGE(TAG, "NVS init failed: %d", res);
display_fatal_error(pax_buffer, ili9341, "Failed to initialize", "NVS failed to initialize", "Flash may be corrupted", NULL);
esp_restart();
}
//display_boot_screen(pax_buffer, ili9341, "Initializing filesystem..."); //display_boot_screen(pax_buffer, ili9341, "Initializing filesystem...");
/* Start internal filesystem */ /* Start internal filesystem */
@ -217,12 +219,14 @@ void app_main(void) {
bool sdcard_ready = (res == ESP_OK); bool sdcard_ready = (res == ESP_OK);
if (sdcard_ready) { if (sdcard_ready) {
ESP_LOGI(TAG, "SD card filesystem mounted"); ESP_LOGI(TAG, "SD card filesystem mounted");
}
/* Start LEDs */ /* LED power is on: start LED driver and turn LEDs off */
ws2812_init(GPIO_LED_DATA); ws2812_init(GPIO_LED_DATA);
uint8_t ledBuffer[15] = {50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0, 50, 0, 0}; const uint8_t led_off[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
ws2812_send_data(ledBuffer, sizeof(ledBuffer)); ws2812_send_data(led_off, sizeof(led_off));
} else {
gpio_set_level(GPIO_SD_PWR, 0); // Disable power to LEDs and SD card
}
/* Start WiFi */ /* Start WiFi */
wifi_init(); wifi_init();

View file

@ -95,7 +95,7 @@ void menu_dev(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili9341)
if (action == ACTION_FPGA_DL) { if (action == ACTION_FPGA_DL) {
fpga_download(buttonQueue, get_ice40(), pax_buffer, ili9341); fpga_download(buttonQueue, get_ice40(), pax_buffer, ili9341);
} else if (action == ACTION_FPGA_TEST) { } else if (action == ACTION_FPGA_TEST) {
fpga_test(buttonQueue, get_ice40(), pax_buffer, ili9341); fpga_test(buttonQueue, pax_buffer, ili9341);
} else if (action == ACTION_FILE_BROWSER) { } else if (action == ACTION_FILE_BROWSER) {
file_browser(buttonQueue, pax_buffer, ili9341, "/sd"); file_browser(buttonQueue, pax_buffer, ili9341, "/sd");
} else if (action == ACTION_FILE_BROWSER_INT) { } else if (action == ACTION_FILE_BROWSER_INT) {

View file

@ -23,3 +23,62 @@ esp_err_t nvs_init() {
} }
return ESP_OK; return ESP_OK;
} }
esp_err_t nvs_get_str_fixed(const char* nvs_namespace, const char* key, char* target, size_t target_size, size_t* size) {
nvs_handle_t handle;
esp_err_t res;
res = nvs_open(nvs_namespace, NVS_READWRITE, &handle);
if (res != ESP_OK) return res;
size_t required_size;
res = nvs_get_str(handle, key, NULL, &required_size);
if (res != ESP_OK) {
nvs_close(handle);
return res;
}
if (required_size > target_size) {
nvs_close(handle);
return ESP_FAIL;
}
res = nvs_get_str(handle, key, target, &required_size);
if (size != NULL) *size = required_size;
nvs_close(handle);
return res;
}
uint8_t nvs_get_u8_default(const char* nvs_namespace, const char* key, uint8_t default_value) {
nvs_handle_t handle;
esp_err_t res;
res = nvs_open(nvs_namespace, NVS_READWRITE, &handle);
if (res != ESP_OK) return default_value;
uint8_t target;
res = nvs_get_u8(handle, key, &target);
if (res != ESP_OK) {
nvs_close(handle);
return default_value;
}
nvs_close(handle);
return target;
}
esp_err_t nvs_set_u8_fixed(const char* nvs_namespace, const char* key, uint8_t value) {
nvs_handle_t handle;
esp_err_t res;
res = nvs_open(nvs_namespace, NVS_READWRITE, &handle);
if (res != ESP_OK) return res;
res = nvs_set_u8(handle, key, value);
nvs_close(handle);
return res;
}

65
main/test_common.c Normal file
View file

@ -0,0 +1,65 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include <driver/gpio.h>
#include "hardware.h"
#include "ili9341.h"
#include "ice40.h"
#include "rp2040.h"
#include "pax_gfx.h"
typedef bool (*test_fn)(uint32_t *rc);
bool test_wait_for_response(uint32_t *rc) {
RP2040* rp2040 = get_rp2040();
rp2040_input_message_t button_message = {0};
if (rc != NULL) *rc = 0;
while (1) {
if (xQueueReceive(rp2040->queue, &button_message, portMAX_DELAY) == pdTRUE) {
if (button_message.state) {
switch(button_message.input) {
case RP2040_INPUT_BUTTON_HOME:
case RP2040_INPUT_BUTTON_MENU:
case RP2040_INPUT_BUTTON_BACK:
return false;
case RP2040_INPUT_BUTTON_ACCEPT:
if (rc != NULL) *rc = 1;
return true;
default:
break;
}
}
}
}
}
bool run_test(pax_buf_t* pax_buffer, const pax_font_t *font, ILI9341* ili9341, int line, const char *test_name, test_fn fn) {
bool test_result;
uint32_t rc;
/* Test name */
pax_draw_text(pax_buffer, 0xffffffff, font, 18, 0, 20*line, test_name);
if (ili9341) ili9341_write(ili9341, pax_buffer->buf);
/* Run the test */
test_result = fn(&rc);
/* Display result */
if (!test_result) {
/* Error */
char buf[10];
snprintf(buf, sizeof(buf), "%08x", rc);
pax_draw_text(pax_buffer, 0xffff0000, font, 18, 200, 20*line, buf);
} else {
/* OK ! */
pax_draw_text(pax_buffer, 0xff00ff00, font, 18, 200, 20*line, " OK");
}
if (ili9341) ili9341_write(ili9341, pax_buffer->buf);
/* Pass through the 'OK' status */
return test_result;
}