#include <16F628.h>
//**************************************************************************************************************
// Programme de gestion de la carte UC video Explobotique / VERSION 0.1 du 24/10/2005 RL
// pour tout renseignements : explobotique@free.fr ou forum sur http://explobotique.free.fr
// Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier au titre des
// clauses de la Licence Publique Générale GNU, telle que publiée par la Free Software Foundation ; soit la
// version 2 de la Licence, ou (à votre discrétion) une version ultérieure quelconque. Ce programme est
// distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE GARANTIE ; sans même une garantie implicite de
// COMMERCIABILITE ou DE CONFORMITE A UNE UTILISATION PARTICULIERE. Voir la Licence Publique Générale GNU pour
// plus de détails. Vous devriez avoir reçu un exemplaire de la Licence Publique Générale GNU avec ce programme
// si ce n'est pas le cas, écrivez à la Free Software Foundation Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// vous pouvez consulter la licence sur  : http://www.gnu.org/copyleft/gpl.html
// ou sa traduction (non officielle) sur : http://fsffrance.org/gpl/gpl-fr.fr.html
//**************************************************************************************************************

/* A FAIRE :
voir //TODO

*/


// Affectation des I/O -------------------------------------------------------
/*
RA0   S  Phare 1
RA1   S  Phare 2
RA2   S  Step ( moteur pas a pas )
RA3   S  Selection FDC/EAU ou Dir moteur pas a pas )
RA4   S  Led debug ( 0=led ON )
RA5   E  EAU / FDC
RA6   .  quartz
RA7   .  quartz

RB0   S  Cde emission RS485 ( 1= TX ON )
RB1   E  RX up
RB2   S  TX up
RB3   S  Sortie PWM vers decodeur servos
RB4   S  Selection camera bit 0
RB5   S  Selection camera bit 1
RB6   S  Selection entree FDC/EAU bit 0
RB7   S  Selection entree FDC/EAU bit 1
*/

// adresses des registres ----------------------------------------------------
#byte TRISA=0x85
#byte TRISB=0x86

#byte PORTA=0x05
#byte PORTB=0x06

#byte OPTION=0x81

#byte INTCON=0x0B    // controle global des IT

// timer 1 / 2
#byte CCP1CON=0x17      // capture compare control 1
#byte PR2=0x92          // period register 2
#byte CCPR1L=0x15       // capture compare register LSB
#byte T2CON=0x12        // timer 2 control
#byte TMR2  = 0x11      // timer 2

// comparateurs analogiques
#byte CMCON=0x1F        // comparators control

// port serie
#byte PIE1=0x8C         // registre de validation des IT

#byte SPBRG=0x99        // registre baud rate
#byte TXREG=0x19        // registre de transmission
#byte RCREG=0x1A        // registre de reception
#byte TXSTA=0x98        // transmit status and ctrl TX
#byte RCSTA=0x18        // transmit status and ctrl RX + global


// options du micro
#fuses XT,NOMCLR,NOLVP,WDT,NOPROTECT,PUT,BROWNOUT

// frequence 4MHz
// ATTENTION : UTILISATION EXCLUSIVE DES FCTS delay_ms et delay_us EN IT
// CES FONCTIONS NE SONT PAS REENTRANTES !!!
#use delay(clock=4000000)


//****************************************************************************
// Definitions diverses

#define  VRAI  1
#define  FAUX  0

#define  STX   0x02
#define  ETX   0x03

#define  RAZDOG   #asm CLRWDT #endasm // raz du watchdog

// gestion de la LED
#define  LED_ON      PORTA&=~0x10
#define  LED_OFF     PORTA|= 0x10
#define  TOGGLE_LED  PORTA^= 0x10

// gestion phares
#define  PHARE1_OFF   	PORTA|= 0x01
#define  PHARE1_ON   	PORTA&=~0x01

#define  PHARE2_OFF   	PORTA|= 0x02
#define  PHARE2_ON   	PORTA&=~0x02


//****************************************************************************
// Variables Globales et definitions associees


unsigned char FLAGS ;                     // regroupement des divers flags

// pour tempo
unsigned int16 CPT_DELAI ;


// mot d'etat ( reponse a une interrogation )
unsigned char  REP_ETAT ;


// definitions pour commande servos
#define NB_SERVOS    	8	// IMPERATIVEMENT >1 !!!
#define PULSERVO_REF    187	// 3 ms pour le timer 2 ( 16uS/top )
#define PULSERVO_MIN 	31	// 0,5 mS
#define PULSERVO_MAX 	94	// 1,5 mS
#define PULSERVO_NEUTRE 62	// 1 mS valeur du neutre

#define PERIODE_REF	   219	// 3,5 mS
#define DUREE_PULSE     16 	// 0.25 mS


unsigned char SERVO[NB_SERVOS] ;    // table contenant les tps de pulse pour chaque servo


unsigned char IndServo ;           // indice de parcours de la table des pulses ( 0 = sync )


//****************************************************************************
// gestion de la liaison

// CDE et NO module ( sous-commande )
#define  MA_CDE   0x20
#define  MON_NO   0x00	// en principe 1 seul module video
#define	TAILLE_CDE 13	// nombre de caractere de la cde ( indice ETX + 1 )

// Taille des buffers
#define  TAILLE_BUF_TX  10
#define  TAILLE_BUF_RX  15

#define  VALIDE_TX      PORTB|= 0x01
#define  DEVALIDE_TX    PORTB&=~0x01

unsigned char BUF_TX[TAILLE_BUF_TX] ;     // Buffer de transmission
unsigned char IND_TX ;                    // indice du prochain caractere a transmettre
unsigned char NB_RESTANT_TX ;             // nombre de caractere restant a transmettre

unsigned char DELAI_REP ;                 // delai de reponse ( 0 , 1 , 2 , 4 mS )
unsigned char BUF_RX[TAILLE_BUF_RX] ;     // Buffer de reception
unsigned char IND_RX ;                    // indice du prochain caractere a recevoir
unsigned char NB_CAR_TRAME_RX ;           // nombre de caractere a recevoir
unsigned char CKSM_RX ;                   // cksum reception
#bit FLAG_TRAME_EN_COURS=FLAGS.0          // vrai si on a vu un STX et jusqu'a  fin traitement reception


//----------------------------------------------------------------------------
// fonction d'init de la communication serie
//----------------------------------------------------------------------------
void INIT_SERIE(void)
{
// init variables
IND_RX=0 ;
NB_RESTANT_TX=0 ;
FLAG_TRAME_EN_COURS=FAUX ;

// configure vitesse de transmission ( quartz 4MHz / 19200 Bauds )
SPBRG=12 ;

// valide mode asynchrone / high baud rate / 8bits / TX,RX enable
TXSTA=0x24 ;
RCSTA=0x90 ;

// valide IT reception
PIE1|=0x20 ;
}

//----------------------------------------------------------------------------
// fonction de lancement d'une transmission de n caracteres
//----------------------------------------------------------------------------
void LANCE_TX(unsigned char n)
{
// demande impossible : pas d'action
if( n>TAILLE_BUF_TX ) return ;

// deja une transmission en cours : pas d'action
if( NB_RESTANT_TX!=0)   return ;

// active le transmetteur
VALIDE_TX ;

IND_TX=1 ;
NB_RESTANT_TX=n ;

// envoi le premier caractere
TXREG=BUF_TX[0] ;

// valide les IT TX
PIE1|=0x10 ;
}

//----------------------------------------------------------------------------
// TRAITEMENT de la trame recue
// la trame est deja control�et correcte , seule la commande n'est pas test�
// pour le cas ou le module accepte plusieurs commandes
//----------------------------------------------------------------------------
void TRAITE_RX(void)
{
char i ;
// controle commande et sous-commande
if( ((BUF_RX[0]&0x7F)!=MA_CDE) || (BUF_RX[1]!=MON_NO) )
   return ; // pas pour nous !

//recup�ation des parametres

// pulse servos
for(i=0 ; i<8 ;i++ )
	{
	// controle des valeurs min et max des pulses
	if(BUF_RX[i+3]<PULSERVO_MIN)	      SERVO[i]=PULSERVO_MIN ;
	else if(BUF_RX[i+3]>PULSERVO_MAX)	SERVO[i]=PULSERVO_MAX ;
   else                             	SERVO[i]=BUF_RX[i+3] ;
	}

// bit(s) commande phares + delai reponse
DELAI_REP=BUF_RX[2]&0x03 ;    // delai de reponse ( bits 0 et 1 )

if( (BUF_RX[2]&0x10)==0x10 )  // commande phare 1
   PHARE1_ON ;
else
   PHARE1_OFF ;

if( (BUF_RX[2]&0x20)==0x20 )  // commande phare 2
   PHARE2_ON ;
else
   PHARE2_OFF ;

// selection voie vid� en fonction bits 6&7
i=PORTB&~0x30 ;   // recupere PORTB avec les 2 bits de cde a 0

switch(BUF_RX[2]&0xC0)
   {
      case 0xC0:
         i|=0x30 ;
         break ;

      case 0x80:
         i|=0x20 ;
         break ;

      case 0x40:
         i|=0x10 ;
         break ;

      case 0x00:
      default:
         break ;
   }

PORTB=i ;   // ecriture valeur

// avec ou sans reponse ?
if((BUF_RX[0]&0x80)==0x80)
   {  // reponse demandee

   BUF_TX[0]=0x55 ;     // bourrage
   BUF_TX[1]=STX ;
   BUF_TX[2]=MA_CDE ;
   BUF_TX[3]=MON_NO ;
   BUF_TX[4]=REP_ETAT ;
   BUF_TX[5]=~(MA_CDE+MON_NO+REP_ETAT) ;
   BUF_TX[6]=ETX ;
   BUF_TX[7]=0x55 ;


   // attente du delai de reponse
   switch(DELAI_REP)
      {
      case 1:  delay_us(1000) ;  break ;
      case 2:  delay_us(2000) ;  break ;
      case 3:  delay_us(4000) ;  break ;
      case 0:
      default:                   break ;
      }

   LANCE_TX(8) ;
   }

}

//----------------------------------------------------------------------------
// interruption de transmission ACIA
//----------------------------------------------------------------------------
#INT_TBE
IT_TX(void)
{

if( NB_RESTANT_TX==0)
   {
   PIE1&=~0x10 ;     // devalide les IT TX
   DEVALIDE_TX ;     // devalidation du transmetteur
   }
else if( NB_RESTANT_TX==1)
   {
   // on transmet un caractere bidon (0) pour garder actif le signal
   // de validation du transmetteur pendant le decalage du dernier caractere
   NB_RESTANT_TX=0 ;
   TXREG=0 ;
   }
else
   {
   // transmet le caractere
   TXREG=BUF_TX[IND_TX] ;
   IND_TX++ ;
   NB_RESTANT_TX-- ;
   }
}

//----------------------------------------------------------------------------
// interruption de reception ACIA
// en cas d'erreur , on n'eteind pas la LED avant la prochaine reception
//----------------------------------------------------------------------------
#INT_RDA
IT_RX(void)
{
// controle des erreurs
if((RCSTA&0x06)!=0 )
   {     // en cas d'erreur on abandonne la reception en cours
   FLAG_TRAME_EN_COURS=FAUX ;
   // raz overrun
   RCSTA&=~0x10 ; RCSTA|=0x10 ;
   // raz framing error
   BUF_RX[0]=RCREG ;
   return ;
   }

if(!FLAG_TRAME_EN_COURS)    // pas encore en reception
   {
   if(RCREG==STX)          // debut de trame ?
      {
      LED_ON ;
      IND_RX=0 ;
      NB_CAR_TRAME_RX=4 ;              // trame mini !
      CKSM_RX=0 ;
      FLAG_TRAME_EN_COURS=VRAI ;
      }
   }
 else                      // deja en reception
   {
   BUF_RX[IND_RX]=RCREG ;     // lecture et stockage caractere
   CKSM_RX+=BUF_RX[IND_RX] ;  // calcul cksm
   IND_RX++ ;

   if(IND_RX==1)  // le 1er caractere recue est la cde => donne la longueur
      {
      NB_CAR_TRAME_RX=TAILLE_CDE ; // dans ce cas une seule commande => longueur constante
      }

   else if(IND_RX>=NB_CAR_TRAME_RX)  // fin de reception ?
      {  // oui , controle et traitement
      // presence de la fin de trame
      if(BUF_RX[IND_RX-1]!=ETX)
         {  // NON => trame non valide
            FLAG_TRAME_EN_COURS=FAUX ;
            return ;
         }

      // controle CKSUM
      CKSM_RX-=ETX ;          // on retire le caractere ETX
      if(CKSM_RX!=0xFF)    // et on controle
         {  // ERREUR CKSM => trame non valide
            FLAG_TRAME_EN_COURS=FAUX ;
            return ;
         }

      // on se demasque pour pouvoir traiter IT timer mais on devalide IT RX
      PIE1&=~0x20 ;
      INTCON|=0x80 ;

      // traitement de la trame dans l'IT
      TRAITE_RX() ;

      FLAG_TRAME_EN_COURS=FAUX ;
      LED_OFF ;

      // revalide IT RX
      PIE1|=0x20 ;
      }


   }
}



//----------------------------------------------------------------------------
// interruption overflow timer 0 pour tempo generale ( 256uS pour 4MHz )
//----------------------------------------------------------------------------
#INT_TIMER0
IT_TIMER0(void)
{
if(CPT_DELAI!=0)  CPT_DELAI-- ;

}

//----------------------------------------------------------------------------
// routine de delai 1mS ( en fait 1,024mS )
//----------------------------------------------------------------------------
MY_DELAY_MS( unsigned int16 delai )
{
CPT_DELAI=delai*4 ;

while( CPT_DELAI!=0 )   restart_wdt();

}


// ------------------------------------------------------------------------
// fonction d'init de tout le HARD associ�au servos
void INIT_SERVO(void)
{

for(IndServo=0 ; IndServo<8 ; IndServo++ )	SERVO[IndServo]=PULSERVO_NEUTRE ;

IndServo=0 ;

PR2=PERIODE_REF ;        // pulse de raz 4017
CCPR1L=PULSERVO_REF ;


// on utilise le mode PWM : le timer 2 ( PR2 ) fixe la periode
// et CCPR1L fixe le temps ou la sortie est a 1
CCP1CON=0x0F ; // valide mode pwm et les deux bits sup �0

TRISB &=~0x08 ;       // pin RB3 en sortie

// valide timer 2 avec prediviseur=16 , post=1
// soit 1 top = 16uS ( 4MHz )
T2CON=0x06 ;

TMR2=0 ;       // raz timer et prescaler

PIE1|=0x02 ;	// valide IT timer 2 ( TMR2=PR2 )

}

// ------------------------------------------------------------------------
// fonction d'interuption pour gestion cde servos
// IndServo donne la valeur de l'impulsion en cours ( 0 = reset , 1=servo 0 , ...  )
#INT_TIMER2
IT_SERVO()
{

switch( IndServo )
   {
   case 0:                	// les suivantes sont des pulses de servos
      CCPR1L=DUREE_PULSE;	// valeur pulse suivante = pulse std
      PR2=PERIODE_REF ;    // periode = impulsion de reset ( reference )
      IndServo++ ;
      break ;

   case NB_SERVOS:          	// prochaine = Pulse de REF
      CCPR1L=PULSERVO_REF ;   // prochaine pulse = ref
		PR2=SERVO[NB_SERVOS-1];	// periode actuelle = derniere pulse servo
      IndServo=0 ;           	// raz indice servo
      break ;


   default:          		    // cas standard des pulses de servos
		PR2=SERVO[IndServo-1] ;  // periode = pulse servo courant
      IndServo++ ;
      break ;
   }

}


//-----------------------------------------------------------------------------
// programme principal
//----------------------------------------------------------------------------
unsigned char c ;
unsigned int tmp16 ;
void main() {

RAZDOG ;

// init des E/S
OPTION=0x8F ;     // pas pullup portB, pas IT RB0 , RA4 dispos , prediv 1:128 pour WDT

CMCON=0x07 ;      // devalide les comparateurs

TRISA=0xE0 ;
PORTA=0x13 ;   // phares off, step et dir a 0 , led off

TRISB=0x02 ;
PORTB=0x00 ;   // toutes selection a 0 , txen off

CPT_DELAI=0 ;

INIT_SERVO() ;

INIT_SERIE() ;

INTCON=0xE0 ;  // valide IT generales , periphs et timer 0

MY_DELAY_MS( 10 ) ;

c=0 ;          // no de l'entr� du 151 en cours de lecture

while(1)          // boucle principale
   {

   RAZDOG ;

   if( c==0 )  TOGGLE_LED ; // toute les 700 mS

   // controle des voies d'eau

   // attention : pour eviter des instructions inutiles on tiend compte du fait
   // que la sequence se deroule toujours dans le meme ordre pour selectionner
   // la bonne entree !
   // sauf pour RB6 sel0 / dir qui peut etre modifie pour la cde du moteur berceau

   switch(c)   // selectionne la bonne entree en fct de c
      {
      case 0:  // voie d'eau caisson 2
         PORTA&=~0x10 ;
         PORTB&=~0xC0 ;

         if( (PORTA&0x20)==0 )   REP_ETAT|= 0x01 ;
         else                    REP_ETAT&=~0x01 ;
         break ;

      case 1:  // voie d'eau caisson 1
         PORTB|=0x40 ;

         if( (PORTA&0x20)==0 )   REP_ETAT|= 0x02 ;
         else                    REP_ETAT&=~0x02 ;
         break ;

      case 2:  // voie d'eau caisson 0
         PORTB&=~0x40 ;
         PORTB|=0x80 ;

         if( (PORTA&0x20)==0 )   REP_ETAT|= 0x04 ;
         else                    REP_ETAT&=~0x04 ;
         break ;

      case 3:  // voie d'eau caisson 3
         PORTB|=0x40 ;

         if( (PORTA&0x20)==0 )   REP_ETAT|= 0x08 ;
         else                    REP_ETAT&=~0x08 ;
         break ;

      case 4:  // fin de course 0 : berceau en bas
         PORTA|=0x10 ;
         PORTB&=~0xC0 ;

         if( (PORTA&0x20)==0 )   REP_ETAT|= 0x10 ;
         else                    REP_ETAT&=~0x10 ;
         break ;

      case 5:  // milieu de course (1) : berceau en position neutre
         PORTB|=0x40 ;

         if( (PORTA&0x20)==0 )   REP_ETAT|= 0x20 ;
         else                    REP_ETAT&=~0x20 ;
         break ;

      case 6:  // fin de course 2 : berceau en haut
         PORTB&=~0x40 ;
         PORTB|=0x80 ;

         if( (PORTA&0x20)==0 )   REP_ETAT|= 0x40 ;
         else                    REP_ETAT&=~0x40 ;
         break ;
      }


   if(++c >=7 )   c=0 ;


   MY_DELAY_MS( 100 ) ;
   }


}
