/* ============================================================================ */
/*  Copyright (c) 2024, LEBEL SAS		  	                        */
/*  All rights reserved.                                                        */
/*                                                                              */
/*  Redistribution and use in source and binary forms, with or without          */
/*  modification, are permitted provided that the following conditions          */
/*  are met:                                                                    */
/*                                                                              */
/*  *  Redistributions of source code must retain the above copyright           */
/*     notice, this list of conditions and the following disclaimer.            */
/*                                                                              */
/*  *  Redistributions in binary form must reproduce the above copyright        */
/*     notice, this list of conditions and the following disclaimer in the      */
/*     documentation and/or other materials provided with the distribution.     */
/*                                                                              */
/*  *  Neither the name of LEBEL SAS nor the names of      			*/
/*     its contributors may be used to endorse or promote products derived      */
/*     from this software without specific prior written permission.            */
/*                                                                              */
/*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" */
/*  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,       */
/*  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR      */
/*  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR            */
/*  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,       */
/*  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,         */
/*  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; */
/*  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,    */
/*  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR     */
/*  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,              */
/*  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                          */
/* ============================================================================ */

/********************************************************************
*
* subroutines for the East Rising ER-OLEDM032-1W
* 256X64 Oled display.
*
* LEBEL SAS , Version 1.0 2024
*
* Rev. 1.0, Setup
*
********************************************************************/

/*----------------------------------------------------------------------------*/
/* CONSTANTS		                                                      */
/*----------------------------------------------------------------------------*/

/* constants for SPI 4 wires Oled driving */


// if needed here are the definitions used in the main program
// #define MOSI BIT7   		// Serial Data line 1.7
// #define CLK BIT5   		// Serial Clock line 1.5
// #define /CS BIT2   		// Chip Select line 2.2
// #define /RST BIT1   		// Chip Select line 2.1
// #define D/C BIT0   		// Data/Command line 2.0


/************************************************************
* FUNCTIONS
************************************************************/
/******************************************************************* I2C functions */
/* A crude delay function.  Tune by changing the counter value. */
void delay( unsigned int n ) {
    volatile int i;

    for( ; n; n-- ) {
        for( i = 500; i > 0; i-- );
    }
}

void data_read(void ) {
    P2DIR &= ~I2C_SDA; // float to get ready to read
}

void data_high(void ) {
    P2DIR &= ~I2C_SDA; // float pin to go high
    //delay( 50 );
}

void data_low(void ) {
    P2OUT &= ~I2C_SDA; // assert low
    P2DIR |= I2C_SDA;
    //delay( 50 );
}

void clk_high(void) {
    P1DIR &= ~I2C_SCL;  // float pin to go high
    //delay( 100 );
}

void clk_low(void) {
    P1OUT &= ~I2C_SCL;  // assert low
    P1DIR |= I2C_SCL;
    //delay( 50 );
}
void clk_toggle(void) {
    P1OUT ^= I2C_SCL;  // toggle p1.5 to test
    P2OUT ^= I2C_SDA; // assert low
}

void I2C_Start(void) {
    clk_high();
    data_high();
    data_low();
    clk_low();
}

/* I2C communication stops with both the clock and data
 * lines going high, in that order. */
void I2C_Stop(void) {
    data_low();
    clk_low();
    clk_high();
    data_high();
}

/* Outputs 8-bit command or data via I2C lines. */
void I2C_out(unsigned char d) {
    int n;

    for( n = 8; n > 0; n-- ) {
        if( d & 0x80 ) {
            data_high();
        } else {
            data_low();
        }

        clk_high();
        clk_low();

        d <<= 1;        // Shift next bit into position.
    }

    data_read();        // Set data line to receive.
    clk_high();         // Clock goes high to wait for acknowledge.

    // Slave will pull data line low to acknowledge.
    //P1OUT |= (BIT0);			// chip select not used
    while( P2IN & I2C_SDA ) {
        // Else toggle the clock line and check again
        clk_low();
        clk_high();
    }
    clk_low();
	//P1OUT &= ~(BIT0);			// chip select not used
}
/******************************************************************* reset the display
 *
 */
void reset_display(void){
	int i;
	/*            I2C Display Reset           			 */
	// P1OUT |= (BIT0);			// chip select not used
	P2OUT |= (I2C_RESET);
	for (i = 30000; i > 0; i--);
	P2OUT &= ~(I2C_RESET);
	for (i = 30000; i > 0; i--);
	P2OUT |= (I2C_RESET);
	for (i = 30000; i > 0; i--);
	// P1OUT &= ~(BIT0);		// chip select not used
}

/****************SET PAGE ADDRESS**************/
void Set_Page_Address(unsigned char add)
{
	I2C_Start();
	I2C_out(Oled_Addr);
	I2C_out(Command);
    add=0xb0|add;
    I2C_out(add);
    I2C_Stop();

}

/****************SET COLUMN ADDRESS**************/
void Set_Column_Address(unsigned char add)
{
	I2C_Start();
	I2C_out(Oled_Addr);
	I2C_out(Command);
	I2C_out((0x10|(add>>4)));
	I2C_out(Command);
	I2C_out((0x0f&add));
	I2C_Stop();

}

/******************************************************************* Sends the "clear display" command to the display. */

void clear_display(void){
	int n,i;


	for(i=0;i<2;i++)
	{
	    Set_Page_Address(i);
	    Set_Column_Address(0x00);
	    I2C_Start();
	    I2C_out( Oled_Addr ); // Slave address of panel.
	    I2C_out( Data ); // Control byte: data bytes follow, data is RAM data.

	    for( n = 0; n < 128; n++ )
		{
	    	I2C_out(0x00);
	    }
	    I2C_Stop();
	}
}

/******************************************************************* display controls */
/* Initializes the display panel. */
void init_display(void) {
	reset_display();
    clear_display();

    I2C_Start();

    I2C_out( Oled_Addr );    // Slave address of the LCD panel.
    I2C_out( Command );    // Control byte: all following bytes are commands.
    I2C_out( 0xAE );    // turn off panel
    delay( 100 );

    I2C_out( Command );
    I2C_out( 0x00 );    // set lower start column nibble ??
    I2C_out( Command );
    I2C_out( 0x1F );    // set higher start column nibble ??
    delay( 100 );

    I2C_out( Command );
    I2C_out( 0xA8 );    // set multiplex ratio
    I2C_out( Command );
    I2C_out( 0x0F );    // 1/16
    delay( 100 );

    I2C_out( Command );
    I2C_out( 0xD3 );    // Display offset
    I2C_out( Command );
    I2C_out( 0x00 );    // no offset
    delay( 100 );

    I2C_out( Command );
    I2C_out( 0x8D );    // charge pump
    I2C_out( Command );
    I2C_out( 0x14 );    // is set
    delay( 100 );

    I2C_out( Command );
    I2C_out( 0x40 );    // start line adress
    delay( 100 );    //delay( 10 );

    I2C_out( Command );
    I2C_out( 0xA6 );    // normal display
    delay( 100 );

    I2C_out( Command );
    I2C_out( 0xA4 );    // show RAM content
    delay( 1000 );

    I2C_out( Command );
    I2C_out( 0xA1 );    // segment remap 96 to 1
    delay( 100 );

    I2C_out( Command );
    I2C_out( 0xC8 );    // Set COM Output Scan Direction 16 to 1
   delay( 100 );

    I2C_out( Command );
    I2C_out( 0xDa );    // set com pins hardware configuration
    I2C_out( Command );
    I2C_out( 0x02 );    //
    delay( 100 );

    I2C_out( Command );
    I2C_out( 0x81 );    // set contrast control register
    I2C_out( Command );
    I2C_out( 0x7F);    //
    delay( 100 );

    I2C_out( Command );
    I2C_out( 0xD9 );    // set pre-charge period
    I2C_out( Command );
    I2C_out( 0xF1 );    // impact the brightness
    delay( 100 );

    I2C_out( Command );
    I2C_out( 0xD5 );    // set osc frequency
    I2C_out( Command );
    I2C_out( 0xF0 );    // 7-4 speed 3-0 divider
    delay( 100 );

    I2C_out( Command );
    I2C_out( 0xDB );    // set vcomh
    I2C_out( Command );
    I2C_out( 0x20 );    // 0,83 Vcc
    delay( 100);

    I2C_out( Command );
    I2C_out( 0x22 );    // set page
    I2C_out( Command );
    I2C_out( 0x00 );    //
    I2C_out( Command );
    I2C_out( 0x01 );    //
    delay( 100);

    I2C_out( Command );
    I2C_out( 0x21 );    // set column
    I2C_out( Command );
    I2C_out( 0x00 );    // 00 start
    I2C_out( Command );
    //I2C_out( 0xFF );    //0xFF end 255
    I2C_out( 0x5F );    //0x5F end 96
    delay( 100);

    I2C_out( Command );
    I2C_out( 0xAF );    // turn on led panel
    delay( 100);

    I2C_out( Command );
    I2C_out( 0x20 );    // memory adressing mode
    I2C_out( Command );
    I2C_out( 0x02 );    // page&column, page adressing mode
    delay( 100);

    I2C_Stop();
}


/****************************************************************** write a page, 96 bytes*/
void show( unsigned char page,unsigned char col, unsigned char lenght, unsigned char *text )
{
    int n;

	Set_Page_Address(page);
    Set_Column_Address(col);			// column in pixel
    I2C_Start();
    I2C_out( Oled_Addr ); 				// Slave address of panel.
    I2C_out( Data ); 					// Control byte: data bytes follow, data is RAM data.

    for( n = lenght; n > 0; n-- )
    {
        I2C_out( *text );
        text++;
    }
    I2C_Stop();

}

/****************************************************************** write a full screen, 192 bytes*/
void show_screen(  unsigned char *text ) // 7,25ms @ 16MHz
{
    int n;

	I2C_Start();
	I2C_out(Oled_Addr);
    I2C_out( Command );
    I2C_out( 0x20 );    // memory adressing mode
    I2C_out( Command );
    I2C_out( 0x00 );    // horizontal adressing mode

    I2C_out( Command );
    I2C_out( 0x21 );    // column config
    I2C_out( Command );
    I2C_out( 0x00 );    // column start
    I2C_out( Command );
    I2C_out( 0x5F );    // column end

    I2C_out( Command );
    I2C_out( 0x22 );    // page config
    I2C_out( Command );
    I2C_out( 0x00 );    // page start
    I2C_out( Command );
    I2C_out( 0x01 );    // page end
    I2C_Stop();

    I2C_Start();
    I2C_out( Oled_Addr ); 				// Slave address of panel.
    I2C_out( Data ); 					// Control byte: data bytes follow, data is RAM data.
    for( n = 192; n > 0; n-- )
    {
        I2C_out( *text );				// output screen
        text++;
    }
    I2C_Stop();

    I2C_Start();
	I2C_out(Oled_Addr);
    I2C_out( Command );					// back to standard page adressing mode
    I2C_out( 0x20 );    				// memory page adressing mode
    I2C_out( Command );
    I2C_out( 0x02 );    				// page&column, page adressing mode
    I2C_Stop();
}

/******************************************************************** write a caracter, 8 bytes*/
void showchr( unsigned char page,unsigned char col, unsigned char color, const unsigned char *text )
{
    int n;

	Set_Page_Address(page);
    Set_Column_Address(col*8);					// column in caracter
    I2C_Start();
    I2C_out( Oled_Addr ); 						// Slave address of panel.
    I2C_out( Data ); 							// Control byte: data bytes follow, data is RAM data.
    for( n = 0; n < 8; n++ )					// 8 bytes caracter
    {
        if (color == 0)
        {
        	I2C_out( *text );
        }

        else
        {
        	I2C_out (~( *text ));
        }
        text++;
    }
    I2C_Stop();

}

//////////// left scroll
void hscroll( void ) {
    I2C_Start();
    I2C_out( Oled_Addr ); // Slave address of panel.
    I2C_out( Command );
    I2C_out( 0x27 );    // left scroll
    I2C_out( Command );
    I2C_out( 0x00);    // dummy
    I2C_out( Command );
    I2C_out( 0x00 );    // start page 0
    I2C_out( Command );
    I2C_out( 0x00 );    // set time
    I2C_out( Command );
    I2C_out( 0x01 );    // end page1
    I2C_out( Command );
    I2C_out( 0x00);    // dummy
    I2C_out( Command );
    I2C_out( 0xFF);    // dummy
    I2C_out( Command );
    I2C_out( 0x2F);    // start scroll
    I2C_Stop();
}

//////////// up scroll
void scroll( unsigned char speed, unsigned char direction, unsigned char horizontal, unsigned char vertical) {
    I2C_Start();
    I2C_out( Oled_Addr ); // Slave address of panel.
    I2C_out( Command );
    I2C_out( 0xA3 );    // set vertical scroll area
    I2C_out( Command );
    I2C_out( 0x00);    // start line
    I2C_out( Command );
    I2C_out( 0x10 );    // last line
    I2C_out( Command );
    I2C_out( 0x00 );    // dummy
    I2C_out( Command );
    if (direction == 'L')
    {
    	I2C_out( 0x29 );    // horizontal scroll from left to right
    }
    else
    {
    	I2C_out( 0x2A );    // horizontal scroll from right to left
    }
    I2C_out( Command );
    I2C_out( 0x11);    // dummy
    if ( horizontal == 0)
    {
        I2C_out( Command );
        I2C_out( 0xFF );    // no start page horizontal scroll
        I2C_out( Command );
        I2C_out( speed );    // set time
        I2C_out( Command );
        I2C_out( 0xFF );    // no end page horizontal scroll
    }
    else
    {
        I2C_out( Command );
        I2C_out( 0x01 );    //  start page 1 horizontal scroll
        I2C_out( Command );
        I2C_out( speed );    // set time
        I2C_out( Command );
        I2C_out( 0x01 );    // end page 1 horizontal scroll
    }
    I2C_out( Command );
    I2C_out( vertical );    // 1 line vertical scroll
    I2C_out( Command );
    I2C_out( 0x2F);    // start scroll
    I2C_Stop();
}

//////////// blink
void blink( void )
{
    I2C_Start();
    I2C_out( Oled_Addr ); // Slave address of panel.
    I2C_out( Command );
    I2C_out( 0x23 );    // blink command
    I2C_out( Command );
    I2C_out( 0x11);    // command, bit 4 is cycle mode, 0 to 3 is speed
    I2C_Stop();
}

//////////// contrast
void contrast( unsigned char value )
{
    I2C_Start();
    I2C_out( Oled_Addr ); // Slave address of panel.
    I2C_out( Command );
    I2C_out( 0x81 );    // contrast command
    I2C_out( Command );
    switch (value)
    {
    case 0:
        I2C_out( 0x00);    // low contrast
        break;
    case 1:
        I2C_out( 0x22);    // medium contrast
        break;
    case 2:
        I2C_out( 0xFF);    // high contrast
        break;
    }
    I2C_Stop();
}

////////// display on
void display_on( void )
{
    I2C_Start();
    I2C_out( Oled_Addr ); // Slave address of panel.
    I2C_out( Command );
    I2C_out( 0x8D );    // charge pump setting
    I2C_out( Command );
    I2C_out( 0x14 );    // charge pump on
    I2C_out( Command );
    I2C_out( 0xAF );    // display on command
    I2C_Stop();
}


////////// display off
void display_off( void )
{
    I2C_Start();
    I2C_out( Oled_Addr ); // Slave address of panel.
    I2C_out( Command );
    I2C_out( 0xAE );    // display off command
    I2C_out( Command );
    I2C_out( 0x8D );    // charge pump setting
    I2C_out( Command );
    I2C_out( 0x10 );    // charge pump off
    I2C_Stop();
}

////////// invert display
void display_invert( unsigned char value )
{
    I2C_Start();
    I2C_out( Oled_Addr ); // Slave address of panel.
	if (value == 1)
	{
		I2C_out( Command );
		I2C_out( 0xA7 );    // invert display
	}
	else
	{
		I2C_out( Command );
		I2C_out( 0xA6 );    // normal display
	}
    I2C_Stop();
}



 /***************** display_alphanumeric (cursor)*
// this function highlight (invert) a text zone, inputs are line, char, size, speed
void display_alpha( int page, int line, int size); //, int speed )
{
	int i;

	for ( i=0,i==7,i++)
	{
		if (( i >= caract) || ( i <= (caract+size))
		{
			showchr( page,i, 1, *text );
		}
		else
		{
			showchr( page,i, 0, *text );
		}
		text++;
	}
}
*/

////////// bargraph
// this function put a bargraph (invert) in a graphic zone, inputs are graphic, line, column, size
void bargraph (unsigned char column, unsigned char direction, unsigned char length, unsigned char masque, unsigned char *adresse)
{
	adresse += column;
	while (!(length==0))
	{
		if (direction == 0)				// direction == FWD, L to R
		{
			*(adresse+(--length)) ^= masque; // bargraph plotting

		}
		else							// direction == RWD, R to L
		{
			*(adresse-(--length)) ^= masque; // bargraph plotting
		}
	}
}


////////// peak
// this function put a peak dot in a graphic zone, inputs are graphic, line, column, size
void peak( unsigned char column, unsigned char length, unsigned char masque, unsigned char *adresse)
{
	adresse += column;
	while (!(length==0))
	{
		*(adresse+(--length)) ^= masque; // bargraph plotting
	}

}




/************************************************************
* End of Modules
************************************************************/

