terça-feira, 28 de maio de 2013

controlando motor de passo via serial com MBlab XC8

 Hoje vou demonstrar como controlar um motor de passo via serial,note que o motor usado tem
 4 passos logo 90° por enrolamento, os comando para o controle são:
letra "A" - faz motor girar no sentido anti horário. 
letra "H" - faz girar no sentido horário.
letra "P" - para o motor
 
e os comandos :
"E****" - faz girar no sentido anti horário **** graus ,por exemplo  E0090, faz girar 90°. 
"D****" - faz girar no sentido horário **** graus ,por exemplo  E0180, faz girar 180°.
"V****" - altera a velocidade do motor, quanto menor o valor mais rápido, Ex. V0100.
 
O código fonte: 
 
/*
 *                            controlando motor de passo no MPlab XC8
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 16F648A
 * Autor: aguivone
 * Versão: 1
 * Data :  28 de maio de 2013
 */
#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <stdlib.h>
#define _XTAL_FREQ 4000000    // apesar de não usar cristal devo configurar pois esse define é usado para rotinas de tempo e da serial
#include <xc.h>

/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////
#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTOSC oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
//#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator: High-speed crystal/resonator on RA6/OSC2/CLKOUT and RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

/////////////////////defines///////////////////////////////////////////////////////////////////////////////////////////////
#define A RA0     //enrolamento 1
#define B RA1     //enrolamento 2
#define C RA2     //enrolamento 3
#define D RA3     //enrolamento 4


////////////////////////////////////////////////variaveis globais///////////////////////////////////////////////////////////
char caracter,comando;
bit flag_interrupcao = 0;
int posi_atual=1;//se colocar zero ira travar
long velocidade =500;
char valor[4];
int conta_pos=0;
bit recebendo = 0;
int girando=0;
///////////////////////////////////////////////////interrupção//////////////////////////////////////////////////////////////
void interrupt RS232(void)//vetor de interrupção
 {
     caracter = RCREG;
     RCIF = 0;//  limpa flag de interrupção de recepção
     if((caracter == 'A')||(caracter == 'H')||(caracter=='P'))
     {
          comando = caracter;
          flag_interrupcao = 1; //indica interrupção
     }
     else
     {
         if((caracter == 'E')||(caracter == 'D')||(caracter=='V'))
         {
          comando = caracter;
          conta_pos=0;//prepara buffer
          recebendo = 1;
         }
         else
         {
            if((recebendo == 1)&&(conta_pos<4))
            {
                valor[conta_pos]=caracter;
                if(conta_pos == 3)
                {
                  recebendo = 0;//finaliza recepção
                  flag_interrupcao = 1; //indica interrupção
                }
                conta_pos++;
            }
            else
            {//houve algum erro ou caracter invalido
                recebendo = 0;//finaliza recepção
                flag_interrupcao = 0;
            }
         }

     }
 }

/////////////////////////////////funçoes usadas pela uart //////////////////////////////////////////////////////
void inicializa_RS232(long velocidade,int modo)
{////por padrão é usado o modo 8 bits e sem paridade, mas se necessario ajuste aqui a configuração desejada.
    //verifique datasheet para ver a porcentagem de erro e se a velocidade é possivel para o cristal utilizado.
    RCSTA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono.
    int valor;
        if(modo == 1)
        {//modo = 1 ,modo alta velocidade
         TXSTA = 0X24;//modo assincrono,trasmissao 8 bits.
         valor =(int)(((_XTAL_FREQ/velocidade)-16)/16);//calculo do valor do gerador de baud rate
        }
        else
        {//modo = 0 ,modo baixa velocidade
         TXSTA = 0X20;//modo assincrono,trasmissao 8 bits.
         valor =(int)(((_XTAL_FREQ/velocidade)-64)/64);//calculo do valor do gerador de baud rate
        }
    SPBRG = valor;
    RCIE = 1;//habilita interrupção de recepção
    TXIE = 0;//deixa interrupção de transmissão desligado(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo)
}
void escreve(char valor)
{
    TXIF = 0;//limpa flag que sinaliza envio completo.
    TXREG = valor;
    while(TXIF ==0);//espera enviar caracter
}
void imprime(const char frase[])
{
     char indice = 0;
     char tamanho = strlen(frase);
      while(indice < tamanho ) ///veja que o programa pode travar se aqui não tiver as duas aspas
       {
           escreve(frase[indice]);
           indice++;
       }
}
//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////
/*
 *  if(flag_interrupcao ==  1)
        {//tem dados para ler
         imprime(" \n\r caracter digitado :");
         escreve(caracter);
         flag_interrupcao = 0;
        }

 */
//aprox 1 ms para clok interno

void delay_ms(long mili) {
    while (mili > 0) {
        _delay(1000); //aprox. 1ms com clock de 1mhz(oscilador interno)
        mili--;
    }
    // no caso do 18F o valor fica bem acima então é melhor montar essa funcão para que trabalhe bem
   //////////
}

//////////////rotinas do motor de passo//////////////////////////////////////////////////////////////////////
void desliga_motor(void)
{
             A=0;B=0;C=0;D=0; //desliga todos os enrolamentos
}

void graus_horario(long valor)
{
    valor = valor/90;//assim acha numero de pulsos necessarios
    while((valor>0)&&(comando != 'P'))//isso faz ele parar se receber o comando 'P'
    {
        posi_atual++;
        if(posi_atual > 4)
        {
          posi_atual = 1;
        }
         switch(posi_atual)
        {
            case 1:
            {
             A=0;B=0;C=0;D=1; //desliga todos os enrolamentos
            }break;
            case 2:
            {
             A=0;B=0;C=1;D=0; //desliga todos os enrolamentos
            }break;
            case 3:
            {
             A=0;B=1;C=0;D=0; //desliga todos os enrolamentos
            }break;
            case 4:
            {
             A=1;B=0;C=0;D=0; //desliga todos os enrolamentos
            }break;
        }
        delay_ms(velocidade);
        valor--;

    }
}
void graus_antihorario(long valor)
{
    valor = valor/90;//assim acha numero de pulsos necessarios
    while((valor>0)&&(comando != 'P'))//isso faz ele parar se receber o comando 'P'
    {
        posi_atual--;
        if(posi_atual == 0)
        {
          posi_atual = 4;
        }
        switch(posi_atual)
        {
            case 1:
            {
             A=0;B=0;C=0;D=1; //desliga todos os enrolamentos
            }break;
            case 2:
            {
             A=0;B=0;C=1;D=0; //desliga todos os enrolamentos
            }break;
            case 3:
            {
             A=0;B=1;C=0;D=0; //desliga todos os enrolamentos
            }break;
            case 4:
            {
             A=1;B=0;C=0;D=0; //desliga todos os enrolamentos
            }break;
        }
        delay_ms(velocidade);
        valor--;
    }
}
void girar_horario(void)
{
      graus_horario(360);
}
void girar_antihorario(void)
{

      graus_antihorario(360);
}
long char_to_long(void)
{
    long retorno=0;
    long valor3 = valor[3]-'0';//faz virar um long
    long valor2 = valor[2]-'0';//faz virar um long
    long valor1 = valor[1]-'0';//faz virar um long
    long valor0 = valor[0]-'0';//faz virar um long
    retorno = valor3+(valor2*10)+(valor1*100)+(valor0*1000);
    return(retorno);
}
///////////////////////////////////////////////////////////////////////////////
void main(void)
{
    TRISB = 0X02;//configura portB  B1 (pino RX) como entrada
    TRISA = 0X00;//configura portA como saida
    PORTB = 0;  // limpar as portas que estão configuradas como saidas
    inicializa_RS232(9600,1);//modo de alta velocidade
    PEIE = 1;//habilita interrupção de perifericos do pic
    GIE = 1; //habilita interrupção global 
    CMCON = 7;//desliga modulo comparador
    imprime("Controlador de motor de passo \n\r");
    desliga_motor();
    for(;;)
    {
        if(flag_interrupcao == 1)//tem dados na serial
        {
            switch(comando)
            {
                case 'A':
                {
                  girando = 1;
                }break;
                case 'H':
                {
                  girando = 2;
                }break;
                case 'P':
                {
                  girando = 0;
                }break;
                case 'V':
                {
                    velocidade = char_to_long();
                }break;
                case 'E':
                {
                    graus_antihorario(char_to_long());
                    girando = 0;
                }break;
                case 'D':
                {
                    graus_horario(char_to_long());
                    girando = 0;   
                }break;
            }
         comando = ' ' ;//zera comando
         flag_interrupcao = 0;//limpa flag
        }
        if(girando == 0)
        {
           desliga_motor();
        }
        else
        {
            if(girando == 1)
            {
             girar_antihorario();
            }
            else
            {
              girar_horario();
            }
        }
    }//loop infinito

}
 
A simulação: 
 
para a simulação use o motor-stepper.