// Conferir função getc_temporizado -- Cristiano 08/03

//include Plataforma.h
#include "C:\Documents and Settings\datahominis\Meus documentos\Desenvolvimentos\Linha 2005\Desenvolvimento Plataforma\Software\PIC\Plataforma.h"
#include <math.h>

//------------------------------------------------------------------------------

/*
      Valor do contador do TIMER1 para ajustar freq. de aquisição:

         O clock de Timer1 tem período igual a ((Fosc/4)/Prescaler)
         Assim, o valor a ser definido no contador do timer1 para provocar
         uma interrupção em uma frequencia F deve ser igual a:
         valorContTimer1 = 65535 - [ (1 / (F * ( (Fosc/4) / Prescaler) ) ]
         Como estamos utilizando Prescaler igual a 1 (ver iniciar iniciaAquisicao
         (DIV_BY_1):
         A equação acima se reduz a:

            - Com clock (cristal) de 20MHz no PIC:

                  valorContTimer1 = 65535 - [ 1 / (F * 0.2*10-6) ]

                  Para 150Hz: 32200 - DEFAULT
                  Para 200Hz: 40536
                  Para 300Hz: 48870
*/

void pararAquisicao()
{
   setup_timer_1(T1_DISABLED);      //TIMER1 desligado
   disable_interrupts(INT_TIMER1);  //desabilitar interrupção
   delay_ms(1);                     // aguardar finalização de qualquer INT pendente
}

/****************************** programaGanho *********************************

   Esta rotina programa o MCP6S21 com o ganho especificado pelo software do PC.
   Para efetuar a programação é encessário enviar pela SPI dois bytes:
      -> o primeiro byte escreve no registro de instruções ( 0b01000000 );
      -> o segundo byte escreve no registro de ganhos:
         - para ganho de x1:  0b00000000
         - para ganho de x2:  0b00000001
         - para ganho de x4:  0b00000010
         - para ganho de x5:  0b00000011
         - para ganho de x8:  0b00000100
         - para ganho de x10: 0b00000101
         - para ganho de x16: 0b00000110
         - para ganho de x32: 0b00000111

         ganho (valor ASCII):
            ASCII   Hex
            "1"      31 -> 1x
            "2"      32 -> 2x
            "3"      33 -> 4x
            "4"      34 -> 5x
            "5"      35 -> 8x
            "6"      36 -> 10x
            "7"      37 -> 16x
            "8"      38 -> 32x
*******************************************************************************/

void programaGanho(int ganhoBin)
{
   output_low(CS_PGA);   // Chip select do PGA
   delay_cycles(2);      // Delay para garantir tempo de programação do PGA
   spi_write(0b01000000); //instrução: Write to register, setar ganho
   spi_write(ganhoBin);
   delay_cycles(4);
   output_high(CS_PGA);
   //atraso para slew rate do PGA (4V/us para ganho 1 -- pior caso)
   //como o sinal de entrada pode variar de 5 Volts temos o pior caso em 20uS
   delay_us(20);
}

//converte caracter ASCII para nro correspondente (8 bits)
//O caracter deve representar um nro HEXA 0 - 9, A,B,C,D,E,E
// ou                                     0 - 9, a,b,c,d,e,f
signed int16 converteNroAsciiInt(char nroAscii)
{
      if(nroAscii >= 0x30 && nroAscii <= 0x39) //0 - 9
         return (nroAscii - 0x30);
      else
         {
            if(nroAscii >= 0x41 && nroAscii <= 0x46) // A - F
             return (nroAscii - 0x37);
            else
               {
                  if(nroAscii >= 0x61 && nroAscii <= 0x66) // a - f
                     return (nroAscii - 0x57);
                  else
                     return (-1);
               }
         }
}

//ajusta multiplex para um valor entre 0 e 7
void setarMux(int valor)
{
   int1 a,b,c;

   //garantir que todos os bits não importantes do parametro são 0
   valor = valor & 0x07;

   a = 0;
   b = 0;
   c = 0;

   switch(valor)
   {
      case 1:
      {
               a = 1;
               break;
      }
      case 2:
      {
               b = 1;
               break;
      }
      case 3:
      {
               a = 1;
               b = 1;
               break;
      }
      case 4:
      {
               c = 1;
               break;
      }
      case 5:
      {
               a = 1;
               c = 1;
               break;
      }
      case 6:
      {
               b = 1;
               c = 1;
               break;
      }
      case 7:
      {
               a = 1;
               b = 1;
               c = 1;
               break;
      }
      default:
               break;
   }
   output_bit (mux_a, a);
   output_bit (mux_b, b);
   output_bit (mux_c, c);

   return;
}

//envia Pulso de inicio de conversão
//O sinal Start_conv começa e termina em 1
void enviaSC()
{
   // O pulso deve ter largura entre 40ns e 6us
   output_low(AD_RC);
   delay_cycles(4);
   output_high(AD_RC);
   return;
}


#int_TIMER1
TIMER1_isr()
{
// Início das instruções para aquisição
   int i, msb[8], lsb[8];        // Variáveis para bytes mais e menos significativos
    
   set_timer1(frequencia);    //Configura Timer1 para frequência de aquisição

   for(i = 0; i < nCanais; i++)
   {
      // Coloca o mux no canal selecionado
      setarMux(canais[i]);
      //programa ganho do canal
      programaGanho(CanaisGanhos[i]);
      //Gera o pulso para início de conversão
      enviaSC();
      //Espera o fim da conversão
      delay_us(10);
      //Lê os dados do AD
      output_high(AD_BYTE);   // MSB
      delay_cycles(5);
      msb[i] = input_b();
      output_low(AD_BYTE);    // LSB
      delay_cycles(5);
      lsb[i] = input_b();
   }

   for(i = 0; i < nCanais; i++)
   {
      //Transmite os dados amostrados pela RS232
      putc(msb[i]);
      putc(lsb[i]);
   }
   //Se estiver em conversão continuar apenas reset o timer.
   //Se estiver em conversão continua, coletamos ate completar
   //a quantidade de amostras desejadas
   if(! convContinua)
   {
      nSamplesCanal--; //decrementar nro de amostras coletadas
      if(nSamplesCanal <= 0) //terminou?
         pararAquisicao();
   }

}

void main()
{
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   //SPI do PGA -> MCP6S21 (Fmax = 10MHz)
   //Configura a interface SPI do PIC
   setup_spi(SPI_MASTER|SPI_L_TO_H|SPI_XMIT_L_TO_H|SPI_CLK_DIV_16);

   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
   setup_timer_2(T2_DISABLED,0,1);
   //desabilite int_ext
   disable_interrupts(INT_EXT);

   //define estado inicial dos pinos de controle do sistema
   output_high(CS_PGA);    // Chip select do PGA em nível alto

   /* valores iniciais para controles:
      RA0 (Mux_A) = 0
      RA1 (Mux_B) = 0
      RA2 (Mux_C) = 0
      RA3 (BYTE ad) = 1
      RA4 (SC ad) = 1   ===> 0b0001 1000
   */
   output_bit (mux_a, 0);
   output_bit (mux_b, 0);
   output_bit (mux_c, 0);
   output_bit (AD_RC, 1);
   output_bit (AD_BYTE, 1);

   //handshake
   dadoSerial = '1';

   handshake();

   //Loop principal
   while(true)
   {
      //Chegou algum dado na serial
      //coletar o comando e executar o comando
      executaComando(getc());
   }
}

/*************************** executaComando() **********************************
   Esta função recebe um codido de 8 bits como comando.
   Os possiveis comandos são:

   pcComPrepAquis: Seta o ganho, quantos e quais canais serão
               utilizados no processo
      Ao iniciar a execução deste comando o PIC fica esperando:
         1º -> Quatro caracteres HEXADECIMAIS (0 a F) informando a quantidade de
               amostras (int32 nSamplesCanal) a serem coletadas para cada canal.
               Os bytes devem ser enviados do MSB para o LSB.
               Se o valor enviado for igual a zero o sistema entra em aquisição
               continua (até que receba o comando para parar aquisição).
         2º -> Um byte informando quantos canais serão utilizados (recebe o ascii
               correpondente ao numero);
         º -> Roda um laço com a quantidade de canais informada. Durante a
               execução deste laço o PC envia quais são os canais que serão
               utilizados na aquisição (ascii correpondente) e seus respectivos
               ganhos em sequencia (nro do canal seguido de seu ganho).
               1 (0x31) <= Nro do canal <= 8 (0x38)
               ganho (valor ASCII):
                  ganho1x a ganho32x

         4º -> Recebe o valor que representa a frequencia à qual o sistema
               deverá aquisicionar os dados:

               freq150  -> 150Hz (default);
               freq200  -> 200Hz
               freq300  -> 300Hz

   pcComInitAquis: Inicia aquisição com os parâmetros estabelecidos no comando 0x11 ou
         inicia com os parâmetros default;

   pcComEndAquis: Encerra aquisição
*******************************************************************************/

void executaComando(int comando)
{
   signed int16 res;
   int32 aux;
   signed int16 i;
   switch (comando)
   {
      pararAquisicao(); //para qualquer aquisição em andamento

      case pcComPrepAquis:  //Seta parâmetros de hardware

         //Exemplo de string a ser enviado pelo PC para coleta de
         //Duas amostras por canal ; 8 canais com ganho unitario - TxAmostragem de 250Hz
         //!0002811213141516171812

         nSamplesCanal = 0;
         for(i = 3; i >= 0; i--) //ler quantidade de amostras Nro em HEXA -- LSB first
         {
            if(! getc_temporizado()) //le um byte ("0" a "F")
            {
               putc(picErro);
               return;
            }

            res = converteNroAsciiInt(dadoSerial);
            if(res == -1) //conversão correta?
            {
               putc(picErro);
               return;
            }
            
            //montar nro de amostras (não usar pow(i,j) pois o compilador esta
            //gerando codigo muito grande - provavel bug?
            aux = (int32)res;
            nSamplesCanal = nSamplesCanal + (aux << (8*i));
         }

         if(nSamplesCanal <=0) //conversão continua?
            convContinua = 1; //sim
         else
            convContinua = 0; //não

         //ler nro de canais
         
         if(! getc_temporizado() ) 
         {
            putc(picErro);
            return;
         }
         res = converteNroAsciiInt(dadoSerial);
         if(res == -1) //conversão correta?
         {
            putc(picErro);
            return;
         }

         nCanais = (int) res;  //numero de canais desejado

         for(i = 0; i < nCanais; i++)
         {
            if(! getc_temporizado() ) //le o nro de cada canal ("1" a "8")
            {
               putc(picErro);
               return;
            }

            res = converteNroAsciiInt(dadoSerial);
            if(res == -1) //conversão correta?
            {
               putc(picErro);
               return;
            }

            canais[i] = ((int)res) - 1;     //numero DO canal PC envia 1 a 8 -> MUX usa 0 a 7

            if(! getc_temporizado() ) //le o ganho do canal (1,2,4,5,8,10,16 ou 32)
            {
               putc(picErro);
               return;
            }

            switch(dadoSerial)   //ganho DO canal
            {
               case(ganho1x):
                  canaisGanhos[i] = 0;
                  break;
               case(ganho2x):
                  canaisGanhos[i] = 1;
                  break;
               case(ganho4x):
                  canaisGanhos[i] = 2;
                  break;
               case(ganho5x):
                  canaisGanhos[i] = 3;
                  break;
               case(ganho8x):
                  canaisGanhos[i] = 4;
                  break;
               case(ganho10x):
                  canaisGanhos[i] = 5;
                  break;
               case(ganho16x):
                  canaisGanhos[i] = 6;
                  break;
               case(ganho32x):
                  canaisGanhos[i] = 7;
                  break;
               default:
                  putc(picErro);
                  return;
            }
         }

         if(! getc_temporizado() ) //le o valor indicativo da freq de amostragem
         {
            putc(picErro);
            return;
         }
         switch(dadoSerial)
            {
               case freq150:
                  frequencia = 32200;  // frequencia de amostragem em 150Hz
                  break;
               case freq200:
                  frequencia = 40536;  // frequencia de amostragem em 200Hz
                  break;
               case freq300:
                  frequencia = 48870;  // frequencia de amostragem em 300Hz
                  break;
               default:
                  putc(picErro);
                  return;
            }
         //prepara para iniciar aquisição
         //Com TIMER1 e int0 desligados
         setup_timer_1(T1_DISABLED);
         delay_cycles(3);

         //ajustar contador do timer para estourar o mais rápido possivel. Assim não demoraremos
         //muito para coletar a primeira amostra logo que o comando de iniciar aquisição foi
         //enviado pelo software principal.
         set_timer1(65000);
         //definir parametros do timer
         setup_timer_1( T1_INTERNAL | T1_DIV_BY_1 );

         //avisar que recebeu comando completo e esta preparado para iniciar
         //aquisição
         putc(picOk);
      break;

      case pcComInitAquis:  //Inicia aquisição de dados
         //avisar que recebeu comando completo
         putc(picOk);
         //habilitar interrupção
         enable_interrupts(GLOBAL);
         enable_interrupts(INT_TIMER1);
         break;

      case pcComEndAquis:  //Encerra aquisição de dados
         pararAquisicao();
         //putc(picOk);
         break;
    }
}

/******************************* handshake() ***********************************

      Esta função tem a finalidade de estabelecer a comunicação com o PC e
   garantir que a comunicação está ocorrendo dentro do esperado.
      Para que seja garantida a comunicação os seguintes passos devem ser
   executados:
      1-> O PIC espera o PC enviar pcComHndSk1;
      2-> O PIC envia para o PC a versão do firmware que está sendo utilizado;
      3-> O PC responte pcComHndSk2
      Se a comunicação passar por estes três passos o programa principal entra
   em operação.
*******************************************************************************/

void handshake()
{
   //aguarda PC enviar inicio de handshake:
   waitSerialData(pcHnd_a);

   //PIC avisa que recebeu
   putc(picOk);

   //espera PC responder:
   waitSerialData(pcHnd_b);

   //PIC avisa que recebeu e que entrara em loop de espera de comandos
   putc(picOk);

   //continuar execução a espera de comandos
   return;
}

/****************************** Aguardar até chegar dado igual ao
           enviado como paramentro
/******************************************************************************/
void waitSerialData(char dado)
{
   do
   {
      dadoSerial = getc();
   }
   while(dadoSerial != dado);

   return;
}

/****************************** getc_temporizado() ****************************/

//sucesso = 1
//erro = 0 ==> timeout
/******************************************************************************/
int getc_temporizado()
{
   int32 timeout=0;

   while((! kbhit()) && (++timeout < 100000))
   {  // Espera 1 segundo. Se não chegar nada
      // na serial envia mensagem de erro.
      delay_us(10);   //Não pode demorar muito senão corremos o ricos do buffer
                     //da 232 encher (possui apenas 2 bytes)
   }
   if(kbhit())
   {
      dadoSerial = getc();
      return(1);
   }
   return(0);
}
