//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//
//C-File----------------------------------------------------------------------//
//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//

//----------------------------------------------------------------------------//
//pic config------------------------------------------------------------------//
//----------------------------------------------------------------------------//

// DSPIC33CK64MP206 Configuration Bit Settings

// 'C' source line config statements

// FSEC
#pragma config BWRP = OFF               // Boot Segment Write-Protect bit (Boot Segment may be written)
#pragma config BSS = DISABLED           // Boot Segment Code-Protect Level bits (No Protection (other than BWRP))
#pragma config BSEN = OFF               // Boot Segment Control bit (No Boot Segment)
#pragma config GWRP = OFF               // General Segment Write-Protect bit (General Segment may be written)
#pragma config GSS = DISABLED           // General Segment Code-Protect Level bits (No Protection (other than GWRP))
#pragma config CWRP = OFF               // Configuration Segment Write-Protect bit (Configuration Segment may be written)
#pragma config CSS = DISABLED           // Configuration Segment Code-Protect Level bits (No Protection (other than CWRP))
#pragma config AIVTDIS = OFF            // Alternate Interrupt Vector Table bit (Disabled AIVT)

// FBSLIM
#pragma config BSLIM = 0x1FFF           // Boot Segment Flash Page Address Limit bits (Enter Hexadecimal value)

// FSIGN

// FOSCSEL
#pragma config FNOSC = FRC              // Oscillator Source Selection (Internal Fast RC (FRC))
#pragma config IESO = ON                // Two-speed Oscillator Start-up Enable bit (Start up device with FRC, then switch to user-selected oscillator source)

// FOSC
#pragma config POSCMD = NONE            // Primary Oscillator Mode Select bits (Primary Oscillator disabled)
#pragma config OSCIOFNC = OFF           // OSC2 Pin Function bit (OSC2 is clock output)
#pragma config FCKSM = CSDCMD           // Clock Switching Mode bits (Both Clock switching and Fail-safe Clock Monitor are disabled)
#pragma config PLLKEN = ON              // PLL Lock Status Control (PLL lock signal will be used to disable PLL clock output if lock is lost)
#pragma config XTCFG = G3               // XT Config (24-32 MHz crystals)
#pragma config XTBST = DISABLE          // XT Boost (Default kick-start)

// FWDT
// RWDTPS = No Setting
#pragma config RCLKSEL = LPRC           // Watchdog Timer Clock Select bits (Always use LPRC)
#pragma config WINDIS = OFF             // Watchdog Timer Window Enable bit (Watchdog Timer operates in Window mode)
#pragma config WDTWIN = WIN25           // Watchdog Timer Window Select bits (WDT Window is 25% of WDT period)
// SWDTPS = No Setting
#pragma config FWDTEN = ON_SW           // Watchdog Timer Enable bit (WDT controlled via SW, use WDTCON.ON bit)

// FPOR
#pragma config BISTDIS = DISABLED       // Memory BIST Feature Disable (mBIST on reset feature disabled)

// FICD
#pragma config ICS = PGD2               // ICD Communication Channel Select bits (Communicate on PGC2 and PGD2)
#pragma config JTAGEN = OFF             // JTAG Enable bit (JTAG is disabled)
#pragma config NOBTSWP = DISABLED       // BOOTSWP instruction disable bit (BOOTSWP instruction is disabled)

// FDMTIVTL
#pragma config DMTIVTL = 0xFFFF         // Dead Man Timer Interval low word (Enter Hexadecimal value)

// FDMTIVTH
#pragma config DMTIVTH = 0xFFFF         // Dead Man Timer Interval high word (Enter Hexadecimal value)

// FDMTCNTL
#pragma config DMTCNTL = 0xFFFF         // Lower 16 bits of 32 bit DMT instruction count time-out value (0-0xFFFF) (Enter Hexadecimal value)

// FDMTCNTH
#pragma config DMTCNTH = 0xFFFF         // Upper 16 bits of 32 bit DMT instruction count time-out value (0-0xFFFF) (Enter Hexadecimal value)

// FDMT
#pragma config DMTDIS = OFF             // Dead Man Timer Disable bit (Dead Man Timer is Disabled and can be enabled by software)

// FDEVOPT
#pragma config ALTI2C1 = OFF            // Alternate I2C1 Pin bit (I2C1 mapped to SDA1/SCL1 pins)
#pragma config ALTI2C2 = OFF            // Alternate I2C2 Pin bit (I2C2 mapped to SDA2/SCL2 pins)
#pragma config ALTI2C3 = OFF            // Alternate I2C3 Pin bit (I2C3 mapped to SDA3/SCL3 pins)
#pragma config SMBEN = SMBUS            // SM Bus Enable (SMBus input threshold is enabled)
#pragma config SPI2PIN = PPS            // SPI2 Pin Select bit (SPI2 uses I/O remap (PPS) pins)

// FALTREG
#pragma config CTXT1 = OFF              // Specifies Interrupt Priority Level (IPL) Associated to Alternate Working Register 1 bits (Not Assigned)
#pragma config CTXT2 = OFF              // Specifies Interrupt Priority Level (IPL) Associated to Alternate Working Register 2 bits (Not Assigned)
#pragma config CTXT3 = OFF              // Specifies Interrupt Priority Level (IPL) Associated to Alternate Working Register 3 bits (Not Assigned)
#pragma config CTXT4 = OFF              // Specifies Interrupt Priority Level (IPL) Associated to Alternate Working Register 4 bits (Not Assigned)

// FBTSEQ
#pragma config BSEQ = 0xFFF             // Relative value defining which partition will be active after device Reset; the partition containing a lower boot number will be active (Enter Hexadecimal value)
#pragma config IBSEQ = 0xFFF            // The one's complement of BSEQ; must be calculated by the user and written during device programming. (Enter Hexadecimal value)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include "xc.h"

/* Document data--------------------------------------------------------------//
 * Last editor:
 * Last changes:
 * Date of last change:
 * Describtion:
 * 
 * To be tested---------------------------------------------------------------//
 * 
 * 
 * To be implemented----------------------------------------------------------//
 * 
 * 
 */

//----------------------------------------------------------------------------//
//includes--------------------------------------------------------------------//
//----------------------------------------------------------------------------//

#include "xc.h"
#include "st7541.h"
//----------------------------------------------------------------------------//
//defines---------------------------------------------------------------------//
//----------------------------------------------------------------------------//

//commands
//siehe S33/34/35 in byte (8-Bit) mode

//commands----------------------------------------------------------------------
#define ST7541_CMD_MODE_SET_BYTE_1 0b00111000
#define ST7541_CMD_MODE_SET_BYTE_2 0b00000000
#define ST7541_CMD_ICON 0b10100010
#define ST7541_CMD_SET_PAGE_ADDRESS 0b10110000
#define ST7541_CMD_SET_COLUMN_MSB 0b00010000
#define ST7541_CMD_SET_COLUMN_LSB 0b00000000
#define ST7541_CMD_SET_READ_MODIFY_WRITE 0b11100000
#define ST7541_CMD_RESET_READ_MODIFY_WRITE 0b11101110
#define ST7541_CMD_DISPLAY_CONTROLE 0b10101110
#define ST7541_CMD_DISPLAY_LINE_BYTE1 0b01000000
#define ST7541_CMD_DISPLAY_LINE_BYTE2 0b00000000
#define ST7541_CMD_SET_INITIAL_COM_0_BYTE1 0b01000100
#define ST7541_CMD_SET_INITIAL_COM_0_BYTE2 0b00000000
#define ST7541_CMD_SET_PARTIAL_DISPLAY_DUTY_BYTE1 0b01001000
#define ST7541_CMD_SET_PARTIAL_DISPLAY_DUTY_BYTE2 0b00000000
#define ST7541_CMD_SET_N_LINE_BYTE_1 0b01001100
#define ST7541_CMD_SET_N_LINE_BYTE_2 0b00000000
#define ST7541_CMD_RELS_N_LINE 0b11100100
#define ST7541_CMD_REVERSE_DISPLAY_CONTROLE 0b10100110
#define ST7541_CMD_ENTIRE_DISPLAY_CONTROLE 0b10100100
#define ST7541_CMD_POWER_CONTROLE 0b00101000
#define ST7541_CMD_SEL_DC_DC 0b01100100
#define ST7541_CMD_SEL_REG_RES 0b00100000
#define ST7541_CMD_SET_REF_VOLT_BYTE_1 0b10000001
#define ST7541_CMD_SET_REF_VOLT_BYTE_2 0b00000000
#define ST7541_CMD_HIGH_POWER_ENABLE_BYTE_1 0b11110111
#define ST7541_CMD_HIGH_POWER_ENABLE_BYTE_2 0b00011010
#define ST7541_CMD_HIGH_POWER_CONTROLE_BYTE_1 0b11110011
#define ST7541_CMD_HIGH_POWER_CONTROLE_BYTE_2 0b00001101
#define ST7541_CMD_LCD_BIAS 0b01010000
#define ST7541_CMD_SHL_SEL 0b11000000
#define ST7541_CMD_ADC_SEL 0b10100000
#define ST7541_CMD_OSC_ON 0b10101011
#define ST7541_CMD_SET_POWER_SAVE
#define ST7541_CMD_RLS_POWER_SAVE 0b11100001
#define ST7541_CMD_RESET 0b11100010
#define ST7541_CMD_DISP_DATA_LEN_BYTE_1 0b11101000
#define ST7541_CMD_DISP_DATA_LEN_BYTE_2 0b00000000
#define ST7541_CMD_PWM_FRC 0b10010000
#define ST7541_CMD_NOP 0b11100011
#define ST7541_TEST_INSTR 0b11110000
#define ST7541_SET_SCALE_MODE 0b10001000

//enables-----------------------------------------------------------------------
#define ST7541_DISPLAY_ENABLE 1
#define ST7541_DISPLAY_DISABLE 0
#define ST7541_REV_DISABLE 0
#define ST7541_REV_ENABLE 1
#define ST7541_EON_DISABLE 0
#define ST7541_EON_ENABLE 1

//RESET/N_RESET level-----------------------------------------------------------
#define RESET LOW
#define N_RESET HIGH

//CS level----------------------------------------------------------------------
#define CMD LOW
#define DATA HIGH

//----------------------------------------------------------------------------//
//data declarations-----------------------------------------------------------//
//----------------------------------------------------------------------------//

//----------------------------------------------------------------------------//
//data definitions------------------------------------------------------------//
//----------------------------------------------------------------------------//

//----------------------------------------------------------------------------//
//function declarations-------------------------------------------------------//
//----------------------------------------------------------------------------//

//commands----------------------------------------------------------------------

void st7541_cmd_mode_set(uint8_t frame_rate, uint8_t boost_efficiency);
/* 2-byte instruction to set FR (Frame frequency control) and BE (Booster efficiency control)
 * Booster Efficiency The ST7541 incorporates software configurable Booster Efficiency Command.
 * It could be used with Voltage multiplier to get the suitable Vout and Power consumption. Default setting is Level 2.
 * This command is used to set the frame frequency */

void st7541_cmd_write_display_data(uint8_t data);
/* 8-bit data of Display Data from the microprocessor can be written to the RAM location specified by the column address and page address.
 * The column address is increased by 1 automatically so that the microprocessor can continuously write data to the addressed page.
 * During auto-increment, the column address wraps to 0 after the last column is written. */

void st7541_cmd_icon_enable(bool icon);
/* This instruction makes ICON enable or disable. By default, ICON display is disabled (ICON= 0).
 * When ICON control register is set to ?1?, ICON display is enabled and page address is set to ?16?.
 * Then user can write data for icons. It is impossible to set the page address to ?16? by Set Page Address instruction.
 * Therefore, when writing data for icons, ICON control register ON instruction would be used to set the page address to ?16?.
 * When ICON control register is set to ?0?, ICON display is disabled. */

void st7541_cmd_set_page_address(uint8_t page);
/* Sets the Page Address of display data RAM from the microprocessor into the page address register.
 * Any RAM data bit can be accessed when its Page Address and column address are specified.
 * Along with the column address, the Page Address defines the address of the display RAM to write or read display data.
 * Changing the Page Address doesn't affect the display status. Set Page Address instruction can not be used to set the page address to ?16?.
 * Use ICON control register ON/OFF instruction to set the page address to ?16?. */

void st7541_cmd_set_column_address(uint8_t column);
/* Sets the Column Address of display RAM from the microprocessor into the column address register.
 * Along with the Column Address, the Column Address defines the address of the display RAM to write or read display data.
 * When the microprocessor reads or writes display data to or from display RAM, Column Addresses are automatically increased */

void st7541_cmd_set_read_modify_write(void);
/* This instruction stops the automatic increment of the column address by the read display data instruction,
 * but the column address is still increased by the write display data instruction.
 * And it reduces the load of microprocessor when the data of a specific area is repeatedly changed during cursor blinking or others.
 * This mode is canceled by the reset Read-modify-Write instruction. */

void st7541_cmd_reset_read_modify_write(void);
/* This instruction releases the Read-modify-Write mode, and makes the column address return to its initial value just before the set Read-modify-Write instruction. */

void st7541_cmd_display_enable(bool ctrl);
/* Turns the display ON or OFF. This command has priority over Entire Display On/Off and Reverse Display On/Off.
 * Commands are accepted while the display is off, but the visual state of the display does not change. */

void st7541_cmd_initial_display_line(uint8_t disp_line);
/* Sets the line address of display RAM to determine the initial display line using 2-byte instruction.
 * The RAM display data is displayed at the top of row(COM0) of LCD panel. */

void st7541_cmd_initial_com_0(uint8_t com);
/* Sets the initial row (COM) of the LCD panel using the 2-byte instruction.
 * By using this instruction, it is possible to realize the window moving without the change of display data. */

void st7541_cmd_set_partial_display_duty(uint8_t duty);
/* Sets the duty within range of 16 ~ 128 (ICON disabled) or 17 to 129 (ICON enabled) to realize partial display by using the 2-byte instruction. */

void st7541_cmd_set_n_line_inversion(uint8_t innv);
/* Sets the inverted line number within range of 3 to 33 to improve the display quality.
 * It controls the phase of the internal LCD frame signal. To get better performance, the display duty (L) should not be complete divide by N-line setting (N).
 * If ?L? can be complete divide by ?N? (assume K = L / N), the factor (K) should not be even number.*/

void st7541_cmd_rls_n_line_inversion(void);
/* Returns to the frame inversion condition from the n-line inversion condition. */

void st7541_cmd_reverse_display_enable(bool rev);
/* Reverses the display status on LCD panel without rewriting the contents of the display data RAM.
 * REV|DDRAM 00|DDRAM 01|DDRAM 10|DDRAM 11
 * ---+--------+--------+--------+--------|
 *  0 |   00   |   01   |   10   |   11   |
 *  1 |   11   |   10   |   01   |   00   | */

void st7541_cmd_entire_display_enable(bool eon);
/* Forces the whole LCD points to be turned on regardless of the contents of the display data RAM. At this time, the contents of the display data RAM are held.
 * This instruction has priority over the Reverse Display ON / OFF instruction.
 * REV|DDRAM 00|DDRAM 01|DDRAM 10|DDRAM 11
 * ---+--------+--------+--------+--------|
 *  0 |   00   |   01   |   10   |   11   |
 *  1 |   11   |   11   |   11   |   11   | */

void st_7541_cmd_power_enable(bool vc, bool vr, bool vf);
/* Selects one of eight power circuit functions by using 3-bit register. An external power supply and part of internal power supply functions can be used simultaneously.
 * VC: Internal voltage converter circuit
 * VR: Internal voltage regulator circuit
 * VF: Internal voltage follower circuit */

void st_7541_cmd_sel_dc_dc(uint8_t dc);
/* Selects one of 4 DC-DC step-up to reduce the power consumption by this instruction. It is very useful to realize the partial display function. */

void st_7541_cmd_sel_reg_res(uint8_t res);
/* Selects resistance ratio of the internal resistor used in the internal voltage regulator. See voltage regulator section in power supply circuit. */

void st7541_cmd_set_elec_vol(uint8_t ev);
/* Consist of 2-byte Instructions. The 1st instruction set Reference Voltage mode, the 2nd one updates the contents of reference voltage register.
 * After second instruction, Reference Voltage mode is released. */

void st7541_cmd_high_power_enable(void);
/* This 2-byte Instruction enables the high power mode. The high power mode control command is valid after this 2-byte Instruction. */

void st7541_cmd_high_power_controle(void);
/* This double command controls the high power mode. The driving strength is enhanced and the current consumption will be larger. */

void st7541_cmd_lcd_bias(uint8_t b);
/* Selects LCD bias ratio of the voltage required for driving the LCD. */

void st7541_cmd_shl_sel(bool shl);
/* COM output scanning direction is selected by this instruction which determines the LCD driver output status. */

void st7541_cmd_adc_sel(bool adc);
/* Changes the relationship between RAM column address and segment driver.
 * The direction of segment driver output pins could be reversed by software.
 * This makes IC layout flexible in LCD module assembly. */

void st7541_cmd_osc_on(void);
/* This instruction enables the built-in oscillator circuit. */

void st7541_cmd_set_power_save(bool p);
/* The ST7541 enters the Power Save status to reduce the power consumption to
 * the static power consumption value and returns to the normal operation status by the following instructions.
 * P = 0: normal mode , P = 1: sleep mode */

void st7541_cmd_rls_power_save(void);
/* The ST7541 enters the Power Save status to reduce the power consumption to
 * the static power consumption value and returns to the normal operation status by the following instructions. */

void st7541_reset(void);
/* RESET instruction initial display line, column address, page address, and common output status select to their initial status, but dose not affect the contents of display data RAM.
 * This instruction cannot initialize the LCD power supply, which is initialized by the RST pin. */

void st7541_cmd_disp_data_len(uint8_t DDL);
/* This command is used in 3-Line SPI mode only(PS0 = ?L? and PS1 = ?L? ).
 * It will be two continuous commands, the first byte control the data direction(write mode only) and inform the LCD driver the second byte will be number
 * of data bytes will be write. When A0 is not used, the Display Data Length instruction is used to indicate that a specified number
 * of display data bytes are to be transmitted. The next byte after the display data string is handled as command data. */

void st7541_cmd_pwm_frc(bool frc, uint8_t pwm);
/* Selects 3/4 FRC and 9 / 12 / 15 PWM */

void st7541_cmd_nop(void);
/* No operation */

void st7541_test_instr(void);
/* This instruction is for testing IC. Please do not use it. */

void st7541_set_scale_mode(uint8_t gm, uint8_t data);
/* Consists of 2 bytes instruction. The first byte sets grayscale mode and the second byte updates the contents of gray scale register without issuing any other instruction. */

void st7541_di(bool cmd_indic);

//----------------------------------------------------------------------------//
//function definitions--------------------------------------------------------//
//----------------------------------------------------------------------------//

//init--------------------------------------------------------------------------

#define ST7541_PWR_CTRL_LVL_1 1,0,0
#define ST7541_PWR_CTRL_LVL_2 1,1,0
#define ST7541_PWR_CTRL_LVL_3 1,1,1
#define ST7541_BOOSTER_3 0x00
#define ST7541_BOOSTER_6 0x03
#define ST7541_LCD_BIAS_12 0b111

void st7541_init(void) {
    //precondition: Spi hast to be initialized previous
    //parameter: none
    //restult: initializes the display driver ST7541

    //init di pin
    TRISDbits.TRISD1 = OUTPUT; //LCD-DI-Selected
    LATDbits.LATD1 = LOW;

    //init reset pin
    TRISCbits.TRISC15 = OUTPUT;
    LATCbits.LATC15 = RESET;

    //hw reset st7541
    time_delay(TIME_500MS); //wait until voltage stabilizes
    LATCbits.LATC15 = N_RESET;
    time_delay(TIME_10MS); //wait until voltage stabilizes

    //ram setup
    st7541_cmd_set_partial_display_duty(128); //valid all 128 lines
    st7541_cmd_adc_sel(0x00); //relationship between RAM column adress and segment driver to normal dricetion
    st7541_cmd_shl_sel(0x01); //COM output scanning dirction to reverse direction
    st7541_cmd_initial_com_0(0x00); //set initial ram line

    //power setup
    st7541_cmd_osc_on(); //turn osc on
    st_7541_cmd_sel_reg_res(0b111); //schwarz bei 0b111 & elec =
    st7541_cmd_set_elec_vol(44); //soll 44
    st7541_cmd_lcd_bias(ST7541_LCD_BIAS_12);

    //power startup
    st_7541_cmd_sel_dc_dc(ST7541_BOOSTER_3); //step up reg set booster to 3x
    time_delay(TIME_200MS);
    st_7541_cmd_power_enable(ST7541_PWR_CTRL_LVL_1); //turn intern voltage converter circuit on
    st_7541_cmd_sel_dc_dc(ST7541_BOOSTER_6); //step up reg set booster to 6x
    time_delay(TIME_200MS);
    st_7541_cmd_power_enable(ST7541_PWR_CTRL_LVL_2); //turn intern voltage regulator circuit on
    time_delay(TIME_10MS);
    st_7541_cmd_power_enable(ST7541_PWR_CTRL_LVL_3); //turn intern voltage follower circuit on

    //user functions setup
    //    st7541_cmd_mode_set(0x0, 1); //set framerate and booster efficiency

    //colour setup
    //    st7541_cmd_pwm_frc(0b0, 0b00);
    //    st7541_set_scale_mode(0b000, 0x00);
    //    st7541_set_scale_mode(0b001, 0x00);
    //    st7541_set_scale_mode(0b010, 0xdd);
    //    st7541_set_scale_mode(0b011, 0xdd);
    //    st7541_set_scale_mode(0b100, 0xaa);
    //    st7541_set_scale_mode(0b101, 0xaa);
    //    st7541_set_scale_mode(0b110, 0xff);
    //    st7541_set_scale_mode(0b111, 0xff);

    return;
}

void st7541_di(bool cmd_indic) {

    //wait untile last transmission is finished
    while (spi_check_idle() == SPI_BUSY);

    //select chip
    LATDbits.LATD1 = cmd_indic;

    return;
}

//write-------------------------------------------------------------------------

void st7541_write_data(uint8_t *data, int len) {
    //writes data to display ram

    //transmit cmd
    st7541_di(CMD);
    st7541_cmd_set_page_address(0b1111);
    st7541_cmd_set_column_address(0b0111);

    //transmit data
    st7541_di(DATA);
    int i = 0;
    for (i = 0; i < len; i++) {
        st7541_cmd_write_display_data(*data++);
    }

    return;
}

//commands----------------------------------------------------------------------

void st7541_cmd_mode_set(uint8_t frame_rate, uint8_t boost_efficiency) {
    /* 2-byte instruction to set FR (Frame frequency control) and BE (Booster efficiency control)
     * Booster Efficiency The ST7541 incorporates software configurable Booster Efficiency Command.
     * It could be used with Voltage multiplier to get the suitable Vout and Power consumption. Default setting is Level 2.
     * This command is used to set the frame frequency */

    //format input data
    frame_rate &= 0b1111;
    boost_efficiency &= 0b10;

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_MODE_SET_BYTE_1);
    spi_write(ST7541_CMD_MODE_SET_BYTE_2 + (frame_rate << 4)+(boost_efficiency << 2));

    return;
}

void st7541_cmd_write_display_data(uint8_t data) {
    /* 8-bit data of Display Data from the microprocessor can be written to the RAM location specified by the column address and page address.
     * The column address is increased by 1 automatically so that the microprocessor can continuously write data to the addressed page.
     * During auto-increment, the column address wraps to 0 after the last column is written. */

    //format input data
    data &= 0b11111111;

    //transmit data
    st7541_di(DATA);
    spi_write(data); //msb
    spi_write(data); //lsb

    return;
}

void st7541_cmd_icon_enable(bool icon) {
    /* This instruction makes ICON enable or disable. By default, ICON display is disabled (ICON= 0).
     * When ICON control register is set to ?1?, ICON display is enabled and page address is set to ?16?.
     * Then user can write data for icons. It is impossible to set the page address to ?16? by Set Page Address instruction.
     * Therefore, when writing data for icons, ICON control register ON instruction would be used to set the page address to ?16?.
     * When ICON control register is set to ?0?, ICON display is disabled. */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_ICON + icon);

    return;
}

void st7541_cmd_set_page_address(uint8_t page) {
    /* Sets the Page Address of display data RAM from the microprocessor into the page address register.
     * Any RAM data bit can be accessed when its Page Address and column address are specified.
     * Along with the column address, the Page Address defines the address of the display RAM to write or read display data.
     * Changing the Page Address doesn't affect the display status. Set Page Address instruction can not be used to set the page address to ?16?.
     * Use ICON control register ON/OFF instruction to set the page address to ?16?. */

    //format input data
    page &= 0b1111;

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_SET_PAGE_ADDRESS + page);

    return;
}

void st7541_cmd_set_column_address(uint8_t column) {
    /* Sets the Column Address of display RAM from the microprocessor into the column address register.
     * Along with the Column Address, the Column Address defines the address of the display RAM to write or read display data.
     * When the microprocessor reads or writes display data to or from display RAM, Column Addresses are automatically increased */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_SET_COLUMN_MSB + ((column >> 4) & 0b0111));
    spi_write(ST7541_CMD_SET_COLUMN_LSB + (column & 0x0f));

    return;
}

void st7541_cmd_set_read_modify_write(void) {
    /* This instruction stops the automatic increment of the column address by the read display data instruction,
     * but the column address is still increased by the write display data instruction.
     * And it reduces the load of microprocessor when the data of a specific area is repeatedly changed during cursor blinking or others.
     * This mode is canceled by the reset Read-modify-Write instruction. */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_SET_READ_MODIFY_WRITE);

    return;
}

void st7541_cmd_reset_read_modify_write(void) {
    /* This instruction releases the Read-modify-Write mode, and makes the column address return to its initial value just before the set Read-modify-Write instruction. */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_RESET_READ_MODIFY_WRITE);

    return;
}

void st7541_cmd_display_enable(bool ctrl) {
    /* Turns the display ON or OFF. This command has priority over Entire Display On/Off and Reverse Display On/Off.
     * Commands are accepted while the display is off, but the visual state of the display does not change. */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_DISPLAY_CONTROLE + ctrl);

    return;
}

void st7541_cmd_initial_display_line(uint8_t disp_line) {
    /* Sets the line address of display RAM to determine the initial display line using 2-byte instruction.
     * The RAM display data is displayed at the top of row(COM0) of LCD panel. */

    //format input data
    disp_line &= 0b01111111;

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_DISPLAY_LINE_BYTE1);
    spi_write(ST7541_CMD_DISPLAY_LINE_BYTE2 + disp_line);

    return;
}

void st7541_cmd_initial_com_0(uint8_t com) {
    /* Sets the initial row (COM) of the LCD panel using the 2-byte instruction.
     * By using this instruction, it is possible to realize the window moving without the change of display data. */

    //format input data
    com &= 0b01111111;

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_SET_INITIAL_COM_0_BYTE1);
    spi_write(ST7541_CMD_SET_INITIAL_COM_0_BYTE2 + com);

    return;
}

void st7541_cmd_set_partial_display_duty(uint8_t duty) {
    /* Sets the duty within range of 16 ~ 128 (ICON disabled) or 17 to 129 (ICON enabled) to realize partial display by using the 2-byte instruction. */

    //format input data
    duty &= 0b11111111;

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_SET_PARTIAL_DISPLAY_DUTY_BYTE1);
    spi_write(ST7541_CMD_SET_PARTIAL_DISPLAY_DUTY_BYTE2 + duty);

    return;
}

void st7541_cmd_set_n_line_inversion(uint8_t innv) {
    /* Sets the inverted line number within range of 3 to 33 to improve the display quality.
     * It controls the phase of the internal LCD frame signal. To get better performance, the display duty (L) should not be complete divide by N-line setting (N).
     * If ?L? can be complete divide by ?N? (assume K = L / N), the factor (K) should not be even number.*/

    //format input data
    innv &= 0b00011111;

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_SET_N_LINE_BYTE_1);
    spi_write(ST7541_CMD_SET_N_LINE_BYTE_2 + innv);

    return;
}

void st7541_cmd_rls_n_line_inversion(void) {
    /* Returns to the frame inversion condition from the n-line inversion condition. */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_RELS_N_LINE);

    return;
}

void st7541_cmd_reverse_display_enable(bool rev) {
    /* Reverses the display status on LCD panel without rewriting the contents of the display data RAM.
     * REV|DDRAM 00|DDRAM 01|DDRAM 10|DDRAM 11
     * ---+--------+--------+--------+--------|
     *  0 |   00   |   01   |   10   |   11   |
     *  1 |   11   |   10   |   01   |   00   | */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_REVERSE_DISPLAY_CONTROLE + rev);

    return;
}

void st7541_cmd_entire_display_enable(bool eon) {
    /* Forces the whole LCD points to be turned on regardless of the contents of the display data RAM. At this time, the contents of the display data RAM are held.
     * This instruction has priority over the Reverse Display ON / OFF instruction.
     * REV|DDRAM 00|DDRAM 01|DDRAM 10|DDRAM 11
     * ---+--------+--------+--------+--------|
     *  0 |   00   |   01   |   10   |   11   |
     *  1 |   11   |   11   |   11   |   11   | */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_ENTIRE_DISPLAY_CONTROLE + eon);

    return;
}

void st_7541_cmd_power_enable(bool vc, bool vr, bool vf) {
    /* Selects one of eight power circuit functions by using 3-bit register. An external power supply and part of internal power supply functions can be used simultaneously.
     * VC: Internal voltage converter circuit
     * VR: Internal voltage regulator circuit
     * VF: Internal voltage follower circuit */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_POWER_CONTROLE + (vc << 2)+(vr << 1) + vf);

    return;
}

void st_7541_cmd_sel_dc_dc(uint8_t dc) {
    /* Selects one of 4 DC-DC step-up to reduce the power consumption by this instruction. It is very useful to realize the partial display function. */

    //format input data
    dc &= 0b0011;

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_SEL_DC_DC + dc);

    return;
}

void st_7541_cmd_sel_reg_res(uint8_t res) {
    /* Selects resistance ratio of the internal resistor used in the internal voltage regulator. See voltage regulator section in power supply circuit. */

    //format input data
    res &= 0b0111;

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_SEL_REG_RES + res);

    return;
}

void st7541_cmd_set_elec_vol(uint8_t ev) {
    /* Consist of 2-byte Instructions. The 1st instruction set Reference Voltage mode, the 2nd one updates the contents of reference voltage register.
     * After second instruction, Reference Voltage mode is released. */

    //format input data
    ev &= 0b00111111;

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_SET_REF_VOLT_BYTE_1);
    spi_write(ST7541_CMD_SET_REF_VOLT_BYTE_2);

    return;
}

void st7541_cmd_high_power_enable(void) {
    /* This 2-byte Instruction enables the high power mode. The high power mode control command is valid after this 2-byte Instruction. */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_HIGH_POWER_ENABLE_BYTE_1);
    spi_write(ST7541_CMD_HIGH_POWER_ENABLE_BYTE_2);

    return;
}

void st7541_cmd_high_power_controle(void) {
    /* This double command controls the high power mode. The driving strength is enhanced and the current consumption will be larger. */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_HIGH_POWER_CONTROLE_BYTE_1);
    spi_write(ST7541_CMD_HIGH_POWER_CONTROLE_BYTE_2);

    return;
}

void st7541_cmd_lcd_bias(uint8_t b) {
    /* Selects LCD bias ratio of the voltage required for driving the LCD. */

    //format input data
    b &= 0b0111;

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_LCD_BIAS + b);

    return;
}

void st7541_cmd_shl_sel(bool shl) {
    /* COM output scanning direction is selected by this instruction which determines the LCD driver output status. */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_SHL_SEL + (shl << 3));

    return;
}

void st7541_cmd_adc_sel(bool adc) {
    /* Changes the relationship between RAM column address and segment driver.
     * The direction of segment driver output pins could be reversed by software.
     * This makes IC layout flexible in LCD module assembly. */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_ADC_SEL + adc);

    return;
}

void st7541_cmd_osc_on(void) {
    /* This instruction enables the built-in oscillator circuit. */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_OSC_ON);

    return;
}

void st7541_cmd_set_power_save(bool p) {
    /* The ST7541 enters the Power Save status to reduce the power consumption to
     * the static power consumption value and returns to the normal operation status by the following instructions.
     * P = 0: normal mode , P = 1: sleep mode */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_SET_POWER_SAVE + p);

    return;
}

void st7541_cmd_rls_power_save(void) {
    /* The ST7541 enters the Power Save status to reduce the power consumption to
     * the static power consumption value and returns to the normal operation status by the following instructions. */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_RLS_POWER_SAVE);

    return;
}

void st7541_reset(void) {
    /* RESET instruction initial display line, column address, page address, and common output status select to their initial status, but dose not affect the contents of display data RAM.
     * This instruction cannot initialize the LCD power supply, which is initialized by the RST pin. */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_RESET);

    return;
}

void st7541_cmd_disp_data_len(uint8_t DDL) {
    /* This command is used in 3-Line SPI mode only(PS0 = ?L? and PS1 = ?L? ).
     * It will be two continuous commands, the first byte control the data direction(write mode only) and inform the LCD driver the second byte will be number
     * of data bytes will be write. When A0 is not used, the Display Data Length instruction is used to indicate that a specified number
     * of display data bytes are to be transmitted. The next byte after the display data string is handled as command data. */

    //format input data
    DDL &= 0b11111111;

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_DISP_DATA_LEN_BYTE_1);
    spi_write(ST7541_CMD_DISP_DATA_LEN_BYTE_2 + DDL);

    return;
}

void st7541_cmd_pwm_frc(bool frc, uint8_t pwm) {
    /* Selects 3/4 FRC and 9 / 12 / 15 PWM */

    //format input data
    pwm &= 0b0011;

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_PWM_FRC + (frc << 2) + pwm);

    return;
}

void st7541_cmd_nop(void) {
    /* No operation */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_CMD_NOP);

    return;
}

void st7541_test_instr(void) {
    /* This instruction is for testing IC. Please do not use it. */

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_TEST_INSTR);

    return;
}

void st7541_set_scale_mode(uint8_t gm, uint8_t data) {
    /* Consists of 2 bytes instruction. The first byte sets grayscale mode and the second byte updates the contents of gray scale register without issuing any other instruction. */

    //format input data
    gm &= 0b0111;
    data &= 0b11111111;

    //transmit cmd
    st7541_di(CMD);
    spi_write(ST7541_SET_SCALE_MODE + gm);
    spi_write(data);

    return;
}

//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//
//Header-File-----------------------------------------------------------------//
//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//


#ifndef ST7541_H
#define	ST7541_H

/* Document data--------------------------------------------------------------//
 * Last editor:
 * Last changes:
 * Date of last change:
 * Describtion:
 * 
 * To be tested---------------------------------------------------------------//
 * 
 * 
 * To be implemented----------------------------------------------------------//
 * 
 * 
 */

//----------------------------------------------------------------------------//
//includes--------------------------------------------------------------------//
//----------------------------------------------------------------------------//

#include "xc.h"
#include "main.h"

//----------------------------------------------------------------------------//
//defines---------------------------------------------------------------------//
//----------------------------------------------------------------------------//

//----------------------------------------------------------------------------//
//data declarations-----------------------------------------------------------//
//----------------------------------------------------------------------------//

//----------------------------------------------------------------------------//
//data definitions------------------------------------------------------------//
//----------------------------------------------------------------------------//

//----------------------------------------------------------------------------//
//function declarations-------------------------------------------------------//
//----------------------------------------------------------------------------//

//init--------------------------------------------------------------------------

void st7541_init(void);
//precondition: Spi hast to be initialized previous
//parameter: none
//restult: initializes the display driver ST7541

//----------------------------------------------------------------------------//
//Debug functions-------------------------------------------------------------//
//----------------------------------------------------------------------------//

#ifdef DEBUG

//commands----------------------------------------------------------------------
#define ST7541_CMD_MODE_SET_BYTE_1 0b00111000
#define ST7541_CMD_MODE_SET_BYTE_2 0b00000000
#define ST7541_CMD_ICON 0b10100010
#define ST7541_CMD_SET_PAGE_ADDRESS 0b10110000
#define ST7541_CMD_SET_COLUMN_MSB 0b00010000
#define ST7541_CMD_SET_COLUMN_LSB 0b00000000
#define ST7541_CMD_SET_READ_MODIFY_WRITE 0b11100000
#define ST7541_CMD_RESET_READ_MODIFY_WRITE 0b11101110
#define ST7541_CMD_DISPLAY_CONTROLE 0b10101110
#define ST7541_CMD_DISPLAY_LINE_BYTE1 0b01000000
#define ST7541_CMD_DISPLAY_LINE_BYTE2 0b00000000
#define ST7541_CMD_SET_INITIAL_COM_0_BYTE1 0b01000100
#define ST7541_CMD_SET_INITIAL_COM_0_BYTE2 0b00000000
#define ST7541_CMD_SET_PARTIAL_DISPLAY_DUTY_BYTE1 0b01001000
#define ST7541_CMD_SET_PARTIAL_DISPLAY_DUTY_BYTE2 0b00000000
#define ST7541_CMD_SET_N_LINE_BYTE_1 0b01001100
#define ST7541_CMD_SET_N_LINE_BYTE_2 0b00000000
#define ST7541_CMD_RELS_N_LINE 0b11100100
#define ST7541_CMD_REVERSE_DISPLAY_CONTROLE 0b10100110
#define ST7541_CMD_ENTIRE_DISPLAY_CONTROLE 0b10100100
#define ST7541_CMD_POWER_CONTROLE 0b00101000
#define ST7541_CMD_SEL_DC_DC 0b01100100
#define ST7541_CMD_SEL_REG_RES 0b00100000
#define ST7541_CMD_SET_REF_VOLT_BYTE_1 0b10000001
#define ST7541_CMD_SET_REF_VOLT_BYTE_2 0b00000000
#define ST7541_CMD_HIGH_POWER_ENABLE_BYTE_1 0b11110111
#define ST7541_CMD_HIGH_POWER_ENABLE_BYTE_2 0b00011010
#define ST7541_CMD_HIGH_POWER_CONTROLE_BYTE_1 0b11110011
#define ST7541_CMD_HIGH_POWER_CONTROLE_BYTE_2 0b00001101
#define ST7541_CMD_LCD_BIAS 0b01010000
#define ST7541_CMD_SHL_SEL 0b11000000
#define ST7541_CMD_ADC_SEL 0b10100000
#define ST7541_CMD_OSC_ON 0b10101011
#define ST7541_CMD_SET_POWER_SAVE
#define ST7541_CMD_RLS_POWER_SAVE 0b11100001
#define ST7541_CMD_RESET 0b11100010
#define ST7541_CMD_DISP_DATA_LEN_BYTE_1 0b11101000
#define ST7541_CMD_DISP_DATA_LEN_BYTE_2 0b00000000
#define ST7541_CMD_PWM_FRC 0b10010000
#define ST7541_CMD_NOP 0b11100011
#define ST7541_TEST_INSTR 0b11110000
#define ST7541_SET_SCALE_MODE 0b10001000

//enables-----------------------------------------------------------------------
#define ST7541_DISPLAY_ENABLE 1
#define ST7541_DISPLAY_DISABLE 0
#define ST7541_REV_DISABLE 0
#define ST7541_REV_ENABLE 1
#define ST7541_EON_DISABLE 0
#define ST7541_EON_ENABLE 1

//RESET/N_RESET level-----------------------------------------------------------
#define RESET LOW
#define N_RESET HIGH

//CS level----------------------------------------------------------------------
#define CMD LOW
#define DATA HIGH

#define ST7541_PWR_CTRL_LVL_1 1,0,0
#define ST7541_PWR_CTRL_LVL_2 1,1,0
#define ST7541_PWR_CTRL_LVL_3 1,1,1
#define ST7541_BOOSTER_3 0x00
#define ST7541_BOOSTER_6 0x03
#define ST7541_LCD_BIAS_12 0b111

void st7541_test() {
    //preconditions: st7541 has to be initialized previous
    //parameters: none
    //result: function to create custom test of st7541 funcionality

    //precondition: Spi hast to be initialized previous
    //parameter: none
    //restult: initializes the display driver ST7541

    //___init___
    tmr1_init();
    spi_init();

    //init di pin
    TRISDbits.TRISD1 = OUTPUT; //LCD-DI-Selected
    LATDbits.LATD1 = LOW;

    //init reset pin
    TRISCbits.TRISC15 = OUTPUT;
    LATCbits.LATC15 = RESET;

    //hw reset st7541

    time_delay(TIME_1S); //wait until voltage stabilizes
    LATCbits.LATC15 = N_RESET;

    time_delay(TIME_2S); //wait until voltage stabilizes

    //user functions setup
    st7541_cmd_mode_set(0x0, 1); //set framerate and booster efficiency
    st7541_cmd_osc_on(); //turn osc on
    st_7541_cmd_sel_dc_dc(ST7541_BOOSTER_6); //step up reg set booster to 6x
    st7541_cmd_lcd_bias(ST7541_LCD_BIAS_12);
    st7541_cmd_set_partial_display_duty(128); //valid all 128 lines
    st_7541_cmd_sel_reg_res(0b111); //schwarz bei 0b111 & elec =
    st7541_cmd_set_elec_vol(63); //soll 44

    st_7541_cmd_power_enable(ST7541_PWR_CTRL_LVL_1); //turn intern voltage converter circuit on

    time_delay(TIME_1S);
    st_7541_cmd_power_enable(ST7541_PWR_CTRL_LVL_2); //turn intern voltage regulator circuit on

    time_delay(TIME_1S);
    st_7541_cmd_power_enable(ST7541_PWR_CTRL_LVL_3); //turn intern voltage follower circuit on

    time_delay(TIME_1S);
    st7541_cmd_pwm_frc(0b0, 0b00);

    //gray scale setup
    int i = 0;
    st7541_set_scale_mode(i++, 0x00);
    st7541_set_scale_mode(i++, 0x00);
    st7541_set_scale_mode(i++, 0xdd);
    st7541_set_scale_mode(i++, 0xdd);
    st7541_set_scale_mode(i++, 0xaa);
    st7541_set_scale_mode(i++, 0xaa);
    st7541_set_scale_mode(i++, 0xff);
    st7541_set_scale_mode(i++, 0xff);

    st7541_cmd_shl_sel(0x01); //COM output scanning dirction to reverse direction
    st7541_cmd_display_enable(ENABLE);

    //___wirte data to display___    
    time_delay(TIME_10MS);

    unsigned int page = 0;
    unsigned int start_page = 0;
    unsigned int stop_page = 16;

    unsigned int column = 0;
    unsigned int start_column = 0;
    unsigned int stop_column = 128;

    unsigned int start_line = 0;

    while (1) {

        st7541_cmd_initial_display_line(start_line); //set initial display line
        for (page = start_page; page < stop_page; page++) {
            st7541_cmd_set_page_address(page); //set Page adr  
            st7541_cmd_set_column_address(start_column); //set collumn adr
            for (column = start_column; column < stop_column * 2; column++) {
                st7541_cmd_write_display_data(0xf0);
            }
        }

        time_delay(TIME_1S);

    }

    //turn display on
    //    st7541_cmd_display_enable(ENABLE);

    //    st7541_cmd_reverse_display_enable(ENABLE);

    while (1)continue;
}

#endif //Debug

#endif	/* ST7541_H */

