added voltage and temperature diagnostic

This commit is contained in:
Mateusz Faderewski 2024-01-19 04:48:05 +01:00
parent e1ecb0cac3
commit 5c4ad29f61
14 changed files with 274 additions and 78 deletions

View File

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
BUILDER_IMAGE="ghcr.io/polprzewodnikowy/sc64env:v1.8" BUILDER_IMAGE="ghcr.io/polprzewodnikowy/sc64env:v1.9"
pushd $(dirname $0) > /dev/null pushd $(dirname $0) > /dev/null

View File

@ -4,27 +4,28 @@
## N64 commands ## N64 commands
| id | name | arg0 | arg1 | rsp0 | rsp1 | description | | id | name | arg0 | arg1 | rsp0 | rsp1 | description |
| --- | --------------------- | -------------- | ------------ | ---------------- | -------------- | ---------------------------------------------------------- | | --- | --------------------- | ------------- | ------------ | ---------------- | -------------- | ---------------------------------------------------------- |
| `v` | **IDENTIFIER_GET** | --- | --- | identifier | --- | Get flashcart identifier `SCv2` | | `v` | **IDENTIFIER_GET** | --- | --- | identifier | --- | Get flashcart identifier `SCv2` |
| `V` | **VERSION_GET** | --- | --- | major/minor | revision | Get flashcart firmware version | | `V` | **VERSION_GET** | --- | --- | major/minor | revision | Get flashcart firmware version |
| `c` | **CONFIG_GET** | config_id | --- | --- | current_value | Get config option | | `c` | **CONFIG_GET** | config_id | --- | --- | current_value | Get config option |
| `C` | **CONFIG_SET** | config_id | new_value | --- | previous_value | Set config option and get previous value | | `C` | **CONFIG_SET** | config_id | new_value | --- | previous_value | Set config option and get previous value |
| `a` | **SETTING_GET** | setting_id | --- | --- | current_value | Get persistent setting option | | `a` | **SETTING_GET** | setting_id | --- | --- | current_value | Get persistent setting option |
| `A` | **SETTING_SET** | setting_id | new_value | --- | --- | Set persistent setting option | | `A` | **SETTING_SET** | setting_id | new_value | --- | --- | Set persistent setting option |
| `t` | **TIME_GET** | --- | --- | time_0 | time_1 | Get current RTC value | | `t` | **TIME_GET** | --- | --- | time_0 | time_1 | Get current RTC value |
| `T` | **TIME_SET** | time_0 | time_1 | --- | --- | Set new RTC value | | `T` | **TIME_SET** | time_0 | time_1 | --- | --- | Set new RTC value |
| `m` | **USB_READ** | pi_address | length | --- | --- | Receive data from USB to flashcart | | `m` | **USB_READ** | pi_address | length | --- | --- | Receive data from USB to flashcart |
| `M` | **USB_WRITE** | pi_address | length/type | --- | --- | Send data from from flashcart to USB | | `M` | **USB_WRITE** | pi_address | length/type | --- | --- | Send data from from flashcart to USB |
| `u` | **USB_READ_STATUS** | --- | --- | read_status/type | length | Get USB read status and type/length | | `u` | **USB_READ_STATUS** | --- | --- | read_status/type | length | Get USB read status and type/length |
| `U` | **USB_WRITE_STATUS** | --- | --- | write_status | --- | Get USB write status | | `U` | **USB_WRITE_STATUS** | --- | --- | write_status | --- | Get USB write status |
| `i` | **SD_CARD_OP** | pi_address | operation | --- | return_data | Perform special operation on SD card | | `i` | **SD_CARD_OP** | pi_address | operation | --- | return_data | Perform special operation on SD card |
| `I` | **SD_SECTOR_SET** | sector | --- | --- | --- | Set starting sector for next SD card R/W operation | | `I` | **SD_SECTOR_SET** | sector | --- | --- | --- | Set starting sector for next SD card R/W operation |
| `s` | **SD_READ** | pi_address | sector_count | --- | --- | Read sectors from SD card to flashcart | | `s` | **SD_READ** | pi_address | sector_count | --- | --- | Read sectors from SD card to flashcart |
| `S` | **SD_WRITE** | pi_address | sector_count | --- | --- | Write sectors from flashcart to SD card | | `S` | **SD_WRITE** | pi_address | sector_count | --- | --- | Write sectors from flashcart to SD card |
| `D` | **DISK_MAPPING_SET** | pi_address | table_size | --- | --- | Set 64DD disk mapping for SD mode | | `D` | **DISK_MAPPING_SET** | pi_address | table_size | --- | --- | Set 64DD disk mapping for SD mode |
| `w` | **WRITEBACK_PENDING** | --- | --- | pending_status | --- | Get save writeback status (is write queued to the SD card) | | `w` | **WRITEBACK_PENDING** | --- | --- | pending_status | --- | Get save writeback status (is write queued to the SD card) |
| `W` | **WRITEBACK_SD_INFO** | pi_address | --- | --- | --- | Load writeback SD sector table and enable it | | `W` | **WRITEBACK_SD_INFO** | pi_address | --- | --- | --- | Load writeback SD sector table and enable it |
| `K` | **FLASH_PROGRAM** | pi_address | length | --- | --- | Program flash with bytes loaded into data buffer | | `K` | **FLASH_PROGRAM** | pi_address | length | --- | --- | Program flash with bytes loaded into data buffer |
| `p` | **FLASH_WAIT_BUSY** | wait | --- | erase_block_size | --- | Wait until flash ready / get block erase size | | `p` | **FLASH_WAIT_BUSY** | wait | --- | erase_block_size | --- | Wait until flash ready / get block erase size |
| `P` | **FLASH_ERASE_BLOCK** | pi_address | --- | --- | --- | Start flash block erase | | `P` | **FLASH_ERASE_BLOCK** | pi_address | --- | --- | --- | Start flash block erase |
| `%` | **DIAGNOSTIC_GET** | diagnostic_id | --- | --- | value | Get diagnostic data |

View File

@ -170,7 +170,7 @@ Available packet IDs are listed in the [asynchronous packets](#asynchronous-pack
| `f` | **FIRMWARE_BACKUP** | address | --- | --- | status/length | Backup firmware to specified memory address | | `f` | **FIRMWARE_BACKUP** | address | --- | --- | status/length | Backup firmware to specified memory address |
| `F` | **FIRMWARE_UPDATE** | address | length | --- | status | Update firmware from specified memory address | | `F` | **FIRMWARE_UPDATE** | address | length | --- | status | Update firmware from specified memory address |
| `?` | **DEBUG_GET** | --- | --- | --- | debug_data | Get internal FPGA debug info | | `?` | **DEBUG_GET** | --- | --- | --- | debug_data | Get internal FPGA debug info |
| `%` | **STACK_USAGE_GET** | --- | --- | --- | stack_usage | Get per task stack usage | | `%` | **DIAGNOSTIC_GET** | --- | --- | --- | diagnostic_data | Get diagnostic data |
--- ---

View File

@ -68,7 +68,7 @@ const uint8_t font_data[96][FONT_CHAR_BYTES] = {
{ 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x00, }, { 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x00, },
{ 0x00, 0x38, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, }, { 0x00, 0x38, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, },
{ 0x00, 0x18, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, }, { 0x1C, 0x63, 0x63, 0x1C, 0x00, 0x00, 0x00, 0x00, },
{ 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, }, { 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, },
{ 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, }, { 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, },
{ 0x00, 0x3C, 0x66, 0x06, 0x06, 0x06, 0x7C, 0x00, }, { 0x00, 0x3C, 0x66, 0x06, 0x06, 0x06, 0x7C, 0x00, },

View File

@ -48,6 +48,7 @@ typedef enum {
CMD_ID_FLASH_PROGRAM = 'K', CMD_ID_FLASH_PROGRAM = 'K',
CMD_ID_FLASH_WAIT_BUSY = 'p', CMD_ID_FLASH_WAIT_BUSY = 'p',
CMD_ID_FLASH_ERASE_BLOCK = 'P', CMD_ID_FLASH_ERASE_BLOCK = 'P',
CMD_ID_DIAGNOSTIC_GET = '%',
} sc64_cmd_id_t; } sc64_cmd_id_t;
typedef enum { typedef enum {
@ -436,3 +437,14 @@ sc64_error_t sc64_flash_erase_block (void *address) {
}; };
return sc64_execute_cmd(&cmd); return sc64_execute_cmd(&cmd);
} }
sc64_error_t sc64_get_diagnostic (sc64_diagnostic_id_t id, uint32_t *value) {
sc64_cmd_t cmd = {
.id = CMD_ID_DIAGNOSTIC_GET,
.arg = { id }
};
sc64_error_t error = sc64_execute_cmd(&cmd);
*value = cmd.rsp[1];
return error;
}

View File

@ -93,6 +93,10 @@ typedef enum {
BUTTON_MODE_DD_DISK_SWAP, BUTTON_MODE_DD_DISK_SWAP,
} sc64_button_mode_t; } sc64_button_mode_t;
typedef enum {
DIAGNOSTIC_ID_VOLTAGE_TEMPERATURE,
} sc64_diagnostic_id_t;
typedef struct { typedef struct {
sc64_boot_mode_t boot_mode; sc64_boot_mode_t boot_mode;
sc64_cic_seed_t cic_seed; sc64_cic_seed_t cic_seed;
@ -174,5 +178,7 @@ sc64_error_t sc64_flash_wait_busy (void);
sc64_error_t sc64_flash_get_erase_block_size (size_t *erase_block_size); sc64_error_t sc64_flash_get_erase_block_size (size_t *erase_block_size);
sc64_error_t sc64_flash_erase_block (void *address); sc64_error_t sc64_flash_erase_block (void *address);
sc64_error_t sc64_get_diagnostic (sc64_diagnostic_id_t id, uint32_t *value);
#endif #endif

View File

@ -10,10 +10,22 @@
static void test_sc64_cfg (void) { static void test_sc64_cfg (void) {
sc64_error_t error; sc64_error_t error;
uint32_t button_state;
uint32_t identifier; uint32_t identifier;
uint16_t major; uint16_t major;
uint16_t minor; uint16_t minor;
uint32_t revision; uint32_t revision;
uint32_t tmp;
display_printf("Waiting for the button to be released... ");
do {
if ((error = sc64_get_config(CFG_ID_BUTTON_STATE, &button_state)) != SC64_OK) {
error_display("Command CONFIG_GET [BUTTON_STATE] failed: %d", error);
}
} while (button_state != 0);
display_printf("done\n\n");
if ((error = sc64_get_identifier(&identifier)) != SC64_OK) { if ((error = sc64_get_identifier(&identifier)) != SC64_OK) {
error_display("Command IDENTIFIER_GET failed: %d", error); error_display("Command IDENTIFIER_GET failed: %d", error);
@ -25,9 +37,19 @@ static void test_sc64_cfg (void) {
return; return;
} }
display_printf("Identifier: 0x%08X\n\n", identifier); if ((error = sc64_get_diagnostic(DIAGNOSTIC_ID_VOLTAGE_TEMPERATURE, &tmp)) != SC64_OK) {
error_display("Command DIAGNOSTIC_GET failed: %d", error);
return;
}
display_printf("SC64 firmware version: %d.%d.%d\n", major, minor, revision); uint16_t voltage = (uint16_t) (tmp >> 16);
int16_t temperature = (int16_t) (tmp & 0xFFFF);
display_printf("Identifier: 0x%08X\n", identifier);
display_printf("SC64 firmware version: %d.%d.%d\n\n", major, minor, revision);
display_printf("Voltage: %d.%03d V\n", (voltage / 1000), (voltage % 1000));
display_printf("Temperature: %d.%01d `C\n", (temperature / 10), (temperature % 10));
} }
static void test_rtc (void) { static void test_rtc (void) {
@ -233,7 +255,7 @@ bool test_check (void) {
error_display("Command CONFIG_GET [BUTTON_STATE] failed: %d", error); error_display("Command CONFIG_GET [BUTTON_STATE] failed: %d", error);
} }
return button_state != 0; return (button_state != 0);
} }
static struct { static struct {

View File

@ -4,6 +4,7 @@
#include "dd.h" #include "dd.h"
#include "flash.h" #include "flash.h"
#include "fpga.h" #include "fpga.h"
#include "hw.h"
#include "isv.h" #include "isv.h"
#include "led.h" #include "led.h"
#include "rtc.h" #include "rtc.h"
@ -84,6 +85,10 @@ typedef enum {
SD_CARD_OP_BYTE_SWAP_OFF = 5, SD_CARD_OP_BYTE_SWAP_OFF = 5,
} sd_card_op_t; } sd_card_op_t;
typedef enum {
DIAGNOSTIC_ID_VOLTAGE_TEMPERATURE = 0,
} diagnostic_id_t;
typedef enum { typedef enum {
SDRAM = (1 << 0), SDRAM = (1 << 0),
FLASH = (1 << 1), FLASH = (1 << 1),
@ -226,6 +231,22 @@ static bool cfg_set_save_type (save_type_t save_type) {
return false; return false;
} }
static bool cfg_read_diagnostic_data (uint32_t *args) {
switch (args[0]) {
case DIAGNOSTIC_ID_VOLTAGE_TEMPERATURE: {
uint16_t voltage;
int16_t temperature;
hw_adc_read_voltage_temperature(&voltage, &temperature);
args[1] = ((uint32_t) (voltage) << 16) | ((uint32_t) (temperature));
break;
}
default:
return true;
}
return false;
}
uint32_t cfg_get_identifier (void) { uint32_t cfg_get_identifier (void) {
return fpga_reg_get(REG_CFG_IDENTIFIER); return fpga_reg_get(REG_CFG_IDENTIFIER);
@ -700,6 +721,13 @@ void cfg_process (void) {
} }
break; break;
case '%':
if (cfg_read_diagnostic_data(args)) {
cfg_set_error(CFG_ERROR_BAD_CONFIG_ID);
return;
}
break;
default: default:
cfg_set_error(CFG_ERROR_UNKNOWN_CMD); cfg_set_error(CFG_ERROR_UNKNOWN_CMD);
return; return;

View File

@ -45,23 +45,23 @@ static void hw_clock_init (void) {
static void hw_timeout_init (void) { static void hw_timeout_init (void) {
RCC->APBENR1 |= RCC_APBENR1_DBGEN; RCC->APBENR1 |= RCC_APBENR1_DBGEN;
DBG->APBFZ2 |= DBG_APB_FZ2_DBG_TIM17_STOP; DBG->APBFZ2 |= DBG_APB_FZ2_DBG_TIM1_STOP;
RCC->APBENR2 |= RCC_APBENR2_TIM17EN; RCC->APBENR2 |= RCC_APBENR2_TIM1EN;
TIM17->CR1 = TIM_CR1_OPM; TIM1->CR1 = TIM_CR1_OPM;
TIM17->PSC = (((CPU_FREQ / 1000 / 1000) * TIMEOUT_US_PER_TICK) - 1); TIM1->PSC = (((CPU_FREQ / 1000 / 1000) * TIMEOUT_US_PER_TICK) - 1);
TIM17->EGR = TIM_EGR_UG; TIM1->EGR = TIM_EGR_UG;
} }
static void hw_timeout_start (void) { static void hw_timeout_start (void) {
TIM17->CR1 &= ~(TIM_CR1_CEN); TIM1->CR1 &= ~(TIM_CR1_CEN);
TIM17->CNT = 0; TIM1->CNT = 0;
TIM17->CR1 |= TIM_CR1_CEN; TIM1->CR1 |= TIM_CR1_CEN;
} }
static bool hw_timeout_occured (uint32_t timeout_us) { static bool hw_timeout_occured (uint32_t timeout_us) {
uint16_t count = TIM17->CNT; uint16_t count = TIM1->CNT;
uint32_t adjusted_timeout = ((timeout_us + (TIMEOUT_US_PER_TICK - 1)) / TIMEOUT_US_PER_TICK); uint32_t adjusted_timeout = ((timeout_us + (TIMEOUT_US_PER_TICK - 1)) / TIMEOUT_US_PER_TICK);
@ -73,29 +73,46 @@ static bool hw_timeout_occured (uint32_t timeout_us) {
} }
#define DELAY_US_PER_TICK (1)
#define DELAY_MS_PER_TICK (1) #define DELAY_MS_PER_TICK (1)
static void hw_delay_init (void) { static void hw_delay_init (void) {
RCC->APBENR1 |= RCC_APBENR1_DBGEN; RCC->APBENR1 |= RCC_APBENR1_DBGEN;
DBG->APBFZ2 |= DBG_APB_FZ2_DBG_TIM16_STOP; DBG->APBFZ1 |= DBG_APB_FZ1_DBG_TIM3_STOP;
RCC->APBENR2 |= RCC_APBENR2_TIM16EN; RCC->APBENR1 |= RCC_APBENR1_TIM3EN;
TIM16->CR1 = TIM_CR1_OPM; TIM3->CR1 = TIM_CR1_OPM;
TIM16->PSC = (((CPU_FREQ / 1000) * DELAY_MS_PER_TICK) - 1); TIM3->EGR = TIM_EGR_UG;
TIM16->EGR = TIM_EGR_UG; }
void hw_delay_us (uint32_t delay_us) {
TIM3->CR1 &= ~(TIM_CR1_CEN);
TIM3->PSC = (((CPU_FREQ / 1000 / 1000) * DELAY_US_PER_TICK) - 1);
TIM3->CNT = 0;
TIM3->EGR = TIM_EGR_UG;
TIM3->CR1 |= TIM_CR1_CEN;
uint32_t adjusted_delay = ((delay_us + (DELAY_US_PER_TICK - 1)) / DELAY_US_PER_TICK);
uint16_t count;
do {
count = TIM3->CNT;
} while ((count < adjusted_delay) && (count != 0xFFFF));
} }
void hw_delay_ms (uint32_t delay_ms) { void hw_delay_ms (uint32_t delay_ms) {
TIM16->CR1 &= ~(TIM_CR1_CEN); TIM3->CR1 &= ~(TIM_CR1_CEN);
TIM16->CNT = 0; TIM3->PSC = (((CPU_FREQ / 1000) * DELAY_MS_PER_TICK) - 1);
TIM16->CR1 |= TIM_CR1_CEN; TIM3->CNT = 0;
TIM3->EGR = TIM_EGR_UG;
TIM3->CR1 |= TIM_CR1_CEN;
uint32_t adjusted_delay = ((delay_ms + (DELAY_MS_PER_TICK - 1)) / DELAY_MS_PER_TICK); uint32_t adjusted_delay = ((delay_ms + (DELAY_MS_PER_TICK - 1)) / DELAY_MS_PER_TICK);
uint16_t count; uint16_t count;
do { do {
count = TIM16->CNT; count = TIM3->CNT;
} while ((count < adjusted_delay) && (count != 0xFFFF)); } while ((count < adjusted_delay) && (count != 0xFFFF));
} }
@ -493,6 +510,61 @@ void hw_loader_get_parameters (loader_parameters_t *parameters) {
} }
#define ADC_VREF_CAL_POINT (3000)
#define ADC_VREF_CAL_VALUE (*(uint16_t *) (0x1FFF75AAUL))
#define TEMP_CAL_POINT_1 (30)
#define TEMP_CAL_VALUE_1 (*(uint16_t *) (0x1FFF75A8UL))
#define TEMP_CAL_POINT_2 (130)
#define TEMP_CAL_VALUE_2 (*(uint16_t *) (0x1FFF75CAUL))
#define TEMP_CAL_VREF (3000)
#define TEMP_SCALE (10)
static void hw_adc_init (void) {
RCC->APBENR2 |= RCC_APBENR2_ADCEN;
ADC1->CFGR2 = ADC_CFGR2_CKMODE_1;
ADC->CCR = (ADC_CCR_TSEN | ADC_CCR_VREFEN);
ADC1->CR = ADC_CR_ADVREGEN;
hw_delay_us(120);
ADC1->ISR = ADC_ISR_EOCAL;
ADC1->CR |= ADC_CR_ADCAL;
while (!(ADC1->ISR & ADC_ISR_EOCAL));
ADC1->CFGR1 = (ADC_CFGR1_AUTOFF | ADC_CFGR1_WAIT | ADC_CFGR1_SCANDIR);
ADC1->CFGR2 |= (ADC_CFGR2_OVSS_2 | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSR_0 | ADC_CFGR2_OVSE);
ADC1->SMPR = (ADC_SMPR_SMP1_2 | ADC_SMPR_SMP1_1 | ADC_SMPR_SMP1_0);
ADC1->ISR = ADC_ISR_CCRDY;
ADC1->CHSELR = (ADC_CHSELR_CHSEL13 | ADC_CHSELR_CHSEL12);
while (!(ADC1->ISR & ADC_ISR_CCRDY));
ADC1->CR |= ADC_CR_ADEN;
}
void hw_adc_read_voltage_temperature (uint16_t *voltage, int16_t *temperature) {
ADC1->CR |= ADC_CR_ADSTART;
while (!(ADC1->ISR & ADC_ISR_EOC));
uint16_t adc_vref = ((ADC_VREF_CAL_POINT * ADC_VREF_CAL_VALUE) / ADC1->DR);
while (!(ADC1->ISR & ADC_ISR_EOC));
int16_t adc_temp = ((ADC1->DR * adc_vref) / TEMP_CAL_VREF);
*voltage = adc_vref;
*temperature = (
((adc_temp - TEMP_CAL_VALUE_1) * (TEMP_CAL_POINT_2 - TEMP_CAL_POINT_1) * TEMP_SCALE) /
(TEMP_CAL_VALUE_2 - TEMP_CAL_VALUE_1)
) + (TEMP_CAL_POINT_1 * TEMP_SCALE);
}
static void hw_led_init (void) { static void hw_led_init (void) {
hw_gpio_init(GPIO_ID_LED, GPIO_OUTPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_NONE, GPIO_AF_0, 0); hw_gpio_init(GPIO_ID_LED, GPIO_OUTPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_NONE, GPIO_AF_0, 0);
} }
@ -530,6 +602,7 @@ void hw_app_init (void) {
hw_clock_init(); hw_clock_init();
hw_timeout_init(); hw_timeout_init();
hw_delay_init(); hw_delay_init();
hw_adc_init();
hw_led_init(); hw_led_init();
hw_misc_init(); hw_misc_init();
hw_uart_init(); hw_uart_init();

View File

@ -85,6 +85,8 @@ void hw_reset (loader_parameters_t *parameters);
void hw_loader_get_parameters (loader_parameters_t *parameters); void hw_loader_get_parameters (loader_parameters_t *parameters);
void hw_adc_read_voltage_temperature (uint16_t *voltage, int16_t *temperature);
void hw_primer_init (void); void hw_primer_init (void);
void hw_loader_init (void); void hw_loader_init (void);
void hw_app_init (void); void hw_app_init (void);

View File

@ -3,6 +3,7 @@
#include "dd.h" #include "dd.h"
#include "flash.h" #include "flash.h"
#include "fpga.h" #include "fpga.h"
#include "hw.h"
#include "rtc.h" #include "rtc.h"
#include "timer.h" #include "timer.h"
#include "update.h" #include "update.h"
@ -21,6 +22,9 @@
#define DEBUG_WRITE_TIMEOUT_MS (1000) #define DEBUG_WRITE_TIMEOUT_MS (1000)
#define DIAGNOSTIC_DATA_MARKER (1 << 31)
#define DIAGNOSTIC_DATA_VERSION (1)
enum rx_state { enum rx_state {
RX_STATE_IDLE, RX_STATE_IDLE,
@ -381,15 +385,19 @@ static void usb_rx_process (void) {
p.response_info.data[1] = fpga_reg_get(REG_DEBUG_1); p.response_info.data[1] = fpga_reg_get(REG_DEBUG_1);
break; break;
case '%': case '%': {
uint16_t voltage;
int16_t temperature;
hw_adc_read_voltage_temperature(&voltage, &temperature);
p.rx_state = RX_STATE_IDLE; p.rx_state = RX_STATE_IDLE;
p.response_pending = true; p.response_pending = true;
p.response_info.data_length = 16; p.response_info.data_length = 16;
p.response_info.data[0] = 0; p.response_info.data[0] = (DIAGNOSTIC_DATA_MARKER | DIAGNOSTIC_DATA_VERSION);
p.response_info.data[1] = 0; p.response_info.data[1] = (uint32_t) (voltage);
p.response_info.data[2] = 0; p.response_info.data[2] = (uint32_t) (temperature);
p.response_info.data[3] = 0; p.response_info.data[3] = 0;
break; break;
}
default: default:
p.rx_state = RX_STATE_IDLE; p.rx_state = RX_STATE_IDLE;

View File

@ -738,7 +738,7 @@ fn handle_info_command(connection: Connection) -> Result<(), sc64::Error> {
println!(" LED blink: {}", state.led_enable); println!(" LED blink: {}", state.led_enable);
println!(" IS-Viewer 64 offset: 0x{:08X}", state.isv_address); println!(" IS-Viewer 64 offset: 0x{:08X}", state.isv_address);
println!(" FPGA debug data: {}", state.fpga_debug_data); println!(" FPGA debug data: {}", state.fpga_debug_data);
println!(" MCU stack usage: {}", state.mcu_stack_usage); println!(" Diagnostic data: {}", state.diagnostic_data);
Ok(()) Ok(())
} }

View File

@ -12,7 +12,7 @@ pub use self::{
server::ServerEvent, server::ServerEvent,
types::{ types::{
BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState, DdDriveType, DdMode, BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState, DdDriveType, DdMode,
DebugPacket, DiskPacket, DiskPacketKind, FpgaDebugData, McuStackUsage, SaveType, DebugPacket, DiagnosticData, DiskPacket, DiskPacketKind, FpgaDebugData, SaveType,
SaveWriteback, Switch, TvType, SaveWriteback, Switch, TvType,
}, },
}; };
@ -55,7 +55,7 @@ pub struct DeviceState {
pub led_enable: Switch, pub led_enable: Switch,
pub datetime: DateTime<Local>, pub datetime: DateTime<Local>,
pub fpga_debug_data: FpgaDebugData, pub fpga_debug_data: FpgaDebugData,
pub mcu_stack_usage: McuStackUsage, pub diagnostic_data: DiagnosticData,
} }
const SC64_V2_IDENTIFIER: &[u8; 4] = b"SCv2"; const SC64_V2_IDENTIFIER: &[u8; 4] = b"SCv2";
@ -350,7 +350,7 @@ impl SC64 {
Ok(u32::from_be_bytes(data[0..4].try_into().unwrap()).try_into()?) Ok(u32::from_be_bytes(data[0..4].try_into().unwrap()).try_into()?)
} }
fn command_debug_get(&mut self) -> Result<FpgaDebugData, Error> { fn command_fpga_debug_data_get(&mut self) -> Result<FpgaDebugData, Error> {
let data = self.link.execute_command(&Command { let data = self.link.execute_command(&Command {
id: b'?', id: b'?',
args: [0, 0], args: [0, 0],
@ -359,7 +359,7 @@ impl SC64 {
Ok(data.try_into()?) Ok(data.try_into()?)
} }
fn command_stack_usage_get(&mut self) -> Result<McuStackUsage, Error> { fn command_diagnostic_data_get(&mut self) -> Result<DiagnosticData, Error> {
let data = self.link.execute_command(&Command { let data = self.link.execute_command(&Command {
id: b'%', id: b'%',
args: [0, 0], args: [0, 0],
@ -557,8 +557,8 @@ impl SC64 {
rom_extended_enable: get_config!(self, RomExtendedEnable)?, rom_extended_enable: get_config!(self, RomExtendedEnable)?,
led_enable: get_setting!(self, LedEnable)?, led_enable: get_setting!(self, LedEnable)?,
datetime: self.get_datetime()?, datetime: self.get_datetime()?,
fpga_debug_data: self.command_debug_get()?, fpga_debug_data: self.command_fpga_debug_data_get()?,
mcu_stack_usage: self.command_stack_usage_get()?, diagnostic_data: self.command_diagnostic_data_get()?,
}) })
} }

View File

@ -828,37 +828,81 @@ impl Display for FpgaDebugData {
} }
} }
pub struct McuStackUsage { pub struct DiagnosticDataV0 {
pub cic: u32, pub cic: u32,
pub rtc: u32, pub rtc: u32,
pub led: u32, pub led: u32,
pub gvr: u32, pub gvr: u32,
} }
impl TryFrom<Vec<u8>> for McuStackUsage { pub struct DiagnosticDataV1 {
pub voltage: f32,
pub temperature: f32,
}
pub enum DiagnosticData {
V0(DiagnosticDataV0),
V1(DiagnosticDataV1),
Unknown,
}
impl TryFrom<Vec<u8>> for DiagnosticData {
type Error = Error; type Error = Error;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> { fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
if value.len() != 16 { if value.len() < 4 {
return Err(Error::new("Invalid data length for MCU stack usage")); return Err(Error::new("Invalid data length for diagnostic data"));
}
let raw_version = u32::from_be_bytes(value[0..4].try_into().unwrap());
let unversioned = raw_version & (1 << 31) == 0;
let version = raw_version & !(1 << 31);
if unversioned {
if value.len() != 16 {
return Err(Error::new("Invalid data length for V0 diagnostic data"));
}
return Ok(DiagnosticData::V0(DiagnosticDataV0 {
cic: u32::from_be_bytes(value[0..4].try_into().unwrap()),
rtc: u32::from_be_bytes(value[4..8].try_into().unwrap()),
led: u32::from_be_bytes(value[8..12].try_into().unwrap()),
gvr: u32::from_be_bytes(value[12..16].try_into().unwrap()),
}));
}
match version {
1 => {
if value.len() != 16 {
return Err(Error::new("Invalid data length for V1 diagnostic data"));
}
let raw_voltage = u32::from_be_bytes(value[4..8].try_into().unwrap()) as f32;
let raw_temperature = u32::from_be_bytes(value[8..12].try_into().unwrap()) as f32;
Ok(DiagnosticData::V1(DiagnosticDataV1 {
voltage: raw_voltage / 1000.0,
temperature: raw_temperature / 10.0,
}))
}
_ => Ok(DiagnosticData::Unknown),
} }
Ok(McuStackUsage {
cic: u32::from_be_bytes(value[0..4].try_into().unwrap()),
rtc: u32::from_be_bytes(value[4..8].try_into().unwrap()),
led: u32::from_be_bytes(value[8..12].try_into().unwrap()),
gvr: u32::from_be_bytes(value[12..16].try_into().unwrap()),
})
} }
} }
impl Display for McuStackUsage { impl Display for DiagnosticData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.cic > 0 { match self {
f.write_fmt(format_args!("CIC: {}, ", self.cic))?; DiagnosticData::V0(d) => {
if d.cic > 0 {
f.write_fmt(format_args!("CIC: {}, ", d.cic))?;
}
f.write_fmt(format_args!(
"RTC: {}, LED: {}, GVR: {}",
d.rtc, d.led, d.gvr
))
}
DiagnosticData::V1(d) => f.write_fmt(format_args!(
"{:.03} V / {:.01} °C",
d.voltage, d.temperature
)),
DiagnosticData::Unknown => f.write_str("Unknown"),
} }
f.write_fmt(format_args!(
"RTC: {}, LED: {}, GVR: {}",
self.rtc, self.led, self.gvr
))
} }
} }
@ -874,7 +918,7 @@ macro_rules! get_config {
macro_rules! get_setting { macro_rules! get_setting {
($sc64:ident, $setting:ident) => {{ ($sc64:ident, $setting:ident) => {{
// Note: remove 'allow(irrefutable_let_patterns)' below when more settings are added // NOTE: remove 'allow(irrefutable_let_patterns)' below when more settings are added
#[allow(irrefutable_let_patterns)] #[allow(irrefutable_let_patterns)]
if let Setting::$setting(value) = $sc64.command_setting_get(SettingId::$setting)? { if let Setting::$setting(value) = $sc64.command_setting_get(SettingId::$setting)? {
Ok(value) Ok(value)