/*********************************************************************************************************//**
 * @file    BMD26M088_HT32/src/BMD26M088.c
 * @version V1.0.1
 * @date    2024-08-01
 * @brief   I2C communication with the BMD26M088 and control the RGB.
 *************************************************************************************************************
 * @attention
 *
 * Firmware Disclaimer Information
 *
 * 1. The customer hereby acknowledges and agrees that the program technical documentation, including the
 *    code, which is supplied by Holtek Semiconductor Inc., (hereinafter referred to as "HOLTEK") is the
 *    proprietary and confidential intellectual property of HOLTEK, and is protected by copyright law and
 *    other intellectual property laws.
 *
 * 2. The customer hereby acknowledges and agrees that the program technical documentation, including the
 *    code, is confidential information belonging to HOLTEK, and must not be disclosed to any third parties
 *    other than HOLTEK and the customer.
 *
 * 3. The program technical documentation, including the code, is provided "as is" and for customer reference
 *    only. After delivery by HOLTEK, the customer shall use the program technical documentation, including
 *    the code, at their own risk. HOLTEK disclaims any expressed, implied or statutory warranties, including
 *    the warranties of merchantability, satisfactory quality and fitness for a particular purpose.
 *
 * <h2><center>Copyright (C) Holtek Semiconductor Inc. All rights reserved</center></h2>
 ************************************************************************************************************/

/* Includes ------------------------------------------------------------------------------------------------*/
#include "BMD26M088.h"
#include "FONT.h"
/* Private macro -------------------------------------------------------------------------------------------*/
#if (CHECK_WIREn(BMD26M088_WIRE) == -1)
  #error "Error: BMD26M088_WIRE doesn't support the current WIRE number. Check ht32_board_config.h."
#endif

/* Global variables ----------------------------------------------------------------------------------------*/
uint32_t gBMD26M088_WIRE = BMD26M088_WIRE;

/* Private constants ---------------------------------------------------------------------------------------*/
const uint8_t RAM_INDEX_TABLE[8][8] = // 8x8RGB RAM address index
{
  /*
     You can set data to this address to set led on or off,
     such as set number RGB1,write R G B value to 0x03 0x04 0x05.
  */
  {0x03, 0x06, 0x09, 0x0C, 0x13, 0x16, 0x19, 0x1C},  /* Column 1                                            */
  {0x23, 0x26, 0x29, 0x2C, 0x33, 0x36, 0x39, 0x3C},  /* Column 2                                            */
  {0x41, 0x46, 0x49, 0x4C, 0x51, 0x56, 0x59, 0x5C},  /* Column 3                                            */
  {0x61, 0x66, 0x69, 0x6C, 0x71, 0x74, 0x79, 0x7C},  /* Column 4                                            */
  {0x81, 0x84, 0x89, 0x8C, 0x91, 0x94, 0x99, 0x9C},  /* Column 5                                            */
  {0xA1, 0xA4, 0xA7, 0xAC, 0xB1, 0xB4, 0xB7, 0xBC},  /* Column 6                                            */
  {0xC1, 0xC4, 0xC7, 0xCC, 0xD1, 0xD4, 0xD7, 0xDA},  /* Column 7                                            */
  {0xE1, 0xE4, 0xE7, 0xEA, 0xF1, 0xF4, 0xF7, 0xFA}   /* Column 8                                            */
};

const uint8_t LED_ON_OFF_16_16[32] = // See HT16D33A datasheet page21 ~ 22
{
  0xF8, 0x7F,                                        /* 0x00,0x01                                           */
  0xF8, 0x7F,                                        /* 0x02,0x03                                           */
  0xF8, 0x7F,                                        /* 0x04,0x05                                           */
  0xF8, 0x7F,                                        /* 0x06,0x07                                           */
  0xCE, 0x7F,                                        /* 0x08,0x09                                           */
  0xCE, 0x7F,                                        /* 0x0A,0x0B                                           */
  0xCE, 0x7F,                                        /* 0x0C,0x0D                                           */
  0x7E, 0x7E,                                        /* 0x0E,0x0F                                           */
  0x7E, 0x7E,                                        /* 0x10,0x11                                           */
  0x7E, 0x7E,                                        /* 0x12,0x13                                           */
  0xFE, 0x73,                                        /* 0x14,0x15                                           */
  0xFE, 0x73,                                        /* 0x16,0x17                                           */
  0xFE, 0x73,                                        /* 0x18,0x19                                           */
  0xFE, 0x1F,                                        /* 0x1A,0x1B                                           */
  0xFE, 0x1F,                                        /* 0x1C,0x1D                                           */
  0xFE, 0x1F                                         /* 0x1E,0x1F                                           */
};

/* Private function prototypes -----------------------------------------------------------------------------*/
static void _initI2CMaster(uint32_t i2c_clock);
static bool _writeRegister(uint8_t i2c_addr, I2C_WriteTypeDef *writeData, uint8_t length);
static uint8_t _readRegister(uint8_t i2c_addr, uint8_t registerAddress);

/* Global functions ----------------------------------------------------------------------------------------*/
/*********************************************************************************************************//**
 * @brief  BMD26M088 module wire number select.
 * @param wire_number: select wire number.
 * @retval None
 ************************************************************************************************************/
BMD26M088_selStatus BMD26M088_selWire(uint32_t wire_number)
{
  if(CHECK_WIREn(wire_number) == -1)
  {
    return BMD26M088_FAILURE;
  }

  gBMD26M088_WIRE = wire_number;
  return BMD26M088_SUCCESS;
}

/*********************************************************************************************************//**
 * @brief  BMD26M088 module initial.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  i2c_clock: I2C communication rate.
 * @retval None
 ************************************************************************************************************/
void BMD26M088_Init(uint8_t i2c_addr, uint32_t i2c_clock)
{
  _initI2CMaster(i2c_clock);

  /* Static display image                                                                                   */
  BMD26M088_writeCmd(i2c_addr, BMD26M088_HT16D33_CMD_DISPLAY_MODE, BMD26M088_DISPLAY_STATIC);

  /* Display value mode, Matrix type is 16x16                                                               */
  BMD26M088_writeCmd(i2c_addr, BMD26M088_HT16D33_CMD_CONFIG_MODE, BMD26M088_DISPLAY_VALUE|BMD26M088_MATRIX_16x16); 

  /* Configure HT16D33B and Set the constant flow rate to 6ma                                               */
  BMD26M088_writeCmd(i2c_addr, BMD26M088_HT16D33_CMD_SET_CURRENT, BMD26M088_CCR_6MA);

  /* The global brightness,maximum                                                                          */
  BMD26M088_writeCmd(i2c_addr, BMD26M088_HT16D33_CMD_SET_BRIGHTNESS, 0xff);

  /* Blank area voltage control, default 0x8f                                                               */
  BMD26M088_writeCmd(i2c_addr, BMD26M088_HT16D33_CMD_SET_V_BLANK, 0x8f);

  /* Set Internal crystal oscillator on                                                                     */
  BMD26M088_writeCmd(i2c_addr, BMD26M088_HT16D33_CMD_SYS_CONTROL, BMD26M088_SYS_OSC_ON);

  /* Clear screen                                                                                           */
  BMD26M088_clearAll(i2c_addr);

  /* Set led ram on                                                                                         */
  BMD26M088_setLedRAMEnable(i2c_addr);

  /* Set mode for normaly display                                                                           */
  BMD26M088_writeCmd(i2c_addr, BMD26M088_HT16D33_CMD_SYS_CONTROL, BMD26M088_SYS_DIS_ON);
}

/*********************************************************************************************************//**
 * @brief  Check connection.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  i2c_clock: I2C communication rate.
 * @retval TRUE or FALSE
 ************************************************************************************************************/
bool BMD26M088_isConnected(uint8_t i2c_addr, uint32_t i2c_clock)
{
  uint8_t i;
  volatile uint16_t j;
  uint8_t triesBeforeGiveup = 5;
  I2C_WriteTypeDef writeData;

  _initI2CMaster(i2c_clock); /* Initial I2C Master needs to be performed before execution.                  */

  writeData.registerAddress = 0xFF; /* write dummy registerAddress to check BMD26M088 connected             */

  /* Try to check connection, 5 times                                                                       */
  for (i = 0; i < triesBeforeGiveup; i++)
  {
    /* If I2CResult == I2C_MASTER_FINISH, it mean BMD26M088 connected                                       */
    if (_writeRegister(i2c_addr, &writeData, 1) == TRUE)
    {
      return TRUE;
    }

    /* Release I2C bus for other master by adding a delay                                                   */
    for (j = 0; j < 5000; j++)
    {
      __NOP(); // Prevent delay loop be optimized
    }
  }

  return FALSE;
}

/*********************************************************************************************************//**
 * @brief  Clear all.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @retval None
 ************************************************************************************************************/
void BMD26M088_clearAll(uint8_t i2c_addr)
{
  BMD26M088_writeAllRGB(i2c_addr, 0, 0, 0);
}

/*********************************************************************************************************//**
 * @brief  Clear one.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  RGB_Number: RGB index.
 * @retval None
 ************************************************************************************************************/
void BMD26M088_clearRGB(uint8_t i2c_addr, uint8_t RGB_Number)
{
  BMD26M088_writeRGB(i2c_addr, RGB_Number, 0, 0, 0);
}

/*********************************************************************************************************//**
 * @brief  Clear row.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  RowIndex: Row number 0 ~ 7.
 * @retval None
 ************************************************************************************************************/
void BMD26M088_clearRow(uint8_t i2c_addr, uint8_t RowIndex)
{
  BMD26M088_writeRow(i2c_addr, RowIndex, 0, 0, 0);
}

/*********************************************************************************************************//**
 * @brief  Clear column.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  ColumnIndex: Column number 0 ~ 7.
 * @retval None
 ************************************************************************************************************/
void BMD26M088_clearColumn(uint8_t i2c_addr, uint8_t ColumnIndex)
{
  BMD26M088_writeColumn(i2c_addr, ColumnIndex, 0, 0, 0);
}

/*********************************************************************************************************//**
 * @brief  Write a rgb to display,the color data from color seting.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  RGB_Number: RGB index.
 * @param  R: Red color data.
 * @param  G: Green color data.
 * @param  B: Blue color data.
 * @retval None
 ************************************************************************************************************/
void BMD26M088_writeRGB(uint8_t i2c_addr, uint8_t RGB_Number, uint8_t R, uint8_t G, uint8_t B)
{
  const uint8_t *p=RAM_INDEX_TABLE[0];
  I2C_WriteTypeDef writeData;

  writeData.registerAddress = BMD26M088_HT16D33_CMD_WRITE_LED_DATA;
  writeData.data[0] = p[RGB_Number];
  writeData.data[1] = R;
  writeData.data[2] = G;
  writeData.data[3] = B;

  _writeRegister(i2c_addr, &writeData, 5);
}

/*********************************************************************************************************//**
 * @brief  Write a column to display the same color from color table.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  ColumnIndex: Column number 0 ~ 7.
 * @param  R: Red color data.
 * @param  G: Green color data.
 * @param  B: Blue color data.
 * @retval None
 ************************************************************************************************************/
void BMD26M088_writeColumn(uint8_t i2c_addr, uint8_t ColumnIndex, uint8_t R, uint8_t G, uint8_t B)
{
  uint8_t i;
  I2C_WriteTypeDef writeData;

  writeData.registerAddress = BMD26M088_HT16D33_CMD_WRITE_LED_DATA;

  for (i = 0; i < 8; i++)
  {
    writeData.data[0] = RAM_INDEX_TABLE[i][ColumnIndex];
    writeData.data[1] = R;
    writeData.data[2] = G;
    writeData.data[3] = B;

    _writeRegister(i2c_addr, &writeData, 5);
  }
}

/*********************************************************************************************************//**
 * @brief  Write a row to display the same color from color table.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  RowIndex: Row number 0 ~ 7.
 * @param  R: Red color data.
 * @param  G: Green color data.
 * @param  B: Blue color data.
 * @retval None
 ************************************************************************************************************/
void BMD26M088_writeRow(uint8_t i2c_addr, uint8_t RowIndex, uint8_t R, uint8_t G, uint8_t B)
{
  uint8_t ram_addr=RAM_INDEX_TABLE[RowIndex][0];
  uint8_t rgb_count = 0;
  uint8_t data_index = 0;
  I2C_WriteTypeDef writeData;

  writeData.registerAddress = BMD26M088_HT16D33_CMD_WRITE_LED_DATA;
  writeData.data[data_index++] = ram_addr;

  /* Combine rgb data and some dummy data to linear address                                                 */
  while (1)
  {
    writeData.data[data_index++] = R;/* Start filling RGB data from index 1                                 */
    writeData.data[data_index++] = G;
    writeData.data[data_index++] = B;
    rgb_count++; /* Point to next rgb                                                                       */
    if (rgb_count > 7)
    {
      break; /* Is last rgb                                                                                 */
    }
    ram_addr += 3; /* The address for next rgb                                                              */
    /* If the ram_addr no equal to the next address                                                         */
    while (ram_addr < RAM_INDEX_TABLE[RowIndex][rgb_count])
    {
      writeData.data[data_index++] = 0; /* insert zero                                                      */
      ram_addr++; /* ram_addr add 1                                                                         */
    }
  }

  _writeRegister(i2c_addr, &writeData, data_index + 1); /* Length = "data_index" + "1 (registerAddress)"    */
}

/*********************************************************************************************************//**
 * @brief  Write all rgb to display the same color from seting.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  R: Red color data.
 * @param  G: Green color data.
 * @param  B: Blue color data.
 * @retval None
 ************************************************************************************************************/
void BMD26M088_writeAllRGB(uint8_t i2c_addr, uint8_t R, uint8_t G, uint8_t B)
{
  uint8_t i, data_index, rgb_count;
  uint8_t ram_addr = RAM_INDEX_TABLE[0][0];
  I2C_WriteTypeDef writeData;

  writeData.registerAddress = BMD26M088_HT16D33_CMD_WRITE_LED_DATA;

  for (i = 0; i < 8; i++)
  {
    data_index = 0;
    rgb_count = 0;
    ram_addr = RAM_INDEX_TABLE[i][0];
    writeData.data[data_index++] = ram_addr;

    while (1)
    {
      writeData.data[data_index++] = R; /* Start filling RGB data from index 1                              */
      writeData.data[data_index++] = G;
      writeData.data[data_index++] = B;
      rgb_count++;
      if (rgb_count > 7)
      {
        break;
      }
      ram_addr += 3;
      while (ram_addr < RAM_INDEX_TABLE[i][rgb_count])
      {
        writeData.data[data_index++] = 0;
        ram_addr++;
      }
    }

    _writeRegister(i2c_addr, &writeData, data_index + 1); /* Length = "data_index" + "1 (registerAddress)"  */
  }
}

/*********************************************************************************************************//**
 * @brief  Set display a char from char table.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  ch: ASCII numeric values or characters.
 * @param  R: Red color data.
 * @param  G: Green color data.
 * @param  B: Blue color data.
 * @retval None
 ************************************************************************************************************/
void BMD26M088_DrawAsciiChar(uint8_t i2c_addr, unsigned char Ch, uint8_t R, uint8_t G, uint8_t B)
{
  uint8_t Temp, Index, i, j;

  Index = Ch - 32;

  for (i = 0; i < 8; i++)
  {
    Temp = ASCII_8X8[Index][i];
    for (j = 0; j < 8; j++)
    {
      if ((Temp >> j)  & 0x01)
      {
        BMD26M088_writeRGB(i2c_addr, ((i * 8) + j), R, G, B);
      }
    }
  }
}

/*********************************************************************************************************//**
 * @brief  Set Led RAM Enable.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @retval None
 ************************************************************************************************************/
void BMD26M088_setLedRAMEnable(uint8_t i2c_addr)
{
  uint8_t i;
  I2C_WriteTypeDef writeData;

  writeData.registerAddress = BMD26M088_HT16D33_CMD_SET_LED_ON_OFF;
  writeData.data[0] = 0x00; /* RAM Address                                                                  */
  for (i = 0; i < 32; i++)
  {
    writeData.data[i + 1] = LED_ON_OFF_16_16[i];
  }

  _writeRegister(i2c_addr, &writeData, 34);
}

/*********************************************************************************************************//**
 * @brief  Disable led ram.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @retval None
 ************************************************************************************************************/
void BMD26M088_setLedRAMDisable(uint8_t i2c_addr)
{
  uint8_t i;
  I2C_WriteTypeDef writeData;

  writeData.registerAddress = BMD26M088_HT16D33_CMD_SET_LED_ON_OFF;
  writeData.data[0] = 0x00; /* RAM Address                                                                  */
  for (i = 0; i < 32; i++)
  {
    writeData.data[i + 1] = 0x00;
  }

  _writeRegister(i2c_addr, &writeData, 34);
}

/*********************************************************************************************************//**
 * @brief  Reset BMD26M088 module.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @retval None
 ************************************************************************************************************/
void BMD26M088_reset(uint8_t i2c_addr)
{
  I2C_WriteTypeDef writeData;

  writeData.registerAddress = BMD26M088_HT16D33_CMD_RESET;

  _writeRegister(i2c_addr, &writeData, 1);
}

/*********************************************************************************************************//**
 * @brief  Write data to the register.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  cmd: command
 *   This parameter can be one of the following values:
 *     @arg  HT16D33_CMD_DISPLAY_MODE        : 0x30
 *     @arg  HT16D33_CMD_CONFIG_MODE         : 0x31
 *     @arg  HT16D33_CMD_CONNECT_MODE        : 0x34
 *     @arg  HT16D33_CMD_SYS_CONTROL         : 0x35
 *     @arg  HT16D33_CMD_SET_CURRENT         : 0x36
 *     @arg  HT16D33_CMD_SET_BRIGHTNESS      : 0x37
 *     @arg  HT16D33_CMD_SET_V_BLANK         : 0x39
 * @param  data: data.
 * @retval None
 ************************************************************************************************************/
void BMD26M088_writeCmd(uint8_t i2c_addr, uint8_t cmd, uint8_t data)
{
  I2C_WriteTypeDef writeData;

  writeData.registerAddress = cmd;
  writeData.data[0] = data;

  _writeRegister(i2c_addr, &writeData, 2);
}

/*********************************************************************************************************//**
 * @brief  Read data from the register.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  cmd: read CMD
 * @param  data: data.
 * @retval Read data from the register
 ************************************************************************************************************/
uint8_t BMD26M088_readCmd(uint8_t i2c_addr, uint8_t cmd)
{
  return _readRegister(i2c_addr, cmd);
}

/*********************************************************************************************************//**
 * @brief  Set all RGB brightness.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  brightness: Brightness, 0~255, the max brightness is 255.
 * @retval None
 ************************************************************************************************************/
void BMD26M088_setBrightness( uint8_t i2c_addr, uint8_t brightness)
{
  I2C_WriteTypeDef writeData;

  writeData.registerAddress = BMD26M088_HT16D33_CMD_SET_BRIGHTNESS;
  writeData.data[0] = brightness;

  _writeRegister(i2c_addr, &writeData, 2);
}

/*********************************************************************************************************//**
 * @brief  Set current.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  level: level current = 1 + (3*level) (mA).
 * @retval None
 ************************************************************************************************************/
void BMD26M088_setCurrent(uint8_t i2c_addr, uint8_t level)
{
  I2C_WriteTypeDef writeData;

  writeData.registerAddress = BMD26M088_HT16D33_CMD_SET_CURRENT;
  writeData.data[0] = level;

  _writeRegister(i2c_addr, &writeData, 2);
}

/*********************************************************************************************************//**
 * @brief  Set Over Temperature Protection funtion.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  status: Enable or disable the over temperature protection.
 *     @arg  1 : Enable the over temperature protection
 *     @arg  0 : Disable the over temperature protection.
 * @param  auto_control: If the over temperature protection occurs, Automatic control or not ,to control the LED RAM on/off.
 *   This parameter can be one of the following values:
 *     @arg  ENABLE  : Enable automatic control.
 *     @arg  DISABLE : Disable automatic control.
 * @retval None
 ************************************************************************************************************/
#define MODE_CONROL_CMD 0x38
void BMD26M088_setOverTemperatureProtect(uint8_t i2c_addr, uint8_t status, bool auto_control)
{
  uint8_t param = 0;

  if (status == 1)
  {
    param |= 0x80; /* Enable OverTemperatureProtect                                                         */
    if (auto_control)
    {
      param |= 0x40; /* Enable auto_control                                                                 */
    }
    else
    {
      param &= ~0x40; /* Disable auto_control                                                               */
    }
  }
  else
  {
    param &= 0x7f;
  }

  BMD26M088_writeCmd(i2c_addr, MODE_CONROL_CMD, param);
}

/*********************************************************************************************************//**
 * @brief  Return Over Temperature Protection flag.
 *         (If over temperature protection and manual control are enabled.)
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @retval TRUE or FALSE (TRUE: Over Temperature Protection flag triggered.)
 ************************************************************************************************************/
#define FUNTION_FLAG_CMD 0x73
bool BMD26M088_getOverTemperatureFlag(uint8_t i2c_addr)
{
  uint8_t data;

  data = BMD26M088_readCmd(i2c_addr, FUNTION_FLAG_CMD);

  if (data & 0x01)
  {
    return TRUE;
  }
  else
  {
    return FALSE;
  }
}

/*********************************************************************************************************//**
 * @brief  Set  gradient.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  mode: set Gradient work mode is Gradient garma/linker or blink.
 * @param  T1: The time of dark to light 
 * @param  T2: Keep light time.
 * @param  T3: The time of light to dark.
 * @param  T4: Keep dark time.
 * @retval None
 ************************************************************************************************************/
#define GROBAL_GRADIENT_CONTROL_CMD 0x32
#define SET_GROBAL_GRADIENT_TIME_CMD 0x33
void BMD26M088_setGradient(uint8_t i2c_addr, uint8_t mode, uint8_t T1, uint8_t T2, uint8_t T3, uint8_t T4)
{
  uint8_t param0 = 0;
  uint8_t param1 = 0;

  param0 = 0x80; /* Enable                                                                                  */

  param0 |= 0x20; /* Globe                                                                                  */
  param0 |= T3 << 4; /* T3                                                                                  */
  param0 |= T4 << 2; /* T4                                                                                  */
  param0 |= T2;//T2

  switch (mode)
  {
    case BMD26M088_GFS_GRADIENT_GARMA:
    {
      param0 |= 0x40; /* Garma                                                                              */
      break;
    }
    case BMD26M088_GFS_GRADIENT_LINEAR:
    {
      break;
    }
    case BMD26M088_GFS_BLINK:
    {
      param1 = 0x80; /* Blink                                                                               */
      break;
    }
    default:
    {
      break;
    }
  }

  param1 |= T1; /* Param1                                                                                   */

  BMD26M088_writeCmd(i2c_addr, GROBAL_GRADIENT_CONTROL_CMD, param0);
  BMD26M088_writeCmd(i2c_addr, SET_GROBAL_GRADIENT_TIME_CMD, param1);
}

/* Private functions ---------------------------------------------------------------------------------------*/
/*********************************************************************************************************//**
 * @brief  Initial I2C Master.
 * @param  i2c_clock: I2C communication rate.
 * @retval None
 ************************************************************************************************************/
static void _initI2CMaster(uint32_t i2c_clock)
{
  /*
    !!! PORTING !!!
    Implement I2C Master Initial.
  */
  I2CMaster_Init(gBMD26M088_WIRE, WIRE_MASTER_ADDRESS, i2c_clock);
}

/*********************************************************************************************************//**
 * @brief  Write Register.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  writeData: pointer to a I2C_WriteTypeDef structure.
 * @param  length: Tx Length ("Data length" + "1 (registerAddress)")
 * @retval None
 ************************************************************************************************************/
static bool _writeRegister(uint8_t i2c_addr, I2C_WriteTypeDef *writeData, uint8_t length)
{
  /*
    !!! PORTING !!!
    Implement I2C write operation.
  */
  I2CMaster_Status_Enum I2CResult = I2C_MASTER_GOING;
  I2CMaster_Typedef I2CMaster = { 0 };

  I2CMaster.uSlaveAddress = i2c_addr;
  I2CMaster.Tx.puBuffer = (uint8_t *)writeData;
  I2CMaster.Tx.uLength = length;
  I2CMaster.uTimeout_us = 30000;
  I2CMaster.isDynamicOnOff_I2C = FALSE;

  I2CMaster_Write(gBMD26M088_WIRE, &I2CMaster);

  do {
  I2CResult = I2CMaster_GetStatus(gBMD26M088_WIRE);
  } while (I2CResult == I2C_MASTER_GOING);

  if (I2CResult == I2C_MASTER_FINISH)
  {
    /* If I2CResult == I2C_MASTER_FINISH, it mean BMD26M088 connected                                       */
    return TRUE;
  }

  return FALSE;
}

/*********************************************************************************************************//**
 * @brief  Read Register.
 * @param  i2c_addr: I2C slave address.
 *   This parameter can be one of the following values:
 *     @arg  BMD26M088_I2C_ADDRESS_VDD       : 0x67
 *     @arg  BMD26M088_I2C_ADDRESS_GND       : 0x64
 *     @arg  BMD26M088_I2C_ADDRESS_SCL       : 0x65
 *     @arg  BMD26M088_I2C_ADDRESS_SDA       : 0x66
 *     @arg  BMD26M088_I2C_ADDRESS_BOARDCAST : 0x2E
 * @param  registerAddress: Register Address.
 * @retval read register data
 ************************************************************************************************************/
static uint8_t _readRegister(uint8_t i2c_addr, uint8_t registerAddress)
{
  /*
    !!! PORTING !!!
    Implement I2C read operation.
  */
  uint8_t uTxCmd = 0;
  uint8_t uRxData = 0;
  I2CMaster_Status_Enum I2CResult = I2C_MASTER_GOING;
  I2CMaster_Typedef I2CMaster = { 0 };

  I2CMaster.uSlaveAddress = i2c_addr;
  I2CMaster.Tx.puBuffer = (uint8_t *)&uTxCmd;
  I2CMaster.Tx.uLength = 1;
  I2CMaster.Rx.puBuffer = (uint8_t *)&uRxData;
  I2CMaster.Rx.uLength = 1;
  I2CMaster.uTimeout_us = 30000;
  I2CMaster.isDynamicOnOff_I2C = FALSE;

  uTxCmd = registerAddress;

  I2CMaster_WriteRead(gBMD26M088_WIRE, &I2CMaster);

  do {
    I2CResult = I2CMaster_GetStatus(gBMD26M088_WIRE);
  } while (I2CResult == I2C_MASTER_GOING);

  if (I2CResult == I2C_MASTER_FINISH)
  {
    return uRxData;
  }

  return 0;
}

