/**
  **********************************************************************************************************************
  * @file    oled.c
  * @author  ������
  * @version V0.1.0
  * @date    2021-3-4
  * @brief   ���ļ��ṩ����ģ���������ܣ��Թ���OLED�������¹��ܣ�
  *           + �ӿ�����ͨ�Žӿڶ���
  *           + ������ʼ������
  *           + OLED ���ƺ���
  *           + ����/���㺯��
  *           + OLED ��������
  *           + �����������
  *           + ��ͼ�κ���
  *           + �����ı�/���ֺ���
  *           + ��ʽ���������
  *
  **********************************************************************************************************************
  * ��ֲʱֻ���޸� "�����ӿ�ͨ��" �����ĺ�������
  * ʹ�÷�ʽ:
  *    1����ֲ���޸Ķ˿ں궨�塢���� DriveInit; ͨ���궨�� "_DRIVE_INTERFACE_TYPE" ѡ��ӿ�����
  *    2��ʹ��ǰ��ʼ������ OLED_Init, ͬʱ����ʹ�� OLED_Clear ��������
  *    3�����ڵ��ú��� OLED_ShowTask, ����ͬ����������(������ͬ���� OLED ����ʾ)
  *    4���ɶ���궨�� "_USE_OLED_PRINTF" ʹ��OLED ��ʽ�����������, ������ printf �Ĺ���
  *
  **********************************************************************************************************************
  */

/* Includes ----------------------------------------------------------------------------------------------------------*/
#include "oled.h"
#include "stdlib.h"
#include "font12x12.h"
#include "font16x16.h"
#include "font24x24.h"
#include "font32x32.h"
#include "delay.h"
#include "sys.h"

#ifdef _USE_OLED_PRINTF
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#endif // _USE_OLED_PRINTF

/* Private typedef ---------------------------------------------------------------------------------------------------*/

/**
  * @brief �������ýṹ�嶨��
  */
typedef struct
{
    uint8_t width;                              /*!< �ֿ�, ��ӦӢ�ij���Ϊ�ֿ���һ�� */

    uint8_t high;                               /*!< �ָ�, ��ӦӢ�ĸ߶Ⱥ��ָ�һ�� */

    const uint8_t *c_pCharData;					/*!< ASCII�ַ�����(ȡģ���ã����� + ����ʽ + ���� + 16���� + C51��ʽ) */

    const uint8_t *c_pFontIdx;					/*!< ������������ */

    const uint8_t *c_pFontData;					/*!< ������������ */
}LCD_FontCfgType;

/**
  * @brief OLED ��Ҫ�������ṹ�嶨��
  */
typedef struct  tag_OLedDev
{										    
	oledsize_t width;			    /*!< OLED ���� */
    
	oledsize_t height;	            /*!< OLED �߶� */
    
	uint8_t  dir;			        /*!< ���������������ƣ�0��������1������ */
    
    eOledcolor backColor;           /*!< ����ɫ */
    
    eOledcolor pointColor;          /*!< ����ɫ */
}OLedDev_t;

#ifdef _USE_OLED_PRINTF

/**
  * @brief OLED Printf �����ض���
  */
typedef struct  tag_OLedPrint
{										    
    oledsize_t printX;              /*!< ��Ļ������������ */
    
    oledsize_t printY;              /*!< ��Ļ������������ */
    
    oledsize_t printWidth;          /*!< ��Ļ������� */
    
    oledsize_t printHeight;         /*!< ��Ļ����߶� */
      
    efontSize  printFontSize;       /*!< ��Ļ����������С */
    
    uint8_t lastTextLenth;          /*!< ���һ���ı����ݳ��� */
    
    char szlastText[100];           /*!< ���һ���ı����� */

}OLedPrint_t;

#endif // _USE_OLED_PRINTF

/* Private define ----------------------------------------------------------------------------------------------------*/
/* Private macro -----------------------------------------------------------------------------------------------------*/

/* ȡ����ֵ */
#define LCD_ABS(x)               ((x) > 0 ? (x) : -(x))

/* Private variables -------------------------------------------------------------------------------------------------*/

//OLED���Դ�
static uint8_t sg_arrFrameBuffer[128][8] = {0};  // OLED����

/** �����С���ñ� */
static LCD_FontCfgType sg_tFontCfgTable[FONT_MAX_NUM] =
{
    {12, 12, ASCII_6X12_DATA, FONT_12X12_IDX, FONT_12X12_DATA},
	{16, 16, ASCII_8X16_DATA, FONT_16X16_IDX, FONT_16X16_DATA},
    {24, 24, ASCII_12X24_DATA, FONT_24X24_IDX, FONT_24X24_DATA},
    {32, 32, ASCII_16X32_DATA, FONT_32X32_IDX, FONT_32X32_DATA},
};

/** OLED ��Ҫ���������ñ� */
static OLedDev_t sg_tOLedDevInfo = {
    128,
    64,
    1,
    OLED_BLACK,
    OLED_WHITE,
};

#ifdef _USE_OLED_PRINTF
/** OLED Printf ������ñ� */
static OLedPrint_t sg_tOLedPrintInfo = {
    OLED_PRINT_X,
    OLED_PRINT_Y,
    OLED_PRINT_WIDTH,
    OLED_PRINT_HIGH,
    OLED_PRINT_FONT,
    0,
    {0}
};
#endif // _USE_OLED_PRINTF

/* Private function prototypes ---------------------------------------------------------------------------------------*/
static void DriveInit(void);
#if _DRIVE_INTERFACE_TYPE == OLED_IIC_INTERFACE
static void IIC_Start(void);
static void IIC_Stop(void);
static void IIC_WriteByte(uint8_t data);
#else
static void SPI_WriteByte(uint8_t data);
#endif
static void WriteCmd(uint8_t cmd);
static void WriteData(uint8_t data);
static void DrawOneChar(oledsize_t x, oledsize_t y, uint8_t chr, oledsize_t width, oledsize_t high, const uint8_t *c_pData);
static int IsFontExit(uint8_t *pszFont, const uint8_t *c_pInx);
static void DrawOneFont(oledsize_t x, oledsize_t y, uint8_t *pszFont, oledsize_t width, oledsize_t high, const uint8_t *c_pInx, const uint8_t *c_pData);
static int GetLineString(char *pszDest, const char *pszSrc, oledsize_t x, oledsize_t setWidth, oledsize_t fontWidth, uint8_t init);
static void DrawLine(oledsize_t sx, oledsize_t sy, oledsize_t ex, oledsize_t ey);
static uint32_t myPow(uint8_t m,uint8_t n);
static void UintToStr(uint32_t num, char* str, uint8_t intLen, uint8_t zero);
static void IntToStr(int32_t num, char* str, uint8_t intLen, uint8_t zero);
static void FloatToStr(double num, char* str, uint8_t intLen, uint8_t decLen, uint8_t zero);

/* Private function --------------------------------------------------------------------------------------------------*/

/** @defgroup �����ӿ�ͨ��
  * @{
  */

/**
  * @brief      Ӳ���ײ��ʼ��.
  * @retval     None.
  */
static void DriveInit(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;

    /* ʹ�ܶ˿ڸ���ʱ�� GPIOA GPIOB ʱ��*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //ʧ��JTAG
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		//�������
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_SetBits(GPIOB, GPIO_Pin_3 | GPIO_Pin_5);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_15;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_ResetBits(GPIOA, GPIO_Pin_11);
    GPIO_SetBits(GPIOA, GPIO_Pin_15);
}

#if _DRIVE_INTERFACE_TYPE == OLED_IIC_INTERFACE

/**
  * @brief      IIC ͨ�ſ�ʼ.
  * @retval     None.
  */
static void IIC_Start(void)
{
    OLED_SCLK_Set() ;
    OLED_SDIN_Set();
    OLED_SDIN_Clr();
    OLED_SCLK_Clr();
}

/**
  * @brief      IIC ͨ��ֹͣ.
  * @retval     None.
  */
static void IIC_Stop(void)
{
    OLED_SCLK_Clr();
    OLED_SDIN_Clr();
    OLED_SDIN_Set();
    OLED_SCLK_Set() ;
}

/**
  * @brief      IIC д��һ���ֽ�.
  * @param[in]  data  �ֽ�����.
  * @retval     None.
  */
static void IIC_WriteByte(uint8_t data)
{
    char i = 8;

    while (i--)
    {
        OLED_SCLK_Clr();
        
        if (data & 0x80)
        {
            OLED_DIN_Set();
        }
        else
        {
            OLED_DIN_Clr();
        }

        OLED_SCLK_Set();
        data <<= 1;
    }
    
    OLED_SCLK_Clr();
    OLED_SCLK_Set();
}

#else

/**
  * @brief      SPI д��һ���ֽ�.
  * @param[in]  data  �ֽ�����.
  * @retval     None.
  */
static void SPI_WriteByte(uint8_t data)
{
    char i = 8;

    OLED_CLK_Clr();

    while (i--)
    {
        if (data & 0x80)
        {
            OLED_DIN_Set();
        }
        else
        {
            OLED_DIN_Clr();
        }

        OLED_CLK_Set();
        OLED_CLK_Clr();
        data <<= 1;
    }
}

#endif

/**
  * @}
  */

/**
  * @brief      дָ��.
  * @param[in]  cmd  ָ��.
  * @retval     None.
  */
static void WriteCmd(uint8_t cmd)
{
#if _DRIVE_INTERFACE_TYPE == OLED_IIC_INTERFACE
    IIC_Start();
    IIC_WriteByte(0x78);            //Slave address,SA0=0
    IIC_WriteByte(0x00);			//write command
    IIC_WriteByte(cmd);
    IIC_Stop();
#else
    OLED_DC_Clr();
    SPI_WriteByte(cmd);
#endif
}

/**
  * @brief      ���.
  * @param[in]  data  ����.
  * @retval     None.
  */
static void WriteData(uint8_t data)
{
#if _DRIVE_INTERFACE_TYPE == OLED_IIC_INTERFACE
    IIC_Start();
    IIC_WriteByte(0x78);			//D/C#=0; R/W#=0
    IIC_WriteByte(0x40);			//write data
    IIC_WriteByte(data);
    IIC_Stop();
#else
    OLED_DC_Set();
    SPI_WriteByte(data);
#endif
}

/**
  * @brief      OLED ��ʼ��.
  * @retval     None.
  */
void OLED_Init(void)
{
    DriveInit();
    
    WriteCmd(0xae);//--turn off oled panel
    WriteCmd(0x00);//---set low column address
    WriteCmd(0x10);//---set high column address
    WriteCmd(0x40);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
    WriteCmd(0x81);//--set contrast control register
    WriteCmd(0xcf);// Set SEG Output Current Brightness
    WriteCmd(0xa1);//--Set SEG/Column Mapping     0xa0���ҷ��� 0xa1����
    WriteCmd(0xc8);//Set COM/Row Scan Direction   0xc0���·��� 0xc8����
    WriteCmd(0xa6);//--set normal display
    WriteCmd(0xa8);//--set multiplex ratio(1 to 64)
    WriteCmd(0x3f);//--1/64 duty
    WriteCmd(0xd3);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
    WriteCmd(0x00);//-not offset
    WriteCmd(0xd5);//--set display clock divide ratio/oscillator frequency
    WriteCmd(0x80);//--set divide ratio, Set Clock as 100 Frames/Sec
    WriteCmd(0xd9);//--set pre-charge period
    WriteCmd(0xf1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    WriteCmd(0xda);//--set com pins hardware configuration
    WriteCmd(0x12);
    WriteCmd(0xdb);//--set vcomh
    WriteCmd(0x40);//Set VCOM Deselect Level
    WriteCmd(0x20);//-Set Page Addressing Mode (0x00/0x01/0x02)
    WriteCmd(0x02);//
    WriteCmd(0x8d);//--set Charge Pump enable/disable
    WriteCmd(0x14);//--set(0x10) disable
    WriteCmd(0xa4);// Disable Entire Display On (0xa4/0xa5)
    WriteCmd(0xa6);// Disable Inverse Display On (0xa6/a7)
    WriteCmd(0xaf);//--turn on oled panel
}

/**
  * @brief      ���ù��.
  * @param[in]  data  �ֽ�����.
  * @retval     None.
  */
void SetCursor(oledsize_t x, oledsize_t y)
{
    WriteCmd(0xb0 + (y >> 3));
    WriteCmd(((x & 0xf0) >> 4) | 0x10);
    WriteCmd((x & 0x0f));
    //WriteCmd((x & 0x0f) | 0x01);
}

/**
  * @brief      ����OLED��ʾ.
  * @retval     None.
  */
void OLED_DisplayOn(void)
{
    WriteCmd(0X8D); //SET DCDC����
    WriteCmd(0X14); //DCDC ON
    WriteCmd(0XAF); //DISPLAY ON
}

/**
  * @brief      �ر�OLED��ʾ.
  * @retval     None.
  */
void OLED_DisplayOff(void)
{
    WriteCmd(0X8D); //SET DCDC����
    WriteCmd(0X10); //DCDC OFF
    WriteCmd(0XAE); //DISPLAY OFF
}

/**
  * @brief      ���㺯��.
  * @param[in]  x  ������
  * @param[in]  y  ������
  * @retval     None.
  */
void OLED_DrawPoint(oledsize_t x, oledsize_t y, eOledcolor color)
{
    oledsize_t tmpY;
    
    x = x > sg_tOLedDevInfo.width ? sg_tOLedDevInfo.width : x;
    y = y > sg_tOLedDevInfo.height ? sg_tOLedDevInfo.height : y;
    
    tmpY = 0 + (y >> 3);
    
    if (color == OLED_WHITE)
    {
        sg_arrFrameBuffer[x][tmpY] = sg_arrFrameBuffer[x][tmpY] | (0x01 << (y % 8));
    }
    else
    {
        sg_arrFrameBuffer[x][tmpY] = sg_arrFrameBuffer[x][tmpY] & (~(0x01 << (y % 8)));
    }
}

void OLED_DrawRoughPoint(oledsize_t x, oledsize_t y, eOledcolor color, uint8_t size)
{
    uint8_t i, j;
    
    for (i = 0; i < size; i++)
    {
        for (j = 0; j < size; j++)
        {
            OLED_DrawPoint(x + i, y + j, color);
        }
    }
}

/**
  * @brief      ���㺯��.
  * @param[in]  x  ������
  * @param[in]  y  ������
  * @retval     None.
  */
eOledcolor OLED_ReadPoint(oledsize_t x, oledsize_t y)
{
    oledsize_t tmpY = 0 + (y >> 3);
    
    if (sg_arrFrameBuffer[x][tmpY] & (0x01 << (y % 8)))
    {
        return OLED_WHITE;
    }
    else
    {
        return OLED_BLACK;
    }
}

/**
  * @brief      ȫ�����.
  * @note       ��Ļ������ˢ��
  * @param[in]  color  ������ɫ.
  * @retval     None.
  */
void OLED_Clear(uint8_t color)
{
    oledsize_t x, y;
    
    for (y = 0; y < 8; y++)
    {
        for (x = 0; x < 128; x++)
        {
            sg_arrFrameBuffer[x][y] = color;
        }
    }
    
    OLED_SyncScreen(0, 0, 128, 64);
}

/**
  * @brief      �ֲ����.
  * @param[in]  sx  ���������
  * @param[in]  sy  ���������
  * @param[in]  width ����
  * @param[in]  high  �߶�
  * @param[in]  color  ������ɫ.
  * @retval     None.
  */
void OLED_SetFill(oledsize_t sx, oledsize_t sy, oledsize_t width, oledsize_t high, eOledcolor color)
{
    oledsize_t x, y;
    
    for (y = sy; y < high; y++)
    {
        for (x = sx; x < width; x++)
        {
            OLED_DrawPoint(x, y, color);
        }
    }
}

/**
  * @brief      ͬ�����溯��.
  * @param[in]  sx  ���������
  * @param[in]  sy  ���������
  * @param[in]  width ����
  * @param[in]  high  �߶�
  * @retval     None.
  */
void OLED_SyncScreen(oledsize_t sx, oledsize_t sy, oledsize_t width, oledsize_t high)
{
    uint8_t i, n;
    oledsize_t ex = sx + width;
    oledsize_t ey = sy + high;

    sx = sx > sg_tOLedDevInfo.width ? sg_tOLedDevInfo.width : sx;
    ex = ex > sg_tOLedDevInfo.width ? sg_tOLedDevInfo.width : ex;

    sy = sy > sg_tOLedDevInfo.height ? sg_tOLedDevInfo.height : sy;
    ey = ey > sg_tOLedDevInfo.height ? sg_tOLedDevInfo.height : ey;

    sy = sy / 8;
    ey % 8 == 0 ? (ey = ey / 8) : (ey = ey / 8 + 1);

    for (i = sy; i < ey; i++)
    {
        WriteCmd(0xb0 + i);  //����ҳ��ַ��0~7��
        WriteCmd(0x02);      //������ʾλ�á��е͵�ַ
        WriteCmd(0x10);      //������ʾλ�á��иߵ�ַ

        for (n = sx; n < ex; n++)
        {
            WriteData(sg_arrFrameBuffer[n][i]);
        }
    } //������ʾ
}

/**
  * @brief      ��Ļˢ����ʾ����.
  *             ���滭��ͬ������
  * @retval     None.
  */
void OLED_ShowTask(void)
{
    OLED_SyncScreen(0, 0, 128, 64);
}

/**
  * @brief      ָ���������л�����ָ������ƫ��, ������Ļ�������Զ���䱳��ɫ.
  * @param[in]  x       ����������.
  * @param[in]  y       ����������
  * @param[in]  width   ���ڿ���
  * @param[in]  high    ���ڸ߶�
  * @param[in]  dir     ƫ�Ʒ���
  *                      @arg 0 ����ƫ��
  *                      @arg 1 ����ƫ��
  *                      @arg 2 ����ƫ��
  *                      @arg 3 ����ƫ��
  * @param[in]  pixels  ƫ�Ƶ����ص�
  * @retval     None.
  */
void OLED_SetScreenOffset(oledsize_t x, oledsize_t y, oledsize_t width, oledsize_t high, uint8_t dir, uint8_t pixels)
{
    uint8_t i,j;
    
    /* ��Ļ���ִ�������ƫ����ʧ */
    if (dir == 0)
    {
        for (i = x; i < x + width; i++)
        {
            for (j = y; j < y + high; j++)
            {
                if (j + pixels < (y + high))
                {
                    OLED_DrawPoint(i, j, OLED_ReadPoint(i, j + pixels));
                }
                else
                {
                    OLED_DrawPoint(i, j, sg_tOLedDevInfo.backColor);
                }
            }
        }
    }
    else if (dir == 1) /* ��Ļ���ִ�������ƫ����ʧ */
    {
        for (i = x; i < x + width; i++)
        {
            for (j = y + high; j > y; j--)
            {
                if (j - 1 - pixels > y)
                {
                    OLED_DrawPoint(i, j - 1, OLED_ReadPoint(i, j - 1 - pixels));
                }
                else
                {
                    OLED_DrawPoint(i, j - 1, sg_tOLedDevInfo.backColor);
                }
            }
        }
    }
    else if (dir == 2) /* ��Ļ���ִ�������ƫ����ʧ */
    {
        for (j = y; j < y + high; j++)
        {
            for (i = x; i < x + width; i++)
            {
                if (i + pixels < (x + width))
                {
                    OLED_DrawPoint(i, j, OLED_ReadPoint(i + pixels, j));
                }
                else
                {
                    OLED_DrawPoint(i, j, sg_tOLedDevInfo.backColor);
                }
            }
        }
    }
    else if (dir == 3) /* ��Ļ���ִ�������ƫ����ʧ */
    {
        for (j = y; j < y + high; j++)
        {
            for (i = x + width; i > x; i--)
            {
                if (i - 1 - pixels > x)
                {
                    OLED_DrawPoint(i - 1, j, OLED_ReadPoint(i - 1 - pixels, j));
                }
                else
                {
                    OLED_DrawPoint(i - 1, j, sg_tOLedDevInfo.backColor);
                }
            }
        }
    }
}

/**
  * @brief      ��ָ��λ�û�һ���ǵ��ӵ��ַ�.
  * @param      x       ����������.
  * @param      y       ����������
  * @param      chr     �ַ�(" "--->"~")
  * @param      width   ��ʾ�Ŀ���
  * @param      high    ��ʾ�ĸ߶�
  * @param      c_pData �ַ���������
  * @retval     None.
  */
static void DrawOneChar(oledsize_t x, oledsize_t y, uint8_t chr, oledsize_t width, oledsize_t high, const uint8_t *c_pData)
{
    oledsize_t temp;
    oledsize_t tmpx, tmpy;
    uint16_t dataSize;
    uint16_t idx = chr - ' ';/* �õ�ƫ�ƺ��ֵ */
    eOledcolor colortemp[2];
    
    colortemp[1] = sg_tOLedDevInfo.pointColor;
    colortemp[0] = sg_tOLedDevInfo.backColor;

    /* ��8Ϊ��λ����, ��ȡ������� */
    if (high & 0x07)
    {
        dataSize = ((high >> 3) + 1) * width;
    }
    else
    {
    	dataSize = (high >> 3) * width;
    }

    if (x > sg_tOLedDevInfo.width - width || y > sg_tOLedDevInfo.height - high)
    {
        return;
    }

    idx *= dataSize;
    
    /* ����ʽ�㷨 */
    for (tmpx = 0; tmpx < width; tmpx++)
    {
        for (tmpy = 0; tmpy < high; tmpy++)
        {
            temp = c_pData[idx + tmpx + (tmpy >> 3)  * width] >> (tmpy & 7);
            OLED_DrawPoint(tmpx + x, tmpy + y, colortemp[temp & 0x01]);
        }
    }
}

/**
  * @brief      �����ַ����Ƿ���ڸú���.
  * @param      pszFont ����(GB2312)
  * @param      c_pInx  �����ַ���
  * @retval     -1, ������; ����,�ú������ڵ��ַ���λ��.
  *             
  */
static int IsFontExit(uint8_t *pszFont, const uint8_t *c_pInx)
{
    uint16_t index = 0;
    
    /* Ѱ���������� */
    while(c_pInx[index] > 127)
    {
        if(c_pInx[index] == pszFont[0] && c_pInx[index + 1] == pszFont[1])
        {
            return index >> 1;
        }

        index += 2;
    }
    
    return -1;
}

/**
  * @brief      ��ָ��λ�û�һ���ǵ��ӵĺ���.
  * @param      x       ����������.
  * @param      y       ����������
  * @param      chr     �ַ�(" "--->"~")
  * @param      width   ��ʾ�Ŀ���
  * @param      high    ��ʾ�ĸ߶�
  * @param      c_pData �ַ���������
  * @retval     None.
  */
static void DrawOneFont(oledsize_t x, oledsize_t y, uint8_t *pszFont, oledsize_t width, oledsize_t high, const uint8_t *c_pInx, const uint8_t *c_pData)
{
    oledsize_t temp;
    oledsize_t tmpx, tmpy;
    uint16_t dataSize;
    int32_t idx;
    eOledcolor colortemp[2];

    colortemp[1] = sg_tOLedDevInfo.pointColor;
    colortemp[0] = sg_tOLedDevInfo.backColor;
    
    /* ��8Ϊ��λ����, ��ȡ������� */
    if (high & 0x07)
    {
        dataSize = ((high >> 3) + 1) * width;
    }
    else
    {
    	dataSize = (high >> 3) * width;
    }

    if (x > sg_tOLedDevInfo.width - width || y > sg_tOLedDevInfo.height - high)
    {
        return;
    }

    if ((idx = IsFontExit(pszFont, c_pInx)) >= 0)
    {
        idx *= dataSize;
        
        /* ����ʽ�㷨 */
        for (tmpx = 0; tmpx < width; tmpx++)
        {
            for (tmpy = 0; tmpy < high; tmpy++)
            {
                temp = c_pData[idx + tmpx + (tmpy >> 3)  * width] >> (tmpy & 7);
                OLED_DrawPoint(tmpx + x, tmpy + y, colortemp[temp & 0x01]);
            }
        }
    }
    else
    {
        /* ����ʽ�㷨 */
        for (tmpx = 0; tmpx < width; tmpx++)
        {
            for (tmpy = 0; tmpy < high; tmpy++)
            {
                OLED_DrawPoint(tmpx + x, tmpy + y, colortemp[0]);
            }
        }
    }
}

/**
  * @brief      ���ñ���ɫ�ͻ���ɫ.
  * @param[in]  backColor  ���ʱ�����ɫ.
  * @param[in]  pointColor  ������ɫ.
  * @retval     None.
  */
void OLED_SetColor(eOledcolor backColor, eOledcolor pointColor)
{
    sg_tOLedDevInfo.backColor = backColor;
    sg_tOLedDevInfo.pointColor = pointColor;
}

/**
  * @brief      ���ݻ��з����趨��Ļ�������εõ�ÿ�е��ı�����(��Ҫ��ε��õõ�).
  * @param[out] pszDest һ�е��ı�����
  * @param[in]  pszSrc  ��Ҫ�������ַ���
  * @param[in]  x       ����������.
  * @param[in]  setWidth �趨��Ļ����
  * @param[in]  fontWidth �ֿ�
  * @param[in]  init    ���»�ȡÿ���ı�����
  * @retval     -1, û�������ı�
  *              0, �س���
  *              1, ���з�.
  *              2, ������Ļ��Χ
  */
static int GetLineString(char *pszDest, const char *pszSrc, oledsize_t x, oledsize_t setWidth, oledsize_t fontWidth, uint8_t init)
{
    oledsize_t tmpWidth = 0;
    static const char *s_pszSrc = 0;
        
    if (init)
    {
        s_pszSrc = pszSrc;
    }
    
    while (s_pszSrc != 0 && *s_pszSrc != 0)
    {
        if (*s_pszSrc == '\n')
        {
            s_pszSrc++;
            return 1;
        }
        else if (*s_pszSrc == '\r')
        {
            s_pszSrc++;
            
            if (*s_pszSrc == '\n')
            {
                s_pszSrc++;
                return 1;
            }
            else
            {
                return 0;
            }
        }
        
    	if (*s_pszSrc > 127)
    	{
            tmpWidth += fontWidth;
            
            if (tmpWidth > setWidth)
            {
                return 2;
            }
            
            *pszDest = *s_pszSrc;
            s_pszSrc++;
            pszDest++;
    	}
    	else
    	{
            tmpWidth += fontWidth / 2;
            
            if (tmpWidth > setWidth)
            {
                return 2;
            }
    	}
        
        *pszDest = *s_pszSrc;
        s_pszSrc++;
        pszDest++;
    }
    
    return -1;
}

/**
  * @brief      ��ָ��λ�������ı�����.
  * @param[in]  x       ����������.
  * @param[in]  y       ����������
  * @param[in]  pszStr  �ı�����
  * @param[in]  isMultiLine 0-��֧�ֶ�����ʾ 1-֧�ֶ�����ʾ
  * @param[in]  size    �ı���С
  * @retval     None.
  */
void OLED_SetText(oledsize_t x, oledsize_t y, const char *pszStr, uint8_t isMultiLine, efontSize size)
{
    uint8_t isAscii = 1;
    oledsize_t tmpX = x;
	oledsize_t tmpY = y;
    uint8_t szFontData[3] = {0};
    uint16_t i = 0;
    
    while (pszStr[i] != '\0')
    {
    	if (pszStr[i] > 127)
    	{
            isAscii = 0;
            
            if (tmpX > sg_tOLedDevInfo.width - sg_tFontCfgTable[size].width)
            {
                if (isMultiLine && tmpY < (sg_tOLedDevInfo.height - sg_tFontCfgTable[size].high))
                {
                    tmpX = x;
                    tmpY += sg_tFontCfgTable[size].high;
                }
                else
                {
                    break;
                }
            }
    	}
    	else
    	{
			isAscii = 1;
            
            if (tmpX > sg_tOLedDevInfo.width - (sg_tFontCfgTable[size].width >> 1))
            {
                if (isMultiLine && tmpY < (sg_tOLedDevInfo.height - sg_tFontCfgTable[size].high))
                {
                    tmpX = x;
                    tmpY += sg_tFontCfgTable[size].high;
                }
                else
                {
                    break;
                }
            }
    	}
        
        if (isAscii == 0)
        {
            if (sg_tFontCfgTable[size].c_pFontIdx != 0 && sg_tFontCfgTable[size].c_pFontData != 0)
            {
                szFontData[0] = pszStr[i];
                szFontData[1] = pszStr[i + 1];
                DrawOneFont(tmpX, tmpY, szFontData, sg_tFontCfgTable[size].width, 
                            sg_tFontCfgTable[size].high, sg_tFontCfgTable[size].c_pFontIdx, sg_tFontCfgTable[size].c_pFontData);
                tmpX += sg_tFontCfgTable[size].width;
            }
            
            i += 2;
        }
        else
        {
            if (sg_tFontCfgTable[size].c_pCharData != 0)
            {
                DrawOneChar(tmpX, tmpY, pszStr[i], sg_tFontCfgTable[size].width >> 1, 
                            sg_tFontCfgTable[size].high, sg_tFontCfgTable[size].c_pCharData);
                tmpX += (sg_tFontCfgTable[size].width >> 1);
            } 
             
            i++;
        }
    }
}

static uint32_t myPow(uint8_t m,uint8_t n)
{
    uint32_t result = 1; 
    
    while(n--)
        result *= m; 
    
    return result;
} 

/**
  * @brief      �޷�������ת�ַ���.
  * @param      num     ��ֵ
  * @param      str     ת������ַ���
  * @param      intLen  ��������
  * @param      zero    0,��λΪ 0 ʱ����ʾ; 1,��λΪ 0 ʱ��ʾ
  * @retval     None.
  */
static void UintToStr(uint32_t num, char* str, uint8_t intLen, uint8_t zero)
{
    uint8_t t;
    uint8_t flag = 0;

    if (intLen > 10) 
        intLen = 10;
    
    for(t = 0; t < intLen; t++)
    {
        str[t] = ((num / myPow(10, intLen - t - 1)) % 10) + '0';
        
        /* ��λ����ʾ && ��λΪ 0 δ������� && ��ǰλΪ 0 && �����������λ���� 0 ����, ��ֹ 0 û����ʾ */
        if (zero == 0 && flag == 0 && str[t] == '0' && (t < intLen - 1)) 
        {
            str[t] = ' ';
        }
        else
        {
            flag = 1; /* ��Ǹ�λΪ 0 �ĸ�������� */
        }
    }
}

/**
  * @brief      �з�������ת�ַ���.
  * @param      num     ��ֵ
  * @param      str     ת������ַ���
  * @param      intLen  ��������
  * @param      zero    0,��λΪ 0 ʱ����ʾ; 1,��λΪ 0 ʱ��ʾ
  * @retval     None.
  */
static void IntToStr(int32_t num, char* str, uint8_t intLen, uint8_t zero)
{
    uint8_t t;
    uint8_t flag = 0;
    uint8_t zeroNum = 0;

    if (intLen > 10) 
        intLen = 10;

    /* ���ӷ���λ */
    str[0] = ' ';
        
    if (num > 0)
    {
        for(t = 0; t < intLen; t++)
        {
            str[t + 1] = ((num / myPow(10, intLen - t - 1)) % 10) + '0';
            
            /* ��λ����ʾ && ��λΪ 0 δ������� && ��ǰλΪ 0 && �����������λ���� 0 ����, ��ֹ 0 û����ʾ */
            if (zero == 0 && flag == 0 && str[t + 1] == '0' && (t < intLen - 1)) 
            {
                str[t + 1] = ' ';
            }
            else
            {
                flag = 1; /* ��Ǹ�λΪ 0 �ĸ�������� */
            }
        }
    }
    else
    {
        num = LCD_ABS(num);
        
        for(t = 0; t < intLen; t++)
        {
            str[t + 1] = ((num / myPow(10, intLen - t - 1)) % 10) + '0';
            
            /* ��λ����ʾ && ��λΪ 0 δ������� && ��ǰλΪ 0 && �����������λ���� 0 ����, ��ֹ 0 û����ʾ */
            if (zero == 0 && flag == 0 && str[t + 1] == '0' && (t < intLen - 1)) 
            {
                str[t + 1] = ' ';
                zeroNum++;
            }
            else
            {
                flag = 1;   /* ��Ǹ�λΪ 0 �ĸ�������� */
            }
        }
        
        if (num != 0)       /* ��ֵΪ 0 ����ʾ���� */
        {
            str[zeroNum] = '-';
        }
    }
}

/**
  * @brief      ������ת�ַ���.
  * @param      num     ��ֵ
  * @param      str     ת������ַ���
  * @param      intLen  ��������
  * @param      decLen  ������
  * @param      zero    0,��λΪ 0 ʱ����ʾ; 1,��λΪ 0 ʱ��ʾ
  * @retval     None.
  */
static void FloatToStr(double num, char* str, uint8_t intLen, uint8_t decLen, uint8_t zero)
{
    uint8_t t;
    uint8_t flag = 0;
    uint8_t zeroNum = 0;
    int16_t integer_num = (int16_t)num;
    int16_t decimal_num = (int16_t)((num - integer_num) * myPow(10, decLen));
    
    if (intLen > 10) 
        intLen = 10;
    
    if (decLen > 10) 
        decLen = 10;
    
    integer_num = LCD_ABS(integer_num);
    decimal_num = LCD_ABS(decimal_num);

    /* ���ӷ���λ */
    str[0] = ' ';
    
    /* ����С��λ */
    if (decLen != 0)
        str[intLen + 1] = '.';

    for(t = 0; t < intLen; t++)
    {
        str[t + 1] = ((integer_num / myPow(10, intLen - t - 1)) % 10) + '0';
        
        /* ��λ����ʾ && ��λΪ 0 δ������� && ��ǰλΪ 0 && �����������λ���� 0 ����, ��ֹ 0 û����ʾ */
        if (zero == 0 && flag == 0 && str[t + 1] == '0' && (t < intLen - 1))
        {
            str[t + 1] = ' ';

            if (num < 0)
            	zeroNum++;
        }
        else
        {
            flag = 1; /* ��Ǹ�λΪ 0 �ĸ�������� */
        }
    }

    for (t = 0; t < decLen; t++)
    {
        str[t + intLen + 2] = ((decimal_num / myPow(10, decLen - t - 1)) % 10) + '0';
    }

    if (num >= 0)
    {
        for (t = 0; t < (decLen + intLen + 2); t++)
        {
            str[t] = str[t + 1];
        }
    }
    else
    {
    	str[zeroNum] = '-';
    }
    

}

/**
  * @brief      ��ָ��λ��������������������.
  * @param[in]  x       ����������.
  * @param[in]  y       ����������
  * @param[in]  num     ����������
  * @param[in]  len     ����ʾ��������󳤶�
  * @param[in]  zero    ���λΪ0�Ƿ���ʾ, 0-����ʾ 1-��ʾ
  * @param[in]  size    �����ı���С
  * @retval     None.
  */
void OLED_SetIntegerNum(oledsize_t x, oledsize_t y, int32_t num, uint8_t len, uint8_t zero, efontSize size)
{
    char tmpStr[25] = {0};
    uint32_t unum = num;
    
    if (num < 0)
    {
        IntToStr(num, tmpStr, len, zero);
    }
    else
    {
        UintToStr(unum, tmpStr, len, zero);
    }
    
    OLED_SetText(x, y, tmpStr, 0, size);
}

/**
  * @brief      ��ָ��λ�����ø�������������.
  * @param[in]  x       ����������.
  * @param[in]  y       ����������
  * @param[in]  num     ����������
  * @param[in]  intLen  ����ʾ����������������󳤶�
  * @param[in]  decLen  ����ʾ��С������������󳤶�
  * @param[in]  zero    ���λΪ0�Ƿ���ʾ, 0-����ʾ 1-��ʾ
  * @param[in]  size    �����ı���С
  * @retval     None.
  */
void OLED_SetFloatNum(oledsize_t x, oledsize_t y, float num, uint8_t intLen, uint8_t decLen, uint8_t zero, efontSize size)
{
    char tmpStr[25] = {0};
    
    FloatToStr(num, tmpStr, intLen, decLen, zero);
    
    OLED_SetText(x, y, tmpStr, 0, size);
}

#ifdef _USE_OLED_PRINTF
/**
  * @brief      ���͸�ʽ������� OLED ���(����).
  * @param[in]  x       ����������.
  * @param[in]  y       ����������
  * @param[in]  isMultiLine 0-��֧�ֶ�����ʾ 1-֧�ֶ�����ʾ
  * @param[in]  size    �ı���С
  * @param[in]  format ��ʽ���ַ���
  * @param[in]  ... ��ʽ���ַ����еIJ�����
  * @retval     None
  */
void OLED_XYPrintf(oledsize_t x, oledsize_t y, uint8_t isMultiLine, efontSize size, const char *format, ...)
{
    char szText[100] = {0};
    va_list aptr;
    
    va_start(aptr, format);
    vsprintf(szText, format, aptr);
    va_end(aptr);
    
    OLED_SetText(x, y, szText, isMultiLine, size);
}

/**
  * @brief      ���͸�ʽ������� OLED ���.
  *             �߱��Զ�����, ͬʱ֧��ʶ��'\r'��'\n'
  * @param[in]  format ��ʽ���ַ���
  * @param[in]  ... ��ʽ���ַ����еIJ�����
  * @retval     None
  */
void OLED_Printf(const char *format, ...)
{
    OLedPrint_t *pOLedPrint = &sg_tOLedPrintInfo;
    oledsize_t printFontHigh = sg_tFontCfgTable[pOLedPrint->printFontSize].high;
    oledsize_t printWindowHeight = pOLedPrint->printHeight;
    uint8_t init = 1;
    int ret = 0;
    char szText[100] = {0};
    va_list aptr;
    
    if (pOLedPrint->lastTextLenth)
    {
        memcpy(szText, pOLedPrint->szlastText, pOLedPrint->lastTextLenth);
    }
    
    va_start(aptr, format);
    vsprintf(&szText[pOLedPrint->lastTextLenth], format, aptr);
    va_end(aptr);
    
    do
    {
        ret = GetLineString(pOLedPrint->szlastText, szText, OLED_PRINT_X, OLED_PRINT_WIDTH, 
                                sg_tFontCfgTable[pOLedPrint->printFontSize].width, init);
        init = 0;
        
        if (pOLedPrint->printY > (printWindowHeight - printFontHigh) && ret >= 0)
        {
            OLED_SetScreenOffset(OLED_PRINT_X, OLED_PRINT_Y, OLED_PRINT_WIDTH, OLED_PRINT_HIGH, 
                                    0, printFontHigh - (printWindowHeight - pOLedPrint->printY));
            pOLedPrint->printY = printWindowHeight - printFontHigh;
        }
        
        OLED_SetText(pOLedPrint->printX, pOLedPrint->printY, pOLedPrint->szlastText, 0, pOLedPrint->printFontSize);
        
        if (ret >= 0)
        {
            pOLedPrint->lastTextLenth = 0;
            memset(pOLedPrint->szlastText, 0, sizeof(pOLedPrint->szlastText));
            
            if (ret > 0)
            {
                pOLedPrint->printY += printFontHigh;
            }
        }
        else
        {
            pOLedPrint->lastTextLenth = strlen(pOLedPrint->szlastText);
        }
    } while (ret >= 0);
}
#endif // _USE_OLED_PRINTF

/**
  * @brief      ����.
  * @param[in]  sx  ���������
  * @param[in]  sy  ���������
  * @param[in]  ex  �������յ�
  * @param[in]  ey  �������յ�
  * @param[in]  color  ������ɫ.
  * @retval     None.
  */
static void DrawLine(oledsize_t sx, oledsize_t sy, oledsize_t ex, oledsize_t ey)
{
    uint16_t t;
    int32_t xerr = 0, yerr = 0, delta_x, delta_y, distance;
    int32_t incx, incy, uRow, uCol;

    delta_x = (int32_t)ex - sx; //������������
    delta_y = (int32_t)ey - sy;
    uRow = sx;
    uCol = sy;

    if(delta_x > 0)
    {
        incx = 1;    //���õ�������
    }
    else if(delta_x == 0)
    {
        incx = 0;    //��ֱ��
    }
    else
    {
        incx = -1;
        delta_x = -delta_x;
    }

    if(delta_y > 0)
    {
        incy = 1;
    }
    else if(delta_y == 0)
    {
        incy = 0;    //ˮƽ��
    }
    else
    {
        incy = -1;
        delta_y = -delta_y;
    }

    if( delta_x > delta_y)
    {
        distance = delta_x;    //ѡȡ��������������
    }
    else
    {
        distance = delta_y;
    }

    for(t = 0; t <= distance + 1; t++ ) //�������
    {
        OLED_DrawPoint(uRow, uCol, sg_tOLedDevInfo.pointColor);
        
        xerr += delta_x ;
        yerr += delta_y ;

        if(xerr > distance)
        {
            xerr -= distance;
            uRow += incx;
        }

        if(yerr > distance)
        {
            yerr -= distance;
            uCol += incy;
        }
    }
}

/**
  * @brief      ����.
  *             ������ݴ�С��������
  * @param[in]  sx  ���������
  * @param[in]  sy  ���������
  * @param[in]  width ����
  * @param[in]  high  �߶�
  * @param[in]  size �����С.
  * @retval     None.
  */
void OLED_DrawLine(oledsize_t sx, oledsize_t sy, oledsize_t width, oledsize_t high, uint8_t size)
{
    uint8_t i;
    oledsize_t ex = sx + width;
    oledsize_t ey = sy + high;
    
    for (i = 0; i < size; i++)
    {
        DrawLine(sx + i, sy + i, ex + i, ey + i);
    }
}

/**
  * @brief      ������.
  *             ������ݴ�С������������
  * @param[in]  sx  ���������
  * @param[in]  sy  ���������
  * @param[in]  width ����
  * @param[in]  high  �߶�
  * @param[in]  size �����С.
  * @retval     None.
  */
void OLED_DrawRectangle(oledsize_t sx, oledsize_t sy, oledsize_t width, oledsize_t high, uint8_t size)
{
    uint8_t i;
    oledsize_t ex = sx + width;
    oledsize_t ey = sy + high;

    for (i = 0; i < size; i++)
    {
        DrawLine(sx + i, sy + i, ex - i,   sy + i);
        DrawLine(sx + i, sy + i, sx + i, ey - i);
        DrawLine(sx + i, ey - i,   ex - i,   ey - i);
        DrawLine(ex - i, sy + i, ex - i,   ey - i);
    }
}

/**
  * @brief      ������.
  *             ������ݴ�С������������
  * @param[in]  sx  ���������
  * @param[in]  sy  ���������
  * @param[in]  ex  ������Ե�
  * @param[in]  ey  ������Ե�
  * @param[in]  size �����С.
  * @retval     None.
  */
void OLED_DrawCircle(oledsize_t x, oledsize_t y, oledsize_t radius, uint8_t size)
{
    uint8_t i;
    oledsize_t tmpRadius = radius;
    int32_t a = 0;
    int32_t di = 3 - (radius << 1);       /* �ж��¸���λ�õı�־ */

    radius = radius - size / 2;
    
    tmpRadius = radius;
    di = 3 - (radius << 1);
    
    for (i = 0; i < size; i++)
    {
        while(a <= tmpRadius)
        {
            OLED_DrawPoint(x - tmpRadius, y - a,       sg_tOLedDevInfo.pointColor);        //3
            OLED_DrawPoint(x + tmpRadius, y - a,       sg_tOLedDevInfo.pointColor);        //0
            
            OLED_DrawPoint(x - a,      y + tmpRadius,  sg_tOLedDevInfo.pointColor);        //1
            OLED_DrawPoint(x - tmpRadius, y - a,       sg_tOLedDevInfo.pointColor);        //7
            
            OLED_DrawPoint(x - a,      y - tmpRadius,  sg_tOLedDevInfo.pointColor);        //2
            OLED_DrawPoint(x + tmpRadius, y + a,       sg_tOLedDevInfo.pointColor);        //4
            
            OLED_DrawPoint(x + a,      y - tmpRadius,  sg_tOLedDevInfo.pointColor);        //5
            OLED_DrawPoint(x + a,      y + tmpRadius,  sg_tOLedDevInfo.pointColor);        //6
            OLED_DrawPoint(x - tmpRadius, y + a,       sg_tOLedDevInfo.pointColor);
            
            a++;

            /* ʹ��Bresenham�㷨��Բ */
            if(di < 0)
            {
                di += 8 * a + 5 - i;
            }
            else
            {
                di += 10 + 8 * (a - tmpRadius) - i;
                tmpRadius--;
            }

            OLED_DrawPoint(x + a, y + tmpRadius, sg_tOLedDevInfo.pointColor);
        }
        
        a = 0;
        tmpRadius = radius - i;
        di = 3 - (radius << 1);
    }
}