mirror of
https://github.com/google/pebble.git
synced 2025-07-14 18:21:58 -04:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
188
platform/snowy/boot/src/drivers/display/ice40lp.c
Normal file
188
platform/snowy/boot/src/drivers/display/ice40lp.c
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ice40lp.h"
|
||||
|
||||
#include "board/board.h"
|
||||
#include "drivers/gpio.h"
|
||||
#include "drivers/spi.h"
|
||||
#include "drivers/pmic.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "util/delay.h"
|
||||
|
||||
#include "stm32f4xx.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// We want the SPI clock to run at 16 by default
|
||||
const uint32_t SPI_DEFAULT_MHZ = 16;
|
||||
static uint32_t s_spi_clock_hz;
|
||||
|
||||
bool display_busy(void) {
|
||||
bool busy = GPIO_ReadInputDataBit(DISP_GPIO, DISP_PIN_BUSY);
|
||||
return busy;
|
||||
}
|
||||
|
||||
static void prv_configure_spi(uint32_t spi_clock_hz) {
|
||||
// Set up a SPI bus on SPI6
|
||||
SPI_InitTypeDef spi_cfg;
|
||||
SPI_I2S_DeInit(DISP_SPI);
|
||||
SPI_StructInit(&spi_cfg);
|
||||
spi_cfg.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
|
||||
spi_cfg.SPI_Mode = SPI_Mode_Master;
|
||||
spi_cfg.SPI_DataSize = SPI_DataSize_8b;
|
||||
spi_cfg.SPI_CPOL = SPI_CPOL_High;
|
||||
spi_cfg.SPI_CPHA = SPI_CPHA_2Edge;
|
||||
spi_cfg.SPI_NSS = SPI_NSS_Soft;
|
||||
spi_cfg.SPI_BaudRatePrescaler = spi_find_prescaler(spi_clock_hz, DISPLAY_SPI_CLOCK_PERIPH);
|
||||
spi_cfg.SPI_FirstBit = SPI_FirstBit_MSB;
|
||||
SPI_Init(DISP_SPI, &spi_cfg);
|
||||
|
||||
SPI_Cmd(DISP_SPI, ENABLE);
|
||||
}
|
||||
|
||||
void display_start(void) {
|
||||
// Enable the GPIOG clock; this is required before configuring the pins
|
||||
gpio_use(DISP_GPIO);
|
||||
|
||||
GPIO_PinAFConfig(DISP_GPIO, GPIO_PINSOURCE_SCK, GPIO_AF_SPI6); // SCK
|
||||
GPIO_PinAFConfig(DISP_GPIO, GPIO_PINSOURCE_MOSI, GPIO_AF_SPI6); // MOSI
|
||||
GPIO_PinAFConfig(DISP_GPIO, GPIO_PINSOURCE_MISO, GPIO_AF_SPI6); // MOSI
|
||||
|
||||
GPIO_InitTypeDef gpio_cfg;
|
||||
gpio_cfg.GPIO_OType = GPIO_OType_PP;
|
||||
gpio_cfg.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||
gpio_cfg.GPIO_Mode = GPIO_Mode_AF;
|
||||
gpio_cfg.GPIO_Speed = GPIO_Speed_25MHz;
|
||||
gpio_cfg.GPIO_Pin = DISP_PIN_SCLK;
|
||||
GPIO_Init(DISP_GPIO, &gpio_cfg);
|
||||
|
||||
gpio_cfg.GPIO_Pin = DISP_PIN_SI;
|
||||
GPIO_Init(DISP_GPIO, &gpio_cfg);
|
||||
|
||||
gpio_cfg.GPIO_Pin = DISP_PIN_SO;
|
||||
GPIO_Init(DISP_GPIO, &gpio_cfg);
|
||||
|
||||
gpio_cfg.GPIO_Mode = GPIO_Mode_IN;
|
||||
gpio_cfg.GPIO_PuPd = GPIO_PuPd_UP;
|
||||
gpio_cfg.GPIO_Pin = DISP_PIN_CDONE;
|
||||
GPIO_Init(DISP_GPIO, &gpio_cfg);
|
||||
|
||||
gpio_cfg.GPIO_Mode = GPIO_Mode_IN;
|
||||
gpio_cfg.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||
gpio_cfg.GPIO_Pin = DISP_PIN_BUSY;
|
||||
GPIO_Init(DISP_GPIO, &gpio_cfg);
|
||||
|
||||
gpio_cfg.GPIO_Mode = GPIO_Mode_OUT;
|
||||
gpio_cfg.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||
gpio_cfg.GPIO_Pin = DISP_PIN_SCS;
|
||||
GPIO_Init(DISP_GPIO, &gpio_cfg);
|
||||
|
||||
gpio_cfg.GPIO_OType = GPIO_OType_OD;
|
||||
gpio_cfg.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||
gpio_cfg.GPIO_Pin = DISP_PIN_CRESET;
|
||||
GPIO_Init(DISP_GPIO, &gpio_cfg);
|
||||
|
||||
RCC_APB2PeriphClockCmd(DISPLAY_SPI_CLOCK, ENABLE);
|
||||
|
||||
s_spi_clock_hz = MHZ_TO_HZ(SPI_DEFAULT_MHZ);
|
||||
prv_configure_spi(s_spi_clock_hz);
|
||||
}
|
||||
|
||||
bool display_program(const uint8_t *fpga_bitstream, uint32_t bitstream_size) {
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_SET);
|
||||
|
||||
// wait a bit.
|
||||
delay_ms(1);
|
||||
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_CRESET, Bit_RESET); // CRESET LOW
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_RESET); // SCS LOW
|
||||
|
||||
delay_ms(1);
|
||||
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_CRESET, Bit_SET); // CRESET -> HIGH
|
||||
|
||||
delay_ms(1);
|
||||
|
||||
PBL_ASSERT(!GPIO_ReadInputDataBit(DISP_GPIO, DISP_PIN_CDONE), "CDONE not low during reset");
|
||||
PBL_ASSERT(GPIO_ReadInputDataBit(DISP_GPIO, DISP_PIN_CRESET), "CRESET not high during reset");
|
||||
|
||||
// Program the FPGA
|
||||
for (unsigned int i = 0; i < bitstream_size; ++i) {
|
||||
display_write_byte(fpga_bitstream[i]);
|
||||
}
|
||||
|
||||
// Set SCS high so that we don't process any of these clocks as commands.
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_SET); // SCS -> HIGH
|
||||
|
||||
// Send dummy clocks
|
||||
for (unsigned int i = 0; i < 8; ++i) {
|
||||
display_write_byte(0x00);
|
||||
}
|
||||
|
||||
if (!GPIO_ReadInputDataBit(DISP_GPIO, DISP_PIN_CDONE)) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "CDONE not high after programming!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void display_power_enable(void) {
|
||||
// The display requires us to wait 1ms between each power rail coming up. The PMIC
|
||||
// initialization brings up the 3.2V rail (VLCD on the display, LD02 on the PMIC) for us, but
|
||||
// we still need to wait before turning on the subsequent rails.
|
||||
delay_ms(2);
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Enabling 6v6 (Display VDDC)");
|
||||
set_6V6_power_state(true);
|
||||
|
||||
delay_ms(2);
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Enabling 4v5 (Display VDDP)");
|
||||
set_4V5_power_state(true);
|
||||
}
|
||||
|
||||
void display_power_disable(void) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Disabling 4v5 (Display VDDP)");
|
||||
set_4V5_power_state(false);
|
||||
|
||||
delay_ms(2);
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Disabling 6v6 (Display VDDC)");
|
||||
set_6V6_power_state(false);
|
||||
|
||||
delay_ms(2);
|
||||
}
|
||||
|
||||
//!
|
||||
//! Write a single byte synchronously to the display. Use this
|
||||
//! sparingly, as it will tie up the micro duing the write.
|
||||
//!
|
||||
void display_write_byte(uint8_t d) {
|
||||
// Block until the tx buffer is empty
|
||||
while (!SPI_I2S_GetFlagStatus(DISP_SPI, SPI_I2S_FLAG_TXE)) continue;
|
||||
SPI_I2S_SendData(DISP_SPI, d);
|
||||
}
|
||||
|
||||
uint8_t display_write_and_read_byte(uint8_t d) {
|
||||
SPI_I2S_ReceiveData(DISP_SPI);
|
||||
while (!SPI_I2S_GetFlagStatus(DISP_SPI, SPI_I2S_FLAG_TXE)) continue;
|
||||
SPI_I2S_SendData(DISP_SPI, d);
|
||||
while (!SPI_I2S_GetFlagStatus(DISP_SPI, SPI_I2S_FLAG_RXNE)) continue;
|
||||
return SPI_I2S_ReceiveData(DISP_SPI);
|
||||
}
|
48
platform/snowy/boot/src/drivers/display/ice40lp.h
Normal file
48
platform/snowy/boot/src/drivers/display/ice40lp.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "drivers/gpio.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// GPIO constants
|
||||
#define DISP_SPI SPI6
|
||||
#define DISP_GPIO GPIOG
|
||||
|
||||
#define DISPLAY_SPI_CLOCK_PERIPH SpiPeriphClockAPB2
|
||||
#define DISPLAY_SPI_CLOCK RCC_APB2Periph_SPI6
|
||||
#define DISP_PIN_SCS GPIO_Pin_8
|
||||
#define DISP_PIN_CDONE GPIO_Pin_9
|
||||
#define DISP_PIN_BUSY GPIO_Pin_10
|
||||
#define DISP_PIN_SO GPIO_Pin_12
|
||||
#define DISP_PIN_SCLK GPIO_Pin_13
|
||||
#define DISP_PIN_SI GPIO_Pin_14
|
||||
#define DISP_PIN_CRESET GPIO_Pin_15
|
||||
|
||||
#define GPIO_PINSOURCE_SCK GPIO_PinSource13
|
||||
#define GPIO_PINSOURCE_MOSI GPIO_PinSource14
|
||||
#define GPIO_PINSOURCE_MISO GPIO_PinSource12
|
||||
|
||||
|
||||
bool display_busy(void);
|
||||
void display_start(void);
|
||||
bool display_program(const uint8_t *fpga_bitstream, uint32_t bitstream_size);
|
||||
void display_write_byte(uint8_t d);
|
||||
uint8_t display_write_and_read_byte(uint8_t d);
|
||||
void display_power_enable(void);
|
||||
void display_power_disable(void);
|
288
platform/snowy/boot/src/drivers/display/snowy_boot_fpga.c
Normal file
288
platform/snowy/boot/src/drivers/display/snowy_boot_fpga.c
Normal file
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/display.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "board/board.h"
|
||||
#include "drivers/dbgserial.h"
|
||||
#include "drivers/display/bootloader_fpga_bitstream.auto.h"
|
||||
#include "drivers/display/ice40lp.h"
|
||||
#include "drivers/flash/s29vs.h"
|
||||
#include "drivers/gpio.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "drivers/pmic.h"
|
||||
#include "drivers/spi.h"
|
||||
#include "flash_region.h"
|
||||
#include "stm32f4xx_gpio.h"
|
||||
#include "stm32f4xx_rcc.h"
|
||||
#include "stm32f4xx_spi.h"
|
||||
#include "system/passert.h"
|
||||
#include "util/delay.h"
|
||||
#include "util/misc.h"
|
||||
|
||||
|
||||
#define CMD_NULL (0)
|
||||
#define CMD_SET_PARAMETER (1)
|
||||
#define CMD_DISPLAY_OFF (2)
|
||||
#define CMD_DISPLAY_ON (3)
|
||||
#define CMD_DRAW_SCENE (4)
|
||||
#define CMD_RESET_RELEASE (8)
|
||||
#define CMD_RESET_ASSERT (9)
|
||||
|
||||
#define SCENE_BLACK (0)
|
||||
#define SCENE_SPLASH (1)
|
||||
#define SCENE_UPDATE (2)
|
||||
#define SCENE_ERROR (3)
|
||||
|
||||
#define UPDATE_PROGRESS_MAX (93)
|
||||
|
||||
// The FPGA bitstream stored in NVCM may be missing or defective; a replacement
|
||||
// bitstream may be stored in the MFG info flash region, prefixed with a
|
||||
// four-byte header. The header is composed of the bitstream length followed by
|
||||
// its complement (all bits inverted).
|
||||
#define FPGA_BITSTREAM_FLASH_ADDR (FMC_BANK_1_BASE_ADDRESS + \
|
||||
FLASH_REGION_MFG_INFO_BEGIN + 0x10000)
|
||||
|
||||
struct __attribute__((packed)) FlashBitstream {
|
||||
uint16_t len;
|
||||
uint16_t len_complement;
|
||||
uint8_t bitstream[0];
|
||||
};
|
||||
|
||||
static bool prv_wait_programmed(void) {
|
||||
// The datasheet lists the typical NVCM configuration time as 56 ms.
|
||||
// Something is wrong if it takes more than twice that time.
|
||||
int timeout = 100 * 10;
|
||||
while (GPIO_ReadInputDataBit(DISP_GPIO, DISP_PIN_CDONE) == 0) {
|
||||
if (timeout-- == 0) {
|
||||
dbgserial_putstr("FPGA CDONE timeout expired!");
|
||||
return false;
|
||||
}
|
||||
delay_us(100);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool prv_reset_into_nvcm(void) {
|
||||
// Reset the FPGA and wait for it to program itself via NVCM.
|
||||
// NVCM configuration is initiated by pulling CRESET high while SCS is high.
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_SET);
|
||||
// CRESET needs to be low for at least 200 ns
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_CRESET, Bit_RESET);
|
||||
delay_ms(1);
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_CRESET, Bit_SET);
|
||||
return prv_wait_programmed();
|
||||
}
|
||||
|
||||
static bool prv_reset_fpga(void) {
|
||||
#ifdef BLANK_FPGA
|
||||
return display_program(s_fpga_bitstream, sizeof(s_fpga_bitstream));
|
||||
#endif
|
||||
|
||||
const struct FlashBitstream *bitstream = (void *)FPGA_BITSTREAM_FLASH_ADDR;
|
||||
// Work around GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=38341
|
||||
uint16_t len_complement_complement = ~bitstream->len_complement;
|
||||
if (bitstream->len != 0xffff && bitstream->len == len_complement_complement) {
|
||||
dbgserial_putstr("Configuring FPGA from bitstream in flash...");
|
||||
if (display_program(bitstream->bitstream, bitstream->len)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Fall back to NVCM.
|
||||
dbgserial_putstr("No FPGA bitstream in flash.");
|
||||
}
|
||||
dbgserial_putstr("Falling back to NVCM.");
|
||||
return prv_reset_into_nvcm();
|
||||
}
|
||||
|
||||
static void prv_start_command(uint8_t cmd) {
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_RESET);
|
||||
delay_us(100);
|
||||
display_write_byte(cmd);
|
||||
}
|
||||
|
||||
static void prv_send_command_arg(uint8_t arg) {
|
||||
display_write_byte(arg);
|
||||
}
|
||||
|
||||
static void prv_end_command(void) {
|
||||
while (SPI_I2S_GetFlagStatus(DISP_SPI, SPI_I2S_FLAG_BSY)) continue;
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_SET);
|
||||
}
|
||||
|
||||
static bool prv_wait_busy(void) {
|
||||
// The display should come out of busy within 35 milliseconds;
|
||||
// it is a waste of time to wait more than twice that.
|
||||
int timeout = 50 * 10;
|
||||
while (display_busy()) {
|
||||
if (timeout-- == 0) {
|
||||
dbgserial_putstr("Display busy-wait timeout expired!");
|
||||
return false;
|
||||
}
|
||||
delay_us(100);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void prv_screen_on(void) {
|
||||
prv_start_command(CMD_DISPLAY_ON);
|
||||
prv_end_command();
|
||||
}
|
||||
|
||||
static void prv_screen_off(void) {
|
||||
prv_start_command(CMD_DISPLAY_OFF);
|
||||
prv_end_command();
|
||||
}
|
||||
|
||||
void prv_draw_scene(uint8_t scene) {
|
||||
prv_start_command(CMD_DRAW_SCENE);
|
||||
prv_send_command_arg(scene);
|
||||
prv_end_command();
|
||||
}
|
||||
void prv_set_parameter(uint32_t param) {
|
||||
prv_start_command(CMD_SET_PARAMETER);
|
||||
// Send in little-endian byte order
|
||||
prv_send_command_arg(param & 0xff);
|
||||
prv_send_command_arg((param >> 8) & 0xff);
|
||||
prv_send_command_arg((param >> 16) & 0xff);
|
||||
prv_send_command_arg((param >> 24) & 0xff);
|
||||
prv_end_command();
|
||||
}
|
||||
|
||||
static uint8_t prv_read_version(void) {
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_RESET);
|
||||
delay_us(100);
|
||||
|
||||
uint8_t version_num = display_write_and_read_byte(0);
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_SET);
|
||||
return version_num;
|
||||
}
|
||||
|
||||
void display_init(void) {
|
||||
display_start();
|
||||
bool program_success = prv_reset_fpga();
|
||||
if (!program_success) {
|
||||
dbgserial_putstr("FPGA configuration failed. Is this a bigboard?");
|
||||
// Don't waste time trying to get the FPGA unstuck if it's not configured.
|
||||
// It's just going to waste time and frustrate bigboard users.
|
||||
return;
|
||||
}
|
||||
|
||||
dbgserial_print("FPGA version: ");
|
||||
dbgserial_print_hex(prv_read_version());
|
||||
dbgserial_putstr("");
|
||||
|
||||
// enable the power rails
|
||||
display_power_enable();
|
||||
|
||||
#ifdef TEST_FPGA_RESET_COMMAND
|
||||
#define ASSERT_BUSY_IS(state) \
|
||||
dbgserial_putstr(GPIO_ReadInputDataBit(DISP_GPIO, DISP_PIN_BUSY) == state? \
|
||||
"Yes" : "No")
|
||||
|
||||
// Test out the FPGA soft-reset capability present in release-03 of the FPGA.
|
||||
dbgserial_putstr("FPGA soft-reset test");
|
||||
|
||||
dbgserial_print("Precondition: BUSY asserted during scene draw? ");
|
||||
prv_draw_scene(SCENE_BLACK);
|
||||
ASSERT_BUSY_IS(Bit_SET);
|
||||
|
||||
dbgserial_print("Is BUSY cleared after the reset command? ");
|
||||
prv_start_command(CMD_RESET_ASSERT);
|
||||
prv_end_command();
|
||||
ASSERT_BUSY_IS(Bit_RESET);
|
||||
|
||||
dbgserial_print("Are draw-scene commands ineffectual while in reset? ");
|
||||
prv_draw_scene(SCENE_BLACK);
|
||||
ASSERT_BUSY_IS(Bit_RESET);
|
||||
|
||||
dbgserial_print("Does releasing reset allow draw-scene commands "
|
||||
"to function again? ");
|
||||
prv_start_command(CMD_RESET_RELEASE);
|
||||
prv_end_command();
|
||||
prv_draw_scene(SCENE_BLACK);
|
||||
ASSERT_BUSY_IS(Bit_SET);
|
||||
|
||||
dbgserial_print("Does the draw-scene command complete? ");
|
||||
dbgserial_putstr(prv_wait_busy()? "Yes" : "No");
|
||||
#endif
|
||||
|
||||
// Work around an issue which some boards exhibit where the FPGA ring
|
||||
// oscillator can start up with higher harmonics, massively overclocking the
|
||||
// design and causing malfunction. When this occurrs, the draw-scene command
|
||||
// will not work, asserting BUSY indefinitely but never updating the display.
|
||||
// Other commands such as display-on and display-off are less affected by the
|
||||
// overclocking, so the display can be turned on while the FPGA is in this
|
||||
// state, showing only garbage.
|
||||
// FPGA malfunction can be detected in software. In an attempt to restore
|
||||
// proper functioning, the FPGA can be reset and reconfigured in the hopes
|
||||
// that the ring oscillator will start up and oscillate without any higher
|
||||
// harmonics. Bootloader release 03 attempts to mitigate this problem by
|
||||
// delaying oscillator startup until after configuration completes. Time will
|
||||
// tell whether this actually fixes things.
|
||||
for (int retries = 0; retries <= 20; ++retries) {
|
||||
prv_draw_scene(SCENE_SPLASH);
|
||||
if (prv_wait_busy()) {
|
||||
prv_screen_on();
|
||||
dbgserial_print("Display initialized after ");
|
||||
dbgserial_print_hex(retries);
|
||||
dbgserial_putstr(" retries.");
|
||||
return;
|
||||
}
|
||||
|
||||
prv_reset_fpga();
|
||||
}
|
||||
|
||||
// It's taken too many attempts and the FPGA still isn't behaving. Give up on
|
||||
// showing the splash screen and keep the screen off so that the user doesn't
|
||||
// see a broken-looking staticky screen on boot.
|
||||
dbgserial_putstr("Display initialization failed.");
|
||||
prv_screen_off();
|
||||
}
|
||||
|
||||
void display_boot_splash(void) {
|
||||
prv_wait_busy();
|
||||
prv_draw_scene(SCENE_SPLASH);
|
||||
// Don't turn the screen on until the boot-splash is fully drawn.
|
||||
prv_wait_busy();
|
||||
prv_screen_on();
|
||||
}
|
||||
|
||||
void display_firmware_update_progress(
|
||||
uint32_t numerator, uint32_t denominator) {
|
||||
static uint8_t last_bar_fill = UINT8_MAX;
|
||||
// Scale progress to the number of pixels in the progress bar,
|
||||
// rounding half upwards.
|
||||
uint8_t bar_fill =
|
||||
((numerator * UPDATE_PROGRESS_MAX) + ((denominator+1)/2)) / denominator;
|
||||
// Don't waste time and power redrawing the same screen repeatedly.
|
||||
if (bar_fill != last_bar_fill) {
|
||||
last_bar_fill = bar_fill;
|
||||
prv_set_parameter(bar_fill);
|
||||
prv_draw_scene(SCENE_UPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
void display_error_code(uint32_t error_code) {
|
||||
prv_set_parameter(error_code);
|
||||
prv_draw_scene(SCENE_ERROR);
|
||||
}
|
||||
|
||||
void display_prepare_for_reset(void) {
|
||||
prv_screen_off();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue