Cpld autodetect & boot splash screen (#1888)

* added delayed error message when hackrf cpld initialization fails

* refactoring

* implemented portapack cpld autodetection

* refactoring

* fixed valid config range

* added lcd fast setup

* added boot splash screen

* added one frame delay to remove flickering

* fixed config persistence
This commit is contained in:
Bernd Herzog
2024-02-14 23:17:33 +01:00
committed by GitHub
parent 8725b01995
commit c3add0ce84
7 changed files with 357 additions and 137 deletions

View File

@@ -23,6 +23,7 @@
#include "hackrf_gpio.hpp"
#include "portapack_hal.hpp"
#include "portapack.hpp"
#include "jtag_target_gpio.hpp"
#include "cpld_max5.hpp"
@@ -30,11 +31,15 @@
#include "portapack_cpld_data.hpp"
#include "hackrf_cpld_data.hpp"
#include "crc.hpp"
#define REV_20150901_CHECKSUM 0xE0EF80FB
#define REV_20170522_CHECKSUM 0xD1BEB722
namespace portapack {
namespace cpld {
CpldUpdateStatus update_if_necessary(
const Config config) {
CpldUpdateStatus update_if_necessary(const Config config) {
jtag::GPIOTarget target{
portapack::gpio_cpld_tck,
portapack::gpio_cpld_tms,
@@ -87,6 +92,121 @@ CpldUpdateStatus update_if_necessary(
return ok ? CpldUpdateStatus::Success : CpldUpdateStatus::Program_failed;
}
static CpldUpdateStatus enter_maintenance_mode(CPLD& cpld) {
/* Unknown state */
cpld.reset();
cpld.run_test_idle();
/* Run-Test/Idle */
if (!cpld.idcode_ok()) {
return CpldUpdateStatus::Idcode_check_failed;
}
cpld.sample();
cpld.bypass();
cpld.enable();
/* If silicon ID doesn't match, there's a serious problem. Leave CPLD
* in passive state.
*/
if (!cpld.silicon_id_ok()) {
return CpldUpdateStatus::Silicon_id_check_failed;
}
return CpldUpdateStatus::Success;
}
static void exit_maintenance_mode(CPLD& cpld) {
cpld.disable();
cpld.bypass();
/* Initiate SRAM reload from flash we just programmed. */
cpld.sample();
cpld.clamp();
cpld.disable();
}
static uint32_t get_firmware_crc(CPLD& cpld) {
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
cpld.prepare_read(0x0000);
for (size_t i = 0; i < 3328; i++) {
uint16_t data = cpld.read();
crc.process_byte((data >> 0) & 0xff);
crc.process_byte((data >> 8) & 0xff);
crc.process_byte((data >> 16) & 0xff);
crc.process_byte((data >> 24) & 0xff);
}
cpld.prepare_read(0x0001);
for (size_t i = 0; i < 512; i++) {
uint16_t data = cpld.read();
crc.process_byte((data >> 0) & 0xff);
crc.process_byte((data >> 8) & 0xff);
crc.process_byte((data >> 16) & 0xff);
crc.process_byte((data >> 24) & 0xff);
}
return crc.checksum();
}
CpldUpdateStatus update_autodetect(const Config config_rev_20150901, const Config config_rev_20170522) {
jtag::GPIOTarget target{
portapack::gpio_cpld_tck,
portapack::gpio_cpld_tms,
portapack::gpio_cpld_tdi,
portapack::gpio_cpld_tdo};
jtag::JTAG jtag{target};
CPLD cpld{jtag};
if (portapack::display.read_display_status())
return CpldUpdateStatus::Success; // LCD is ready
CpldUpdateStatus result = enter_maintenance_mode(cpld);
if (result != CpldUpdateStatus::Success)
return result;
uint32_t checksum = get_firmware_crc(cpld);
if (checksum == REV_20170522_CHECKSUM) {
// H2 firmware present
if (!cpld.program(config_rev_20150901.block_0, config_rev_20150901.block_1))
return CpldUpdateStatus::Program_failed;
} else if (checksum == REV_20150901_CHECKSUM) {
// H1 firmware present
if (!cpld.program(config_rev_20170522.block_0, config_rev_20170522.block_1))
return CpldUpdateStatus::Program_failed;
} else {
// no firmware present
if (!cpld.program(config_rev_20150901.block_0, config_rev_20150901.block_1))
return CpldUpdateStatus::Program_failed;
}
exit_maintenance_mode(cpld);
if (portapack::display.read_display_status())
return CpldUpdateStatus::Success; // LCD is ready
if (checksum != REV_20150901_CHECKSUM && checksum != REV_20170522_CHECKSUM) {
// try the other one
CpldUpdateStatus result = enter_maintenance_mode(cpld);
if (result != CpldUpdateStatus::Success)
return result;
if (!cpld.program(config_rev_20170522.block_0, config_rev_20170522.block_1))
return CpldUpdateStatus::Program_failed;
exit_maintenance_mode(cpld);
if (portapack::display.read_display_status())
return CpldUpdateStatus::Success; // LCD is ready
}
return CpldUpdateStatus::Program_failed;
}
} /* namespace cpld */
} /* namespace portapack */

View File

@@ -34,8 +34,8 @@ enum class CpldUpdateStatus {
Program_failed = 3
};
CpldUpdateStatus update_if_necessary(
const Config config);
CpldUpdateStatus update_if_necessary(const Config config);
CpldUpdateStatus update_autodetect(const Config config_rev_20150901, const Config config_rev_20170522);
} /* namespace cpld */
} /* namespace portapack */

View File

@@ -84,6 +84,17 @@ void lcd_wake() {
lcd_display_on();
}
uint32_t lcd_read_display_status() {
io.lcd_data_write_command_and_data(0x09, {});
io.lcd_read_word();
uint32_t value2 = io.lcd_read_word();
uint32_t value3 = io.lcd_read_word();
uint32_t value4 = io.lcd_read_word();
uint32_t value5 = io.lcd_read_word();
return value5 + (value4 << 8) + (value3 << 16) + (value2 << 24);
}
void lcd_init() {
// LCDs are configured for IM[2:0] = 001
// 8080-I system, 16-bit parallel bus
@@ -260,6 +271,25 @@ void lcd_vertical_scrolling_start_address(
} // namespace
bool ILI9341::read_display_status() {
lcd_reset();
uint32_t display_status = lcd_read_display_status();
/* This tries to validate the display_status.
* The value could vary from device to device, so we are less specific here.
* 0xFFFFFEFF was seen when the display was not reachable
* 0x00610000 was seen when the display was reachable
*/
if (display_status > 0x0E000000ULL)
return false;
if (display_status < 0x00000100ULL)
return false;
return true;
}
void ILI9341::init() {
lcd_reset();
lcd_init();

View File

@@ -42,6 +42,8 @@ class ILI9341 {
ILI9341(ILI9341&&) = delete;
void operator=(const ILI9341&) = delete;
bool read_display_status();
void init();
void shutdown();

View File

@@ -430,13 +430,15 @@ void defaults() {
}
void init() {
const auto switches_state = get_switches_state();
const auto switches_state = swizzled_switches();
// ignore for valid check
auto config_mode_backup = config_mode_storage_direct();
set_config_mode_storage_direct(CONFIG_MODE_NORMAL_VALUE);
if (!(switches_state[(size_t)ui::KeyEvent::Left] && switches_state[(size_t)ui::KeyEvent::Right]) && backup_ram->is_valid()) {
if (!(((switches_state >> (size_t)ui::KeyEvent::Left & 1) == 1) &&
((switches_state >> (size_t)ui::KeyEvent::Right & 1) == 1)) &&
backup_ram->is_valid()) {
// Copy valid persistent data into cache.
cached_backup_ram = *backup_ram;