/*
 * Code found at http://www.instructables.com/id/How-to-use-OLED-display-arduino-module/
 * Thank you very much!
 * Adapted from Arduino to STM32 HAL by wollud1969
 */


#include "oled.h"
#include "oled-fonts.h"
#include "stm32f1xx_hal.h"



#define HIGH GPIO_PIN_SET
#define LOW GPIO_PIN_RESET


// DO: SPI Clk, D1: SPI Data


extern SPI_HandleTypeDef hspi1;






static void __LEDPIN_RST(GPIO_PinState v) {
  HAL_GPIO_WritePin(OLED_RST_GPIO_Port, OLED_RST_Pin, v);
}

static void __LEDPIN_DC(GPIO_PinState v) {
  HAL_GPIO_WritePin(OLED_DC_GPIO_Port, OLED_DC_Pin, v);
}

static void __LEDPIN_CS(GPIO_PinState v) {
  HAL_GPIO_WritePin(OLED_CS_GPIO_Port, OLED_CS_Pin, v);
}

void LED_WrDat(unsigned char data)   
{
  __LEDPIN_CS(LOW);
  __LEDPIN_DC(HIGH);

  HAL_SPI_Transmit(&hspi1, &data, 1, 0);

  __LEDPIN_CS(HIGH);
}
void LED_WrCmd(unsigned char cmd) 
{
  __LEDPIN_CS(LOW);
  __LEDPIN_DC(LOW);

  HAL_SPI_Transmit(&hspi1, &cmd, 1, 0);

  __LEDPIN_CS(HIGH);
}
void LED_Set_Pos(unsigned char x, unsigned char y)
{ 
  LED_WrCmd(0xb0+y);
  LED_WrCmd(((x&0xf0)>>4)|0x10);
  LED_WrCmd((x&0x0f)|0x00);
} 


void LED_Fill(unsigned char bmp_data)
{
  unsigned char y,x;

  for(y=0;y<8;y++)
  {
    LED_WrCmd(0xb0+y);
    LED_WrCmd(0x00);
    LED_WrCmd(0x10);
    for(x=0;x<128;x++)
      LED_WrDat(bmp_data);
  }
} 



void LED_CLS(void) 
{
  unsigned char y,x;
  for(y=0;y<8;y++)
  {
    LED_WrCmd(0xb0+y);
    LED_WrCmd(0x00);
    LED_WrCmd(0x10);
    for(x=0;x<128;x++)
      LED_WrDat(0);
  }
}

void LED_DLY_ms(unsigned int ms)
{                         
  uint32_t start = HAL_GetTick();
  while (HAL_GetTick() < start + ms);
}

void SetStartColumn(unsigned char d)
{
  LED_WrCmd(0x00+d%16);	// Set Lower Column Start Address for Page Addressing Mode
  // Default => 0x00
  LED_WrCmd(0x10+d/16);	// Set Higher Column Start Address for Page Addressing Mode
  // Default => 0x10
}

void SetAddressingMode(unsigned char d)
{
  LED_WrCmd(0x20);			// Set Memory Addressing Mode
  LED_WrCmd(d);			// Default => 0x02
  // 0x00 => Horizontal Addressing Mode
  // 0x01 => Vertical Addressing Mode
  // 0x02 => Page Addressing Mode
}

void SetColumnAddress(unsigned char a, unsigned char b)
{
  LED_WrCmd(0x21);			// Set Column Address
  LED_WrCmd(a);			// Default => 0x00 (Column Start Address)
  LED_WrCmd(b);			// Default => 0x7F (Column End Address)
}

void SetPageAddress(unsigned char a, unsigned char b)
{
  LED_WrCmd(0x22);			// Set Page Address
  LED_WrCmd(a);			// Default => 0x00 (Page Start Address)
  LED_WrCmd(b);			// Default => 0x07 (Page End Address)
}

void SetStartLine(unsigned char d)
{
  LED_WrCmd(0x40|d);		// Set Display Start Line
  // Default => 0x40 (0x00)
}

void SetContrastControl(unsigned char d)
{
  LED_WrCmd(0x81);			// Set Contrast Control
  LED_WrCmd(d);			// Default => 0x7F
}

void Set_Charge_Pump(unsigned char d)
{
  LED_WrCmd(0x8D);			// Set Charge Pump
  LED_WrCmd(0x10|d);		// Default => 0x10
  // 0x10 (0x00) => Disable Charge Pump
  // 0x14 (0x04) => Enable Charge Pump
}

void Set_Segment_Remap(unsigned char d)
{
  LED_WrCmd(0xA0|d);		// Set Segment Re-Map
  // Default => 0xA0
  // 0xA0 (0x00) => Column Address 0 Mapped to SEG0
  // 0xA1 (0x01) => Column Address 0 Mapped to SEG127
}

void Set_Entire_Display(unsigned char d)
{
  LED_WrCmd(0xA4|d);		// Set Entire Display On / Off
  // Default => 0xA4
  // 0xA4 (0x00) => Normal Display
  // 0xA5 (0x01) => Entire Display On
}

void Set_Inverse_Display(unsigned char d)
{
  LED_WrCmd(0xA6|d);		// Set Inverse Display On/Off
  // Default => 0xA6
  // 0xA6 (0x00) => Normal Display
  // 0xA7 (0x01) => Inverse Display On
}

void Set_Multiplex_Ratio(unsigned char d)
{
  LED_WrCmd(0xA8);			// Set Multiplex Ratio
  LED_WrCmd(d);			// Default => 0x3F (1/64 Duty)
}

void Set_Display_On_Off(unsigned char d)
{
  LED_WrCmd(0xAE|d);		// Set Display On/Off
  // Default => 0xAE
  // 0xAE (0x00) => Display Off
  // 0xAF (0x01) => Display On
}

void SetStartPage(unsigned char d)
{
  LED_WrCmd(0xB0|d);		// Set Page Start Address for Page Addressing Mode
  // Default => 0xB0 (0x00)
}

void Set_Common_Remap(unsigned char d)
{
  LED_WrCmd(0xC0|d);		// Set COM Output Scan Direction
  // Default => 0xC0
  // 0xC0 (0x00) => Scan from COM0 to 63
  // 0xC8 (0x08) => Scan from COM63 to 0
}

void Set_Display_Offset(unsigned char d)
{
  LED_WrCmd(0xD3);			// Set Display Offset
  LED_WrCmd(d);			// Default => 0x00
}

void Set_Display_Clock(unsigned char d)
{
  LED_WrCmd(0xD5);			// Set Display Clock Divide Ratio / Oscillator Frequency
  LED_WrCmd(d);			// Default => 0x80
  // D[3:0] => Display Clock Divider
  // D[7:4] => Oscillator Frequency
}

void Set_Precharge_Period(unsigned char d)
{
  LED_WrCmd(0xD9);			// Set Pre-Charge Period
  LED_WrCmd(d);			// Default => 0x22 (2 Display Clocks [Phase 2] / 2 Display Clocks [Phase 1])
  // D[3:0] => Phase 1 Period in 1~15 Display Clocks
  // D[7:4] => Phase 2 Period in 1~15 Display Clocks
}

void Set_Common_Config(unsigned char d)
{
  LED_WrCmd(0xDA);			// Set COM Pins Hardware Configuration
  LED_WrCmd(0x02|d);		// Default => 0x12 (0x10)
  // Alternative COM Pin Configuration
  // Disable COM Left/Right Re-Map
}

void Set_VCOMH(unsigned char d)
{
  LED_WrCmd(0xDB);			// Set VCOMH Deselect Level
  LED_WrCmd(d);			// Default => 0x20 (0.77*VCC)
}

void Set_NOP(void)
{
  LED_WrCmd(0xE3);			// Command for No Operation
}

void oledInit(void)
{
  // LEDPIN_Init();
  //	LED_PORT=0X0F;
  //LED_SCLH;;;
  //LED_RSTL;;;
  //digitalWrite(SCL_PIN,HIGH);;;
  __LEDPIN_RST(LOW);
  //	for(i=0;i<100;i++)asm("nop");
  LED_DLY_ms(50);
  //LED_RSTH;;;
  __LEDPIN_RST(HIGH);

  Set_Display_On_Off(0x00);		  // Display Off (0x00/0x01)
  Set_Display_Clock(0x80);		  // Set Clock as 100 Frames/Sec
  Set_Multiplex_Ratio(0x3F);	  // 1/64 Duty (0x0F~0x3F)
  Set_Display_Offset(0x00);		  // Shift Mapping RAM Counter (0x00~0x3F)
  SetStartLine(0x00);			  // Set Mapping RAM Display Start Line (0x00~0x3F)
  Set_Charge_Pump(0x04);		  // Enable Embedded DC/DC Converter (0x00/0x04)
  SetAddressingMode(0x02);	  // Set Page Addressing Mode (0x00/0x01/0x02)
  Set_Segment_Remap(0x01);	  // Set SEG/Column Mapping
  Set_Common_Remap(0x08);	  // Set COM/Row Scan Direction
  Set_Common_Config(0x10);	  // Set Sequential Configuration (0x00/0x10)
  SetContrastControl(0xCF); // Set SEG Output Current
  Set_Precharge_Period(0xF1);	  // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
  Set_VCOMH(0x40);			  // Set VCOM Deselect Level
  Set_Entire_Display(0x00);		  // Disable Entire Display On (0x00/0x01)
  Set_Inverse_Display(0x00);	  // Disable Inverse Display On (0x00/0x01)
  Set_Display_On_Off(0x01);		  // Display On (0x00/0x01)
  LED_Fill(0x00);                               //clear all
  LED_Set_Pos(0,0);
} 


void LED_P6x8Char(unsigned char x,unsigned char y,unsigned char ch)
{
  unsigned char c=0,i=0;

  c =ch-32;
  if(x>122)
  {
    x=0;
    y++;
  }
  LED_Set_Pos(x,y);
  for(i=0;i<6;i++)
  {
    LED_WrDat(F6x8[c][i]);
  }
}

void LED_P6x8Str(unsigned char x,unsigned char y,char ch[])
{
  unsigned char c=0,i=0,j=0;
  while (ch[j]!='\0')
  {
    c =ch[j]-32;
    if(x>126)
    {
      x=0;
      y++;
    }
    LED_Set_Pos(x,y);
    for(i=0;i<6;i++)
    {
      LED_WrDat(F6x8[c][i]);
    }
    x+=6;
    j++;
  }
}

void LED_P8x16Str(unsigned char x,unsigned char y,char ch[])
{
  unsigned char c=0,i=0,j=0;
  while (ch[j]!='\0')
  {
    c =ch[j]-32;
    if(x>120)
    {
      x=0;
      y++;
    }
    LED_Set_Pos(x,y);
    for(i=0;i<8;i++)
    {
      LED_WrDat(F8X16[(c<<4)+i]);
    }
    LED_Set_Pos(x,y+1);
    for(i=0;i<8;i++)
    {
      LED_WrDat(F8X16[(c<<4)+i+8]);
    }
    x+=8;
    j++;
  }
}



void LED_PrintBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char bmp[])
{ 	
  int ii=0;
  unsigned char x,y;
  for(y=y0;y<=y1;y++)
  {
    LED_Set_Pos(x0,y);
    for(x=x0;x<x1;x++)
    {
      LED_WrDat(bmp[ii++]);
    }
  }
}

//void LED_PrintValueC(unsigned char x, unsigned char y, char data)
//{
//  unsigned char i,j,k;
//  if(data < 0)
//  {
//    LED_P6x8Char(x,y,'-');
//    data = - data;
//  }
//  else
//  {
//    LED_P6x8Char(x,y,'+');
//  }
//  i = data/100;
//  j = (data%100)/10;
//  k = data%10;
//  LED_P6x8Char(x+6,y,i+48);
//  LED_P6x8Char(x+12,y,j+48);
//  LED_P6x8Char(x+18,y,k+48);
//}
//
//
//void LED_PrintValueI(unsigned char x, unsigned char y, int data)
//{
//  unsigned char i,j,k,l,m;
//  if(data < 0)
//  {
//    LED_P6x8Char(x,y,'-');
//    data = - data;
//  }
////  else
////  {
////    LED_P6x8Char(x,y,'+');
////  }
//
//  l  = data/10000;
//  m= (data%10000)/1000;
//  i = (data%1000)/100;
//  j = (data%100)/10;
//  k = data%10;
//
//  unsigned char c;
//  c = (l == 0) ? ' ' : (l + 48); LED_P6x8Char(x+6,y,c);
//  c = (m == 0 && l == 0) ? ' ' : (m + 48); LED_P6x8Char(x+12,y,c);
//  c = (i == 0 && m == 0 && l == 0) ? ' ' : (i + 48); LED_P6x8Char(x+18,y,c);
//  c = (j == 0 && i == 0 && m == 0 && l == 0) ? ' ' : (j + 48); LED_P6x8Char(x+24,y,c);
//  LED_P6x8Char(x+30,y,k+48);
//}
//
//void LED_PrintValueFP(unsigned char x, unsigned char y, unsigned int data, unsigned char num)
//{
//  unsigned char m,i,j,k;
//  LED_P6x8Char(x, y, '.');
//  m= data/1000;
//  i = (data%1000)/100;
//  j = (data%100)/10;
//  k = data%10;
//  switch(num)
//  {
//  case 1:  	LED_P6x8Char(x+6,y,k+48);
//  break;
//  case 2:  	LED_P6x8Char(x+6,y,j+48);
//  LED_P6x8Char(x+12,y,k+48);
//  break;
//  case 3:	LED_P6x8Char(x+6,y,i+48);
//  LED_P6x8Char(x+12,y,j+48);
//  LED_P6x8Char(x+18,y,k+48);
//  break;
//  case 4: 	LED_P6x8Char(x+6,y,m+48);
//  LED_P6x8Char(x+12,y,i+48);
//  LED_P6x8Char(x+18,y,j+48);
//  LED_P6x8Char(x+24,y,k+48);
//  break;
//  }
//}
//
//
//void LED_PrintValueF(unsigned char x, unsigned char y, float data, unsigned char num)
//{
//  unsigned char l,m,i,j,k;
//  unsigned char databiti = 6;
//  unsigned int tempdataui = 0;
//  int tempdataii = (int)data;
//  long int tempdatalp = (long int)((data - (int)data)*10000);
//
//
//  if(data < 0.0000001)  LED_P6x8Char(x, y,'-');
//  else LED_P6x8Char(x, y,'+');
//  if(tempdataii < 0)tempdataii = - tempdataii;
//  tempdataui = tempdataii;
//  l  = tempdataui/10000;
//  m= (tempdataui%10000)/1000;
//  i = (tempdataui%1000)/100;
//  j = (tempdataui%100)/10;
//  k = tempdataui%10;
//  if (l != 0)
//  {
//    LED_P6x8Char(x+6,y,l+48);
//    LED_P6x8Char(x+12,y,m+48);
//    LED_P6x8Char(x+18,y,i+48);
//    LED_P6x8Char(x+24,y,j+48);
//    LED_P6x8Char(x+30,y,k+48);
//  }
//  else if(m != 0)
//  {
//    databiti = 5;
//    LED_P6x8Char(x+6,y,m+48);
//    LED_P6x8Char(x+12,y,i+48);
//    LED_P6x8Char(x+18,y,j+48);
//    LED_P6x8Char(x+24,y,k+48);
//  }
//  else if(i != 0)
//  {
//    databiti = 4;
//    LED_P6x8Char(x+6,y,i+48);
//    LED_P6x8Char(x+12,y,j+48);
//    LED_P6x8Char(x+18,y,k+48);
//  }
//  else if(j != 0)
//  {
//    databiti = 3;
//    LED_P6x8Char(x+6,y,j+48);
//    LED_P6x8Char(x+12,y,k+48);
//  }
//  else
//  {
//    databiti = 2;
//    LED_P6x8Char(x+6,y,k+48);
//  }
//  if(tempdatalp < 0)tempdatalp = - tempdatalp;
//  switch(num)
//  {
//  case 0: break;
//  case 1:  LED_PrintValueFP(x + databiti * 6, y, (unsigned int)(tempdatalp / 1000),num);break;
//  case 2:  LED_PrintValueFP(x + databiti * 6, y, (unsigned int)(tempdatalp / 100),num);break;
//  case 3:  LED_PrintValueFP(x + databiti * 6, y, (unsigned int)(tempdatalp / 10),num);break;
//  case 4:  LED_PrintValueFP(x + databiti * 6, y, (unsigned int)(tempdatalp),num);break;
//  }
//}

void LED_Cursor(unsigned char cursor_column, unsigned char cursor_row)
{
  if(cursor_row != 0)
  {
    if(cursor_column == 1) LED_Set_Pos(0, cursor_row + 2);
    else  LED_Set_Pos(80 + (cursor_column - 2)*6, cursor_row + 2);
    LED_WrDat(0xFF);
    LED_WrDat(0xFF);
    LED_WrDat(0xFF);
    LED_WrDat(0xFF);
    LED_WrDat(0xFF);
    LED_WrDat(0xFF);
  }
}

#if 0
void setup()
{
  LEDPIN_Init();
  LED_Init();
}
void loop()
{
  LED_P8x16Str(23,0,"welcome to");
  LED_P8x16Str(40,2,"Smart");
  LED_P8x16Str(20,4,"Prototyping");
}
#endif
