/*************************************************************
 * Christophe Le Lann <totofwebcreation@gmail.com>
 * http://www.totofweb.ma.cx
 *
 * Programme pour le compilateur C2C sous linux, facilement adaptable à d'autres compilateurs
 *
 * LCD TRULY 192 x 64 pixels, drivers KS107B et BS108B
 * trois circuits de commande divisent le lcd en 3 parties de 64x64 pixels (sélectionné avec les pins CSA et CSB)
 * chaque partie est composée de 8 pages (horizontalement)
 * chaque page est composée de 64 segments (verticalement) de 8 pixels
 *
 * Instructions données par la datasheet du KS0108B :
 *  - RS = 0 => mode commande
 *  - RS = 1 => mode donnée
 *  - RW = 0 => mode écriture
 *  - RW = 1 => mode lecture
 *  - EN = 1 puis EN = 0 => validation
 *  - CSA = 0 && CSB = 0 => afficheur de gauche
 *  - CSA = 1 && CSB = 0 => afficheur du milieu
 *  - CSA = 0 && CSB = 1 => afficheur de droite
 *  - RST = 0 puis RST = 1 => reset du LCD
 *  - SendCMD(00111110b); => éteindre l'afficheur
 *  - SendCMD(00111111b); => allumer l'afficheur
 *  - SendCMD(11000000b+start_line); => décalage des lignes, on commence par la ligne "start_line"
 *  - SendCMD(10111000b+page); => Sélection de la page (entre 0 et 7)
 *  - SendCMD(01000000b+ligne); => Sélection de la ligne (entre 0 et 63)
 *  - SendDATA(data); => envoi de "data" sur le segment de 8 pixels sélectionné (et passage au segment suivant automatique)
 *
 * La fonction Compteur(); gère les débordements d'afficheur, de page et de LCD
 *************************************************************/

#define LCD_DATA_PORT PORTB
#define LCD_EN_PORT   PORTD
#define LCD_EN_PIN    7
#define LCD_RW_PORT   PORTD
#define LCD_RW_PIN    6
#define LCD_RS_PORT   PORTD
#define LCD_RS_PIN    5
#define LCD_CSA_PORT  PORTC
#define LCD_CSA_PIN   4
#define LCD_CSB_PORT  PORTD
#define LCD_CSB_PIN   3
#define LCD_RST_PORT  PORTD
#define LCD_RST_PIN   2

#include "font5x7.c"

/****************************************
 * Variables globales
 ****************************************/
char LCD_ligne;
char LCD_page;

/****************************************
 * Fonctions niveau 1
 ****************************************/

void LCD_SendCMD(char cmd) {					// Envoi d'une commande
	clear_bit(LCD_RS_PORT, LCD_RS_PIN);			// Mode commande
	clear_bit(LCD_RW_PORT, LCD_RW_PIN);			// Mode écriture
	TRISB = 00000000b;							// PORTB en sortie
	LCD_DATA_PORT = cmd;						// PORTB = cmd

	// Validation de l'instruction
	set_bit(LCD_EN_PORT,LCD_EN_PIN);			// EN = 1;
	delay_us(50);
	clear_bit(LCD_EN_PORT,LCD_EN_PIN);			// EN = 0;
	delay_us(50);
}
void LCD_SendDATA(char data) {					// Envoi d'une donnée
	set_bit(LCD_RS_PORT, LCD_RS_PIN);			// Mode données
	clear_bit(LCD_RW_PORT, LCD_RW_PIN);			// Mode écriture
	TRISB = 00000000b;							// PORTB en sortie
	LCD_DATA_PORT = data;						// PORTB = data

	// Validation de l'instruction
	set_bit(LCD_EN_PORT,LCD_EN_PIN);			// EN = 1;
	delay_us(50);
	clear_bit(LCD_EN_PORT,LCD_EN_PIN);			// EN = 0;
	delay_us(50);
}

/****************************************
 * Fonctions niveau 2
 ****************************************/

void LCD_Init() {								// Initialisation du LCD
	char page, ligne;

	LCD_DATA_PORT = 00000000b;
	TRISC = 00000000b;
	TRISD = 00000000b;
	PORTD = 00000011b;

	// Reset de l'afficheur
	delay_ms(10);
	clear_bit(LCD_RST_PORT, LCD_RST_PIN);		// RST = 0;
	delay_ms(10);
	set_bit(LCD_RST_PORT, LCD_RST_PIN);			// RST = 1;

	// RAZ de l'afficheur gauche
	set_bit(LCD_CSA_PORT, LCD_CSA_PIN);			// CSA = 1;
	clear_bit(LCD_CSB_PORT, LCD_CSB_PIN);		// CSB = 0;
	LCD_SendCMD(00111110b);						// Display Off
	LCD_SendCMD(00111111b);						// Display On
	LCD_SendCMD(11000000b);						// Display Start Line = 0
	for (page = 000b; page <= 111b; page++) {	// on parcours chaque page
		LCD_SendCMD(10111000b+page);			// Sélection de la page
		for (ligne = 000000b; ligne <= 111111b; ligne++)	// on parcours chaque ligne
			LCD_SendDATA(00000000b);			// Envoi d'une ligne vide
	}

	// RAZ de l'afficheur central
	clear_bit(LCD_CSA_PORT, LCD_CSA_PIN);		// CSA = 0;
	set_bit(LCD_CSB_PORT, LCD_CSB_PIN);			// CSB = 1;
	LCD_SendCMD(00111110b);						// Display Off
	LCD_SendCMD(00111111b);						// Display On
	LCD_SendCMD(11000000b);						// Display Start Line = 0
	for (page = 000b; page <= 111b; page++) {	// on parcours chaque page
		LCD_SendCMD(10111000b+page);			// Sélection de la page
		for (ligne = 000000b; ligne <= 111111b; ligne++)	// on parcours chaque ligne
			LCD_SendDATA(00000000b);			// Envoi d'une ligne vide
	}

	// RAZ de l'afficheur droit
	clear_bit(LCD_CSA_PORT, LCD_CSA_PIN);		// CSA = 0;
	clear_bit(LCD_CSB_PORT, LCD_CSB_PIN);		// CSB = 0;
	LCD_SendCMD(00111110b);						// Display Off
	LCD_SendCMD(00111111b);						// Display On
	LCD_SendCMD(11000000b);						// Display Start Line = 0
	for (page = 000b; page <= 111b; page++) {	// on parcours chaque page
		LCD_SendCMD(10111000b+page);			// Sélection de la page
		for (ligne = 000000b; ligne <= 111111b; ligne++)	// on parcours chaque ligne
			LCD_SendDATA(00000000b);			// Envoi d'une ligne vide
	}

	LCD_SendCMD(10111000b);						// Set page
	LCD_SendCMD(01000000b);						// Set ligne

	LCD_SendDATA(0x00);
	delay_ms(250);

	LCD_SendCMD(10111000b);						// Set page
	LCD_SendCMD(01000000b);						// Set ligne
	LCD_page = 0;
	LCD_ligne = 0;
}
void LCD_SetPosition(char page, char ligne) {	// Se rendre à une position donnée
	LCD_page = page;
	LCD_ligne = ligne;
	if (ligne >= 128) {
		clear_bit(LCD_CSA_PORT, LCD_CSA_PIN);	// CSA = 0;
		set_bit(LCD_CSB_PORT, LCD_CSB_PIN);		// CSB = 1;
		LCD_SendCMD(10111000b+page);			// Set page
		ligne = ligne - 128;
		LCD_SendCMD(01000000b+ligne);			// Set ligne
	} else if (ligne >= 64) {
		set_bit(LCD_CSA_PORT, LCD_CSA_PIN);		// CSA = 1;
		clear_bit(LCD_CSB_PORT, LCD_CSB_PIN);	// CSB = 0;
		LCD_SendCMD(10111000b+page);			// Set page
		ligne = ligne - 64;
		LCD_SendCMD(01000000b+ligne);			// Set ligne
	} else {
		clear_bit(LCD_CSA_PORT, LCD_CSA_PIN);	// CSA = 0;
		clear_bit(LCD_CSB_PORT, LCD_CSB_PIN);	// CSB = 0;
		LCD_SendCMD(10111000b+page);			// Set page
		LCD_SendCMD(01000000b+ligne);			// Set ligne
	}
}
void LCD_ClearPage() {							// Efface la ligne courante du LCD
	char ligne;
	LCD_ligne = 0;

	clear_bit(LCD_CSA_PORT, LCD_CSA_PIN);		// CSA = 0;
	set_bit(LCD_CSB_PORT, LCD_CSB_PIN);			// CSB = 1;
	LCD_SendCMD(10111000b+LCD_page);			// Set page
	LCD_SendCMD(01000000b);						// Set ligne
	for (ligne = 000000b; ligne <= 111111b; ligne++)	// on parcours chaque ligne
		LCD_SendDATA(00000000b);				// Ligne blanche

	set_bit(LCD_CSA_PORT, LCD_CSA_PIN);			// CSA = 1;
	clear_bit(LCD_CSB_PORT, LCD_CSB_PIN);		// CSB = 0;
	LCD_SendCMD(10111000b+LCD_page);			// Set page
	LCD_SendCMD(01000000b);						// Set ligne
	for (ligne = 000000b; ligne <= 111111b; ligne++)	// on parcours chaque ligne
		LCD_SendDATA(00000000b);				// Ligne blanche

	clear_bit(LCD_CSA_PORT, LCD_CSA_PIN);		// CSA = 0;
	clear_bit(LCD_CSB_PORT, LCD_CSB_PIN);		// CSB = 0;
	LCD_SendCMD(10111000b+LCD_page);			// Set page
	LCD_SendCMD(01000000b);						// Set ligne
	for (ligne = 000000b; ligne <= 111111b; ligne++) // on parcours chaque ligne
		LCD_SendDATA(00000000b);				// Ligne blanche
}
void LCD_Compteur() {							// Gestion des débordements de tiers d'écran, de ligne et de lcd

	char ligne_old;
	ligne_old = LCD_ligne;

	LCD_ligne++;
	if (LCD_ligne >= 192 && ligne_old < 192) {
		LCD_page++;
		LCD_ligne = 0;
		if (LCD_page >= 8) {
			LCD_page = 0;
		}
		LCD_SetPosition(LCD_page, LCD_ligne);
		LCD_ClearPage();
	} else if (LCD_ligne >= 128 && ligne_old < 128) {
		LCD_SetPosition(LCD_page, LCD_ligne);
	} else if (LCD_ligne >= 64 && ligne_old < 64) {
		LCD_SetPosition(LCD_page, LCD_ligne);
	}

	if (LCD_page >= 8) {
		LCD_page = 0;
		LCD_SetPosition(LCD_page, LCD_ligne);
		LCD_ClearPage();
	}
}
void LCD_SendDATADecal(char data) {				// Envoi d'une donnée avec gestion des débordements
	LCD_SendDATA(data);
	LCD_Compteur();
}

/****************************************
 * Fonctions niveau 3
 ****************************************/

void LCD_LigneSuivante() {						// "retour chariot"
	char ligne;
	for (ligne = LCD_ligne; ligne < 192; ligne++)
		LCD_SendDATADecal(000000b);
	LCD_SetPosition(LCD_page, LCD_ligne);
}
void LCD_AffLettre(char c) {					// Afficher un caractère. 1 caractère = (5+1)*(7+1) pixels
	char index, vmax;
	if (c < 32 || c > 126)						// caractère non pris en charge
		c = 63;

	if (c <= 63) {
		c = c - 32;
		c = c * 5;
		vmax = c + 5;
		for(index = c; index < vmax; index++)
			LCD_SendDATADecal(ascii[index]);
	} else if (c <= 95) {
		c = c - 64;
		c = c * 5;
		vmax = c + 5;
		for(index = c; index < vmax; index++)
			LCD_SendDATADecal(ascii2[index]);
	} else {
		c = c - 96;
		c = c * 5;
		vmax = c + 5;
		for(index = c; index < vmax; index++)
			LCD_SendDATADecal(ascii3[index]);
	}

	LCD_SendDATADecal(00000000b);				// Espacement des caractères
}
void LCD_sendBINNumber(char data) {				// Afficher un entier binaire
	char i;
	for(i = 0; i < 8; i++) {
		if (data & 10000000b)
			LCD_AffLettre('1');
		else
			LCD_AffLettre('0');
		data <<= 1;
	}
}
void LCD_sendHEXNumber (char data) {			// Afficher un entier héxadécimal
	//Send high nibble
	if ((data>>4) > 9)
		LCD_AffLettre('A' - 10 + (data>>4));
	else
		LCD_AffLettre('0' + (data>>4));
	//Send low nibble
	if((data&0x0F) > 9)
		LCD_AffLettre('A' - 10 + (data&0x0F));
	else
		LCD_AffLettre('0' + (data&0x0F));
}
void LCD_sendDECNumber(char data) {				// Afficher un entier décimal
	LCD_AffLettre('0' + data/100);
	LCD_AffLettre('0' + (data%100)/10);
	LCD_AffLettre('0' + data%10);
}
void LCD_printf (const char* texte) {			// Afficher une chaîne de caractères
	char i = 0;
	while (texte[i] != 0)
		LCD_AffLettre(texte[i++]);
}


/****************************************
 * Programme principal
 ****************************************/

void interrupt() {
}

const char* texte = "mail: totofwebcreation@gmail.com";
void main () {
	char i = 0;

	delay_ms(250);

	LCD_Init();

	while (1) {
		i = 0;
		while (texte[i] != 0)
			LCD_AffLettre(texte[i++]);
		//LCD_LigneSuivante();
		delay_ms(250);
		delay_ms(250);
		delay_ms(250);
		delay_ms(250);
	}
}

