Manual

do

Maker

.

com

Controle de servo-motor com PIC

Controle de servo-motor com PIC

O que a princípio parecia algo simples, transformou-se em um pesadelo; fazer o controle de um servo-motor com PIC.

Quem já o fez em Arduino, sabe o quão simples é, bastando utilizar uma biblioteca própria que inclui exemplos, ou seja, trabalho nulo. Mas você sabe como funciona a comunicação entre um servo-motor e a MCU?

O servo-motor possui um motor DC em seu interior, um conjunto de engrenagens e um pequeno circuito utilizado para intercomunicação. Se você ainda não fez esse controle com nenhuma MCU, repare nesse post e também nesse outro a simplicidade de fazê-lo em Arduino. Fazer o controle do servo-motor com PIC não é fácil, mas também não é o que se possa chamar de difícil.

Basicamente ele possui 3 fios, sendo o central normalmente vermelho para +5v, o marrom para Ground e no outro canto um fio para comunicação, normalmente laranja. Os modelos mais comuns trabalham a 50Htz com intervalo de pulso de 20ms e um ângulo de abertura de 180°. Também há servos com abertura de apenas 90° e servos que atingem 360° sem (ou com) giro continuo. Pulsos PWM dentro do intervalo de 20ms fazem com que o servo interprete um comando de movimento, variando de 1ms (para - 45°) a 2ms (para +45°). É aí que começa um problema.

Se você nunca teve curiosidade de olhar, edite a biblioteca do Arduino referente ao controle de servos. Repare que até o prescaler necessita ser configurado. Como em PIC (mesmo com IDE) a camada entre o usuário é mais baixa, há um trabalho não tão grande, porém cheio de conceitos.

Para funcionar o servo, a primeira coisa que tive que fazer (depois de dias de pesquisa) foi ajustar o clock da MCU para 8MHz (porque não consegui fazer o servo funcionar com frequência superior, confesso). Nesse post anterior eu cito como configurar o clock utilizando o registrador OSCCON e dou uma breve explicação a respeito dos conjuntos de bits.

Colocarei alguns comentários mais conforme dispuser o código de exemplo do controle do servo-motor com PIC, mas não poderei ir muito a fundo porque ainda não tenho o completo entendimento sobre o funcionamento. Portanto, se não funcionar para você, dificilmente eu poderei lhe ajudar a resolver o problema. A MCU utilizada em questão é o PIC16F1827, mas deve funcionar para a maioria dos PICs.

Special Function Register

Trata-se de um controlador de funcionalidades da MCU, tratando I/O, eventos, etc. Há uma variável da MCU que necessita ser monitorada e modificada tanto pelo usuário como pelo sistema. Se você declarar algo no código (lib PWM) e tentar por exemplo, utilizá-la em interrupts() e em main(), obterá um erro. Para permitir que a variável seja manipulada sem controle, então utiliza-se além do monitoramento da MCU, a declaração do tipo volatile.
Sobre o SFR, deixo esse link da wikipedia a respeito.


//Special Function Register
sfr volatile unsigned int CCPR at CCPR1L;
//pino do pwm
sbit SOUT at RA3_bit;
//__FOSC__ - Predefined Globals and Constants (help menu) na IDE MikroC
const unsigned int Servo_Min     = 1000  * __FOSC__ / 4000;
const unsigned int Servo_Max     = 2000  * __FOSC__ / 4000;
const unsigned int Servo_Neutral = 1500  * __FOSC__ / 4000;
const unsigned int Servo_Period  = 20000 * __FOSC__ / 4000;

unsigned int Servo_Current = Servo_Neutral;
int i=0;
void interrupt() {

   if (CCP1IF_bit) {
      if (SOUT) {
         SOUT = 0;
         CCPR = Servo_Period - CCPR;
      }
      else {
         SOUT = 1;
         CCPR = Servo_Current;
      }
      CCP1IF_bit = 0;
   }
}

void main() {

  //Pag 65 datasheet (P16F1827)
  // (1)SPLLEN
  // (1110)IRCF 32MHz ou 8MHz  - manter 8MHz em Project Settings
  // (0) s/ uso
  // (10)SCS System Clock Select bits - determina o clock atraves do IRCF
   OSCCON = 0b11110010;
   ANSELA = 0b00000100; //RA2 le LDR - permanece AN
   TRISA = 0;
   PORTA = 0;

   T1CON = 0;
   TMR1H = 0;
   TMR1L = 0;

   CCP1CON = 0x0B;
   CCPR = Servo_Neutral;
   CCP1IF_bit = 0;
   CCP1IE_bit = 1;

   TMR1ON_bit = 1;
   INTCON = 0xC0;

   while (1) {
     for (i=100;i<201;i++){
       Servo_Current = i * 10 *  Get_Fosc_kHz() / 4000;
       //Delay_us(30);
     }
     Delay_ms(2000);
     for (i=200;i>99;i--){
       Servo_Current = i * 10 *  Get_Fosc_kHz() / 4000;
       Delay_ms(2);
     }
   }
}

Abertamente, não foi uma implementação totalmente minha. Tive que entrar em contato pelo fórum da MikroEletronica (tenho uma licença e o suporte é atencioso), então recebi uma explicação muito boa sobre o funcionamento do servo-motor com PIC, que está (em inglês) nesse link

Se não quiser queimar neurônios, pegue esse meu código de exemplo e manipule apenas OSCCON e o pino que gerará PWM. E falando em PWM, repare que o pino para essa função foi configurado como ANALóGICO. Se você colocar como digital, o pino estará sempre up e você só ouvirá um ruido do motor, nada de movimento.

Vou traduzir a explicação que recebi, mas já antecipei o link porque não quero tomar o mérito desse cara (que na minha opinião, é um grande hacker). A palavra 'ticks' eu mantive, mas traduza como 'um momento' ou 'fração de tempo'.

CCP1CON = 0x0B configura o módulo CCP em modo comparador (trigger special event). Sempre que TMR1 (TMR1H:TMR1L) casar com o CCPR1 (CCPR1H:CCPR1L), o bit CCP1IF bit é ajustado e TMR1 limpo. Os tempos de "ON" e "OFF" são manipulados através do CCPR1.
Se a frequência PWM é de 50Hz (T = 20 ms) e pretende-se um ciclo de 25%, então os tempos de ON e OFF são: Ton = 5 ms, Toff = T - Ton = 15 ms.
Agora converte-se os valores de tempo para os valores do registor (ticks or counts). Considerando a frequência da MCU configurada à 8 MHz, o timer é incrementado a cada 4 clocks (ciclo de instrução): T = 4 * 1 / 8 = 0.5 us.

A fórumla mais genérica seria: T = 4 / f[MHz] ou em kHz: T = 4000 / __FOSC__
( FOSC é uma constante definida na IDE MikroC, procure em Help).

Questão: Se T é o tempo de 1 tick então quantos ticks representam a variável de tempo t?
Resposta: 1 : T = N : t => N = t * 1 / T = t * __FOSC__ / 4000

The final formula:N = t * __FOSC__ / 4000 é usada para traduzir o valor do tempo para o valor do registrador (sendo t dado em microseconds).

Neste exemplo em particular, 50Hz PWM é realizada dessa meneira:

  1. TMR1 inicia a contagem; SOUT = 1; CCPR1 configurado para casar um tempo de 5ms para ON
  2. TMR1 alcança CCPR1 e ocorre a interrupção; TMR1 reinicia automaticamente; SOUT = 0; CCPR1 é ajustado para casar com um tempo de 15 ms OFF
  3. TMR1 alcança CCPR1 após 15 ms e o processo de repete outra vez do primeiro passo

"I hope the explanation was clear enough".

É claríssima, mas envolve alguns conceitos importantes, então acaba parecendo abstrato, porque TMR1 é o Timer1, e a MCU tem também o timer0. CCPR0 também existem, CCP é o módulo que configura PWM, e assim diversos outros detalhes vão incrementando esse post, por isso prefiro ser superficial para não dar informação errada e também para não desanimá-lo com muita informação de uma vez.

Não colocarei video nesse post porque esse código será totalmente aplicado ao cofrinho eletrônico (agora sim, no próximo post) e no video devo explicar algo mais.

Se gostou, não deixe de compartilhar; dê seu like no video e inscreva-se no nosso canal Manual do Maker Brasil no YouTube.

Próximo post a caminho!

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.