/**
  *****************************************************************************
  * @file    sys_oled_display.c
  * @author  Solution Team
  * @version V1.00
  * @date    28-February-2023
  * @brief   System OLED display module
  *****************************************************************************
  * @attention 
  *  Copyright (c) 2017 - 2023 Unicmicro Co., Ltd. 
  *  All Rights Reserved. 
  *****************************************************************************
  */

#include "libc.h"
#include "drivers.h"
#include "drv_oled.h"
#include "usbpd.h"
#include "sys_pd_config.h"
#if SYS_QUICK_CHARGE_ENABLED
#include "sys_qc_hook.h"
#endif /* SYS_QUICK_CHARGE_ENABLED */

#if SYS_OLED_DISPLAY_ENABLED

#include "sys_task.h"

typedef enum SYS_OLED_DISPLAY_e
{
    SYS_OLED_DISPLAY_LOGO,
    SYS_OLED_DISPLAY_CONNECTION,
    SYS_OLED_DISPLAY_PDO_1,
    SYS_OLED_DISPLAY_PDO_2,
    SYS_OLED_DISPLAY_PDO_3,
    SYS_OLED_DISPLAY_PDO_4,
    SYS_OLED_DISPLAY_PDO_5,
    SYS_OLED_DISPLAY_PDO_6,
    SYS_OLED_DISPLAY_PDO_7,
    SYS_OLED_DISPLAY_RDO,
    SYS_OLED_DISPLAY_END
} SYS_OLED_DISPLAY_t;


sys_tid_t g_oled_task_id = SYS_TASK_ID_INVALID;

volatile bool_t g_oled_task_enabled = FALSE;
timer_t g_oled_timer;
sys_tick_t g_oled_start_time = 0;
bool_t g_oled_init_configured = FALSE;
bool_t g_oled_connection_drew = FALSE;

/** mode selection display state */
status_t g_mode_selection_state = SYS_OLED_DISPLAY_LOGO;

#ifdef POWER_Z_LOGO  /* modified by James Gan - 2018-05-21 */
const unsigned char Logo[] =
{
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0xF0,0xF0,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0xE0,0xE0,0x00,0xC0,
    0xE0,0x60,0x70,0x30,0x30,0x30,0x30,0x30,0x30,0x70,0x60,0xE0,0xC0,0x00,0xE0,0xF0,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xE0,0x00,0xC0,0xE0,
    0x60,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0xE0,0xF0,0x30,
    0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x70,0xE0,0xC0,0x00,0x00,0x80,0x80,0x80,
    0x80,0x80,0x00,0x00,0x00,0xF0,0xF8,0xD8,0xD8,0xD8,0xD8,0x78,0xB8,0xD8,0xF8,0xF0,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x1F,0x1F,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x07,0x03,0x00,0x07,
    0x0F,0x1C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1C,0x0F,0x07,0x00,0x1F,0x1F,
    0x18,0x1C,0x0C,0x0C,0x06,0x07,0x0E,0x0C,0x1C,0x18,0x10,0x1F,0x0F,0x00,0x07,0x0F,
    0x19,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x00,0x1F,0x1F,0x06,
    0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x0E,0x1E,0x1F,0x03,0x00,0x00,0x01,0x01,0x01,
    0x01,0x01,0x00,0x00,0x00,0x1F,0x3F,0x37,0x3B,0x3D,0x36,0x37,0x37,0x37,0x3F,0x1F,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
#else  /* OLD logo */
const unsigned char Logo[] =
{
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0xF0,0xF0,0xF0,0xF0,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xF0,0xF0,0xF0,0xF0,0x00,
    0x00,0x00,0xF0,0xF0,0xF0,0xF0,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xF0,0xF0,
    0xF0,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xF0,0xF0,0xF0,0xF0,0x00,0x00,
    0x00,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,
    0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,
    0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,
    0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,
    0x00,0xFF,0xFF,0xFE,0xFC,0xF8,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,
    0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
    0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,
    0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x80,0x80,0x80,0x80,0x80,0xFF,0xFF,0xFF,
    0xFF,0xFF,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,
    0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
    0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x01,0x03,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,
    0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x06,0x04,0x07,0x07,0x07,0x07,0x07,0x00,
    0x00,0x00,0x01,0x03,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,
    0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x06,0x04,0x07,0x07,0x07,0x07,0x07,0x00,0x00,
    0x00,0x01,0x03,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,
    0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x06,0x04,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
#endif /* #ifdef POWER_Z_LOGO */

const char * const oled_power_role_str[] = {
    " |PR:SNK",
    " |PR:SRC",
    " |PR:DRP"
};

static void oled_draw_connection()
{
    u32_t vbus_val;

    if (!g_oled_init_configured)
        return;

    OLED_clear();

    // VBUS Voltage
    vbus_val = dpm_vbus_volt_get();
    OLED_draw_string(0, 0, (const u8_t *)"V:", DEFAULT_FONT_SIZE);
    OLED_draw_digits(2*DEFAULT_FONT_WIDTH_SIZE, 0, vbus_val/1000, 2, DEFAULT_FONT_SIZE);
    OLED_draw_string(4*DEFAULT_FONT_WIDTH_SIZE, 0, (const u8_t *)".", DEFAULT_FONT_SIZE);
    OLED_draw_digits_zero_prefix(5*DEFAULT_FONT_WIDTH_SIZE, 0, (((f32_t)vbus_val)/1000-(vbus_val/1000))*1000, 3, DEFAULT_FONT_SIZE);
    OLED_draw_string(8*DEFAULT_FONT_WIDTH_SIZE, 0, (const u8_t *)"V", DEFAULT_FONT_SIZE);

    // VBUS Current
    vbus_val = 0; //dpm_vbus_current_get();
    OLED_draw_string(0, 2, (const u8_t *)"I:", DEFAULT_FONT_SIZE);
    OLED_draw_digits(2*DEFAULT_FONT_WIDTH_SIZE, 2, vbus_val/1000, 2, DEFAULT_FONT_SIZE);
    OLED_draw_string(4*DEFAULT_FONT_WIDTH_SIZE, 2, (const u8_t *)".", DEFAULT_FONT_SIZE);
    OLED_draw_digits_zero_prefix(5*DEFAULT_FONT_WIDTH_SIZE, 2, (((f32_t)vbus_val)/1000-(vbus_val/1000))*1000, 3, DEFAULT_FONT_SIZE);
    OLED_draw_string(8*DEFAULT_FONT_WIDTH_SIZE, 2, (const u8_t *)"A", DEFAULT_FONT_SIZE);
    
    // Link
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 0, (const u8_t *)" |Link:", FONT_SIZE_8);

#if SYS_QUICK_CHARGE_ENABLED  /* modified by James Gan - 2018-05-18 */
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 1, 
        pe_pd_connected_get() ? ((dpm_req_spec_rev_get() == SPEC_REVISION_3_0) ? (const u8_t *)" |PD3.0" : (const u8_t *)" |PD2.0") :
        ((DPM_QC_SRC_CONNECTED_QC3 == dpm_qc_state_get(&g_usbc_qc_info)) ? (const u8_t *)" |QC3.0" :
            ((DPM_QC_SRC_CONNECTED == dpm_qc_state_get(&g_usbc_qc_info)) ? (const u8_t *)" |QC2.0" :
                (dpm_cable_detect_done_get() ? (const u8_t *)" |CC   " : (const u8_t *)" |NA   "))), FONT_SIZE_8);
#else
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 1, 
        pe_pd_connected_get() ? ((dpm_req_spec_rev_get() == SPEC_REVISION_3_0) ? (const u8_t *)" |PD3.0" : (const u8_t *)" |PD2.0") :
            (dpm_cable_detect_done_get() ? (const u8_t *)" |CC   " : (const u8_t *)" |NA   "), FONT_SIZE_8);

#endif /* #if SYS_QUICK_CHARGE_ENABLED */

    // Power role line
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 2, dpm_cable_detect_done_get() ? (const u8_t *)oled_power_role_str[dpm_current_pwr_mode_get()] : (const u8_t *)oled_power_role_str[usbpd_cfg_port_def_pr_get()], FONT_SIZE_8);

    // Request Index
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 3, (const u8_t *)" |Req:", FONT_SIZE_8);
    OLED_draw_digits(9*DEFAULT_FONT_WIDTH_SIZE+6*FONT_SIZE_8_WIDTH, 3, dpm_src_obj_pos_get(), 1, FONT_SIZE_8);
}

static void oled_update_connection()
{
    u32_t vbus_val;

    if (!g_oled_connection_drew)
        return;

    // VBUS Voltage
    vbus_val = dpm_vbus_volt_get();
    OLED_draw_digits(2*DEFAULT_FONT_WIDTH_SIZE, 0, vbus_val/1000, 2, DEFAULT_FONT_SIZE);
    OLED_draw_digits_zero_prefix(5*DEFAULT_FONT_WIDTH_SIZE, 0, (((f32_t)vbus_val)/1000-(vbus_val/1000))*1000, 3, DEFAULT_FONT_SIZE);

    // VBUS Current
    vbus_val = 0; //dpm_vbus_current_get();
    OLED_draw_digits(2*DEFAULT_FONT_WIDTH_SIZE, 2, vbus_val/1000, 2, DEFAULT_FONT_SIZE);
    OLED_draw_digits_zero_prefix(5*DEFAULT_FONT_WIDTH_SIZE, 2, (((f32_t)vbus_val)/1000-(vbus_val/1000))*1000, 3, DEFAULT_FONT_SIZE);

    // Link
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 0, (const u8_t *)" |Link:", FONT_SIZE_8);
#if SYS_QUICK_CHARGE_ENABLED  /* modified by James Gan - 2018-05-18 */
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 1, 
        pe_pd_connected_get() ? ((dpm_req_spec_rev_get() == SPEC_REVISION_3_0) ? (const u8_t *)" |PD3.0" : (const u8_t *)" |PD2.0") :
        ((DPM_QC_SRC_CONNECTED_QC3 == dpm_qc_state_get(&g_usbc_qc_info)) ? (const u8_t *)" |QC3.0" :
            ((DPM_QC_SRC_CONNECTED == dpm_qc_state_get(&g_usbc_qc_info)) ? (const u8_t *)" |QC2.0" :
                (dpm_cable_detect_done_get() ? (const u8_t *)" |CC   " : (const u8_t *)" |NA   "))), FONT_SIZE_8);
#else
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 1, 
        pe_pd_connected_get() ? ((dpm_req_spec_rev_get() == SPEC_REVISION_3_0) ? (const u8_t *)" |PD3.0" : (const u8_t *)" |PD2.0") :
            (dpm_cable_detect_done_get() ? (const u8_t *)" |CC   " : (const u8_t *)" |NA   "), FONT_SIZE_8);

#endif /* #if SYS_QUICK_CHARGE_ENABLED */

    // Power role line
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 2, dpm_cable_detect_done_get() ? (const u8_t *)oled_power_role_str[dpm_current_pwr_mode_get()] : (const u8_t *)oled_power_role_str[usbpd_cfg_port_def_pr_get()], FONT_SIZE_8);

    // Request Index
    OLED_draw_digits(9*DEFAULT_FONT_WIDTH_SIZE+6*FONT_SIZE_8_WIDTH, 3, dpm_src_obj_pos_get(), 1, FONT_SIZE_8);
}

#if 0  /* deleted by James Gan - 2018-05-18 */
static void oled_draw_pdo(u32_t pos)
{
    volatile UPD_DATA_MSG_SOURCE_CAP_FIXED_SUPPLY_t *p_src_cap_fixed;
    volatile UPD_DATA_MSG_SOURCE_CAP_VARIABLE_SUPPLY_t *p_src_cap_vari;
    volatile UPD_DATA_MSG_SOURCE_CAP_BATTERY_SUPPLY_t *p_src_cap_battery;
    u32_t voltage;
    u32_t current;

    if (!g_oled_init_configured)
        return;

    if (pos > dpm_rx_src_cap_num_get())
        return;

    p_src_cap_fixed = (volatile UPD_DATA_MSG_SOURCE_CAP_FIXED_SUPPLY_t *)dpm_rx_src_cap_object_get(pos);
    if (POWER_SUPPLY_TYPE_BATTERY == p_src_cap_fixed->supply_type)
    {
        p_src_cap_battery = (volatile UPD_DATA_MSG_SOURCE_CAP_BATTERY_SUPPLY_t *)dpm_rx_src_cap_object_get(pos);
        voltage = (p_src_cap_battery->max_volt + p_src_cap_battery->min_volt)/2 * VOLTAGE_DIV;
        current = (u32_t)(p_src_cap_battery->max_allow_pwr) * POWER_DIV / (voltage * VOLTAGE_DIV) * 1000;
    }
    else if (POWER_SUPPLY_TYPE_VARIABLE == p_src_cap_fixed->supply_type)
    {
        p_src_cap_vari = (volatile UPD_DATA_MSG_SOURCE_CAP_VARIABLE_SUPPLY_t *)dpm_rx_src_cap_object_get(pos);
        voltage = (p_src_cap_vari->max_volt + p_src_cap_vari->min_volt)/2 * VOLTAGE_DIV;
        current = p_src_cap_vari->max_curr * CURRENT_DIV;
    }
    else {
        voltage = p_src_cap_fixed->volt * VOLTAGE_DIV;
        current = p_src_cap_fixed->max_curr * CURRENT_DIV;
    }

    OLED_clear();
        
    // PDO Voltage
    OLED_draw_string(0, 0, (const u8_t *)"V:", DEFAULT_FONT_SIZE);
    OLED_draw_digits(2*DEFAULT_FONT_WIDTH_SIZE, 0, voltage, 5, DEFAULT_FONT_SIZE);
    OLED_draw_string(7*DEFAULT_FONT_WIDTH_SIZE, 0, (const u8_t *)"mV", DEFAULT_FONT_SIZE);

    // PDO Current
    OLED_draw_string(0, 2, (const u8_t *)"I:", DEFAULT_FONT_SIZE);
    OLED_draw_digits(2*DEFAULT_FONT_WIDTH_SIZE, 2, current, 5, DEFAULT_FONT_SIZE);
    OLED_draw_string(7*DEFAULT_FONT_WIDTH_SIZE, 2, (const u8_t *)"mA", DEFAULT_FONT_SIZE);
    
    // PDO Right Bar

    // PDO Type
    if (POWER_SUPPLY_TYPE_BATTERY == p_src_cap_fixed->supply_type)
    {
        OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 0, (const u8_t *)" |Battery", FONT_SIZE_8);
    }
    else if (POWER_SUPPLY_TYPE_VARIABLE == p_src_cap_fixed->supply_type)
    {
        OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 0, (const u8_t *)" |Vari", FONT_SIZE_8);
    }
    else {
        OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 0, (const u8_t *)" |Fixed", FONT_SIZE_8);
    }

    // PDO Index
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 1, (const u8_t *)" |PDO:", FONT_SIZE_8);
    OLED_draw_digits(9*DEFAULT_FONT_WIDTH_SIZE+6*FONT_SIZE_8_WIDTH, 1, pos+1, 1, FONT_SIZE_8);

    if ((0 == pos) && (POWER_SUPPLY_TYPE_FIXED == p_src_cap_fixed->supply_type))
    {
        // DRP
        OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 2, (const u8_t *)" |DRP:", FONT_SIZE_8);
        OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE+6*FONT_SIZE_8_WIDTH, 2, p_src_cap_fixed->dual_role_pwr ? (const u8_t *)"On " : (const u8_t *)"Off", FONT_SIZE_8);
        // DRD
        OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 3, (const u8_t *)" |DRD:", FONT_SIZE_8);
        OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE+6*FONT_SIZE_8_WIDTH, 3, p_src_cap_fixed->drs_cap ? (const u8_t *)"On " : (const u8_t *)"Off", FONT_SIZE_8);
    }
    else {
        // Empty line
        OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 2, (const u8_t *)" |     ", FONT_SIZE_8);
        // Empty line
        OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 3, (const u8_t *)" |     ", FONT_SIZE_8);
    }
}

static void oled_update_pdo(u32_t pos)
{
    volatile UPD_DATA_MSG_SOURCE_CAP_FIXED_SUPPLY_t *p_src_cap_fixed;
    volatile UPD_DATA_MSG_SOURCE_CAP_VARIABLE_SUPPLY_t *p_src_cap_vari;
    volatile UPD_DATA_MSG_SOURCE_CAP_BATTERY_SUPPLY_t *p_src_cap_battery;
    u32_t voltage;
    u32_t current;

    if (!g_oled_init_configured)
        return;

    if (pos > dpm_rx_src_cap_num_get())
        return;

    p_src_cap_fixed = (volatile UPD_DATA_MSG_SOURCE_CAP_FIXED_SUPPLY_t *)dpm_rx_src_cap_object_get(pos);
    if (POWER_SUPPLY_TYPE_BATTERY == p_src_cap_fixed->supply_type)
    {
        p_src_cap_battery = (volatile UPD_DATA_MSG_SOURCE_CAP_BATTERY_SUPPLY_t *)dpm_rx_src_cap_object_get(pos);
        voltage = (p_src_cap_battery->max_volt + p_src_cap_battery->min_volt)/2 * VOLTAGE_DIV;
        current = (u32_t)(p_src_cap_battery->max_allow_pwr) * POWER_DIV / (voltage * VOLTAGE_DIV) * 1000;
    }
    else if (POWER_SUPPLY_TYPE_VARIABLE == p_src_cap_fixed->supply_type)
    {
        p_src_cap_vari = (volatile UPD_DATA_MSG_SOURCE_CAP_VARIABLE_SUPPLY_t *)dpm_rx_src_cap_object_get(pos);
        voltage = (p_src_cap_vari->max_volt + p_src_cap_vari->min_volt)/2 * VOLTAGE_DIV;
        current = p_src_cap_vari->max_curr * CURRENT_DIV;
    }
    else {
        voltage = p_src_cap_fixed->volt * VOLTAGE_DIV;
        current = p_src_cap_fixed->max_curr * CURRENT_DIV;
    }
        
    // PDO Voltage
    OLED_draw_digits(2*DEFAULT_FONT_WIDTH_SIZE, 0, voltage, 5, DEFAULT_FONT_SIZE);

    // PDO Current
    OLED_draw_digits(2*DEFAULT_FONT_WIDTH_SIZE, 2, current, 5, DEFAULT_FONT_SIZE);
    
    // PDO Right Bar

    // PDO Type
    if (POWER_SUPPLY_TYPE_BATTERY == p_src_cap_fixed->supply_type)
    {
        OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 0, (const u8_t *)" |Battery", FONT_SIZE_8);
    }
    else if (POWER_SUPPLY_TYPE_VARIABLE == p_src_cap_fixed->supply_type)
    {
        OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 0, (const u8_t *)" |Vari   ", FONT_SIZE_8);
    }
    else {
        OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 0, (const u8_t *)" |Fixed  ", FONT_SIZE_8);
    }

    // PDO Index
    OLED_draw_digits(9*DEFAULT_FONT_WIDTH_SIZE+6*FONT_SIZE_8_WIDTH, 1, pos+1, 1, FONT_SIZE_8);

    if ((0 == pos) && (POWER_SUPPLY_TYPE_FIXED == p_src_cap_fixed->supply_type))
    {
        // DRP
        OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE+6*FONT_SIZE_8_WIDTH, 2, p_src_cap_fixed->dual_role_pwr ? (const u8_t *)"On " : (const u8_t *)"Off", FONT_SIZE_8);
        // DRD
        OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE+6*FONT_SIZE_8_WIDTH, 3, p_src_cap_fixed->drs_cap ? (const u8_t *)"On " : (const u8_t *)"Off", FONT_SIZE_8);
    }
}

static void oled_draw_rdo()
{
    if (!g_oled_init_configured)
        return;

    OLED_clear();
    // RDO Operating Current
    OLED_draw_string(0, 0, (const u8_t *)"O:", DEFAULT_FONT_SIZE);
    OLED_draw_digits(2*DEFAULT_FONT_WIDTH_SIZE, 0, OPR_CURR_GET((UPD_DATA_MSG_REQUEST_FIXED_VARIABLE_t *)dpm_current_rdo_get())*CURRENT_DIV, 5, DEFAULT_FONT_SIZE);
    OLED_draw_string(7*DEFAULT_FONT_WIDTH_SIZE, 0, (const u8_t *)"mA", DEFAULT_FONT_SIZE);

    // RDO Maximal Current
    OLED_draw_string(0, 2, (const u8_t *)"M:", DEFAULT_FONT_SIZE);
    OLED_draw_digits(2*DEFAULT_FONT_WIDTH_SIZE, 2, MAX_OPR_CURR_GET((UPD_DATA_MSG_REQUEST_FIXED_VARIABLE_t *)dpm_current_rdo_get())*CURRENT_DIV, 5, DEFAULT_FONT_SIZE);
    OLED_draw_string(7*DEFAULT_FONT_WIDTH_SIZE, 2, (const u8_t *)"mA", DEFAULT_FONT_SIZE);
    
    // RDO Right Bar

    // RDO Type
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 0, (const u8_t *)" |RDO", FONT_SIZE_8);

    // RDO Index
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 1, (const u8_t *)" |POS:", FONT_SIZE_8);
    OLED_draw_digits(9*DEFAULT_FONT_WIDTH_SIZE+6*FONT_SIZE_8_WIDTH, 1, ((UPD_DATA_MSG_REQUEST_FIXED_VARIABLE_t *)dpm_current_rdo_get())->obj_pos, 1, FONT_SIZE_8);

    // Mismatch
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 2, (const u8_t *)" |Mis:", FONT_SIZE_8);
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE+6*FONT_SIZE_8_WIDTH, 2, ((UPD_DATA_MSG_REQUEST_FIXED_VARIABLE_t *)dpm_current_rdo_get())->cap_mismatch ? (const u8_t *)"Yes" : (const u8_t *)"No ", FONT_SIZE_8);
    // Empty line
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE, 3, (const u8_t *)" |", FONT_SIZE_8);
}

static void oled_update_rdo()
{
    if (!g_oled_init_configured)
        return;

    // RDO Operating Current
    OLED_draw_digits(2*DEFAULT_FONT_WIDTH_SIZE, 0, OPR_CURR_GET((UPD_DATA_MSG_REQUEST_FIXED_VARIABLE_t *)dpm_current_rdo_get())*CURRENT_DIV, 5, DEFAULT_FONT_SIZE);

    // RDO Maximal Current
    OLED_draw_digits(2*DEFAULT_FONT_WIDTH_SIZE, 2, MAX_OPR_CURR_GET((UPD_DATA_MSG_REQUEST_FIXED_VARIABLE_t *)dpm_current_rdo_get())*CURRENT_DIV, 5, DEFAULT_FONT_SIZE);
    
    // RDO Right Bar

    // RDO Index
    OLED_draw_digits(9*DEFAULT_FONT_WIDTH_SIZE+6*FONT_SIZE_8_WIDTH, 1, ((UPD_DATA_MSG_REQUEST_FIXED_VARIABLE_t *)dpm_current_rdo_get())->obj_pos, 1, FONT_SIZE_8);

    // Mismatch
    OLED_draw_string(9*DEFAULT_FONT_WIDTH_SIZE+6*FONT_SIZE_8_WIDTH, 2, ((UPD_DATA_MSG_REQUEST_FIXED_VARIABLE_t *)dpm_current_rdo_get())->cap_mismatch ? (const u8_t *)"Yes" : (const u8_t *)"No ", FONT_SIZE_8);
}
#endif /* #if 0 */

void sys_oled_handler(u32_t arg)
{
    if (g_oled_task_enabled)
    {
        if (!g_oled_init_configured && ((u32_t)(now()-g_oled_start_time) > 300000UL))
    	{
    		OLED_init();
    		OLED_draw_bmp(0,0,OLED_MAX_COL,OLED_MAX_ROW,Logo);
    		g_oled_init_configured = TRUE;
    	}
        
        if (g_oled_init_configured && (!g_oled_connection_drew) && ((u32_t)(now()-g_oled_start_time) > 1500000UL))
        {
            g_mode_selection_state = SYS_OLED_DISPLAY_CONNECTION;
            oled_draw_connection();
            g_oled_connection_drew = TRUE;
        }

    	if (g_oled_connection_drew && timer_expired(&g_oled_timer))
    	{
            timer_start(800000, &g_oled_timer);
            oled_update_connection();
    	}
    }
}

void sys_oled_control(bool_t en)
{
	g_oled_task_enabled = en;
	if (en)
	{
        if (SYS_TASK_ID_INVALID == g_oled_task_id)
        {
		    g_oled_task_id = sys_task_create(sys_oled_handler, 0);
        }
		timer_start(800000UL, &g_oled_timer);
		g_oled_start_time = now();
	}
	else {
		timer_stop(&g_oled_timer);
		if (g_oled_init_configured)
		{
			OLED_display_off();
			g_oled_init_configured = FALSE;
			g_oled_connection_drew = FALSE;
		}
	}
}
#endif
