Haz una pregunta
  Foros de Electrónica » Diseño digital » Microcontroladores y sistemas embebidos
Foros Registrarse ¿Olvidaste tu contraseña?

Temas similares

30/04/2014 #1


Problema con CCS (Interrupción RDA)
Hola a todos. Estoy desarollando un programa el cual envia datos por puerto serie. Resulta que como no me enviaba los datos (por problemas del compilador seguramente) decidí desinstalarlo y volverlo a instalar. Ahora tengo el MPLAB 8.87, con la ultima version de plugins para utilizar ccs y el compilador CCS V4.140.

El programa sigue sin limpiar la interrupcion serie se queda colgado en cuanto envia un primer dato por el puero serie. He probado en utilizar cleaner_interrupt(INT_RDA) pero sigue igual... En otros foros no me dan solucion.

Con ésta version de CCS no debería tener problemas no? Como puedo limpiar manualmente el bit RCIF del PIC16F88 para que el flag del puerto serie vuelva a 0?

Código:
#include <16f88.h>
#device ADC=10 //INDICO EL NÚMERO DE BITS DEL ADC

#include <stdio.h>
#include <string.h>


#fuses HS, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT
#use delay(clock=8000000)
#use rs232(baud=9600, xmit=PIN_B5, rcv=PIN_B2) //CONFIGURO PUERTO SERIE


//**VARIABLES PARA GESTIONAR LA TRAMA RECIBIDA Y LA TRAMA A ENVIAR POR PUERTO SERIE**//

long valor_adc=0; //Variable para lectura ADC

int P_W=0; //puntero escritura
int P_R=0; //puntero lectura

int i=0; //puntero bucle for
int j=0;

int inicio=0;
int final=0;

char string_valor_adc[]=",0000";//Cadena de caracteres para guardar valor ADC en Carcteres
char trama_entrada[91]={0}; //String para guardar el dato de entrada por UART
char direccion_pic[]="P01";
char tipo_dato[]="$ADQ";
char canal[]="CH0";
//char dato_salida[]="$ADQ,P01,CH0,0000\r\n"; //String para guardar lo que envio por UART
char dato[]="$ADQ,P01,CH0\r";


char midireccion[]="P01";
char micabecera[]="$ADQ";
char miretorno[]="\r";
char misimbolo[]="$";
char micanal0[]="CH0";
char micanal1[]="CH1";
char mitest[]="$TST";


short flag_final=0;
short flag_permiso=0;
short flag_tst=0;
short flag_envio=0;


void lectura(){

 for(P_R=0; P_R<91; P_R++){

  if(trama_entrada[P_R]==misimbolo[0]){

  inicio=P_R;
  
  for(P_R=inicio; P_R<91; P_R++){
 
   if(trama_entrada[P_R]==miretorno[0]){

   P_W=0;
   final=P_R;
   flag_permiso=1;
   break;

    }
 
   }
   break;
  }

 }

}


void escritura(){

for(i=inicio; i<=final; i++){ 
  
dato[j]=trama_entrada[i];
j++;

  }

for(i=0;i<91;i++){
trama_entrada[i]='\0';
}
i=0;
j=0;

return;
}


void procesamiento(){

for(i=0; i<3; i++){

direccion_pic[i]=dato[i+5];

}

direccion_pic[3]='\0';


if(strcmp(direccion_pic, midireccion)==0){

for(i=0; i<4; i++){

tipo_dato[i]=dato[i];

}

tipo_dato[4]='\0';


if(strcmp(tipo_dato, micabecera)==0){

flag_tst=0;

for(i=0; i<3; i++){

canal[i]=dato[i+9];

}

canal[3]='\0';


if(strcmp(canal,micanal0)==0){
 set_adc_channel(0);  
   }
  
  
   else if(strcmp(canal,micanal1)==0){
      set_adc_channel(1);
   }

  
delay_us(20);//retardo para leer ADC
valor_adc=read_adc();//lectura ADC

 }

else if(strcmp(tipo_dato, mitest)==0){

flag_tst=1;

}
flag_envio=1;
}
return;
}


void enviar(){

switch(flag_tst){

case 0:

    sprintf(string_valor_adc,",%04ld",valor_adc);

    output_high(PIN_B0);

    printf("$ADQ,P01,%s,%s\r", canal,string_valor_adc); //Envio un solo String con la direccion, canal CHX y el resutado del PIC por UART.

    output_high(PIN_B0);


    break;


case 1:


    output_high(PIN_B0);

    printf("$TST,P01\r"); //Envio un solo String con la direccion, canal CHX y el resutado del PIC por UART.

    output_high(PIN_B0);
    
    

    break;

}
return;
}


#INT_RDA

void INT_UART() {
if(kbhit()){
trama_entrada[P_W]=getc();

if(trama_entrada[P_W]=='\r'){
P_W=0;
flag_final=1;
}

else{
P_W++;
}

} 

}
void main(){

output_low(PIN_B0);

enable_interrupts(GLOBAL); //HABIlLITO TODAS LAS INTERRUPCIONES GLOBALES
enable_interrupts(INT_RDA); //HABILITO LA INTERRUPCIÓN UART
setup_adc_ports(sAN0|sAN1|sAN2|sAN3|sAN4|sAN5); //INDICO EL PIN A0/A1/A2/A3/A4/A5 COMO ENTRADA ANALÓGICA 
setup_adc(ADC_CLOCK_INTERNAL); //CLOCK INTERNO PARA CONVERSIÓN ADC

 while(1){
if(flag_final==1){
flag_final=0;
 lectura();

if(flag_permiso==1){
 flag_permiso=0;
 escritura();
 procesamiento();
if(flag_envio==1){
flag_envio=0;
enviar();
}
  }
 }
}

}
30/04/2014 #2


Wever20 dijo: Ver Mensaje
Hola a todos. Estoy desarollando un programa el cual envia datos por puerto serie. Resulta que como no me enviaba los datos (por problemas del compilador seguramente) decidí desinstalarlo y volverlo a instalar. Ahora tengo el MPLAB 8.87, con la ultima version de plugins para utilizar ccs y el compilador CCS V4.140.

El programa sigue sin limpiar la interrupcion serie se queda colgado en cuanto envia un primer dato por el puero serie. He probado en utilizar cleaner_interrupt(INT_RDA) pero sigue igual... En otros foros no me dan solucion.

Con ésta version de CCS no debería tener problemas no? Como puedo limpiar manualmente el bit RCIF del PIC16F88 para que el flag del puerto serie vuelva a 0?

Código:
#include <16f88.h>
#device ADC=10 //INDICO EL NÚMERO DE BITS DEL ADC

#include <stdio.h>
#include <string.h>


#fuses HS, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT
#use delay(clock=8000000)
#use rs232(baud=9600, xmit=PIN_B5, rcv=PIN_B2) //CONFIGURO PUERTO SERIE


//**VARIABLES PARA GESTIONAR LA TRAMA RECIBIDA Y LA TRAMA A ENVIAR POR PUERTO SERIE**//

long valor_adc=0; //Variable para lectura ADC

int P_W=0; //puntero escritura
int P_R=0; //puntero lectura

int i=0; //puntero bucle for
int j=0;

int inicio=0;
int final=0;

char string_valor_adc[]=",0000";//Cadena de caracteres para guardar valor ADC en Carcteres
char trama_entrada[91]={0}; //String para guardar el dato de entrada por UART
char direccion_pic[]="P01";
char tipo_dato[]="$ADQ";
char canal[]="CH0";
//char dato_salida[]="$ADQ,P01,CH0,0000\r\n"; //String para guardar lo que envio por UART
char dato[]="$ADQ,P01,CH0\r";


char midireccion[]="P01";
char micabecera[]="$ADQ";
char miretorno[]="\r";
char misimbolo[]="$";
char micanal0[]="CH0";
char micanal1[]="CH1";
char mitest[]="$TST";


short flag_final=0;
short flag_permiso=0;
short flag_tst=0;
short flag_envio=0;


void lectura(){

 for(P_R=0; P_R<91; P_R++){

  if(trama_entrada[P_R]==misimbolo[0]){

  inicio=P_R;
  
  for(P_R=inicio; P_R<91; P_R++){
 
   if(trama_entrada[P_R]==miretorno[0]){

   P_W=0;
   final=P_R;
   flag_permiso=1;
   break;

    }
 
   }
   break;
  }

 }

}


void escritura(){

for(i=inicio; i<=final; i++){ 
  
dato[j]=trama_entrada[i];
j++;

  }

for(i=0;i<91;i++){
trama_entrada[i]='\0';
}
i=0;
j=0;

return;
}


void procesamiento(){

for(i=0; i<3; i++){

direccion_pic[i]=dato[i+5];

}

direccion_pic[3]='\0';


if(strcmp(direccion_pic, midireccion)==0){

for(i=0; i<4; i++){

tipo_dato[i]=dato[i];

}

tipo_dato[4]='\0';


if(strcmp(tipo_dato, micabecera)==0){

flag_tst=0;

for(i=0; i<3; i++){

canal[i]=dato[i+9];

}

canal[3]='\0';


if(strcmp(canal,micanal0)==0){
 set_adc_channel(0);  
   }
  
  
   else if(strcmp(canal,micanal1)==0){
      set_adc_channel(1);
   }

  
delay_us(20);//retardo para leer ADC
valor_adc=read_adc();//lectura ADC

 }

else if(strcmp(tipo_dato, mitest)==0){

flag_tst=1;

}
flag_envio=1;
}
return;
}


void enviar(){

switch(flag_tst){

case 0:

    sprintf(string_valor_adc,",%04ld",valor_adc);

    output_high(PIN_B0);

    printf("$ADQ,P01,%s,%s\r", canal,string_valor_adc); //Envio un solo String con la direccion, canal CHX y el resutado del PIC por UART.

    output_high(PIN_B0);


    break;


case 1:


    output_high(PIN_B0);

    printf("$TST,P01\r"); //Envio un solo String con la direccion, canal CHX y el resutado del PIC por UART.

    output_high(PIN_B0);
    
    

    break;

}
return;
}


#INT_RDA

void INT_UART() {
if(kbhit()){
trama_entrada[P_W]=getc();

if(trama_entrada[P_W]=='\r'){
P_W=0;
flag_final=1;
}

else{
P_W++;
}

} 

}
void main(){

output_low(PIN_B0);

enable_interrupts(GLOBAL); //HABIlLITO TODAS LAS INTERRUPCIONES GLOBALES
enable_interrupts(INT_RDA); //HABILITO LA INTERRUPCIÓN UART
setup_adc_ports(sAN0|sAN1|sAN2|sAN3|sAN4|sAN5); //INDICO EL PIN A0/A1/A2/A3/A4/A5 COMO ENTRADA ANALÓGICA 
setup_adc(ADC_CLOCK_INTERNAL); //CLOCK INTERNO PARA CONVERSIÓN ADC

 while(1){
if(flag_final==1){
flag_final=0;
 lectura();

if(flag_permiso==1){
 flag_permiso=0;
 escritura();
 procesamiento();
if(flag_envio==1){
flag_envio=0;
enviar();
}
  }
 }
}

}
Yo también estuve batallando con eso, y no hay otra forma que hacer un getc después de la interrupción para que se limpie la interrupción!, no has intentado asi?

Código:
#include <16f88.h>
#device ADC=10 //INDICO EL NÚMERO DE BITS DEL ADC

#include <stdio.h>
#include <string.h>


#fuses HS, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT
#use delay(clock=8000000)
#use rs232(baud=9600, xmit=PIN_B5, rcv=PIN_B2) //CONFIGURO PUERTO SERIE


//**VARIABLES PARA GESTIONAR LA TRAMA RECIBIDA Y LA TRAMA A ENVIAR POR PUERTO SERIE**//

long valor_adc=0; //Variable para lectura ADC

int P_W=0; //puntero escritura
int P_R=0; //puntero lectura

int i=0; //puntero bucle for
int j=0;
int1 hay_dato;
int inicio=0;
int final=0;

char string_valor_adc[]=",0000";//Cadena de caracteres para guardar valor ADC en Carcteres
char trama_entrada[91]={0}; //String para guardar el dato de entrada por UART
char direccion_pic[]="P01";
char tipo_dato[]="$ADQ";
char canal[]="CH0";
//char dato_salida[]="$ADQ,P01,CH0,0000\r\n"; //String para guardar lo que envio por UART
char dato[]="$ADQ,P01,CH0\r";


char midireccion[]="P01";
char micabecera[]="$ADQ";
char miretorno[]="\r";
char misimbolo[]="$";
char micanal0[]="CH0";
char micanal1[]="CH1";
char mitest[]="$TST";


short flag_final=0;
short flag_permiso=0;
short flag_tst=0;
short flag_envio=0;


void lectura(){

 for(P_R=0; P_R<91; P_R++){

  if(trama_entrada[P_R]==misimbolo[0]){

  inicio=P_R;
  
  for(P_R=inicio; P_R<91; P_R++){
 
   if(trama_entrada[P_R]==miretorno[0]){

   P_W=0;
   final=P_R;
   flag_permiso=1;
   break;

    }
 
   }
   break;
  }

 }

}


void escritura(){

for(i=inicio; i<=final; i++){ 
  
dato[j]=trama_entrada[i];
j++;

  }

for(i=0;i<91;i++){
trama_entrada[i]='\0';
}
i=0;
j=0;

return;
}


void procesamiento(){

for(i=0; i<3; i++){

direccion_pic[i]=dato[i+5];

}

direccion_pic[3]='\0';


if(strcmp(direccion_pic, midireccion)==0){

for(i=0; i<4; i++){

tipo_dato[i]=dato[i];

}

tipo_dato[4]='\0';


if(strcmp(tipo_dato, micabecera)==0){

flag_tst=0;

for(i=0; i<3; i++){

canal[i]=dato[i+9];

}

canal[3]='\0';


if(strcmp(canal,micanal0)==0){
 set_adc_channel(0);  
   }
  
  
   else if(strcmp(canal,micanal1)==0){
      set_adc_channel(1);
   }

  
delay_us(20);//retardo para leer ADC
valor_adc=read_adc();//lectura ADC

 }

else if(strcmp(tipo_dato, mitest)==0){

flag_tst=1;

}
flag_envio=1;
}
return;
}


void enviar(){

switch(flag_tst){

case 0:

    sprintf(string_valor_adc,",%04ld",valor_adc);

    output_high(PIN_B0);

    printf("$ADQ,P01,%s,%s\r", canal,string_valor_adc); //Envio un solo String con la direccion, canal CHX y el resutado del PIC por UART.

    output_high(PIN_B0);


    break;


case 1:


    output_high(PIN_B0);

    printf("$TST,P01\r"); //Envio un solo String con la direccion, canal CHX y el resutado del PIC por UART.

    output_high(PIN_B0);
    
    

    break;

}
return;
}


#INT_RDA

void INT_UART() {
trama_entrada[P_W]=getc();
hay_dato=1;
}

void main(){

output_low(PIN_B0);

enable_interrupts(GLOBAL); //HABIlLITO TODAS LAS INTERRUPCIONES GLOBALES
enable_interrupts(INT_RDA); //HABILITO LA INTERRUPCIÓN UART
setup_adc_ports(sAN0|sAN1|sAN2|sAN3|sAN4|sAN5); //INDICO EL PIN A0/A1/A2/A3/A4/A5 COMO ENTRADA ANALÓGICA 
setup_adc(ADC_CLOCK_INTERNAL); //CLOCK INTERNO PARA CONVERSIÓN ADC

while(1){
if (hay_dato==1){
hay_dato=0;

if(trama_entrada[P_W]=='\r'){
P_W=0;
flag_final=1;
}

else{
P_W++;
}
}

if(flag_final==1){
flag_final=0;
 lectura();

if(flag_permiso==1){
 flag_permiso=0;
 escritura();
 procesamiento();
if(flag_envio==1){
flag_envio=0;
enviar();
}
  }
 }
}

}
30/04/2014 #3
Moderador

Avatar de D@rkbytes

Wever20 dijo: Ver Mensaje
¿Cómo puedo limpiar manualmente el bit RCIF del PIC16F88 para que el flag del puerto serie vuelva a 0?
No uses KBHIT, ya estás usando la interrupción por AUSART y KBHIT ya no es necesario.
Toma únicamente los datos con GETC o GETS.

Como comentario:
Tu programa corre perfectamente, no se traba dentro de la interrupción.
Usa un LED testigo dentro del bucle while para darte cuenta si sigue corriendo el programa principal.

Puedes usar clear_interrupt (INT_RDA); pero no es necesario.
También puedes declarar el bit RCIF usando esto:
#byte PIR1 = getenv ("SFR:PIR1")
#bit RCIF = PIR1.5

Pero como te menciono, durante debug el bit RCIF permanece en 0 y siempre sale de la interrupción.

Suerte.
30/04/2014 #4


¿¿Cuando fuerzas otra interrupción sigue entrando bien??
30/04/2014 #5
Moderador

Avatar de D@rkbytes

Wever20 dijo: Ver Mensaje
¿¿Cuando fuerzas otra interrupción sigue entrando bien??
Sip, no existe problema, el programa siempre sigue corriendo tras una interrupción.
De hecho el flag RCIF se limpia cuando se lee el registro RCREG y el flag TXIF cuando se escribe TXREG.

Prueba con este sencillo programa y verás que no existe problema durante la interrupción.
Código:
#include <16f88.h>
#fuses   NOBROWNOUT
#use     delay (internal = 8MHz)
#use     RS232(UART1)         // AUSART a 9600bps por defecto.

#INT_RDA
void AUSART_ISR (void)
{char b_data;
   
   b_data = getc();           // Guardar el dato de recepción en b_data
   putc(b_data);              // Se reenvía como eco.
   
   if (b_data == 'a')         // Si b_data es la letra "a"...
      output_high(pin_b3);    // RB3 = 1
      
   else                       // Cualquier otro dato diferente a "a"
      output_low(pin_b3);     // RB3 = 0
}


void main (void)
{
   setup_oscillator(OSC_8MHz | OSC_STATE_STABLE);  // oscilador interno estable a 8MHz.
   enable_interrupts(INT_RDA);   // Configurar interrupción por AUSART
   enable_interrupts(GLOBAL);    // Habilitar interrupciones.
   
   output_low(pin_a1);           // RA1 en 0
   output_low(pin_b3);           // RB3 en 0
   
   while (true)
   {
      output_toggle(pin_a1);  // Hacer parpadear un LED por RA1 (LED testigo)
      delay_ms(500);
   }
}
Si llegas a tener problemas por no salir de la interrupción, entonces si tienes problemas con el PCWHD.

Suerte.
01/05/2014 #6


Una pregunta de arduino sabes algo? es que quizás el problema sea este ya que comuniqué más de una vez el pic con el arduino DUE que funciona a 3,3v y el pic da 5v quizás he roto algo del chip del arduino DUE que hace que no pueda recibir bien los datos o no pueda enviarlos bien no?

Luego depsués de un reset para un PIC16F88 debería éste ejecutar la rutina de atención a la interrupción serie? Yo tengo puesto que se encienda un led cuando entra en la interrupcion serie (inicialemnte en el void main lo puse en apagado) y justo despues de un reset entra en ella. sé que entra en la interrupcion serie porque el led realiza el delay que le indico. Puede ser que el pic ande mal?
01/05/2014 #7
Moderador

Avatar de D@rkbytes

Wever20 dijo: Ver Mensaje
Una pregunta de arduino sabes algo? es que quizás el problema sea este ya que comuniqué más de una vez el pic con el arduino DUE que funciona a 3,3v y el pic da 5v quizás he roto algo del chip del arduino DUE que hace que no pueda recibir bien los datos o no pueda enviarlos bien no?
Nop, nunca he pensado trabajar con algún tipo de arduino.
Wever20 dijo: Ver Mensaje
quizás el problema sea este ya que comuniqué más de una vez el pic con el arduino DUE que funciona a 3,3v y el pic da 5v
El PIC16F88 puede trabajar en un rango de voltajes desde 2.0V hasta 5.5V.
Si alimentas ambos a 3.3V todo debe funcionar sin problemas.

Wever20 dijo: Ver Mensaje
Luego después de un reset para un PIC16F88 debería éste ejecutar la rutina de atención a la interrupción serie?
Sip, un reset reinicia el programa, lo que no se reinicia son los valores de las variables, por eso se deben cargar con un valor inicial al principio del programa.
Wever20 dijo: Ver Mensaje
Yo tengo puesto que se encienda un led cuando entra en la interrupción serie (inicialmente en el void main lo puse en apagado) y justo después de un reset entra en ella.
Sé que entra en la interrupción serie porque el led realiza el delay que le indico.
¿Puede ser que el pic ande mal?
Puede ser que estés teniendo un problema con la frecuencia de trabajo del oscilador.
Este PIC tiene varias frecuencias de trabajo para el oscilador interno.

En tu programa estás usando el oscilador HS para trabajar a 8MHz y esa frecuencia también se puede alcanzar con el oscilador interno.
Pero este PIC cuenta con un sistema llamado Fail Safe Clock Monitor.
Este se encarga de detectar posibles fallas en el oscilador externo, y si está activo por medio del fuse FCMEN entonces cuando el oscilador externo falla, se activa el oscilador interno.
Entonces el programa seguirá corriendo pero a otra frecuencia dependiendo de la configuración del registro OSCCON o de los valores que tome en el POR (Power On Reset)

Dices que el tiempo de encendido del LED es el que tú le estás dando, pero a veces te puedes confundir.
Verifica bien que en realidad el PIC esté trabajando a la velocidad de 8MHz que requieres.
Si puedes utiliza otro PIC para que salgas de dudas en el caso que el PIC esté dañado.

Suerte.
01/05/2014 #8


Es que estoy sospechando que puede ser que el pic esté dañado ya que algunas veces cominiqué dos PIC entre ellos pero me equivoqué y puse el RX de uno en el RX del otro y el TX de uno en el TX del otro. Quizás esto sea un posible mal para el PIC.

Luego le he puesto un oscilador externo de 8Mhz pero los condensadores son de 20pF (no los 27pF que pide el datasheet..) Así que quizás sea una de éstas dos cosas.

Muchas gracias por tu respuesta.
01/05/2014 #9
Moderador

Avatar de D@rkbytes

Wever20 dijo: Ver Mensaje
Es que estoy sospechando que puede ser que el pic esté dañado ya que algunas veces comuniqué dos PIC entre ellos pero me equivoqué y puse el RX de uno en el RX del otro y el TX de uno en el TX del otro. Quizás esto sea un posible mal para el PIC.
Por eso te puse ese programa de prueba para que vieras físicamente el funcionamiento del PIC.
Esa prueba con el hyperterminal es muy sencilla y puedes verificar recepción y transmisión.

No creo que el PIC se haya dañado por conectar inversamente Tx y Rx, pero con ese programa lo sabrás.
Wever20 dijo: Ver Mensaje
Luego le he puesto un oscilador externo de 8Mhz pero los condensadores son de 20pF (no los 27pF que pide el datasheet.) Así que quizás sea una de éstas dos cosas.
Ese valor no es muy critico, son 7pF de diferencia por rama que te darán una ligera desviación de la frecuencia.
Lo que si tienes que verificar es que realmente sí esté funcionando el oscilador a cristal.
En todo caso prueba con el interno a 8MHz.
01/05/2014 #10


Bueno amigo la interrupción cuando le envío un caracter sí funciona. Creo que puede ser problema de la placa arduino que no envía correctamente los datos o del micro porque cuando le envío una frase de más de un byte (14 bytes) se queda como "colgado".

Por cierto sabes el MPLAB 8.87 junto con el compilador CCS 4.140 es buena opción para programar pics?

Muchas gracias por tu ayuda D@arkbytes!!
01/05/2014 #11
Moderador

Avatar de D@rkbytes

Wever20 dijo: Ver Mensaje
Bueno amigo la interrupción cuando le envío un caracter sí funciona. Creo que puede ser problema de la placa arduino que no envía correctamente los datos o del micro porque cuando le envío una frase de más de un byte (14 bytes) se queda como "colgado".
Si tienes problemas cuando envías muchos bytes, entonces debes aumentar la frecuencia de reloj para que puedas también aumentar la velocidad transmisión.
Por ejemplo, a 16 o 20MHz con 19200 o 28800Bps.
Wever20 dijo: Ver Mensaje
Por cierto sabes el MPLAB 8.87 junto con el compilador CCS 4.140 es buena opción para programar pics?
Yo prefiero usar el entorno de desarrollo que viene con cada lenguaje de programación.
El MPLAB lo uso únicamente para los lenguajes de programación de Microchip.
A muchos le gusta usar los plug-ins para usar MPLAB con otros lenguajes, yo en lo personal, no.
Tal vez para hacer uso de los debuggers, pero cada entorno tiene el suyo también.
A final de cuentas al compilar un programa de CCS con MPLAB se usa el PCM Compiler, no el MPASM.
Wever20 dijo: Ver Mensaje
Muchas gracias por tu ayuda D@rkbytes!!
De nada, suerte.
04/05/2014 #12


Problema con CCS (Interrupción RDA)
Pasa que estoy haciendo una alarma para una materia de la universidad que se llama Sistemas Digitales 3 y la alarma funciona bien, pero cuando le agrego la rutina de de servicio de la interrupcion del puerto serie simulada funciona bien pero cuando la implemento no funciona se traba y sin esa rutina funciona perfectamente tanto implementada como simulada, les agrego el codigo para ver si me pueden apoyar:



Código:
#include &lt;16F887.h&gt;                                          
 #FUSES INTRC_IO,NOWDT,NOBROWNOUT
   //Internal RC Osc, no CLKOUT
 #use delay(clock=4000000)
 #use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
 #include &lt;input.c&gt;
 #include &lt;stdlib.h&gt;
 #include &lt;string.h&gt;
 //#INCLUDE&lt;TONES.C&gt;
 #include &lt;flex_lcd.c&gt;  
#include &lt;kbd2.c&gt; 
#USE FAST_IO(B)
#USE FAST_IO(D)
#rom 0x2100={\'1\',\'2\',\'3\',\'4\'}
#define CRISTAL  4000000 
#define FRECUENCIA_TIMER1 (CRISTAL / 4)  // 1 tick = frecuencia del cristal/4
#DEFINE TIEMPO 0X00
int l=0,b;
unsigned int zone=0;
char zonas[8];
INT1 FLAGR=0;
INT J=0;                //CONTADORES PARA EL AREA DE PASSWORD
INT i=0;
//int fc=0;
int8 Timesirena=0;

INT ESTADO,PASSOK;
int time;
CHAR K;
CHAR DATO[4],CLAVE[4],CADENA[7];
ENUM ESTADOS{DESARMADO,ARMANDO,CAMPASS,ARMADO,DISPARADA,DESARMANDO};
CONST CHAR NUMEROS[10]={\'0\',\'1\',\'2\',\'3\',\'4\',\'5\',\'6\',\'7\',\'8\',\'9\'};
VOID PTOB();
//////////////////////////////////////////////////////////////////////////////
//          INTERRUPCION TMR0                                     //
/////////////////////////////////////////////////////////////////////////////
#INT_TIMER0
VOID TIMER0_ISR()
{


OUTPUT_TOGGLE(PIN_E1);
SET_TIMER0(170);

}





//////////////////////////////////////////////////////////////////////////////
//           TERMINA ISR TMR0                                      //
/////////////////////////////////////////////////////////////////////////////

#INT_RDA
ISR_SERIE()
{

GET_STRING(CADENA,7);
IF(CADENA[0]==\'N\' &amp;&amp; CADENA[1]==\'P\' &amp;&amp; CADENA[2]!=0 &amp;&amp; CADENA[3]!=0 &amp;&amp; CADENA[4]!=0 &amp;&amp; CADENA[5]!=0 )
 {
 WRITE_EEPROM(0X2100,CADENA[2]);
 WRITE_EEPROM(0X2101,CADENA[3]);
 WRITE_EEPROM(0X2102,CADENA[4]);
 WRITE_EEPROM(0X2103,CADENA[5]);
 LCD_PUTC(&quot;\\fPASS CAMB /SERIE&quot;);
 PRINTF(&quot;CONTRASEÑA CAMBIADA\\n\\r&quot;);
 DELAY_MS(1000);
 //OUTPUT_A(0XFF);
 }
 IF(ESTADO==DESARMADO &amp;&amp; CADENA[0]==\'A\' &amp;&amp; CADENA[1]==\'R\' &amp;&amp; CADENA[2]==\'M\' &amp;&amp; CADENA[3]==\'A\' &amp;&amp; CADENA[4]==\'R\'  )
 {
 
 LCD_PUTC(&quot;\\f ARMANDO&quot;);
 FLAGR=1;

 }
 IF((ESTADO==DISPARADA || ESTADO==ARMADO) &amp;&amp; CADENA[0]==\'D\' &amp;&amp; CADENA[1]==\'E\' &amp;&amp; CADENA[2]==\'S\' &amp;&amp; CADENA[3]==\'A\' &amp;&amp; CADENA[4]==\'R\' &amp;&amp; CADENA[5]==\'M\'&amp;&amp; CADENA[6]==\'A\'  )
 {
 
 LCD_PUTC(&quot;\\f DESARMANDO&quot;);
 FLAGR=1;

 }
 IF(ESTADO==DESARMADO &amp;&amp; CADENA[0]==\'E\' &amp;&amp; CADENA[1]==\'N\' &amp;&amp; CADENA[2]==\'T\' &amp;&amp; CADENA[3]==\'R\' &amp;&amp; CADENA[4]==\'A\' &amp;&amp; CADENA[5]==\'D\'&amp;&amp; CADENA[6]==\'A\'  )
 {
 
 
 FLAGR=1;
 PTOB();

 }
 }
 //////////////////////////////////////////////////////////////////////////////
//           TERMINA ISR PTO SERIE                                           //
/////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
//           CONFIG TMR1                                                    //
/////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//           TERMINA CONFIG TMR1                                            //
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//           CONFIFURACION                                                  //
/////////////////////////////////////////////////////////////////////////////
 void configuracion()
 {
 lcd_init();
 kbd_init();
 SET_TRIS_B(0XFF);
 SET_TRIS_E(0X00);
 OUTPUT_LOW(PIN_E1);
ENABLE_INTERRUPTS(GLOBAL);
ENABLE_INTERRUPTS(INT_RDA);
SETUP_TIMER_0(RTCC_INTERNAL|RTCC_DIV_16);
SET_TIMER0(TIEMPO);

 
 }
 //////////////////////////////////////////////////////////////////////////////
//           TERMINA CONFIGURACION                                      //
/////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
//           INICIO                                                         //
/////////////////////////////////////////////////////////////////////////////
void inicio()
{
lcd_putc(&quot;INICIANDO&quot;);
//delay_ms(1000);
lcd_putc(&quot;\\fINICIANDO.&quot;);
//delay_ms(1000);
lcd_putc(&quot;\\fINICIANDO..&quot;);
//delay_ms(1000);
lcd_putc(&quot;\\fINICIANDO...&quot;);
//delay_ms(1000);
lcd_putc(&quot;\\fINICIANDO....&quot;);
//delay_ms(1000);
}
//////////////////////////////////////////////////////////////////////////////
//           TERMINA INICIO                                                 //
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//           ESCANEO PUERTO B                                              //
/////////////////////////////////////////////////////////////////////////////
VOID PTOB()
{
















  IF(INPUT(PIN_B0)==0 )
   {

   LCD_GOTOXY(1,2);
   PRINTF(LCD_PUTC,&quot;%C&quot;,NUMEROS[1]);
   IF(ESTADO==DISPARADA || ESTADO==ARMADO)
   PRINTF(&quot;ZONA %C ABIERTA\\n\\r&quot;,NUMEROS[1]);

   }
    else
   {
   LCD_GOTOXY(1,2);
   LCD_PUTC(&quot; &quot;);
   }

   IF(INPUT(PIN_B1)==0)
   {
   LCD_GOTOXY(3,2);
   PRINTF(LCD_PUTC,&quot;%C&quot;,NUMEROS[2]);
   IF(ESTADO==DISPARADA || ESTADO==ARMADO)
   PRINTF(&quot;ZONA %C ABIERTA\\n\\r&quot;,NUMEROS[2]);
   }
   else
   {
   LCD_GOTOXY(3,2);
   LCD_PUTC(&quot; &quot;);
   }
   IF(INPUT(PIN_B2)==0)
   {
   LCD_GOTOXY(5,2);
   PRINTF(LCD_PUTC,&quot;%C&quot;,NUMEROS[3]);
   IF(ESTADO==DISPARADA || ESTADO==ARMADO)
   PRINTF(&quot;ZONA %C ABIERTA \\n\\r&quot;,NUMEROS[3]);
   }
    else
   {
   LCD_GOTOXY(5,2);
   LCD_PUTC(&quot; &quot;);
   }
   IF(INPUT(PIN_B3)==0)
   {
   LCD_GOTOXY(7,2);
   PRINTF(LCD_PUTC,&quot;%C&quot;,NUMEROS[4]);
   IF(ESTADO==DISPARADA || ESTADO==ARMADO)
   PRINTF(&quot;ZONA %C ABIERTA\\n\\r&quot;,NUMEROS[4]);
   }
    else
   {
   LCD_GOTOXY(7,2);
   LCD_PUTC(&quot; &quot;);
   }
   IF(INPUT(PIN_B4)==0)
   {
   LCD_GOTOXY(9,2);
   PRINTF(LCD_PUTC,&quot;%C&quot;,NUMEROS[5]);
   IF(ESTADO==DISPARADA || ESTADO==ARMADO)
   PRINTF(&quot;ZONA %C ABIERTA\\n\\r&quot;,NUMEROS[5]);
   }
    else
   {
   LCD_GOTOXY(9,2);
   LCD_PUTC(&quot; &quot;);
   }
   IF(INPUT(PIN_B5)==0)
   {
   LCD_GOTOXY(11,2);
   PRINTF(LCD_PUTC,&quot;%C&quot;,NUMEROS[6]);
   IF(ESTADO==DISPARADA || ESTADO==ARMADO)
   PRINTF(&quot;ZONA %C ABIERTA\\n\\r&quot;,NUMEROS[6]);
   }
    else
   {
   LCD_GOTOXY(11,2);
   LCD_PUTC(&quot; &quot;);
   }
   IF(INPUT(PIN_B6)==0)
   {
   LCD_GOTOXY(13,2); 
   PRINTF(LCD_PUTC,&quot;%C&quot;,NUMEROS[7]);
   IF(ESTADO==DISPARADA || ESTADO==ARMADO)
   PRINTF(&quot;ZONA %C ABIERTA\\n\\r&quot;,NUMEROS[7]);
   }
    else
   {
   LCD_GOTOXY(13,2);
   LCD_PUTC(&quot; &quot;);
   }
   IF(INPUT(PIN_E2)==0)
   {
   LCD_GOTOXY(15,2);
   PRINTF(LCD_PUTC,&quot;%C&quot;,NUMEROS[8]);
   IF(ESTADO==DISPARADA || ESTADO==ARMADO)
   PRINTF(&quot;ZONA %C ABIERTA\\n\\r&quot;,NUMEROS[8]);
   }
    else
   {
   LCD_GOTOXY(15,2);
   LCD_PUTC(&quot; &quot;);
   }
}

//////////////////////////////////////////////////////////////////////////////
//           TERMINA ESCANEO PUERTO B                                     //
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//          SISTEMA DESARMADO                                              //
/////////////////////////////////////////////////////////////////////////////
VOID SISTEMADESARMADO()
{
while(1)
{
lcd_gotoxy(1,2);
   IF(INPUT(PIN_B0) &amp;&amp; INPUT(PIN_B1) &amp;&amp; INPUT(PIN_B2) &amp;&amp; INPUT(PIN_B3) &amp;&amp; INPUT(PIN_B4) &amp;&amp; INPUT(PIN_B5) &amp;&amp; INPUT(PIN_B6) &amp;&amp; INPUT(PIN_E2))
   {
   lcd_putc(&quot;             &quot;);
   lcd_command(cursor_home);
   LCD_PUTC(&quot;PUEDE ARMAR&quot;);
   
      DO
      {
      
      K=kbd_getc();
         IF(k==\'A\')
         {  
         ESTADO=ARMANDO;
         
         RETURN;
         } 
         ELSE IF (K==\'B\')
         {
         ESTADO=CAMPASS;
        
         return;
         }
      }  WHILE(K==0 &amp;&amp; INPUT(PIN_B0) &amp;&amp; INPUT(PIN_B1) &amp;&amp; INPUT(PIN_B2) &amp;&amp; INPUT(PIN_B3) &amp;&amp; INPUT(PIN_B4) &amp;&amp; INPUT(PIN_B5) &amp;&amp; INPUT(PIN_B6) &amp;&amp; INPUT(PIN_E2) &amp;&amp; FLAGR==0);
      IF(FLAGR==1)
      {
      ESTADO=ARMADO;
      FLAGR=0;
      RETURN;
      }
   RETURN;
   }
   ELSE
   {
   PTOB();
   lcd_putc(&quot;               &quot;);
   lcd_command(cursor_home);
   }
}
}

//////////////////////////////////////////////////////////////////////////////
//           TERMINA SISTEMA DESARMADO                                      //
/////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//           CONTRASEÑA                                      //
/////////////////////////////////////////////////////////////////////////////
void pass()
{

lcd_putc(&quot;\\fINGRESE CLAVE&quot;);


while(i&lt;4)
         {
            k=kbd_getc(); // leer la tecla presiono 
            if(k!=0)
            {
                     dato[i]=k; 
                     i++;
                     lcd_gotoxy(i,2); // posicion i de la segunda linea 
                     lcd_putc(\'*\');
            }
         }
         
 for(i=0;i&lt;4;i++)
 clave[i]=read_eeprom(i); //guarda la constraseña  en el vector clave 
   
if( dato[0]==clave[0] &amp;&amp;  dato[1]==clave[1] &amp;&amp; dato[2]==clave[2] &amp;&amp; dato[3]==clave[3])
{  
      lcd_putc(&quot;\\fClave Correcta! &quot;);
      lcd_putc(&quot;\\nAdelante!!  &quot;);
      PASSOK=1;
      i=0;
      return;
}
   else
   {
   
     lcd_putc(&quot;\\fClave incorecta &quot;);
     lcd_putc(&quot;\\nINTENTELO DE NUEVO&quot;);
     delay_ms(500);
     lcd_putc(&quot;\\f&quot;);
     i=0;
     j++;
     PASSOK=0;
      
      if(j==3)
      {
        lcd_putc(&quot;\\fTecladoBloqueado &quot;);
        lcd_putc(&quot;\\n      -_-  &quot;);
        delay_ms(60000);
        j=0;
      }
    }
}
//////////////////////////////////////////////////////////////////////////////
//           TERMINA COMPROBACION CONTRASEÑA                                //
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//           CAMBIO CONTRASEÑA                                              //
/////////////////////////////////////////////////////////////////////////////
void cambiopass()
{
if(i!=0)
i=0;
if(J!=0)
{
 j=0;
 return;
}

lcd_putc(&quot;\\fINGRESE NUEVO PASS&quot;);
while(i&lt;4){

            k=kbd_getc(); // leer la tecla presiono 
            if(k!=0){
                     dato[i]=k; 
                     i++;
                     lcd_gotoxy(i,2); // posicion i de la segunda linea 
                     lcd_putc(\'*\');
                    }
         }
 for(i=0;i&lt;4;i++)
   write_eeprom(0x2100+i,dato[i]);
   i=0;
   return;
}
VOID ESCANEO()
{
   LCD_PUTC(&quot;\\fSISTEMA ARMADO&quot;);
      DO
      {
      
      K=kbd_getc();
       IF(K==\'A\')
         {
         ESTADO=DESARMANDO;
         }
         ELSE
         {
         ESTADO=DISPARADA;
         }
      }WHILE(K==0 &amp;&amp; INPUT(PIN_B0) &amp;&amp; INPUT(PIN_B1) &amp;&amp; INPUT(PIN_B2) &amp;&amp; INPUT(PIN_B3) &amp;&amp; INPUT(PIN_B4) &amp;&amp; INPUT(PIN_B5) &amp;&amp; INPUT(PIN_B6) &amp;&amp; INPUT(PIN_E2) &amp;&amp; FLAGR==0);
    IF(FLAGR==1)
    {
    FLAGR=0;
    ESTADO=DESARMADO;
    }
      
}
//////////////////////////////////////////////////////////////////////////////
//           TERMINA CAMBIO CONTRASEÑA                                      //
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//           INICIA SIRENA                                                  //
/////////////////////////////////////////////////////////////////////////////
VOID SIRENA ()                //LISTO //EN ESTA FUNCION SIEMPRE SE ESTAN MONITOREANDO LAS ENTRADAS 
{           
   
   ENABLE_INTERRUPTS(INT_TIMER0);             
   LCD_PUTC(&quot;\\fDISPARADA&quot;);         
   LCD_GOTOXY(1,2);
      DO
      {
       
      
       K=kbd_getc();
       PTOB();
       
      
      
       
         }WHILE(k!=\'A\' &amp;&amp; FLAGR==0);
         IF(FLAGR==1)
         {
         
         ESTADO=DESARMADO;
         FLAGR=0;
         RETURN;
         }
         
         ESTADO=DESARMANDO;
         
         
} 
     
  //////////////////////////////////////////////////////////////////////////////
//           TERMINA SIRENA                                                  //
/////////////////////////////////////////////////////////////////////////////

void main()
{


PRINTF(&quot;INICIANDO \\n&quot;);
configuracion();
inicio();
ESTADO=DESARMADO;
WHILE(1)
      {
               switch(ESTADO)
               {
                  case DESARMADO:    
                     OUTPUT_LOW(PIN_E1);
                     disable_interrupts(int_timer0);
                     lcd_command(CURSOR_HOME);
                     lcd_putc(&quot;DESARMADO        &quot;);
                     //delay_ms(50);
                     SISTEMADESARMADO();
                     BREAK;
                  case ARMANDO:
                     PASS();
                     IF(PASSOK==1)
                     {
                     ESTADO=ARMADO;
                     PASSOK=0;
                     }
                     ELSE
                     
                     BREAK;
                  case ARMADO:
                       PRINTF(&quot;ARMADA\\n\\r&quot;);
                       ESCANEO();
                       BREAK;
                  case DISPARADA:
                       SIRENA();
                       BREAK;
                  case DESARMANDO:
                       
                        PASS();
                        IF(PASSOK==1)
                        {
                        
                        
                        ESTADO=DESARMADO;
                        PRINTF(&quot;DESARMADA\\n\\r&quot;);
                        LCD_GOTOXY(1,2);
                         LCD_PUTC(&quot;SISTEMA DESARMADO&quot;);  
                         DELAY_MS(1500);
                          LCD_GOTOXY(1,2);
                        LCD_PUTC(&quot;                    &quot;); 
                        PASSOK=0;
                        }
                        BREAK;
                  case CAMPASS:
                        pass();                       
                        IF(PASSOK==1)
                        {
                        CAMBIOPASS();
                        ESTADO=DESARMADO;
                         }
                        ELSE
                        ESTADO=DESARMADO;
                        BREAK;                    
               }
               
      }


}
...
05/05/2014 #13
Moderador

Avatar de D@rkbytes

felipeduarte1088 dijo: Ver Mensaje
les agrego el codigo para ver si me pueden apoyar
Es mejor que adjuntes tu programa incluyendo librerías, dentro de un archivo comprimido, porque contiene código basura que agrega el navegador y lo hace casi incompresible.
Si tienes la simulación también es necesario que la incluyas.

Saludos.
05/05/2014 #14


D@rkbytes me resulta muy extraño que el pic NUEVO (recien comprado) cuando lo programo solo entra una sola vez a la interrupción! Es que me ocurre exactamente lo mismo... Hay alguna manera de saber si se trata del CCS que tengo o el MPLAB o alguna configuracion que no estoy haciendo bien?? Es que me estoy volviendo absoltamente loco... debería funcionar y no lo hace! :(((((
05/05/2014 #15
Moderador

Avatar de D@rkbytes

Wever20 dijo: Ver Mensaje
D@rkbytes me resulta muy extraño que el pic NUEVO (recién comprado) cuando lo programo solo entra una sola vez a la interrupción! Es que me ocurre exactamente lo mismo. ¿Hay alguna manera de saber si se trata del CCS que tengo o el MPLAB o alguna configuración que no estoy haciendo bien? Es que me estoy volviendo absolutamente loco.
Debería funcionar y no lo hace! :(((((
Pues podrías realizar un programa sencillo en otro lenguaje.
¿Ya probaste con el programa que puse?
05/05/2014 #16


D@rkbytes dijo: Ver Mensaje
Pues podrías realizar un programa sencillo en otro lenguaje.
¿Ya probaste con el programa que puse?
Tu programa en un pic me funcionó pero en éste ya no me funciona es curioso. También debo decirte que el pic lo acabo de conectar unos segundos al revés porque me he confundido... quizás lo he roto.

Otra pregunta, qué otro lengaje puedo utilizar? yo el entorno de trabajo que domino es MPLAB y el programador utilizo el ICD2
06/05/2014 #17
Moderador

Avatar de D@rkbytes

Wever20 dijo: Ver Mensaje
Tu programa en un pic me funcionó pero en éste ya no me funciona es curioso.
¿Lo probaste con otro PIC16F88 o en otro PIC diferente?

Wever20 dijo: Ver Mensaje
También debo decirte que el pic lo acabo de conectar unos segundos al revés porque me he confundido... Quizás lo he roto.
Probablemente.
Puedes realizar una secuencia de comprobación de todos los pines para probar su funcionamiento.
Por hardware puedes verificar el estado con LED's o una punta lógica.
Wever20 dijo: Ver Mensaje
Otra pregunta, ¿qué otro lenguaje puedo utilizar? Yo el entorno de trabajo que domino es MPLAB y el programador utilizo el ICD2
Está bien que uses C, pero debes utilizar el lenguaje del que más tengas conocimientos.
Si conoces PICBasic te recomiendo Proton IDE.
Todos los lenguajes de alto nivel tienen sus pros y sus contras, programando en ensamblador tardarás más en hacer un programa, pero entenderás muchas cosas y sobre todo, cómo funcionan los registros del PIC.
Mira este sencillo ejemplo adjunto en ensamblador para que te des una idea.
El programa está 100% garantizado de que funciona físicamente.
También con este programa puedes comprobar el funcionamiento del AUSART.

Suerte.
Archivos Adjuntos
Tipo de Archivo: rar 16F88 AUSART.rar (28,8 KB (Kilobytes), 27 visitas)
07/05/2014 #18


D@rkbytes he probado tu programa y todo anda OK. Mi programa ahora utilizando las funciones PRINTF, SPRINTF y STRCAT anda bien. Resulta que el problema era la version del CCS. La versión en la que han arreglado todo ésto es a partir de la 5.013.

Tengo otro pequeño o gran problema. Resulta que cuando envio primeramente un dato de solicitud del estilo "$ADQ,P01,CH0\r" o "$ADQ,P01,CH1\r" y luego un dato de solicitud del estilo "$TST,P01\r" SOLO ME RESPONDE SIEMPRE al primer dato. Y si lo hago al revés primero le envio "$TST,P01\r" y luego "$ADQ,P01,CHx\r" solo me hace el primero. Y siempre responde solo al primer tipo de dato que le envio. Si luego le envio otro tipo de dato ya no responde.

A qué puede ser debido? Si te vuelvo a dar el código podrías ayudarme a solucionar ésto? Yo ya no veo cual es el fallo amigo...

La solucion que he probado es meterle un reset_cpu() una vez envio los datos y entonces va bien. Pero no es la mejor solucion esta...
07/05/2014 #19


Tal ves se satura el buffer de recepcion y se queda trabado el Pic no has probado limpiarlo despues de recibir algunos caracteres?
07/05/2014 #20


luis30 dijo: Ver Mensaje
Tal ves se satura el buffer de recepcion y se queda trabado el Pic no has probado limpiarlo despues de recibir algunos caracteres?
El buffer de recepcion siempre lo estoy leyendo. En principio cuando leo caracteres el buffer se limpia no? Hay alguna intruccion que permite limpiarlo cuando se desee? Hay algo raro porque cuando hago un reset todo anda OK.
¿Tienes una mejor respuesta a este tema? ¿Quieres hacerle una pregunta a nuestra comunidad y sus expertos? Registrate

Foros de Electrónica » Diseño digital » Microcontroladores y sistemas embebidos

Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO ©2011, Crawlability, Inc.