/*************************************************************
 * Christophe Le Lann <totofwebcreation@gmail.com>
 * http://www.totofweb.ma.cx
 *
 * Ce programme actionne un servo sur RB1 en utilisant
 * Timer 0
 *************************************************************/

/****************************************
 * Configuration du compilateur
 ****************************************/

// Quartz de 20MHz (utilisé pour les délais)
#pragma CLOCK_FREQ 20000000

/****************************************
 * Variables globales
 ****************************************/

char nbtimers_commande;					// Nomber de timers de 50µs à écouler entre le début du signal PWM et la mise à 0V du pin du servo
long nbtimers_effectue;					// Nombre de timers de 50µs effectués depuis le début du signal PWM

/****************************************
 * Fonctions
 ****************************************/

void generation_pwm() {					// toutes les 50µs

	if (nbtimers_effectue >= 400) {		// 400*50µs = 20ms => nouveau signal PWM
		nbtimers_effectue = 0;			// On recompte pour un nouveau signal
		if (nbtimers_commande > 0)		// On considère qu'on ne doit pas envoyer de signal si la commande est de 0
			set_bit(PORTB,0);			// Etat haut sur RB0
	} else {
		if (nbtimers_effectue >= nbtimers_commande) // si l'on a atteint la commande
			clear_bit(PORTB,0);			// Fin de l'état haut
	}

	nbtimers_effectue++;				// Comptage des timers écoulés
}

/****************************************
 * Interruptions
 ****************************************/

void interrupt() {						// appelé toutes les 50µs
	if (INTCON & 00000100b) {			// Interruption sur TIMER0, voir page 22 de la datasheet du 16F877. l'opérateur & sert à utiliser la table de vérité AND sur les deux variables 8bits (voir BigOnOff)
		generation_pwm();
		TMR0 = 4;						// Remise à 4 du TIMER0 pour compter jusqu'à la prochaine interruption
		clear_bit(INTCON,2);			// Efface le flag d'interruption
	}
}

/****************************************
 * Programme principal
 ****************************************/

void main() {

	OPTION_REG=00001000b;				// 1/1e sur WDT, voir datasheet du 16F877
	enable_interrupt(GIE);				// Les interruptions sont activées
	TMR0 = 4;
	set_bit(INTCON,5);					// Activation des interruptions sur TIMER0 (voir page 22 de la datasheet du 16F877)

	/*
	 * Le pic est à 20MHz, une opération prend 4 cycles d'horloges et un compteur propre à timer0 s'incrémente à chache opération.
	 * Il va donc s'incrémenter 5000000 fois par seconde (20M/4).
	 * Nous spécifions le prescaler à 1/1, c'est à dire que ce compteur s'incrémentera effectivement à chaque opération.
	 * Ce compteur est un compteur 8bits, il compte donc jusqu'à 2^8 = 256. Là, il revient à 0 et ce débordement déclenche une interruption.
	 * Mais nous le faisons partir de 4, le timer0 sera donc appelé toutes les ~250 opérations (et pas 5 car le timer met quelques ns avant de se réinitialiser)
	 * L'interruption sera donc appelée 20000 fois par secondes (20M/4)/250, soit toutes les 50µs
	 */

	TRISB = 00000000b;					// Configuration du port B en mode sortie
	PORTB = 00000000b;					// Mise à 0V de tous les pins du port B

	while (1) {							// Boucle infinie
		for (nbtimers_commande = 0; nbtimers_commande <= 40; nbtimers_commande++) { // envoi d'un signal PWM d'amplitude variant entre 0ms et 2ms à intervalle de 200ms
			delay_ms(200);
		}
	}
}

