#ifndef __ST7789_H__
#define __ST7789_H__

#include <stdint.h>

#define ST7789_NOP      0x00
#define ST7789_SWRST    0x01
#define ST7789_RDDID    0X04
#define ST7789_RDDST    0X09
#define ST7789_RDDPM    0X0A
#define ST7789_RDMADCTL 0X0B
#define ST7789_RDCOLMOD 0X0C
#define ST7789_RDDIM    0X0D
#define ST7789_RDDSM    0X0E
#define ST7789_RDDSDR   0x0F
#define ST7789_SLPIN    0x10
#define ST7789_SLPOUT   0X11
#define ST7789_PTLON    0x12
#define ST7789_NORON    0x13
#define ST7789_PTLOFF   NORON
#define ST7789_NOROFF   PTLON
#define ST7789_INVOFF   0x20
#define ST7789_INVON    0x21
#define ST7789_GAMSET   0x26
#define ST7789_DISPOFF  0x28
#define ST7789_DISPON   0X29
#define ST7789_CASET    0x2a
#define ST7789_PASET    0x2B
#define ST7789_RAMWR    0x2c
#define ST7789_RAMRD    0x2e
#define ST7789_PTLAR    0x30
#define ST7789_VSCRDEF  0x33
#define ST7789_TEOFF    0x34
#define ST7789_TEON     0x35
#define ST7789_MADCTL   0x36
#define ST7789_VSCRSADD 0x37
#define ST7789_IDMOFF   0x38
#define ST7789_IDMON    0x39
#define ST7789_COLMOD   0x3a
#define ST7789_RAMWRC   0x3c
#define ST7789_RAMRDC   0x3e
#define ST7789_TESCAN   0x44
#define ST7789_RDTESCAN 0x45
#define ST7789_WRDISBV  0x51
#define ST7789_RDDISBV  0x52
#define ST7789_WRCTRLD  0x53
#define ST7789_RDCTRLD  0x54
#define ST7789_WRCACE   0x55
#define ST7789_RDCABC   0x56
#define ST7789_WRCABCMB 0x5e
#define ST7789_RDCABCMB 0x5f
#define ST7789_RDABCSDR 0x68
#define ST7789_RAMCTRL  0xb0
#define ST7789_RGBCTRL  0XB1
#define ST7789_PORCTRL  0XB2
#define ST7789_FRCCTRL  0XB3
#define ST7789_GCTRL    0XB7
#define ST7789_DGMEN    0xba
#define ST7789_VCOMS    0XBB
#define ST7789_LCMCTRL  0XC0
#define ST7789_VDVVRHEN 0XC2
#define ST7789_VRHS     0XC3
#define ST7789_VDVSET   0XC4
#define ST7789_VCMOFSET 0XC5
#define ST7789_FRCTRL2  0XC6
#define ST7789_CABCCTRL 0XC7
#define ST7789_REGSEL1  0XC8
#define ST7789_REGSEL2  0XCA
#define ST7789_PWMFRSEL 0XCC
#define ST7789_PWCTRL1  0XD0
#define ST7789_VAPVANEN 0XD2
#define ST7789_CMD2EN   0XDF
#define ST7789_PVGAMCTRL 0XE0
#define ST7789_NVGAMCTRL 0xE1
#define ST7789_DGMLUTR  0xE2
#define ST7789_DGMLUTB  0XE3
#define ST7789_GATECTRL 0XE4
#define ST7789_PWCTRL2  0XE8
#define ST7789_EQCTRL   0XE9
#define ST7789_PROMCTRL 0XEC
#define ST7789_PROMEN   0XFA
#define ST7789_NVMSET   0XFC
#define ST7789_PROMACT  0XFE
2020-05-16-22-07-12.png
inline uint16_t RGB888_TO_565(uint32_t color){
    return (color << 4);
}

/* common controller data */
struct point{
    int x; 
    int y;
};

struct rect{
    struct point top_left;
    struct point bottom_right;
};

struct st7789{
    int width;
    int height;
    struct point curr_pos;
    char* disp_buffer;
    void* priv_data;
    #ifdef ST7789_ID_REQUIRED
    uint64_t id;
    #endif
};

/* init and deinit */
void st7789_init(struct st7789* dev);
void st7789_deinit(struct st7789* dev);

/* hardware reset, connect to RST pins */
extern void st7789_hw_rst();

/* wait some time. */
extern void st7789_wait_ms(long time);

/* send a single command */
extern int st7789_write_cmd_single(uint8_t cmd);
#if 0
/* send some command */
extern int st7789_write_cmd(void* cmd, long length);
/* send a lot of command */
extern int st7789_write_cmd_dma(void* cmd, long length);
#endif

/* send a single data */
extern int st7789_write_data_single(uint8_t data);
/* send some data */
extern int st7789_write_data(void* data, long length);
/* send a lot of data */
extern int st7789_write_data_dma(void* data, long length);
/* send a lot of same data */
extern int st7789_write_same_data_dma(uint16_t data, long length);

#ifdef ST7789_ID_REQUIRED
/* read a single data */
extern uint8_t st7789_read_data_single();
/* read some data */
extern int st7789_read_data(void* buf, long length);
/* read a lot of data */
extern int st7789_write_data_dma(void* buf, long length);
#endif

/** @brief soft reset controller. */
void st7789_sw_rst();

/* -----------------------------------------
*
*   MODULE DEFINED FUNCTION.
*
*  ----------------------------------------- 
*/


/** @brief set sleep status.
 * @param status set to 1 if try to enter sleep in mode.
 * !!! after reset default is sleep in.
 * sleep status will be changed to sleep out during init.
*/
void st7789_sleep(uint8_t status);

/** @brief set partial display.
 * @param status set to 1 to enter partial display mode.
 * after reset default normal display mode on.
*/
void st7789_partial_display(uint8_t status);

/** @brief set display inversion.
 * @param status set to 1 display will invert.
 * after reset inversion is off.
*/
void st7789_inversion(uint8_t status);

/** @brief set gama value.
 *  @param gamma 4bit value GC[0:3].
 *  after reset gamma value is 0x1.
*/
void st7789_gamma(uint8_t gamma);

/** @brief set display status.
 *  @param status set to 0 display will turn off.
 *  after reset value is display off.
 */
void st7789_display(uint8_t status);

/** @brief set mem write range.
 *  @param start top-left point.
 *  @param end   bottom-right point.
 *  used in partial refresh, the next step is ramwr.
 */
void st7789_setXY_range(struct rect* area);

/** @brief write buffer to screen
 *  @param buf frame buffer
 *  @param buflen frame buffer size
 */
void st7789_write_buf(uint8_t* buf, long buflen);

#ifdef ST7789_ID_REQUIRED
/** @brief read buffer
 *  @param buf frame buffer
 *  @param buflen frame buffer size 
 */
void st7789_read_buf(uint8_t* buf, long buflen);
#endif 

/** @brief set partial display area.
 *  @param start top position.
 *  @param end   bottom position.
 */
void st7789_partial_area(uint16_t start, uint16_t end);

/** @brief set vertical display area.
 *  @param top top fixed area height.
 *  @param view scroll area height.
 *  @param bottom bottom fixed height.
 *  next step is ramwr. 
 *  !!!The condition is TFA+VSA+BFA = 320, otherwise Scrolling mode is undefined. 
 */
void st7789_vertical_scoll(uint16_t top, uint16_t view, uint16_t bottom);

#define MADCTL_PAGE_TOP_TO_BOTTOM 0x80
#define MADCTL_PAGE_BOTTOM_TO_TOP 0x00
#define MADCTL_COLUMN_LEFT_TO_RIGHT 0x40
#define MADCTL_COLUMN_RIGHT_TO_LEFT 0x00
#define MADCTL_ORDER_REVERSED       0x02
#define MADCTL_ORDER_NORMAL         0X00
#define MADCTL_BIT_ORDER_BGR        0x04
#define MADCTL_BIT_ORDER_RGB        0x00
/** @brief: change memory access.
*   @param access memory access value.
*   reset to 0x0000, sw reset not change.
*   MADCTL_*
*/
void st7789_memory_access(uint8_t access);

/** @brief: idle mode.
*   @param status set to 0 to exit idle mode.
*   after reset value is 0.
*   in idle mode color reduce to 3bits.
*/
void st7789_idle_mode(uint8_t status);

#define PIXFMT_RGB_65K   (0b101 << 4)
#define PIXFMT_RGB_262K  (0b110 << 4)
#define PIXFMT_RGB_12BITS 0b011
#define PIXFMT_RGB_16BITS 0b101
#define PIXFMT_RGB_18BITS 0b110
/** @brief change interface pixel format.
*   @param data colmod byte.
*   PIXFMT_*
*/  
void st7789_pixel_format(uint8_t data);

/** @brief change brightness.
*   @param data 0xff means the highest brightness. 
*   default is 0x00. 
*   this command will not change backlight brightness.
*/  
void st7789_brightness(uint8_t brightness);

#define DPCTRL_BCTRL_ON  0x02
#define DPCTRL_BCTRL_OFF 0X00
#define DPCTRL_DD_ON     0x08
#define DPCTRL_DD_OFF    0X00
#define DPCTRL_BL_ON     0x00 
#define DPCTRL_BL_OFF    0X04
/** @brief change ctrl dispaly register.
 *  @param data register value.
 *  DPCTRL_*
 */
void st7789_ctrl_display(uint8_t data);

#define EHN_OFF     0X00
#define EHN_USER    (0X01 << 4)
#define EHN_STILL   (0X02 << 4)
#define EHN_MOVING  (0X03 << 4)
#define EHN_LOW     0X00
#define EHN_MEDIUM  0X01
#define EHN_HIGH    0x03
/** @brief change display color enhance.
 *  @param data register value.
 *  EHN_OFF EHN_USER EHN_STILL EHN_MOVING.
 *  EHN_LOW EHN_MEDIUM EHN_HIGH.
 *  default is 0x0000;
 */
void st7789_set_enhance(uint8_t data);

/** @brief change minium brightness.
 *  @param data register value.
 *  default is 0x0000;
 */
void st7789_set_minium_brightness(uint8_t data);

#define RAMCTL_MCU   0x0000
#define RAMCTL_RGB   0X0011
#define RAMCTL_VSYNC 0X0012
#define RAMCTL_LE    0X0800
#define RAMCTL_BE    0X0000
#define RAMCTL_18BIT 0X0400
#define RAMCTL_6BIT  0X0000
/** @brief ram control
*   @param data register value.
*   @note Little Endian only can be supported in 65K 8-bit and 9-bit interface
*   RAMCTL_MCU RAMCTL_RGB RAMCTL_VSYNC
*   RAMCTL_LE RAMCTL_BE
*   RAMCTL_18BIT RAMCTL_6BIT
*/
void st7789_ramctl(uint16_t data);

#define RGBCTL_WO_DIRECT (0X00 << 7)
#define RGBCTL_WO_SHIFT  (0X01 << 7)
#define RGBCTL_RCM_MCU   (0X00 << 5)
#define RGBCTL_RCM_DE    (0X10 << 5)
#define RGBCTL_RCM_HV    (0X11 << 5)
#define RGBCTL_VSYNC_LOW (0X00 << 3)
#define RGBCTL_VSYNC_HIGH (0X1 << 3)
#define RGBCTL_HSPL_LOW   (0X00 << 2)
#define RGBCTL_HSPL_HIGH  (0X01 << 2)
// DOTCLK
#define RGBCTL_DPL_POS    (0X00 << 1)
#define RGBCTL_DPL_NEG    (0X01 << 1)
// ENABLE
#define RGBCTL_EPL_HIGH   (0X00 << 0)
#define RGBCTL_EPL_LOW    (0X01 << 0)

#define RGBCTL_VYSNC_BACK_PORCH(x) (((x&0x7f)|0x02) << 8)
#define RGBCTL_HSYNC_BACK_PORCH(x) ((x&0x1f) << 16)
/** @brief RGB Interface Control 
 *  @param data register value.
 *  HSYNC_BACK_PORCH have minium
 *  RGBCTL_*
 */
void st7789_rgbctl(uint32_t data);

#define PORCTL_BACK_PORCH_NORM(x)  ((x & 0x7f) | 0X01)
#define PORCTL_FRONT_PORCH_NORM(x) (((x & 0x7f) | 0X01) << 8)
#define PORCTL_PRCH_CTRL_ENABLE    (0x01 << 16)
#define PORCTL_PRCH_CTRL_DISABLE   (0x00 << 16)
#define PORCTL_BACK_PORCH_IDLE(x)  (((x & 0X0F) | 0X01) << 28)
#define PORCTL_FRONT_PORCH_IDLE(x) (((x & 0X0F) | 0X01) << 24)
#define PORCTL_BACK_PORCH_PART(x)  (((x & 0x0f) | 0x01) << 36)
#define PORCTL_FRONT_PORCH_PART(x) (((x & 0x0f) | 0x01) << 32)
/** @brief Porch setting
 *  @param data register value 
 *  PORCTL_*
 *  defalut is 0x08, 0x08, disabled, 0x02, 0x02, 0x02, 0x02 
 */
void st7789_porctrl(uint64_t data);

#define FRCTL_ENABLE            (0x01 << 4)
#define FRCTL_DISABLE           (0x00 << 4)
#define FRCTL_DIV_8             (0x11 << 0)
#define FRCTL_DIV_4             (0x10 << 0)
#define FRCTL_DIV_2             (0x01 << 0)
#define FRCTL_DIV_1             (0x00 << 0)
#define FRCTL_INVERSION_DOT     (0X00 << 13)
#define FRCTL_INVERSION_COLUMN  (0X07<< 13)
#define FRCTL_FRAMERATE(x)      ((x & 0x1f) << 8)
#define FRCTL_IDLE(x)           x
#define FRCTL_PARTIAL(x)        (x << 8)
#define FRAMERATE_119Hz         0x00
#define FRAMERATE_90Hz          0x05
#define FRAMERATE_60Hz          0x0f
#define FRAMERATE_50Hz          0x15
#define FRAMERATE_45Hz          0x19
#define FRAMERATE_39Hz          0x1f
/** @brief framerate setting in idel mode and partial mode.
 *  @param data register value 
 *  FRCTL_* FRAMERATE_*
 *  defalut is disabled, 60fps.
 */
void st7789_framerate_sp(uint32_t data);

/** @brief framerate setting in idel mode and partial mode.
 *  @param data register value 
 *  defalut is 0x35: 13.26, -10.43 
 */
void st7789_gate_voltage(uint8_t data);

/** @brief vcom voltage setting
 *  @param voltage 0.1V ~ 1.675V
 *  default is 0.9V
 */
void st7789_com_voltage(float voltage);

/** @brief frame rate setting in normal mode
 *  @param framerate FRAMERATE_*Hz
 *  default is 60Hz
 */
void st7789_framerate(uint8_t framerate);

/** @brief fill area with color
 *  @param area  
 *  @param color rgb565 format color
 */
void st7789_fill(struct rect* area, uint16_t color);

/** @brief fill area with color
*   @return dev->id
*/
void st7789_init(struct st7789* dev);

/** @brief display buffer at poistion.
*/
void st7789_disp_buf_at(struct rect* area, uint8_t* buf);

#endif