Autor Tema: Encoder ADC RS232 PIC16F877 (Programa)  (Leído 8278 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Encoder ADC RS232 PIC16F877 (Programa)
« en: 10 de Marzo de 2008, 04:48:18 »
Bueno esto es algo con lo que estoy trabajando en estos momentos y consiste en controlar y sensar la posición de 4 articulaciones, dos de ellas rotoides y dos lineales, para las rotoides uso dos encoders incrementales y para las lineales uso dos potenciómetros lineales, el programa está hecho en C utilizando el pic16f877, la idea u objetivo que busco abriendo éste post es compartir éste código  que e realizado, pero principalmente por los encoders, porque me puse a buscar en éste foro y en internet y no conseguí un programa que los procese, en éste foro también hay muy buenos ejemplos de comunicación serial RS232 y otros de como usar el conversor AD de nuestro PIC, bueno en éste programa también están incluídos estos, pero más bien serían adicionales porque repito, lo que considero importante es el programa para manejar los encoders, para todo aquel que lo necesite, los puede analizar y adaptarlo para sus propios proyectos, la parte de como conectar los encoders con el pic, yo utilicé unos optoaclopadores, especificamente el 4N37, voy a obviar esa parte de como conectarlo, incluyo una simulación en proteus para que puedan observar el funcionamiento y conexión del mismo. Los comandos se los envío desde mi calculadora hp48gx, porque originalmente está pensado para que se comunique con otro pic vía rs232, pero es cuestión de analizarlo y adaptarlo a las necesidades o requerimientos propios como dije antes, en los potenciometros tuve que colocar unos capacitores electrolíticos de 0.1uF para que la señal se mantuviera más o menos estable.

Código: [Seleccionar]
#include <16f877.h>
#device adc=10//La resolución del módulo A/D es de 10 bits.
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT,PUT
#use delay (clock=20000000)//20Mhz
#USE RS232 (baud=9600,bits=8,parity=N,xmit=PIN_C6,rcv=PIN_C7)

#use standard_io(a)
#use standard_io(b)
#use fast_io(c)
#use fast_io(d)
#use standard_io(e)

#byte port_a      = 0x05
#byte port_b      = 0x06
#byte port_c      = 0x07
#byte port_d      = 0x08
#byte port_e      = 0x09

#bit TX           = port_c.6// Bit de Stop

typedef struct  {

  int low  : 8;

  int high  : 8; } myWord;

// Declaración de variables
long cont_art_0=0, cont_art_1=0, cont_art_2=0, cont_art_3=0;

int1 pos_ant_A1, pos_ant_B1, pos_ant_A2, pos_ant_B2;
// Posiciones o lecturas anteriores de las señales de los encoders

int1 pos_act_A1, pos_act_B1, pos_act_A2, pos_act_B2;
// Posiciones o lecturas actuales de las señales de los encoders

int dato;// Dato recibido via RS232

#define A1 PIN_D7
#define B1 PIN_D6
#define A2 PIN_D5
#define B2 PIN_D4

// Señales A y B de los encoders 1 y 2

int1 invierto_enc_1=0, invierto_enc_2=0;
// Si sucede el caso en el que se incrementa el contador de una articulación
// en un sentido de giro X, pero en realidad nos interesa que en ese sentido
// de giro dicha variable se decremente, entonces mediante un comando RS232
// podemos modificar éstas variables y lograr el objetivo, los comandos están
// en la rutina de interrupción por recepción de la UART

// Declaración de los prototipos de las funciones

long Toma_adc(int);        // Devuelve la lectura del conversor AD
                           // del canal seleccionado
                           
void Procesa_encoder(int); // Procesa el encoder seleccionado y actualiza
                           // los valores los valores de los contadores
                           // de las articulaciones correspondientes
                           
int1 Lee(int);             // Lee un pin y regresa un valor estable.
                           // Considerando un valor estable el que permanece
                           // 50us sin cambio, éste tiempo fué establecido
                           // en ese valor gracias a observación de la señal
                           // proveniente del encoder en un osciloscopio
                           // digital
                           
int1 Beta(int, int);       // Devuelve 1 si a ocurrido una transición Beta, ó
                           // flanco descendente, en caso contrario regresa 0

// Interrupción por recepción de datos RS232
#int_rda
void rda_isr() {
  dato=getc();// Lee y almacena el dato recibido vía RS232
  switch (dato){
     
      case 0:
         putc(((myWord)cont_art_0).low);
      break;
     
      case 1:
         putc(((myWord)cont_art_0).high);
      break;
     
      case 2:
         putc(((myWord)cont_art_1).low);
      break;
     
      case 3:
         putc(((myWord)cont_art_1).high);
      break;
     
      case 4:
         putc(((myWord)cont_art_2).low);
      break;
     
      case 5:
         putc(((myWord)cont_art_2).high);
      break;
     
      case 6:
         putc(((myWord)cont_art_3).low);
      break;
     
      case 7:
         putc(((myWord)cont_art_3).high);
      break;
     
      case 8:// Resetea los contadores de todas las articulaciones
         cont_art_0=0;
         cont_art_1=0;
         cont_art_2=0;
         cont_art_3=0;
      break;
     
      case 9:// Resetea el contador de las articulación 0 solamente
         cont_art_0=0;
      break;
     
      case 10:// Resetea el contador de las articulación 1 solamente
         cont_art_1=0;
      break;
     
      case 11:// Resetea el contador de las articulación 2 solamente
         cont_art_2=0;
      break;
     
      case 12:// Resetea el contador de las articulación 3 solamente
         cont_art_3=0;
      break;
     
      case 13: // Invierto sentido de giro del encoder 1
     
         if (invierto_enc_1==0){
            invierto_enc_1=1;
         }
         else invierto_enc_1=0;
         
      break;// Tipo Toggle ;-)
     
      case 14: // Invierto sentido de giro del encoder 2
     
         if (invierto_enc_2==0){
            invierto_enc_2=1;
         }
         else invierto_enc_2=0;
         
      break;// Tipo Toggle ;-)
     
  }
}

void main(){
 
   setup_adc(ADC_CLOCK_DIV_32); //configura el reloj del conversor
   setup_adc_ports(ALL_ANALOG);
   enable_interrupts(int_rda);
   enable_interrupts(global);
   
   set_tris_d(0xF0);
   set_tris_c(0b10000000);
   
   TX=1;// Bit de Stop = 1
   
   // Incialización de las posiciones anteriores de los encoders
 
   pos_ant_A1=Lee(A1);// Lee y regresa una señal estable de A1
   pos_ant_B1=Lee(B1);// Lee y regresa una señal estable de B1
   pos_ant_A2=Lee(A2);// Lee y regresa una señal estable de A2
   pos_ant_B2=Lee(B2);// Lee y regresa una señal estable de B2
// Son posiciones referenciales para saber el estado anterior de un pin
   
   while (1){
      Procesa_encoder(1);// Procesa el encoder y actualiza el cont_art_0
      cont_art_1=Toma_adc(0);// Lee AN0
      delay_us(5);// Espero 5 us antes de empezar una nueva conversión
      cont_art_2=Toma_adc(1);// Lee AN1
      Procesa_encoder(2);// Procesa el encoder y actualiza el cont_art_3
   }
}

// Desarrolo de funciones
long Toma_adc(int canal){

   long valor;

   set_adc_channel(canal);
   delay_us(20);
   Read_ADC(ADC_START_ONLY);
   delay_us(20);
   valor=read_adc(ADC_READ_ONLY);
   return(valor);
}

void Procesa_encoder(int enc){

   switch (enc){
   
      case 1:// Procesa el encoder 1
         pos_act_A1=Lee(A1);// Lee las posiciones actuales de las señales
         pos_act_B1=Lee(B1);// proveniente del encoder
         
         if (Beta(pos_ant_A1, pos_act_A1)){// Ocurrió un flanco descendente?
            if (pos_act_B1==1){
               if (!invierto_enc_1) {cont_art_0++;}
               else{ if (cont_art_0>0) cont_art_0--;};
            };
         };
         
         if (Beta(pos_ant_B1, pos_act_B1)){// Ocurrió un flanco descendente?
            if (pos_act_A1==1){
               if (!invierto_enc_1) {if (cont_art_0>0) cont_art_0--;}
               else cont_art_0++;
            };
         };
       
         // Actualiza posición anterior de las señales del encoder
         pos_ant_A1=pos_act_A1;
         pos_ant_B1=pos_act_B1;

      break;
     
      case 2:// Procesa el encoder 2
         pos_act_A2=Lee(A2);// Lee las posiciones actuales de las señales
         pos_act_B2=Lee(B2);// proveniente del encoder
         
         if (Beta(pos_ant_A2, pos_act_A2)){// Ocurrió un flanco descendente?
            if (pos_act_B2==1){
               if (!invierto_enc_2) {cont_art_3++;}
               else{ if (cont_art_3>0) cont_art_3--;};
            };
         };
         
         if (Beta(pos_ant_B2, pos_act_B2)){// Ocurrió un flanco descendente?
            if (pos_act_A2==1){
               if (!invierto_enc_2) {if (cont_art_3>0) cont_art_3--;}
               else cont_art_3++;
            };
         };
         
         // Actualiza posición anterior de las señales del encoder
         pos_ant_A2=pos_act_A2;
         pos_ant_B2=pos_act_B2;
         
      break;
   }
}

int1 Lee(int pin){// Regresa el valor estable de la lectura de un pin

      int pos_ant, estable=0;
     
      do{
      pos_ant=input(pin);
      delay_us(50);// Espera 50us para ver si la señal se mantiene estable
      if (pos_ant==input(pin)) estable=1;
      }while (!estable);// Mientras no sea estable se mantiene en el ciclo
      if (input(pin)) {
      return(1);
      }
      else return(0);
      }
     
int1 Beta(int1 pos_inic, int1 pos_final){// Devuelve 1 si ocurrió una transición
                                         // Beta, en caso contrario regresa 0
   
   if ((pos_inic==1) && (pos_final==0))
   {
      return(1);
   }
   else return(0);
}

Le pueden hacer los cambios que quieran, yo utilizo el pic16f877a y lo comunico a 19200Baudios con otro pic, especificamente el 18f4550.

Saludos y espero que le sea útil a otra persona así como ha sido y es para mí en éste momento.

Y que viva el foro!  que me gusta mucho :mrgreen:

PD: Quizás alguno se preguntará porqué no aprovecho todos los flancos del encoder y la respuesta es: porque tengo un encoder que tiene 1000 pulsos por revolución además los tengo acoplados mediante unos engranajes que me hacen más pequeña esa resolución y por lo tanto obtengo más pulsos por vuelta , que si divido eso entre 4 apenas veo al encoder y ya me está contando pulsos  :shock: la única forma de que me trabajara más tranquilo fué así.  ;-)

Un poco de teoría
« Última modificación: 10 de Marzo de 2008, 21:42:03 por gu1llermo »

Desconectado stk500

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 4919
Re: Encoder ADC RS232 PIC16F877 (Programa)
« Respuesta #1 en: 10 de Marzo de 2008, 05:02:36 »
Muchas gracias amigo por compartirlo
eso nos sera util a todos
Saludo

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Encoder ADC RS232 PIC16F877 (Programa)
« Respuesta #2 en: 10 de Marzo de 2008, 21:35:05 »
De nada  :mrgreen:

Hoy probé el programa en el encoder y los 50us que coloqué para filtrar la señal no fueron necesarios, es más habían pulsos que no llegaba a sensar gracias a esos 50us así que lo eliminé para ésta aplicación, el programa resultante lo dejé así:

Código: [Seleccionar]
#include <16f877.h>
#device adc=10//La resolución del módulo A/D es de 10 bits.
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT,PUT
#use delay (clock=20000000)//20Mhz
#USE RS232 (baud=9600,bits=8,parity=N,xmit=PIN_C6,rcv=PIN_C7)

#use standard_io(a)
#use standard_io(b)
#use fast_io(c)
#use fast_io(d)
#use standard_io(e)

#byte port_a      = 0x05
#byte port_b      = 0x06
#byte port_c      = 0x07
#byte port_d      = 0x08
#byte port_e      = 0x09

#bit TX           = port_c.6// Bit de Stop

typedef struct  {

  int low  : 8;

  int high  : 8; } myWord;

// Declaración de variables
long cont_art_0=0, cont_art_1=0, cont_art_2=0, cont_art_3=0;

int1 pos_ant_A1, pos_ant_B1, pos_ant_A2, pos_ant_B2;
// Posiciones o lecturas anteriores de las señales de los encoders

int1 pos_act_A1, pos_act_B1, pos_act_A2, pos_act_B2;
// Posiciones o lecturas actuales de las señales de los encoders

int dato;// Dato recibido via RS232

#define A1 PIN_D7
#define B1 PIN_D6
#define A2 PIN_D5
#define B2 PIN_D4

// Señales A y B de los encoders 1 y 2

int1 invierto_enc_1=0, invierto_enc_2=0;
// Si sucede el caso en el que se incrementa el contador de una articulación
// en un sentido de giro X, pero en realidad nos interesa que en ese sentido
// de giro dicha variable se decremente, entonces mediante un comando RS232
// podemos modificar éstas variables y lograr el objetivo, los comandos están
// en la rutina de interrupción por recepción de la UART

// Declaración de los prototipos de las funciones

long Toma_adc(int);        // Devuelve la lectura del conversor AD
                           // del canal seleccionado
                           
void Procesa_encoder(int); // Procesa el encoder seleccionado y actualiza
                           // los valores los valores de los contadores
                           // de las articulaciones correspondientes
                           
int1 Lee(int);             // Lee un pin
                           
int1 Beta(int, int);       // Devuelve 1 si a ocurrido una transición Beta, ó
                           // flanco descendente, en caso contrario regresa 0

// Interrupción por recepción de datos RS232
#int_rda
void rda_isr() {
  dato=getc();// Lee y almacena el dato recibido vía RS232
  switch (dato){
     
      case 0:
         putc(((myWord)cont_art_0).low);
      break;
     
      case 1:
         putc(((myWord)cont_art_0).high);
      break;
     
      case 2:
         putc(((myWord)cont_art_1).low);
      break;
     
      case 3:
         putc(((myWord)cont_art_1).high);
      break;
     
      case 4:
         putc(((myWord)cont_art_2).low);
      break;
     
      case 5:
         putc(((myWord)cont_art_2).high);
      break;
     
      case 6:
         putc(((myWord)cont_art_3).low);
      break;
     
      case 7:
         putc(((myWord)cont_art_3).high);
      break;
     
      case 8:// Resetea los contadores de todas las articulaciones
         cont_art_0=0;
         cont_art_1=0;
         cont_art_2=0;
         cont_art_3=0;
      break;
     
      case 9:// Resetea el contador de las articulación 0 solamente
         cont_art_0=0;
      break;
     
      case 10:// Resetea el contador de las articulación 1 solamente
         cont_art_1=0;
      break;
     
      case 11:// Resetea el contador de las articulación 2 solamente
         cont_art_2=0;
      break;
     
      case 12:// Resetea el contador de las articulación 3 solamente
         cont_art_3=0;
      break;
     
      case 13: // Invierto sentido de giro del encoder 1
     
         if (invierto_enc_1==0){
            invierto_enc_1=1;
         }
         else invierto_enc_1=0;
         
      break;// Tipo Toggle ;-)
     
      case 14: // Invierto sentido de giro del encoder 2
     
         if (invierto_enc_2==0){
            invierto_enc_2=1;
         }
         else invierto_enc_2=0;
         
      break;// Tipo Toggle ;-)
     
  }
}

void main(){
 
   setup_adc(ADC_CLOCK_DIV_32); //configura el reloj del conversor
   setup_adc_ports(ALL_ANALOG);
   enable_interrupts(int_rda);
   enable_interrupts(global);
   
   set_tris_d(0xF0);
   set_tris_c(0b10000000);
   
   TX=1;// Bit de Stop = 1
   
   // Incialización de las posiciones anteriores de los encoders
 
   pos_ant_A1=Lee(A1);// Lee A1
   pos_ant_B1=Lee(B1);// Lee B1
   pos_ant_A2=Lee(A2);// Lee A2
   pos_ant_B2=Lee(B2);// Lee B2
// Son posiciones referenciales para saber el estado anterior de un pin
   
   while (1){
      Procesa_encoder(1);// Procesa el encoder y actualiza el cont_art_0
      cont_art_1=Toma_adc(0);// Lee AN0
      delay_us(5);// Espero 5 us antes de empezar una nueva conversión
      cont_art_2=Toma_adc(1);// Lee AN1
      Procesa_encoder(2);// Procesa el encoder y actualiza el cont_art_3
   }
}

// Desarrolo de funciones
long Toma_adc(int canal){

   long valor;

   set_adc_channel(canal);
   delay_us(20);
   Read_ADC(ADC_START_ONLY);
   delay_us(20);
   valor=read_adc(ADC_READ_ONLY);
   return(valor);
}

void Procesa_encoder(int enc){

   switch (enc){
   
      case 1:// Procesa el encoder 1
         pos_act_A1=Lee(A1);// Lee las posiciones actuales de las señales
         pos_act_B1=Lee(B1);// proveniente del encoder
         
         if (Beta(pos_ant_A1, pos_act_A1)){// Ocurrió un flanco descendente?
            if (pos_act_B1==1){
               if (!invierto_enc_1) {cont_art_0++;}
               else{ if (cont_art_0>0) cont_art_0--;};
            };
         };
         
         if (Beta(pos_ant_B1, pos_act_B1)){// Ocurrió un flanco descendente?
            if (pos_act_A1==1){
               if (!invierto_enc_1) {if (cont_art_0>0) cont_art_0--;}
               else cont_art_0++;
            };
         };
       
         // Actualiza posición anterior de las señales del encoder
         pos_ant_A1=pos_act_A1;
         pos_ant_B1=pos_act_B1;

      break;
     
      case 2:// Procesa el encoder 2
         pos_act_A2=Lee(A2);// Lee las posiciones actuales de las señales
         pos_act_B2=Lee(B2);// proveniente del encoder
         
         if (Beta(pos_ant_A2, pos_act_A2)){// Ocurrió un flanco descendente?
            if (pos_act_B2==1){
               if (!invierto_enc_2) {cont_art_3++;}
               else{ if (cont_art_3>0) cont_art_3--;};
            };
         };
         
         if (Beta(pos_ant_B2, pos_act_B2)){// Ocurrió un flanco descendente?
            if (pos_act_A2==1){
               if (!invierto_enc_2) {if (cont_art_3>0) cont_art_3--;}
               else cont_art_3++;
            };
         };
         
         // Actualiza posición anterior de las señales del encoder
         pos_ant_A2=pos_act_A2;
         pos_ant_B2=pos_act_B2;
         
      break;
   }
}

int1 Lee(int pin){// Regresa el valor de la lectura de un pin

      if (input(pin)) {
      return(1);
      }
      else return(0);
      }
     
int1 Beta(int1 pos_inic, int1 pos_final){// Devuelve 1 si ocurrió una transición
                                         // Beta, en caso contrario regresa 0
   
   if ((pos_inic==1) && (pos_final==0))
   {
      return(1);
   }
   else return(0);
}

Éste si me funciona mejor.

Adicionalmente conecté la malla de tierra del cable del sensor en la carcaza del equipo y de esta forma pude obtener lecturas más estables, es decir más bonitas  :mrgreen:

Mañana haré otras pruebas en físico, cualquier cosa lo publico.

Ah!, otra cosa que se me olvidaba es que el archivo anterior solo estaba la simulación en proteus sin el .hex, lo corrijo y subo tanto el .dsn y el .hex en una misma carpeta comprimida con winrar.

Saludos.
« Última modificación: 10 de Marzo de 2008, 21:41:01 por gu1llermo »

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Encoder ADC RS232 PIC16F877 (Programa)
« Respuesta #3 en: 11 de Marzo de 2008, 22:58:46 »
Bueno hoy hice varios cambios, me dí cuenta que no podía sensar las señales del encoder en el programa principal sino que para obtener mejores resultados lo cambié a procesarlos mediante interrupción, la cuestión es que tengo dos encoders con sus señales A y B cada uno, lo bueno es que el pic16f877 tiene interrupción por cambio de estado en los pines RB7:RB4 entonces lo que hice fué trasladé las señales para allá y habilité dicha interrupción, el cambio fue notorio ahora si veía más pulsos, pero todavía notaba que el pic se quedaba corto en velocidad así que como tenía un 18f4455 a la mano pasé el código a dicho pic y probé, aunque los resultados mejoraron no fué mucha la diferencia, pero es lo mejor hasta los momentos que e podido lograr para leer estas señales y procesarlas, los código resultantes son los siguientes:

Para el pic16f877:

Código: [Seleccionar]
#include <16f877.h>
#device adc=10//La resolución del módulo A/D es de 10 bits.
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT,PUT
#use delay (clock=20000000)//20Mhz
#USE RS232 (baud=9600,bits=8,parity=N,xmit=PIN_C6,rcv=PIN_C7)

#use standard_io(a)
#use fast_io(b)// Aqui están las señales del encoder
#use fast_io(c)
#use standard_io(d)
#use standard_io(e)

#byte port_a      = 0x05
#byte port_b      = 0x06
#byte port_c      = 0x07
#byte port_d      = 0x08
#byte port_e      = 0x09

#byte INTCON      = 0x0B// Registro de interrupciones

#bit TX           = port_c.6// Bit de Stop

#bit RBIF         = INTCON.0// RB Port Change interrupt Flag bit

typedef struct  {

  int low  : 8;

  int high  : 8; } myWord;

// Declaración de variables
long cont_art_0=0, cont_art_1=0, cont_art_2=0, cont_art_3=0;

int1 pos_ant_A1, pos_ant_B1, pos_ant_A2, pos_ant_B2;
// Posiciones o lecturas anteriores de las señales de los encoders

int1 pos_act_A1, pos_act_B1, pos_act_A2, pos_act_B2;
// Posiciones o lecturas actuales de las señales de los encoders

int dato;// Dato recibido via RS232

#define A1 PIN_B7
#define B1 PIN_B6
#define A2 PIN_B5
#define B2 PIN_B4

#define Lee input

// Señales A y B de los encoders 1 y 2

int1 invierto_enc_1=1, invierto_enc_2=1;
// Si sucede el caso en el que se incrementa el contador de una articulación
// en un sentido de giro X, pero en realidad nos interesa que en ese sentido
// de giro dicha variable se decremente, entonces mediante un comando RS232
// podemos modificar éstas variables y lograr el objetivo, los comandos están
// en la rutina de interrupción por recepción de la UART


// Declaración de los prototipos de las funciones

long Toma_adc(int);        // Devuelve la lectura del conversor AD
                           // del canal seleccionado
                           
void Procesa_encoder(int); // Procesa el encoder seleccionado y actualiza
                           // los valores los valores de los contadores
                           // de las articulaciones correspondientes
                           
int1 Beta(int, int);       // Devuelve 1 si a ocurrido una transición Beta, ó
                           // flanco descendente, en caso contrario regresa 0

// Interrupción por recepción de datos RS232
#int_rda
void rda_isr() {
  dato=getc();// Lee y almacena el dato recibido vía RS232
  switch (dato){
     
      case 0:
         putc(((myWord)cont_art_0).low);
      break;
     
      case 1:
         putc(((myWord)cont_art_0).high);
      break;
     
      case 2:
         putc(((myWord)cont_art_1).low);
      break;
     
      case 3:
         putc(((myWord)cont_art_1).high);
      break;
     
      case 4:
         putc(((myWord)cont_art_2).low);
      break;
     
      case 5:
         putc(((myWord)cont_art_2).high);
      break;
     
      case 6:
         putc(((myWord)cont_art_3).low);
      break;
     
      case 7:
         putc(((myWord)cont_art_3).high);
      break;
     
      case 8:// Resetea los contadores de todas las articulaciones
         cont_art_0=0;
         cont_art_1=0;
         cont_art_2=0;
         cont_art_3=0;
      break;
     
      case 9:// Resetea el contador de las articulación 0 solamente
         cont_art_0=0;
      break;
     
      case 10:// Resetea el contador de las articulación 1 solamente
         cont_art_1=0;
      break;
     
      case 11:// Resetea el contador de las articulación 2 solamente
         cont_art_2=0;
      break;
     
      case 12:// Resetea el contador de las articulación 3 solamente
         cont_art_3=0;
      break;
     
      case 13: // Invierto sentido de giro del encoder 1
     
         if (invierto_enc_1==0){
            invierto_enc_1=1;
         }
         else invierto_enc_1=0;
         
      break;// Tipo Toggle ;-)
     
      case 14: // Invierto sentido de giro del encoder 2
     
         if (invierto_enc_2==0){
            invierto_enc_2=1;
         }
         else invierto_enc_2=0;
         
      break;// Tipo Toggle ;-)
  }
}

#INT_RB
void rb_isr() {// Si al menos uno de los pines RB7:RB4 cambió de estado
               // entra aquí

   pos_act_A1=Lee(A1);// Lee las posiciones actuales de las señales
   pos_act_B1=Lee(B1);// proveniente del encoder 1
   
   pos_act_A2=Lee(A2);// Lee las posiciones actuales de las señales
   pos_act_B2=Lee(B2);// proveniente del encoder 2
   
   // Procesamos el encoder 1
   
   if (Beta(pos_ant_A1, pos_act_A1)){// Ocurrió un flanco descendente?
      if (pos_act_B1==1){
          if (!invierto_enc_1) {cont_art_0++;}
          else{ if (cont_art_0>0) cont_art_0--;};
      };
   };
         
   if (Beta(pos_ant_B1, pos_act_B1)){// Ocurrió un flanco descendente?
      if (pos_act_A1==1){
          if (!invierto_enc_1) {if (cont_art_0>0) cont_art_0--;}
          else cont_art_0++;
      };
   };
   
   // Procesamos el encoder 2
   
   if (Beta(pos_ant_A2, pos_act_A2)){// Ocurrió un flanco descendente?
       if (pos_act_B2==1){
           if (!invierto_enc_2) {cont_art_3++;}
           else{ if (cont_art_3>0) cont_art_3--;};
       };
   };
         
   if (Beta(pos_ant_B2, pos_act_B2)){// Ocurrió un flanco descendente?
       if (pos_act_A2==1){
           if (!invierto_enc_2) {if (cont_art_3>0) cont_art_3--;}
           else cont_art_3++;
       };
   };
         
    // Actualizamos las posiciones anteriores de los encoders
   
   pos_ant_A1=pos_act_A1;
   pos_ant_B1=pos_act_B1;
   
   pos_ant_A2=pos_act_A2;
   pos_ant_B2=pos_act_B2;
   
}

void main(){
 
   setup_adc(ADC_CLOCK_DIV_32); //configura el reloj del conversor
   setup_adc_ports(ALL_ANALOG);
   enable_interrupts(int_rda);// Interrupción por recepción de datos via rs232
   enable_interrupts(int_rb);// Interrupción por cambio de estado pines RB7:RB4
   enable_interrupts(global);
   
   set_tris_b(0xF0);
   set_tris_c(0b10000000);
   
   TX=1;// Bit de Stop = 1
   
   // Incialización de las posiciones anteriores de los encoders
 
   pos_ant_A1=Lee(A1);
   pos_ant_B1=Lee(B1);
   pos_ant_A2=Lee(A2);
   pos_ant_B2=Lee(B2);
// Son posiciones referenciales para saber el estado anterior de un pin
   
   while (1){
      // Los contadores de las articulaciones 0 y 3, son actualizados
      // a través de la interrupción de cambio de estado del puerto B
      cont_art_1=Toma_adc(0);// Lee AN0
      delay_us(5);// Espero 5 us antes de empezar una nueva conversión
      cont_art_2=Toma_adc(1);// Lee AN1
   }
}

// Desarrolo de funciones
long Toma_adc(int canal){

   long valor;

   set_adc_channel(canal);
   delay_us(20);
   Read_ADC(ADC_START_ONLY);
   delay_us(20);
   valor=read_adc(ADC_READ_ONLY);
   return(valor);
}
     
int1 Beta(int1 pos_inic, int1 pos_final){// Devuelve 1 si ocurrió una transición
                                         // Beta, en caso contrario regresa 0
   
   if ((pos_inic==1) && (pos_final==0))
   {
      return(1);
   }
   else return(0);
}

Y para el 18f4455:

Código: [Seleccionar]
#include <18F4455.h>
#device adc=10//La resolución del módulo A/D es de 10 bits.
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,NOVREGEN
#fuses NOBROWNOUT,PUT,NOPBADEN
#use delay (clock=48000000)//48Mhz

#USE RS232 (baud=19200,bits=8,parity=N,xmit=PIN_C6,rcv=PIN_C7)

#use standard_io(a)
#use fast_io(b)// Aqui están las señales del encoder
#use fast_io(c)
#use standard_io(d)
#use standard_io(e)

#byte port_a      = 0xF80
#byte port_b      = 0xF81
#byte port_c      = 0xF82
#byte port_d      = 0xF83
#byte port_e      = 0xF84

#bit TX           = port_c.6// Bit de Stop

typedef struct  {

  int low  : 8;

  int high  : 8; } myWord;

// Declaración de variables
long cont_art_0=0, cont_art_1=0, cont_art_2=0, cont_art_3=0;

int1 pos_ant_A1, pos_ant_B1, pos_ant_A2, pos_ant_B2;
// Posiciones o lecturas anteriores de las señales de los encoders

int1 pos_act_A1, pos_act_B1, pos_act_A2, pos_act_B2;
// Posiciones o lecturas actuales de las señales de los encoders

int dato;// Dato recibido via RS232

#define A1 PIN_B7
#define B1 PIN_B6
#define A2 PIN_B5
#define B2 PIN_B4

#define Lee input

// Señales A y B de los encoders 1 y 2

int1 invierto_enc_1=1, invierto_enc_2=1;
// Si sucede el caso en el que se incrementa el contador de una articulación
// en un sentido de giro X, pero en realidad nos interesa que en ese sentido
// de giro dicha variable se decremente, entonces mediante un comando RS232
// podemos modificar éstas variables y lograr el objetivo, los comandos están
// en la rutina de interrupción por recepción de la UART


// Declaración de los prototipos de las funciones

long Toma_adc(int);        // Devuelve la lectura del conversor AD
                           // del canal seleccionado
                           
void Procesa_encoder(int); // Procesa el encoder seleccionado y actualiza
                           // los valores los valores de los contadores
                           // de las articulaciones correspondientes
                           
int1 Beta(int, int);       // Devuelve 1 si a ocurrido una transición Beta, ó
                           // flanco descendente, en caso contrario regresa 0

// Interrupción por recepción de datos RS232
#int_rda
void rda_isr() {
  dato=getc();// Lee y almacena el dato recibido vía RS232
  switch (dato){
     
      case 0:
         putc(((myWord)cont_art_0).low);
      break;
     
      case 1:
         putc(((myWord)cont_art_0).high);
      break;
     
      case 2:
         putc(((myWord)cont_art_1).low);
      break;
     
      case 3:
         putc(((myWord)cont_art_1).high);
      break;
     
      case 4:
         putc(((myWord)cont_art_2).low);
      break;
     
      case 5:
         putc(((myWord)cont_art_2).high);
      break;
     
      case 6:
         putc(((myWord)cont_art_3).low);
      break;
     
      case 7:
         putc(((myWord)cont_art_3).high);
      break;
     
      case 8:// Resetea los contadores de todas las articulaciones
         cont_art_0=0;
         cont_art_1=0;
         cont_art_2=0;
         cont_art_3=0;
      break;
     
      case 9:// Resetea el contador de las articulación 0 solamente
         cont_art_0=0;
      break;
     
      case 10:// Resetea el contador de las articulación 1 solamente
         cont_art_1=0;
      break;
     
      case 11:// Resetea el contador de las articulación 2 solamente
         cont_art_2=0;
      break;
     
      case 12:// Resetea el contador de las articulación 3 solamente
         cont_art_3=0;
      break;
     
      case 13: // Invierto sentido de giro del encoder 1
     
         if (invierto_enc_1==0){
            invierto_enc_1=1;
         }
         else invierto_enc_1=0;
         
      break;// Tipo Toggle ;-)
     
      case 14: // Invierto sentido de giro del encoder 2
     
         if (invierto_enc_2==0){
            invierto_enc_2=1;
         }
         else invierto_enc_2=0;
         
      break;// Tipo Toggle ;-)
  }
}

#INT_RB
void rb_isr() {// Si al menos uno de los pines RB7:RB4 cambió de estado
               // entra aquí

   pos_act_A1=Lee(A1);// Lee las posiciones actuales de las señales
   pos_act_B1=Lee(B1);// proveniente del encoder 1
   
   pos_act_A2=Lee(A2);// Lee las posiciones actuales de las señales
   pos_act_B2=Lee(B2);// proveniente del encoder 2
   
   // Procesamos el encoder 1
   
   if (Beta(pos_ant_A1, pos_act_A1)){// Ocurrió un flanco descendente?
      if (pos_act_B1==1){
          if (!invierto_enc_1) {cont_art_0++;}
          else{ if (cont_art_0>0) cont_art_0--;};
      };
   };
         
   if (Beta(pos_ant_B1, pos_act_B1)){// Ocurrió un flanco descendente?
      if (pos_act_A1==1){
          if (!invierto_enc_1) {if (cont_art_0>0) cont_art_0--;}
          else cont_art_0++;
      };
   };
   
   // Procesamos el encoder 2
   
   if (Beta(pos_ant_A2, pos_act_A2)){// Ocurrió un flanco descendente?
       if (pos_act_B2==1){
           if (!invierto_enc_2) {cont_art_3++;}
           else{ if (cont_art_3>0) cont_art_3--;};
       };
   };
         
   if (Beta(pos_ant_B2, pos_act_B2)){// Ocurrió un flanco descendente?
       if (pos_act_A2==1){
           if (!invierto_enc_2) {if (cont_art_3>0) cont_art_3--;}
           else cont_art_3++;
       };
   };
         
    // Actualizamos las posiciones anteriores de los encoders
   
   pos_ant_A1=pos_act_A1;
   pos_ant_B1=pos_act_B1;
   
   pos_ant_A2=pos_act_A2;
   pos_ant_B2=pos_act_B2;
   
}

void main(){
 
   setup_adc(ADC_CLOCK_DIV_32); //configura el reloj del conversor
   setup_adc_ports(ALL_ANALOG);
   enable_interrupts(int_rda);// Interrupción por recepción de datos via rs232
   enable_interrupts(int_rb);// Interrupción por cambio de estado pines RB7:RB4
   enable_interrupts(global);
   
   set_tris_b(0xF0);
   set_tris_c(0b10000000);
   
   TX=1;// Bit de Stop = 1
   
   // Incialización de las posiciones anteriores de los encoders
 
   pos_ant_A1=Lee(A1);
   pos_ant_B1=Lee(B1);
   pos_ant_A2=Lee(A2);
   pos_ant_B2=Lee(B2);
// Son posiciones referenciales para saber el estado anterior de un pin
   
   while (1){
      // Los contadores de las articulaciones 0 y 3, son actualizados
      // a través de la interrupción de cambio de estado del puerto B
      cont_art_1=Toma_adc(0);// Lee AN0
      delay_us(5);// Espero 5 us antes de empezar una nueva conversión
      cont_art_2=Toma_adc(1);// Lee AN1
   }
}

// Desarrolo de funciones
long Toma_adc(int canal){

   long valor;

   set_adc_channel(canal);
   delay_us(20);
   Read_ADC(ADC_START_ONLY);
   delay_us(20);
   valor=read_adc(ADC_READ_ONLY);
   return(valor);
}
     
int1 Beta(int1 pos_inic, int1 pos_final){// Devuelve 1 si ocurrió una transición
                                         // Beta, en caso contrario regresa 0
   
   if ((pos_inic==1) && (pos_final==0))
   {
      return(1);
   }
   else return(0);
}

Adjunto una carpeta comprimida con winrar que contiene el cambio de pines principalmente y se puede ver que funciona, repito utilizo mi calculadora hp48gx a 9600 Baudios para mandarle y recibir datos del pic.
« Última modificación: 11 de Marzo de 2008, 23:15:08 por gu1llermo »

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Encoder ADC RS232 PIC16F877 (Programa)
« Respuesta #4 en: 12 de Marzo de 2008, 23:15:50 »
Bueno hoy hice ya las pruebas finales y definitivas, con las que me quedo, en primer lugar, decir que no pude utilizar el pic 18f4455 porque no me leía el encoder 2 que tenía conectado las señales A y B en los pines RB4 y RB5, sospecho del pin RB4 y una configuración de los fuses, estoy seguro que es eso, pero no sé como solucionarlo :?  así que volví a colocar el pic16f877a pero esta vez programé en ASM el manejo de la interrupción de cambio de estado en los pines RB7:RB4, el programa anda bien, aquí está el código definitivo:

Código: [Seleccionar]
#include <16f877a.h>
#device adc=10//La resolución del módulo A/D es de 10 bits.
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT,PUT
#use delay (clock=20000000)//20Mhz
#use RS232 (baud=19200,bits=8,parity=N,xmit=PIN_C6,rcv=PIN_C7)

#use standard_io(a)
#use fast_io(b)// Aqui están las señales del encoder
#use fast_io(c)
#use standard_io(d)
#use standard_io(e)

#byte STATUS      = 0x03
#byte PORTA       = 0x05
#byte PORTB       = 0x06
#byte PORTC       = 0x07
#byte PORTD       = 0x08
#byte PORTE       = 0x09

#bit TX           = PORTC.6// Bit de Stop

typedef struct  {

  int low  : 8;

  int high  : 8; } myWord;

// Declaración de variables
long cont_art_1=0, cont_art_2=0;// Contadores de las articulaciones 1 y 2
int  cont_art_00=0, cont_art_01=0, cont_art_30=0, cont_art_31=0;
// Contadores de las articulaciones 0 y 3, Byte bajo (X0) y Byte alto (X1)

int pos_ant, pos_act;
// Pos_iciones o lecturas ant_eriores y act_uales de las señales de los encoders

int dato;// Dato recibido via RS232

// Definiciones

// Señales de los encoders que están en el puerto B
#define A1 7// Si en un supuesto caso de elegir una secuencia incorrecta
#define B1 6// es decir que en un sentido de giro incrementa pero queremos
            // que decremente, lo que debemos hacer es intercambiar los
            // valores de A1 y B1, es decir A1=6 y B1=7, lo mismo aplica para
            // el encoder 2

#define A2 5
#define B2 4

#define W      0
#define F      1

// Bits del registro STATUS
#define Z      2// Bit Cero del registro STATUS
#define RP0    5
#define RP1    6

// Declaración de los prototipos de las funciones

long Toma_adc(int);        // Devuelve la lectura del conversor AD
                           // del canal seleccionado

// Interrupción por recepción de datos RS232
#int_rda
void rda_isr() {
  dato=getc();// Lee y almacena el dato recibido vía RS232
  switch (dato){
     
      case 0:
         putc(cont_art_00);// Mandamos el byte bajo
      break;
     
      case 1:
         putc(cont_art_01);// Mandamos el byte alto
      break;
     
      case 2:
         putc(((myWord)cont_art_1).low);
      break;
     
      case 3:
         putc(((myWord)cont_art_1).high);
      break;
     
      case 4:
         putc(((myWord)cont_art_2).low);
      break;
     
      case 5:
         putc(((myWord)cont_art_2).high);
      break;
     
      case 6:
         putc(cont_art_30);// Mandamos el byte bajo
      break;
     
      case 7:
         putc(cont_art_31);// Mandamos el byte alto
      break;
     
      case 8:// Resetea los contadores de todas las articulaciones
         cont_art_00=0;
         cont_art_01=0;
         cont_art_1=0;
         cont_art_2=0;
         cont_art_30=0;
         cont_art_31=0;
         
      break;
     
      case 9:// Resetea el contador de las articulación 0 solamente
         cont_art_00=0;
         cont_art_01=0;
      break;
     
      case 10:// Resetea el contador de las articulación 1 solamente
         cont_art_1=0;
      break;
     
      case 11:// Resetea el contador de las articulación 2 solamente
         cont_art_2=0;
      break;
     
      case 12:// Resetea el contador de las articulación 3 solamente
         cont_art_30=0;
         cont_art_31=0;
      break;
  }
}

#INT_RB
void rb_isr() {// Si al menos uno de los pines RB7:RB4 cambió de estado
               // entra aquí
   #asm
   bcf      STATUS, RP0// Seleccionamos el banco 0 para operar
   bcf      STATUS, RP1//
   
   movf     PORTB, W// Leemos el puerto B
   movwf    pos_act // Leemos la posición actual de los encoders

   // Vamos a determinar si A1 a tenido una transición Beta
   btfss    pos_ant, A1// Pregunta si el valor anterior de A1 fué 1
   goto     Procesa_B1 // En caso contrario Procesa B1
A1_fue_uno:
   btfsc    pos_act, A1// Pregunta si el valor actual de A1 es cero
   goto     Procesa_B1 // En caso contrario Procesa B1
Beta_A1:// A1 tuvo una transición Beta, de 1 --> 0
   btfss    pos_act, B1// El valor actual de B1 es 1?
   goto     Procesa_B1//
Decrementa_Art_0:// Porque B1 es uno
   decf     cont_art_00, F
   movlw    0xFF// w <-- 255
   subwf    cont_art_00, W //cont_art_00 - 255
   btfss    STATUS, Z //Z=1 => cont_art_00 = 255
   goto     Procesa_A2// Porque el valor actual de B1 es 1 y no me interesa
                      // Pasamos a analizar el próximo encoder
   movf     cont_art_01, W// Toma el byte alto
   btfsc    STATUS, Z// Z=0 => cont_art_01 <> 0
   goto     Corrige_enc_1// Si esto se cumple es porque: cont_art_01 = 0
   decf     cont_art_01, F// Decrementa slo si cont_art_01>0
   goto     Procesa_A2// Porque el valor actual de B1 es 1 y no me interesa
                      // Pasamos a analizar el próximo encoder
Corrige_enc_1:
   clrf     cont_art_00// Si cont_art_0<0 => {cont_art_0 <- 0}
   goto     Procesa_A2// Porque el valor actual de B1 es 1 y no me interesa
                      // Pasamos a analizar el próximo encoder
Procesa_B1:
   // Vamos a determinar si B1 a tenido una transición Beta
   btfss    pos_ant, B1// Pregunta si el valor anterior de B1 fué 1
   goto     Procesa_A2 // En caso contrario Procesa A2
B1_fue_uno:
   btfsc    pos_act, B1// Pregunta si el valor actual de B1 es cero
   goto     Procesa_A2 // En caso contrario Procesa A2
Beta_B1:// B1 tuvo una transición Beta, de 1 --> 0
   btfss    pos_act, A1// El valor actual de A1 es 1?
   goto     Procesa_A2//
Incrementa_Art_0:// Porque A1 es uno
   incfsz   cont_art_00, F
   goto     Procesa_A2
   incf     cont_art_01, F

Procesa_A2:
   // Vamos a determinar si A2 a tenido una transición Beta
   btfss    pos_ant, A2// Pregunta si el valor anterior de A2 fué 1
   goto     Procesa_B2 // En caso contrario Procesa B2
A2_fue_uno:
   btfsc    pos_act, A2// Pregunta si el valor actual de A2 es cero
   goto     Procesa_B2 // En caso contrario Procesa B2
Beta_A2:// A2 tuvo una transición Beta, de 1 --> 0
   btfss    pos_act, B2// El valor actual de B2 es 1?
   goto     Procesa_B2//
Decrementa_Art_3:// Porque B2 es uno
   decf     cont_art_30, F
   movlw    0xFF// w <-- 255
   subwf    cont_art_30, W //cont_art_30 - 255
   btfss    STATUS, Z //Z=1 => cont_art_30 = 255
   goto     Fin_rb_isr// Porque el valor actual de B2 es 1 y no me interesa
                      // Terminamos interrupción
   movf     cont_art_31, W// Toma el byte alto
   btfsc    STATUS, Z// Z=0 => cont_art_31 <> 0
   goto     Corrige_enc_2// Si esto se cumple es porque: cont_art_31 = 0
   decf     cont_art_31, F// Decrementa slo si cont_art_31>0
   goto     Fin_rb_isr// Porque el valor actual de B2 es 1 y no me interesa
                      // Terminamos interrupción
Corrige_enc_2:
   clrf     cont_art_30// Si cont_art_0<0 => {cont_art_0 <- 0}
   goto     Fin_rb_isr// Porque el valor actual de B2 es 1 y no me interesa
                      // Terminamos interrupción
Procesa_B2:
   // Vamos a determinar si B2 a tenido una transición Beta
   btfss    pos_ant, B2// Pregunta si el valor anterior de B2 fué 1
   goto     Fin_rb_isr //
B2_fue_uno:
   btfsc    pos_act, B2// Pregunta si el valor actual de B1 es cero
   goto     Fin_rb_isr
Beta_B2:// B2 tuvo una transición Beta, de 1 --> 0
   btfss    pos_act, A2// El valor actual de A2 es 1?
   goto     Fin_rb_isr
Incrementa_Art_3:// Porque A2 es uno
   incfsz   cont_art_30, F
   goto     Fin_rb_isr
   incf     cont_art_31, F
   
Fin_rb_isr:
 // Actualizamos las posiciones anteriores de los encoders
   movf  pos_act, W
   movwf pos_ant
   #endasm
}

void main(){
 
   setup_adc(ADC_CLOCK_DIV_32); //configura el reloj del conversor
   setup_adc_ports(ALL_ANALOG);
   enable_interrupts(int_rda);// Interrupción por recepción de datos via rs232
   enable_interrupts(int_rb);// Interrupción por cambio de estado pines RB7:RB4
   enable_interrupts(global);
   
   set_tris_b(0xF0);
   set_tris_c(0b10000000);
   
   TX=1;// Bit de Stop = 1

   #asm
   movf  PORTB, W;
   movwf pos_ant;// Lee el puerto B para tener un valor referencial
                 // del estado de las señales de los encoders
   #endasm
   
   while (1){
      // Los contadores de las articulaciones 0 y 3, son actualizados
      // a través de la interrupción de cambio de estado del puerto B
      cont_art_1=Toma_adc(0);// Lee AN0
      delay_us(5);// Espero 5 us antes de empezar una nueva conversión
      cont_art_2=Toma_adc(1);// Lee AN1
   }
}

// Desarrolo de las funciones

long Toma_adc(int canal){

   long valor;

   set_adc_channel(canal);
   delay_us(20);
   Read_ADC(ADC_START_ONLY);
   delay_us(20);
   valor=read_adc(ADC_READ_ONLY);
   return(valor);
}

Ahora si leo todas las señales, la única observación es que si el brazo robot que estoy controlando tiembla por efecto de un frenado allí obtengo unos pulsos de más, aunque no mucho pero sip, entonces una recomendación que podría dar es que si trabajan con encoders de alta resolución como el que tengo que es de 1000 pulsos por revolución además de estar acoplado al eje de giro de la articulación mediante una relación de transmisión igual a 0,21, esto quiere decir 0,21x0.36=0,0756º, ó lo que es lo mismo 1 pulso proveniente del encoder es igual a 0,0756º de rotación de la articulación, es que construyan una estructura que sea estable y firme, que no tiemble en el frenado, que sea un movimiento suave y frenado igual, sé que podía haber controlado la velocidad del motor DC mediante el PWM del pic, pero ya el circuito estaba montado con un control de velocidad de pwm utilizando un 555 así que esa variable de velocidad la ajusté a lo mínimo que pude variando el potenciómetro.

Saludos.  ;-)


 

anything