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.