Fork me on GitHub

05/06/13

Modulo CCP

TUTORIAL: Módulo CCP

Modo de Captura

O módulo CCP( Capture / Compare / PWM ) é um periférico que permite, ao programador, controlar e medir diversos eventos.

Modo de Captura:
Permite medir a duração de tempo durante um evento. Este circuito monitora o estado do TIMER1, que constantemente muda de valor.

Este modo possui dois registros, CCPR1H e CCPR1L, que copia os valores do TIMER1 ( registros TMR1H e TMR1L), nas seguintes situação:
  1. Cada flanco descendente no pino CCP1;
  2. Cada flanco ascendente no pino CCP1;
  3. Cada 4 flancos ascendente no pino CCP1;
  4. Cada 16 flancos ascendente no pino CCP1;
 
Modo de Comparação:
Neste modo o valor de CCP1 (registros CCPR1H e CCPR1L) é constantemente comparado com o valor do TIMER1( registros TMR1H e TMR1L). quando os valores coincidem, o estado lógico do pino CCP1 é alterado, a flag CCP1IF é setada e/não é gerada uma interrupção. Veja a configuração dos modos logo abaixo, para uma melhor compreensão.


Modo PWM:
Há um outro post falando sobre isto, vocês podem ver aqui.



DCxB1 e DCxB0: É usado somente no modo PWM;

CCPxM3:CCPxM0: Modos:

0000 - Módulo desabilitado;
0001 - Não usado;
0010 - Modo Comparação: Alterna saída quando ocorre a comparação, e o bit CCPxIF é setado;
0011 - Não usado;
0100 - modo Captura: flanco ascendente no pino CCPx;
0101 - modo Captura: flanco descendente no pino CCPx;
0110 - modo Captura: 4 flancos ascendente no pino CCPx;
0111 - modo Captura: 16 flancos ascendente no pino CCPx;
1000 - modo Comparação: Inicializa pino CCPx=0, quando ocorre a comparação, pino CCPx=1 e CCPxIF = 1;
1001 - modo Comparação: Inicializa pino CCPx=1, quando ocorre a comparação, pino CCPx=0 e CCPxIF=1;
1010 - modo Comparação: Gera uma interrupção quando ocorre a comparação;
1011 - modo Comparação: EVENTO ESPECIAL, Timer resetado, e inicia uma conversão AD quando ocorre a comparação, CCPxIF=1;
11xx - modo PWM;

EXEMPLO: Captura o tempo de nivel alto de um pulso
char Flags = 0;
unsigned int tempo2, tempo1;
char resultado[16];

#define bordaDescida Flags.F0
#define capturaOK Flags.F1

void interrupt()
{
   if(CCP1IF_bit)
   {
     if(bordaDescida == 0)//se for borda de subida(indo para o nível alto)
     {
       tempo1 = (CCPR1H << 8) + CCPR1L;//captura o 1º valor
       bordaDescida = 1;
     }
     else          //se for borda de descida
     {
       tempo2 = (CCPR1H << 8) + CCPR1L;//enquanto esteve no nível alto, captura o 2º valor
       bordaDescida = 0;
       capturaOK = 1;
     }
     CCP1CON.F0 = ~CCP1CON.F0; //inverte o modo( borda de subida <-> borda de descida)
     CCP1IF_bit = 0;
   }
}

void main()
{
 //Clock = 8Mhz, prescaller 1:2, t = (4/8) * 2 * 1 = 1[us]
 T1CON = 0B00010001; //cada unidade do valor de timer1 corresponde a 1[us]
 CCP1CON = 0B00000101; //borda de subida
 TRISC.F2 = 1;
 INTCON.GIE = 1;
 INTCON.PEIE = 1;
 PIE1.CCP1IE = 1; //habilita interrupção do modulo CCP
 uart1_init(9600);
 delay_ms(10);

 while(1)
 {
  if(capturaOK)
  {
     tempo2 = tempo2 - tempo1; //resultado em [us]
     inttostr(tempo2, resultado);
     uart1_write_text(resultado);
     capturaOK = 0;
  }
 }
}


Utilizando o modo de captura, você consegue medir tempos com uma alta precisão, de frequências baixas até frequências altas.

17 comentários:

  1. Para enviar a saída por LCD, além das configurações iniciais do LCD, só mudaria a penultima linha "uart1_write_text(resultado);"
    por :

    Lcd_Init();
    Lcd_Cmd(_Lcd_clear);
    Lcd_Cmd(_Lcd_cursor_off);
    Lcd_out(1,1,resultado);

    ?

    ResponderExcluir
    Respostas
    1. isso, so que voce deve colocar antes do while(1):
      Lcd_Init();
      Lcd_Cmd(_Lcd_clear);
      Lcd_Cmd(_Lcd_cursor_off);

      e substituir uart1_write_text por:
      Lcd_out(1,1, resultado);

      Excluir
    2. Olá, aproveitando essa pergunta, eu também estou enviando dados adquiridos do CCP para o LCD. No entanto, tento realizar operações com tempo2. Assim:

      tempo2 = tempo2 - tempo1; //resultado em [us]
      Ang = (tempo2*360)/16666.666;
      lcd_out(2,1,Ang);


      Por exemplo, se eu substituir tempo2 por 1390, então Ang = 30.0240. Mas isso só funciona se eu substituir direto o número 1390 em Ang. Agora se eu deixar Ang em função de tempo2 não dá certo. Eu queria lhe pedir uma ajuda pra descobrir porq.

      Excluir
    3. Evitar fazer calculos com float do jeito que voce, tudo numa so linha. É muita coisa pro microcontrolador calcular e ira causar um estouro na memoria. por isso aparece um valor errado.
      Vc falou que substituir tempo2 por 1390 da certo. Como nao possui variavel, o compilador ja calcula o resultado.
      Faça dessa maneira, quando utilizar float:
      Ang = tempo2;
      Ang *= 360;
      Ang /= 16666.666;

      Excluir
  2. Para um clock de 4MHz, como ficariam as alterações?

    ResponderExcluir
    Respostas
    1. se for utilizar um clock de 4mhz, voce deve alterar o prescaler para 1:1, ou seja, T1CON = 0B00000001;

      Excluir
  3. Olá Tiago, primeiramente gostaria de parabenizar-lhe pelo blog. Estou aprendendo muito sobre MCUs aqui.
    Estou com um pequeno problema, estou tentando escrever no LCD o resultado igual explicado nos post acima, porém não to conseguindo, o lcd não mostra nada (com outros programas que já uso o LCD Escreve normal).
    Será que CCP está usando algum pino que eu configurei para o LCD?
    Segue o código (18F4520) Compilador MickoC:



    // configuração dos pinos do LCD
    sbit LCD_RS at RE2_bit;
    sbit LCD_EN at RE1_bit;
    sbit LCD_D7 at RD7_bit;
    sbit LCD_D6 at RD6_bit;
    sbit LCD_D5 at RD5_bit;
    sbit LCD_D4 at RD4_bit;

    // direção dos pinos do pic
    sbit LCD_RS_Direction at TRISE2_bit;
    sbit LCD_EN_Direction at TRISE1_bit;
    sbit LCD_D7_Direction at TRISD7_bit;
    sbit LCD_D6_Direction at TRISD6_bit;
    sbit LCD_D5_Direction at TRISD5_bit;
    sbit LCD_D4_Direction at TRISD4_bit;




    char Flags = 0;
    unsigned int tempo2, tempo1;
    char resultado[16];
    #define bordaDescida Flags.F0
    #define capturaOK Flags.F1
    void interrupt()
    {
    if(CCP1IF_bit)
    {
    if(bordaDescida == 0)//se for borda de subida(indo para o nível alto)
    {
    tempo1 = (CCPR1H << 8) + CCPR1L;//captura o 1º valor
    bordaDescida = 1;
    }
    else //se for borda de descida
    {
    tempo2 = (CCPR1H << 8) + CCPR1L;//enquanto esteve no nível alto, captura o 2º valor
    bordaDescida = 0;
    capturaOK = 1;
    }
    CCP1CON.F0 = ~CCP1CON.F0; //inverte o modo( borda de subida <-> borda de descida)
    CCP1IF_bit = 0;
    }
    }

    void main()
    {

    Lcd_Init();
    Lcd_Cmd(_Lcd_cursor_off);


    //Clock = 8Mhz, prescaller 1:2, t = (4/8) * 2 * 1 = 1[us]
    T1CON = 0B00001001; //cada unidade do valor de timer1 corresponde a 1[us]
    CCP1CON = 0B00000101; //borda de subida
    TRISC.F2 = 1;
    INTCON.GIE = 1;
    INTCON.PEIE = 1;
    PIE1.CCP1IE = 1; //habilita interrupção do modulo CCP
    delay_ms(10);



    while(1)
    {
    if(capturaOK)
    {
    tempo2 = tempo2 - tempo1; //resultado em [us]
    IntToStr(tempo2, resultado);
    Lcd_out(1,4, "frequencia");
    Lcd_out(2,2, resultado);
    capturaOK = 0;

    }
    }
    }

    ResponderExcluir
    Respostas
    1. Vc usou os pinos RE1, RE2. Estes pinos tbm sao analogicos. entao vc deve desativar. Nao tenho certeza, mas acho q vc deve configurar o registro ADCON1=0X0F.

      Excluir
    2. Fala Tiago,
      Estou usando o Pic16f628A, ele só tem um modulo CCP.
      Tem como usar mais de uma porta com o CCP para capturar tempo?
      Abraços
      Andersosn

      Excluir
  4. Cara onde vc pega essa imagem do PIC

    Obrigado

    ResponderExcluir
    Respostas
    1. eu pego essas imagens da apostila.
      Procure aqui no blog por Apostilas sobre Microcontroladores. É uma apostila com a capa verde.

      Abraços,
      Tiago.

      Excluir
  5. Boa noite meu caro, primeiramente queria lhe parabenizar pelo trabalho. Coloquei o teu código pra rodar aqui e compilou de boa, quando fui simular usando o proteus fiz um pequena modificação com a finalidade de mostrar no lcd16x2 os valores de referentes aos períodos de uma função senoidal, gerada a partir do próprio proteus com a finalidade obter a frequência da mesma, porém o erro gerado é enorme,pensei que talvez pelo fato do código está dimensionado pra um pulso, mas quando coloquei um onda quadrada o resultado também não foi satisfatório, por ex uma onda senoidal de 440 hz e v =5v , está imprimindo na variável resultado 172, você poderia me indicar alguma correção?

    agradeço desde já.

    ResponderExcluir
  6. Alguem tem um projeto de frequencímetro para usar em um radio receptor analógico ,ou seja para mostrar as estaçãoes sintonizadas?
    QSL do Brasil
    http://qsldobrasil.blogspot.com

    ResponderExcluir
  7. Fala pessoal!
    Estou com um projeto já a 4 anos.
    Estou usando o PIC 16f628A para ler sinal PPM que vaira de 800ms a 2200 ms.
    Acabei utilizando a interrupção do PORTB em conjunto com o Timer1.
    Eu estava com problemas para ler mais de 2 canais simultaneos.
    Por fim, depois de 4 anos, consegui resolver o problema.
    Eu não conhecia essa opção do CCP.
    Por Exemplo, neste modelo de pic que estou utilizando, só tem um modulo CCP, e creio eu que deve
    ser utilizado na Porta RB3 (PWM).
    Pergunta, tem como utilizar essa função CCP para capturar tempo em outras portas do PORTB?
    Abraços
    Anderson

    ResponderExcluir
    Respostas
    1. Não, o PIC16F628A só tem apenas um modulo CCP e somente o pino RB3 é usado como entrada para a captura.
      Abraços.

      Excluir
    2. Fala Tiago,
      Então, não tem como eu ler mais de um tipo de Entrada usando apenas um CCP?
      eu já ví alguns códigos por aí, onde o pessoal faz um for para Ler os Canais usando o CCP.
      Como eu eu não entendia muito o bem o CCP, acabei deixando de lado.
      Vc acha possível trabalhar a interrupção em conjunto com o CCP?
      exemplo: A cada interrupção, copio o estado da porta em questão para a porta do CCP e depois capturo o tempo?
      Só que com isso, irei perder a minha saída PWM, correto?

      Excluir
  8. Tiago, parabéns pelo blog, sempre busco ajuda aqui!

    Estou precisando medir duas frequência diferentes usando o modo capture (CCP1 e CCP2 - PIC16F876A). Pelo que entendi os dois usam o TIMER1 como base de tempo e assim não estou sabendo como fazer para zerar o flag capturando os tempos simultaneamente. Tenho que fazer tudo dentro de uma mesma rotina de interrupção? Estou usando como base um projeto seu com 18F628 postado qui no site. Tem como ajudar aí? Desde já agradeço.
    Att,

    ResponderExcluir

Postagens Relacionadas!!