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
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 357 additions and 137 deletions

View file

@ -28,6 +28,7 @@
#include "hackrf_hal.hpp"
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
#include "jtag_target_gpio.hpp"
#include "clock_manager.hpp"
#include "event_m0.hpp"
@ -50,6 +51,7 @@ using asahi_kasei::ak4951::AK4951;
#include "file.hpp"
#include "sd_card.hpp"
#include "string_format.hpp"
#include "bitmap.hpp"
namespace portapack {
@ -174,50 +176,21 @@ constexpr I2CConfig i2c_config_fast_clock{
enum class PortaPackModel {
R1_20150901,
R2_20170522,
AGM,
AUTODETECT,
};
static bool save_config(int8_t value) {
persistent_memory::set_config_cpld(value);
if (sd_card::status() == sd_card::Status::Mounted) {
ensure_directory("/hardware");
File file;
auto sucess = file.create("/hardware/settings.txt");
if (!sucess.is_valid()) {
file.write_line(to_string_dec_uint(value));
}
}
portapack::persistent_memory::cache::persist();
return true;
}
int read_file(std::string name) {
std::string return_string = "";
File file;
auto success = file.open(name);
if (!success.is_valid()) {
char one_char[1];
for (size_t pointer = 0; pointer < file.size(); pointer++) {
file.seek(pointer);
file.read(one_char, 1);
return_string += one_char[0];
}
return std::stoi(return_string);
}
return -1;
}
static int load_config() {
static Optional<int> config_value;
if (!config_value.is_valid()) {
int8_t value = portapack::persistent_memory::config_cpld();
if ((value <= 0 || value >= 5) && sd_card::status() == sd_card::Status::Mounted) {
int data = read_file("/hardware/settings.txt");
if (data != -1) {
config_value = data;
}
} else {
config_value = value;
}
config_value = value;
}
return config_value.value();
}
@ -226,39 +199,49 @@ static PortaPackModel portapack_model() {
static Optional<PortaPackModel> model;
if (!model.is_valid()) {
const auto switches_state = get_switches_state();
// Only save config if no other multi key boot action is triggered (like pmem reset)
if (switches_state.count() == 1) {
if (switches_state[(size_t)ui::KeyEvent::Up]) {
jtag::GPIOTarget target{
portapack::gpio_cpld_tck,
portapack::gpio_cpld_tms,
portapack::gpio_cpld_tdi,
portapack::gpio_cpld_tdo};
jtag::JTAG jtag{target};
portapack::cpld::CPLD cpld{jtag};
cpld.reset();
cpld.run_test_idle();
uint32_t idcode = cpld.get_idcode();
if (idcode == 0x25610) {
model = PortaPackModel::AGM;
} else {
const auto switches_state = swizzled_switches();
// chDbgPanic(to_string_hex((uint32_t)switches_state.count(), 8).c_str());
// Only save config if no other multi key boot action is triggered (like pmem reset)
if (((switches_state >> (size_t)ui::KeyEvent::Up) & 1) == 1) {
save_config(1);
// model = PortaPackModel::R2_20170522; // Commented these out as they should be set down below anyway
} else if (switches_state[(size_t)ui::KeyEvent::Down]) {
} else if (((switches_state >> (size_t)ui::KeyEvent::Down) & 1) == 1) {
save_config(2);
// model = PortaPackModel::R1_20150901;
} else if (switches_state[(size_t)ui::KeyEvent::Left]) {
} else if (((switches_state >> (size_t)ui::KeyEvent::Left) & 1) == 1) {
save_config(3);
// model = PortaPackModel::R1_20150901;
} else if (switches_state[(size_t)ui::KeyEvent::Right]) {
} else if (((switches_state >> (size_t)ui::KeyEvent::Right) & 1) == 1) {
save_config(4);
// model = PortaPackModel::R2_20170522;
} else if (switches_state[(size_t)ui::KeyEvent::Select]) {
} else if (((switches_state >> (size_t)ui::KeyEvent::Select) & 1) == 1) {
save_config(0);
}
}
if (load_config() == 1) {
model = PortaPackModel::R2_20170522;
} else if (load_config() == 2) {
model = PortaPackModel::R1_20150901;
} else if (load_config() == 3) {
model = PortaPackModel::R1_20150901;
} else if (load_config() == 4) {
model = PortaPackModel::R2_20170522;
} else {
if (audio_codec_wm8731.detected()) {
model = PortaPackModel::R1_20150901; // H1R1
if (load_config() == 1) {
model = PortaPackModel::R2_20170522;
} else if (load_config() == 2) {
model = PortaPackModel::R1_20150901;
} else if (load_config() == 3) {
model = PortaPackModel::R1_20150901;
} else if (load_config() == 4) {
model = PortaPackModel::R2_20170522;
} else {
model = PortaPackModel::R2_20170522; // H1R2, H2, H2+
model = PortaPackModel::AUTODETECT;
}
}
}
@ -345,6 +328,129 @@ static void shutdown_base() {
clock_manager.shutdown();
}
static void set_cpu_clock_speed() {
/* Incantation from LPC43xx UM10503 section 12.2.1.1, to bring the M4
* core clock speed to the 110 - 204MHz range.
*/
/* Step into the 90-110MHz M4 clock range */
/* OG:
* Fclkin = 40M
* /N=2 = 20M = PFDin
* Fcco = PFDin * (M=10) = 200M
* r9:
* Fclkin = 10M
* /N=1 = 10M = PFDin
* Fcco = PFDin * (M=20) = 200M
* Fclk = Fcco / (2*(P=1)) = 100M
*/
cgu::pll1::ctrl({
.pd = 1,
.bypass = 0,
.fbsel = 0,
.direct = 0,
.psel = 0,
.autoblock = 1,
.nsel = hackrf_r9 ? 0UL : 1UL,
.msel = hackrf_r9 ? 19UL : 9UL,
.clk_sel = cgu::CLK_SEL::GP_CLKIN,
});
cgu::pll1::enable();
while (!cgu::pll1::is_locked())
;
set_clock_config(clock_config_pll1_step);
/* Delay >50us at 90-110MHz clock speed */
volatile uint32_t delay = 1400;
while (delay--)
;
set_clock_config(clock_config_pll1);
/* Remove /2P divider from PLL1 output to achieve full speed */
cgu::pll1::direct();
}
static void draw_splash_screen_icon(int16_t n, const ui::Bitmap& bitmap) {
ui::Painter painter;
painter.draw_bitmap(
{portapack::display.width() / 2 - 8 - 40 + (n * 20), portapack::display.height() / 2 - 8 + 40},
bitmap,
ui::Color::white(),
ui::Color::black());
}
static bool is_portapack_present() {
systime_t timeout = 50;
uint8_t wm8731_reset_command[] = {0x0f, 0x00};
if (i2c0.transmit(0x1a /* wm8731 */, wm8731_reset_command, 2, timeout) == false) {
audio_codec_ak4951.reset();
uint8_t ak4951_init_command[] = {0x00, 0x00};
i2c0.transmit(0x12 /* ak4951 */, ak4951_init_command, 2, timeout);
chThdSleepMilliseconds(10);
if (i2c0.transmit(0x12 /* ak4951 */, ak4951_init_command, 2, timeout) == false) {
shutdown_base();
return false;
}
}
return true;
}
static bool check_portapack_cpld() {
switch (portapack_model()) {
case PortaPackModel::AUTODETECT: {
portapack::cpld::CpldUpdateStatus result = portapack::cpld::update_autodetect(
portapack::cpld::rev_20150901::config, portapack::cpld::rev_20170522::config);
if (result != portapack::cpld::CpldUpdateStatus::Success) {
shutdown_base();
return false;
}
} break;
case PortaPackModel::R1_20150901:
case PortaPackModel::R2_20170522: {
portapack::cpld::CpldUpdateStatus result = portapack::cpld::update_if_necessary(portapack_cpld_config());
if (result == portapack::cpld::CpldUpdateStatus::Program_failed) {
chThdSleepMilliseconds(10);
// Mode left (R1) and right (R2,H2,H2+) bypass going into hackrf mode after failing CPLD update
// Mode center (autodetect), up (R1) and down (R2,H2,H2+) will go into hackrf mode after failing CPLD update
if (load_config() != 3 /* left */ && load_config() != 4 /* right */) {
shutdown_base();
return false;
}
}
} break;
case PortaPackModel::AGM:
// the AGM devices are always factory flashed. so do nothing
break;
}
return true;
}
static void initialize_boot_splash_screen() {
ui::Painter painter;
portapack::display.init();
painter.fill_rectangle(
{0, 0, portapack::display.width(), portapack::display.height()},
ui::Color::black());
chThdSleepMilliseconds(17);
portapack::backlight()->on();
painter.draw_bitmap(
{portapack::display.width() / 2 - 40, portapack::display.height() / 2 - 8},
ui::bitmap_titlebar_image,
ui::Color::white(),
ui::Color::black());
}
/* Clock scheme after exiting bootloader in SPIFI mode:
*
* XTAL_OSC = powered down
@ -396,23 +502,24 @@ init_status_t init() {
i2c0.start(i2c_config_boot_clock);
// Keeping this here for now incase we need to revert
// if( !portapack::cpld::update_if_necessary(portapack_cpld_config()) ) {
// shutdown_base();
// return false;
// }
// if( !hackrf::cpld::load_sram() ) {
// chSysHalt();
// }
chThdSleepMilliseconds(100);
configure_pins_portapack();
portapack::io.init();
persistent_memory::cache::init();
const auto switches_state = swizzled_switches() & (~(0xC0 | 0x80));
bool lcd_fast_setup = switches_state == 0 && portapack::display.read_display_status();
if (lcd_fast_setup) {
initialize_boot_splash_screen();
} else {
if (check_portapack_cpld() == false)
return init_status_t::INIT_PORTAPACK_CPLD_FAILED;
}
/* Cache some configuration data from persistent memory. */
persistent_memory::cache::init();
rtc_time::dst_init();
chThdSleepMilliseconds(10);
@ -425,48 +532,10 @@ init_status_t init() {
set_clock_config(clock_config_irc);
cgu::pll1::disable();
/* Incantation from LPC43xx UM10503 section 12.2.1.1, to bring the M4
* core clock speed to the 110 - 204MHz range.
*/
set_cpu_clock_speed();
/* Step into the 90-110MHz M4 clock range */
/* OG:
* Fclkin = 40M
* /N=2 = 20M = PFDin
* Fcco = PFDin * (M=10) = 200M
* r9:
* Fclkin = 10M
* /N=1 = 10M = PFDin
* Fcco = PFDin * (M=20) = 200M
* Fclk = Fcco / (2*(P=1)) = 100M
*/
cgu::pll1::ctrl({
.pd = 1,
.bypass = 0,
.fbsel = 0,
.direct = 0,
.psel = 0,
.autoblock = 1,
.nsel = hackrf_r9 ? 0UL : 1UL,
.msel = hackrf_r9 ? 19UL : 9UL,
.clk_sel = cgu::CLK_SEL::GP_CLKIN,
});
cgu::pll1::enable();
while (!cgu::pll1::is_locked())
;
set_clock_config(clock_config_pll1_step);
/* Delay >50us at 90-110MHz clock speed */
volatile uint32_t delay = 1400;
while (delay--)
;
set_clock_config(clock_config_pll1);
/* Remove /2P divider from PLL1 output to achieve full speed */
cgu::pll1::direct();
if (lcd_fast_setup)
draw_splash_screen_icon(0, ui::bitmap_icon_memory);
usb_serial.initialize();
@ -474,18 +543,11 @@ init_status_t init() {
chThdSleepMilliseconds(10);
/* Check if portapack is attached by checking if any of the two audio chips is present. */
systime_t timeout = 50;
uint8_t wm8731_reset_command[] = {0x0f, 0x00};
if (i2c0.transmit(0x1a /* wm8731 */, wm8731_reset_command, 2, timeout) == false) {
audio_codec_ak4951.reset();
uint8_t ak4951_init_command[] = {0x00, 0x00};
i2c0.transmit(0x12 /* ak4951 */, ak4951_init_command, 2, timeout);
chThdSleepMilliseconds(10);
if (i2c0.transmit(0x12 /* ak4951 */, ak4951_init_command, 2, timeout) == false) {
shutdown_base();
return init_status_t::INIT_NO_PORTAPACK;
}
}
if (lcd_fast_setup == false && is_portapack_present() == false)
return init_status_t::INIT_NO_PORTAPACK;
if (lcd_fast_setup)
draw_splash_screen_icon(1, ui::bitmap_icon_remote);
touch::adc::init();
controls_init();
@ -501,23 +563,20 @@ init_status_t init() {
chThdSleepMilliseconds(10);
portapack::cpld::CpldUpdateStatus result = portapack::cpld::update_if_necessary(portapack_cpld_config());
if (result == portapack::cpld::CpldUpdateStatus::Program_failed) {
chThdSleepMilliseconds(10);
// Mode left (R1) and right (R2,H2,H2+) bypass going into hackrf mode after failing CPLD update
// Mode center (autodetect), up (R1) and down (R2,H2,H2+) will go into hackrf mode after failing CPLD update
if (load_config() != 3 /* left */ && load_config() != 4 /* right */) {
shutdown_base();
return init_status_t::INIT_PORTAPACK_CPLD_FAILED;
}
}
if (lcd_fast_setup)
draw_splash_screen_icon(2, ui::bitmap_icon_sd);
init_status_t return_code = init_status_t::INIT_SUCCESS;
if (!hackrf::cpld::load_sram()) {
if (lcd_fast_setup)
chDbgPanic("HACKRF CPLD FAILED");
return_code = init_status_t::INIT_HACKRF_CPLD_FAILED;
}
if (lcd_fast_setup)
draw_splash_screen_icon(3, ui::bitmap_icon_hackrf);
chThdSleepMilliseconds(10); // This delay seems to solve white noise audio issues
LPC_CREG->DMAMUX = portapack::gpdma_mux;
@ -527,6 +586,13 @@ init_status_t init() {
audio::init(portapack_audio_codec());
if (lcd_fast_setup)
draw_splash_screen_icon(4, ui::bitmap_icon_speaker);
else {
portapack::display.init();
portapack::backlight()->on();
}
return return_code;
}