/*
 * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
 * @author Neven Boyanov
 *Modified by Ajoy
 */
#include <stdint.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <string.h>
#include "font16x32_digits.h"
uint8_t x=0; uint8_t y=0;
unsigned char pDest[2][32];
unsigned char  s[16];
uint8_t renderingFrame=0xB0;
uint8_t drawingFrame=0x40;
// ----------------------------------------------------------------------------
// ---------------uses Alternate Pins for I2C ---------------------------------
// -----(+)--------------->	// Vcc,	Pin 8
// -----(-)--------------->	// GND,	Pin 4
#define SSD1306_SCL		PB2	// SCL,	Pin 7
//#define SSD1306_SDA		PB1	// SDA,	Pin 6 *PB0 used for Vref Bypass Capacitor
#define SSD1306_SDA		PB0	// SDA,	Pin 6
#define SSD1306_SA		0x78	// Slave address



// ----------------------------------------------------------------------------
//fUNCTIONS
		void ssd1306_init(void);
		void ssd1306_xfer_start(void);
		void ssd1306_xfer_stop(void);
		void ssd1306_send_byte(uint8_t byte);
		void ssd1306_send_command(uint8_t command);
		void ssd1306_send_command3(uint8_t command1, uint8_t command2, uint8_t command3);
		void ssd1306_send_data_start(void);
		void ssd1306_send_data_stop(void);
		void setColumnAddress(uint8_t startAddress, uint8_t endAddress);
		void setPageAddress(uint8_t startPage, uint8_t endPage);
		void ssd1306_setpos(uint8_t x, uint8_t y,uint8_t renderingFrame);
		void ssd1306_switchFrame(void);
		void ssd1306_switchDisplayFrame(void);
		void ssd1306_switchRenderFrame(void);
		void initFrame(void);		
		void ssd1306_fillscreen(uint8_t fill);		
		void ssd1306_char_font6x8(char ch);
		void ssd1306_string_font6x8(char *s);		
		void ssd1306_send_command_start(void);
		void ssd1306_send_command_stop(void);
		void digit_16x32(uint8_t x, uint8_t y, const char *number, uint8_t numChar);
		void display_V(uint16_t VADC);
//--------------------------------------------------------------------


// ----------------------------------------------------------------------------

#define DIGITAL_WRITE_HIGH(PORT) PORTB |= (1 << PORT)
#define DIGITAL_WRITE_LOW(PORT) PORTB &= ~(1 << PORT)


const uint8_t ssd1306_initial_sequence [] PROGMEM = {	// Initialization Sequence
	0xAE,			// Display OFF (sleep mode)
	0x20, 0b10,		// Set Memory Addressing Mode //00
					// 00=Horizontal Addressing Mode; 01=Vertical Addressing Mode;
					// 10=Page Addressing Mode (RESET); 11=Invalid
	0xB4,	//0xB4		// Set Page Start Address for Page Addressing Mode, 0-7
	0xC0,			// Set COM Output Scan Direction C8
	0x00,			// ---set low column address
	0x01,			// ---set high column address
	0x40,	//0x60    	// --set display start line address
	0x81, 0x3F,		// Set contrast control register
	0xA0,			// Set Segment Re-map. A0=address mapped; A1=address 127 mapped. 
	0xA6,			// Set display mode. A6=Normal; A7=Inverse
	0xA8, 0x1F,		// Set multiplex ratio(1 to 64) 3F
	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, 0x02,		// Set com pins hardware configuration	0x12	
	0xDB,			// --set vcomh
	0x20,			// 0x20,0.77xVcc
	0x8D, 0x14,		// Set DC-DC enable
	0xAF			// Display ON in normal mode
	
};

// Program:    5248 bytes

void ssd1306_init(void)
{
	DDRB |= (1 << SSD1306_SDA);	// Set port as output
	DDRB |= (1 << SSD1306_SCL);	// Set port as output
	
	for (uint8_t i = 0; i < sizeof (ssd1306_initial_sequence); i++) {
		ssd1306_send_command(pgm_read_byte(&ssd1306_initial_sequence[i]));
	}
}
void initFrame(void){
	renderingFrame=0xB0;
	drawingFrame=0x40;
	ssd1306_send_command(drawingFrame);
	ssd1306_setpos(0,0,renderingFrame);
}

void ssd1306_xfer_start(void)
{
	DIGITAL_WRITE_HIGH(SSD1306_SCL);	// Set to HIGH
	DIGITAL_WRITE_HIGH(SSD1306_SDA);	// Set to HIGH
	DIGITAL_WRITE_LOW(SSD1306_SDA);		// Set to LOW
	DIGITAL_WRITE_LOW(SSD1306_SCL);		// Set to LOW
}

void ssd1306_xfer_stop(void)
{
	DIGITAL_WRITE_LOW(SSD1306_SCL);		// Set to LOW
	DIGITAL_WRITE_LOW(SSD1306_SDA);		// Set to LOW
	DIGITAL_WRITE_HIGH(SSD1306_SCL);	// Set to HIGH
	DIGITAL_WRITE_HIGH(SSD1306_SDA);	// Set to HIGH
}

void ssd1306_send_byte(uint8_t byte)
{
	uint8_t i;
	for (i = 0; i < 8; i++)
	{
		if ((byte << i) & 0x80)
			DIGITAL_WRITE_HIGH(SSD1306_SDA);
		else
			DIGITAL_WRITE_LOW(SSD1306_SDA);
		
		DIGITAL_WRITE_HIGH(SSD1306_SCL);
		DIGITAL_WRITE_LOW(SSD1306_SCL);
	}
	DIGITAL_WRITE_HIGH(SSD1306_SDA);
	DIGITAL_WRITE_HIGH(SSD1306_SCL);
	DIGITAL_WRITE_LOW(SSD1306_SCL);
}

void ssd1306_send_command_start(void) {
	ssd1306_xfer_start();
	ssd1306_send_byte(SSD1306_SA);  // Slave address, SA0=0
	ssd1306_send_byte(0x00);	// write command
}

void ssd1306_send_command_stop(void) {
	ssd1306_xfer_stop();
}

void ssd1306_send_command(uint8_t command)
{
	ssd1306_send_command_start();
	ssd1306_send_byte(command);
	ssd1306_send_command_stop();
}
void ssd1306_send_command3(uint8_t command1, uint8_t command2, uint8_t command3) {
	ssd1306_send_command_start();
	ssd1306_send_byte(command1);
	ssd1306_send_byte(command2);
	ssd1306_send_byte(command3);
	ssd1306_send_command_stop();
}
void ssd1306_send_data_start(void)
{
	ssd1306_xfer_start();
	ssd1306_send_byte(SSD1306_SA);
	ssd1306_send_byte(0x40);	//write data
}

void ssd1306_send_data_stop(void)
{
	ssd1306_xfer_stop();
}
void setColumnAddress(uint8_t startAddress, uint8_t endAddress) {
	ssd1306_send_command3(0x21, startAddress & 0x7F, endAddress & 0x7F);
}
void setPageAddress(uint8_t startPage, uint8_t endPage) {
	ssd1306_send_command3(0x22, startPage & 0x07, endPage & 0x07);
}
void ssd1306_setpos(uint8_t x, uint8_t y,uint8_t renderingFrame)
{
	ssd1306_send_command_start();
	ssd1306_send_byte(renderingFrame + (y & 0x07));
	ssd1306_send_byte( y);//(y & 0x07));
	ssd1306_send_byte(((x & 0xf0) >> 4) | 0x10); // | 0x10
	ssd1306_send_byte((x & 0x0f) | 0x01); // | 0x01
	ssd1306_send_command_stop();
}
void ssd1306_switchFrame(void) {
	ssd1306_switchDisplayFrame();
	ssd1306_switchRenderFrame();
}
void ssd1306_switchDisplayFrame(void) {
	drawingFrame ^= 0x20;
	ssd1306_send_command(drawingFrame);
}
void ssd1306_switchRenderFrame(void) {
	renderingFrame ^= 0x04;
}
void ssd1306_fillscreen(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_byte(fill);
		}
		ssd1306_send_data_stop();
	}
}


void digit_16x32(uint8_t x, uint8_t y, const char *number, uint8_t numChar){
	// big number pixels: 16 x 32
	// numChar = number of characters
	// *number = string ",-./0123456789:"
	// Y - page (4 pages)
	// x - pixel position 0-127
	uint8_t column = 0;
	uint8_t count = 0;
	uint8_t value;		
	while(number[count] && count<numChar){			
		ssd1306_setpos(x, y,renderingFrame);				
		for(uint8_t i=0; i<4; i++) {
			for(uint8_t j=0; j<15; j++) {
			ssd1306_send_data_start();
			// if character is not "0-9" or ':'
			//./0123456789:	
			if(number[count] < 44 || number[count] > 60)
				ssd1306_send_byte(0);
				else				
				ssd1306_send_byte(pgm_read_byte(&font16x32_digits[number[count]-44][i*16+j]));
			ssd1306_send_data_stop();
			}				
			ssd1306_setpos(x, ++y,renderingFrame);	
										
		}				
		count++;		
		x = x + 18;
		y = y - 4;		
	}
}
void display_V(uint16_t VADC){
	char dispstr[5];
	dispstr[5]=';';
	uint16_t V;
	VADC = VADC & 0x3FF;
	V = VADC;	
	//V= (VADC *0x0F)/10;	//scaling
	dispstr[0]=(V/1000)+'0';
	if (dispstr[0]=='0'){
		dispstr[0]=' ';
	};
	V=V%1000; //Remainder of integer division
	dispstr[1]=V/100+'0';
	dispstr[2]='.';
	V=V%100;
	dispstr[3]=V/10+'0';
	V=V%10;
	dispstr[4]=V+'0';
	
	digit_16x32(12,0,dispstr,6);
}


// ----------------------------------------------------------------------------

