text: /*
   * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
   *
   * @created: 2014-08-12
   * @author: Neven Boyanov
   *
   * Source code available at: https://bitbucket.org/tinusaur/ssd1306xled
   *
   */
  
  // ----------------------------------------------------------------------------
  
  #include <stdlib.h>
  #include <avr/io.h>
  
  #include <avr/pgmspace.h>
  
  #include "DigisparkOLED.h"
  
  #ifndef _nofont_8x16 // tBUG Optional removal to save code space
  #include "font8x16.h"
  #endif
  
  // ----------------------------------------------------------------------------
  
  // Some code based on "IIC_wtihout_ACK" by http://www.14blog.com/archives/1358
  
  const uint8_t ssd1306_init_sequence[] PROGMEM = {
  	// Initialization Sequence
  	0xAE,		// Display OFF (sleep mode)
  	0x20, 0b00, // Set Memory Addressing Mode
  	// 00=Horizontal Addressing Mode; 01=Vertical Addressing Mode;
  	// 10=Page Addressing Mode (RESET); 11=Invalid
  	0xB0,		// Set Page Start Address for Page Addressing Mode, 0-7
  	0xC8,		// Set COM Output Scan Direction
  	0x00,		// ---set low column address
  	0x10,		// ---set high column address
  	0x40,		// --set start line address
  	0x81, 0x3F, // Set contrast control register
  	0xA1,		// Set Segment Re-map. A0=address mapped; A1=address 127 mapped.
  	0xA6,		// Set display mode. A6=Normal; A7=Inverse
  	0xA8, 0x3F, // Set multiplex ratio(1 to 64)
  	0xA4,		// Output RAM to Display
  	// 0xA4=Output follows RAM content; 0xA5,Output ignores RAM content
  	0xD3, 0x00, // Set display offset. 00 = no offset
  	0xD5,		// --set display clock divide ratio/oscillator frequency
  	0xF0,		// --set divide ratio
  	0xD9, 0x22, // Set pre-charge period
  	0xDA, 0x12, // Set com pins hardware configuration
  	0xDB,		// --set vcomh
  	0x20,		// 0x20,0.77xVcc
  	0x8D, 0x14, // Set DC-DC enable
  	0xAF		// Display ON in normal mode
  
  };
  
  uint8_t oledFont=FONT8X16, oledX, oledY = 0;
  
  // Program:    5248 bytes
  
  SSD1306Device::SSD1306Device(void) {}
  
  void SSD1306Device::begin(void)
  {
  	Wire.begin();
  
  	for (uint8_t i = 0; i < sizeof(ssd1306_init_sequence); i++)
  	{
  		ssd1306_send_command(pgm_read_byte(&ssd1306_init_sequence[i]));
  	}
  //	clear();
    fill(0x00);
  }
  
  void SSD1306Device::ssd1306_send_command_start(void)
  {
  	Wire.beginTransmission(SSD1306);
  	Wire.write(0x00); // write command
  }
  
  void SSD1306Device::ssd1306_send_command_stop(void)
  {
  	Wire.endTransmission();
  }
  
  void SSD1306Device::ssd1306_send_data_byte(uint8_t byte)
  {
  	if (Wire.writeAvailable())
  	{
  		ssd1306_send_data_stop();
  		ssd1306_send_data_start();
  	}
  	Wire.write(byte);
  }
  
  void SSD1306Device::ssd1306_send_command(uint8_t command)
  {
  	ssd1306_send_command_start();
  	Wire.write(command);
  	ssd1306_send_command_stop();
  }
  
  void SSD1306Device::ssd1306_send_data_start(void)
  {
  	Wire.beginTransmission(SSD1306);
  	Wire.write(0x40); //write data
  }
  
  void SSD1306Device::ssd1306_send_data_stop(void)
  {
  	Wire.endTransmission();
  }
  
  void SSD1306Device::setCursor(uint8_t x, uint8_t y)
  {
  	ssd1306_send_command_start();
  	Wire.write(0xb0 + y);
  	Wire.write(((x & 0xf0) >> 4) | 0x10); // | 0x10
  	Wire.write((x & 0x0f) | 0x01);		  // | 0x01
  	ssd1306_send_command_stop();
  	oledX = x;
  	oledY = y;
  }
  
  //void SSD1306Device::clear(void)
  //{
  //	fill(0x00);
  //}
  
  void SSD1306Device::fill(uint8_t fill)
  {
  	uint8_t m, n;
  	for (m = 0; m < 8; m++)
  	{
  		ssd1306_send_command(0xb0 + m); // page0 - page1
  		ssd1306_send_command(0x00);		// low column start address
  		ssd1306_send_command(0x10);		// high column start address
  		ssd1306_send_data_start();
  		for (n = 0; n < 128; n++)
  		{
  			ssd1306_send_data_byte(fill);
  		}
  		ssd1306_send_data_stop();
  	}
  	setCursor(0, 0);
  }
  
  size_t SSD1306Device::write(byte c)
  {
  	uint8_t i = 0;
  	uint8_t ci = c - 32;
  	if (c == '\r')
  		return 1;
  	if (c == '\n')
  	{
  
  		oledY += 2;	   //tBUG  Large Font up by two
  		if (oledY > 6) // tBUG
  			oledY = 6;
  		setCursor(0, oledY);
  		return 1;
  	}
  
  #ifndef _nofont_8x16 // tBUG
  	else
  	{
  		if (oledX > 120)
  		{
  			oledX = 0;
  			oledY += 2;	   //tBUG  Large Font up by two
  						   //			oledY++;
  			if (oledY > 6) // tBUG
  				oledY = 6;
  			setCursor(oledX, oledY);
  		}
  
  		ssd1306_send_data_start();
  		for (i = 0; i < 8; i++)
  		{
  			Wire.write(pgm_read_byte(&ssd1306xled_font8x16[ci * 16 + i]));
  		}
  		ssd1306_send_data_stop();
  		setCursor(oledX, oledY + 1);
  		ssd1306_send_data_start();
  		for (i = 0; i < 8; i++)
  		{
  			Wire.write(pgm_read_byte(&ssd1306xled_font8x16[ci * 16 + i + 8]));
  		}
  		ssd1306_send_data_stop();
  		setCursor(oledX + 8, oledY - 1);
  	}
  #endif
  	return 1;
  }
  
  
  void SSD1306Device::bitmap(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t bitmap[])
  {
    uint16_t j = 0;
    uint8_t y, x;
    // if (y1 % 8 == 0) y = y1 / 8;   // else y = y1 / 8 + 1;   // tBUG :: this does nothing as y is initialized below
    //  THIS PARAM rule on y makes any adjustment here WRONG   //usage oled.bitmap(START X IN PIXELS, START Y IN ROWS OF 8 PIXELS, END X IN PIXELS, END Y IN ROWS OF 8 PIXELS, IMAGE ARRAY);
    for (y = y0; y < y1; y++)
    {
      setCursor(x0,y);
      ssd1306_send_data_start();
      for (x = x0; x < x1; x++)
      {
        ssd1306_send_data_byte(pgm_read_byte(&bitmap[j++]));
      }
      ssd1306_send_data_stop();
    }
    setCursor(0, 0);
  }
  
  
  SSD1306Device oled;
  
  // ----------------------------------------------------------------------------
  