Autor Tema: Ejemplito 16F876A: Recibiendo del RS232 sobre un Buffer y procesandolo posteriormente.  (Leído 22668 veces)

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

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5544
    • Picmania by Redraven
Este nuevo ejemplito surge del trabajo preparatorio del siguiente que vamos a realizar (ya lo veréis en cuanto esté listo). Como he tenido que desarrollar un metodo de recibir comandos, con argumento (datos) añadidos, y tiene la suficiente enjundia como para publicarlo como artículo separado, he decidido hacerlo así y aquí tenéis el resultado.

Hasta ahora, en los anteriores ejemplitos 16F876A, utilizábamos comandos enviados vía RS232 consistentes en un único carácter, con el que haciamos ejecutar alguna de las funciones que nuestro programa implementaba. Este método es de muy corto alcance por dos motivos fundamentales: Primero porque  eran ejecutados inmediatamente tan pronto eran recibidos y segundo porque no podíamos enviarle datos (una cadena de caracteres) para ser procesados.

En este ejemplito vamos a solucionar exactamente eso. He imaginado un programa que admite dos comándos de alto nivel: uno de lectura "" sin argumentos y otro de escritura "w" que va seguido de un argumento tan largo como deseemos.

Para manejar el envío de ambos comandos asi como el/los argumentos necesarios he implementado lo básico para manejar el buffer en el PIC, a base de teclas únicas, cada una con su función: [INTRO] 0x0D para indicar que hemos terminado de escribir el comando, [RETROCESO] 0x08 para borrar el último caracter enviado y [ESCAPE] 0x1B para borrar todo el contenido actual del buffer. (Como podéis imaginar este miniprograma puede complicarse hasta el infinito y solo tenéis que añadir comandos a vuestra entera discreción)

El programa va entonces recibiendo caracteres, uno tras otro, y guardandolos en el Buffer. Si recibe el comando [INTRO] habilita un flag para que se procese el comando, y tras ser procesado borra el buffer y vuelve a empezar. Las otras dos teclas de [RETROCESO] y [ESCAPE] las usamos para editar el contenido del buffer antes de enviarle la orden de procesado.

He intentado comentar profusamente el programa para que sea meridianamente claro su funcionamiento y hacer así innecesarias mas explicaciones:

Codigo:

// _232_buffered.c

#include <16f876a.h>                          // Definiciones del PIC 16F876A
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT  // Los Fuses de siempre
#use delay(clock=4000000)                     // Oscilador a 4 Mhz
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)// RS232 Estándar

// CONSTANTES /////////////////////////////////////////////////////////////////

int const lenbuff=32;                  // Longitud de buffer, Ajustar
                                       // a lo que desees (o te sea posible)

// VARIABLES EN RAM ///////////////////////////////////////////////////////////

int  xbuff=0x00;                       // Índice: siguiente char en cbuff
char cbuff[lenbuff];                   // Buffer
char rcvchar=0x00;                     // último caracter recibido
int1 flagcommand=0;                    // Flag para indicar comando disponible

// Declaración de Funciones ///////////////////////////////////////////////////

void inicbuff(void);                   // Borra buffer
int  addcbuff(char c);                 // añade caracter recibido al buffer
void echos(char c);                    // Eco selectivo sobre RS232
void procesa_comando(void);            // Procesa comando

// INTERRUPCIONES /////////////////////////////////////////////////////////////

#int_rda
void serial_isr() {                    // Interrupción recepción serie USART

   rcvchar=0x00;                       // Inicializo caracter recibido                    
   if(kbhit()){                        // Si hay algo pendiente de recibir ...
      rcvchar=getc();                  // lo descargo y ...
      addcbuff(rcvchar);               // lo añado al buffer y ...
      echos(rcvchar);                  // hago eco (si procede).
   }
}

// Desarrollo de Funciones ////////////////////////////////////////////////////

void echos(char c){                    // Echo selectivo ----------------------

      switch(c){
         case 0x0D: printf(" [Ent] " ); // Si he pulsado la tecla [Intro]
                    break;
         case 0x08: printf(" [Del] " ); // Si he pulsado la tecla [Retroceso]
                    break;
         case 0x1B: printf(" [Esc] " ); // Si he pulsado la tecla [Escape]
                    break;
         default:   putc(rcvchar);     // Echo de cualquier otro caracter
      }
}

void inicbuff(void){                   // Inicia a cbuff -------------------
   int i;

   for(i=0;i<lenbuff;i++){             // Bucle que pone a 0 todos los
      cbuff[ i ]=0x00;                   // caracteres en el buffer
   }
   xbuff=0x00;                         // Inicializo el indice de siguiente
                                       // caracter
}

int addcbuff(char c){                  // Añade a cbuff -----------------------

      switch(c){
         case 0x0D:                    // Enter -> Habilita Flag para procesar
            flagcommand=1;             // Comando en Main
            break;
         case 0x08:                    // Del   -> Borra último caracter del Buffer
            if(xbuff>0) cbuff[--xbuff]=0x00;
            break;
         case 0x01B:                   // Esc   -> Borra el Buffer completamente
            inicbuff();
            break;
         default:
            cbuff[xbuff++]=c;          // Añade caracter recibido al Buffer
      }
}


// Programa Principal /////////////////////////////////////////////////////////

void main() {

   inicbuff();                                   // Borra buffer al inicio
 
   printf("
** RS232 Buffered **

" );  // Presenta menú
   printf("[Enter] Procesa comando
" );
   printf("[Escape] Borra todo el buffer
" );
   printf("[Delete] Borra último carácter del buffer
" );
   printf("[\w] Comando Escribe
" );
   printf("[\r] Comando Lee
" );
   printf("
" );

   enable_interrupts(int_rda);                   // Habilita Interrupción RDA
   enable_interrupts(global);                    // Habilita interrupciones

   do {
   
      if(flagcommand) procesa_comando();         // Si hay comando pendiente
                                                 // de procesar ... lo procesa.

   } while (TRUE);

}

// Procesador de Comandos /////////////////////////////////////////////////////

void procesa_comando(void){

   int i;
   char arg[lenbuff];                   // Argumento de comando (si lo tiene)

   flagcommand=0;                       // Desactivo flag de comando pendiente.
   printf("
Procesando ... " );       // Monitorizo procesando ...

   for(i=0;i<lenbuff;i++){             // Bucle que pone a 0 todos los
      arg[ i ]=0x00;                     // caracteres en el argumento
   }

   if(cbuff[0]=="\"&&cbuff[1]=="r"){   // Comparo inicio del buffer con comando ""
   
      printf("Leyendo ... " );           // Aqui lo que deseemos hacer con comando ""
     
   }

   if(cbuff[0]=="\"&&cbuff[ 1 ]=="w"){   // Comparo inicio del buffer con comando "w"
      i=2;
      do{                               // Extraemos argumento del buffer
         arg[i-2]=cbuff;             // a partir del 3er byte y hasta .
      }while(cbuff[++i ]!=0x00);
     
      printf("Escribiendo %s ... ",arg);// Aqui lo que deseemos hacer con comando "w"
                                        // Monitorizamos el argunmento.
   }

   inicbuff();                          // Borro buffer.

   printf("Procesado.

" );        // Monitorizo procesado.
}




Si desais descargar el programa c para realizar vuestras propias modificaciones lo podéis hacer aqui

Los resultados vistos en mi monitor de conexión serie son:






Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado xootraoox

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 248
hola amigo RedPic,
           Bueno te comento un poco....  estoy usando una parte de este codigo el cual trabaja bastante bien pero tengo un peueño problema a la hora de trabajar con numeros..... imagino obviamente que debe de ser un problema mio..... detallo un poco lo que me pasa.


  resulta que desde visual basic estoy enviando unos comandos junto con unos valores, como por ejemplo 127, bueno lo que hace esa cadena es setear una variable en el pic, con el valor 127 respectivamente, pero resulta que la variable queda siempre con valor 160, sea el valor que sea el que le envie.

tu en tu codigo colocaste una variable llamada arg, en la cual se debiera guardar respectivamente el contenido que le envio (en este caso 127) la cual es de tipo char, por lo que yo pense que podia ser ese el problema por lo que me decidi a copiar la variable arg en otra variable de tipo int, para que los caracteres ahora fuesen numeros, pero no dio resultado ya que sigue cargando cualquier valor menos el que le envio. bueno pegare el trozo de codigo para que lo puedas ver para que me des una mano si es posible ya que llevo 3 noches completas tratando de solucionar este problema sin ningun resultado.

Codigo:
if(cbuff[0]=="\"&&cbuff[1]=="B"){
      i=2;
      do{                                               // Extraemos argumento del buffer
         arg[i-2]=cbuff;                        // a partir del 3er byte y hasta .
      }while(cbuff[++i]!=0x00);
      valor=arg;                                    // donde valor es respectivamente de tipo int
      temp_duty_cycle2=valor;
      printf("-duty cycle = %u", valor);
}


desde ya quedo muy agradecido si me puedez ayudar
despide

Desconectado xootraoox

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 248
bueno este es solo para adjuntar una captura realizada con un analizador de puertos, el cual me permite ver lo que esta pasando atravez de los cables...




Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5544
    • Picmania by Redraven
Ahora no puedo mirarte este tema (no tengo mis cacharros a mano) pero en cuanto llegue a casa pruebo el sistema (que yo ya he modificado para otros asuntos) y te contesto sobre la marcha.

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado xootraoox

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 248
te lo agradeceria...... ya que esto me ha hecho salir humo de las orejasMuchas risasMuchas risas

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5544
    • Picmania by Redraven
Bueno, amigo xootraoox, vamos a ver si soy capaz de explicarme claramente para que puedas completar tu desarrollo con éxito:


Tal como está escrito este programa, y como muy bien dices en tu post, en la variable arg recibo los argumentos que acompañan al comando correspondiente.

En tu ejemplo mandas los carácteres A127[newline][return] o lo que es lo mismo los valores hexadecimales 0x5C 0x41 0x31 0x32 0x37 0x0D 0x0A cosa que corresponde al comando A con el argumento 127 teniendo en cuenta que 127 representa un entero.

Pero, y este pero es importante, tú no mandas el entero 127 sino que envias la representacion del mismo mediante un string, una cadena, de tres caracteres ASCII consecutivos, el 1 (0x31) el 2 (0x32) y el 7 (0x37)

Así en arg lo que realmente tienes es [0x31][0x32][0x37][0x00] que es un string estándar de C acabado en .

Hacer la asignación que haces de una variable de tipo entero directamente igual a una de tipo string solo puede dar un resultado indefinido.

Lo que ahora hay que realizar es la conversión de un string cuyo contenido es la representación ASCII de un valor entero a su valor entero.

Para ello me voy al Reference Manual del CCS-C y en la página número 66, como el mago que saca el conejo de su chistera, encuentro:

Codigo:

ATOI( )
ATOL( )
ATOI32()
Syntax: ivalue = atoi(string) or lvalue = atol(string) or i32value = atoi32(string)
Parameters: string is a pointer to a null terminated string of characters.
Returns: ivalue is an 8 bit int.lvalue is a 16 bit int. i32value is a 32 bit int.
Function: Converts the string pointed too by ptr to int representation. Accepts both decimal and hexadecimal argument. If the result cannot be represented, the behavior is undefined.
Availability: All devices.
Requires: #include <stdlib.h>
Examples:
char string[10];
int x;
strcpy(string,"123"Giño;
x = atoi(string);
// x is now 123



Así que ya sabes:

1º coloca arriba del todo de tú codigo el #include <stdlib.h>
2º el codigo que has escrito quedaría así: valor=atoi(arg);

y 3º compila, prueba y me cuentas.


Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5544
    • Picmania by Redraven
El post anterior era puramente teórico. Este es absolutamente real.

He realizado los cambios que te propuse:

Codigo:

...
#include <stdlib.h>
...

void procesa_comando(void){
   int i, xv;
...

   if(cbuff[0]=="\"&&cbuff[1]=="w"){   // Comparo inicio del buffer con comando "w"
      i=2;
      do{                               // Extraemos argumento del buffer
         arg[i-2]=cbuff[ i ];             // a partir del 3er byte y hasta .
      }while(cbuff[++i]!=0x00);

      xv = atoi(arg);

      printf("Escribiendo %s %u... ",arg,xv ) ; // Aqui lo que deseemos hacer con comando "w"
                                        // Monitorizamos el argunmento.
   }
...



y el resultado es ...... tatatachán



Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado xootraoox

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 248
Amigo RedPic,

          que puedo decirte..... quedo muy agradecido..... yo tratando 3 noches y tu en un instante lo resuelves....... la verdad es que pense que ese era el problema, pero no lo podia solucionar y por mas que buscaaba no me tope con atoi y itoa, aunque el codigo se elevo bastante resulto ser la solucion justa ya que la aplicacion no es muy grandey creo que me alcanzara bien en el micro que estoy usando (16f628).

despide agradecido

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5544
    • Picmania by Redraven
No, hombre, no. Ocurre que tenemos a nuestra disposición este magnifico foro.

Y he seguido de cerca el hilo Itoa y atoi en ccs donde el maestro Pocher ha desplegado su proverbial sapiencia en ayuda de humildes principiantes como nosotros.

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado pocher

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 2568
Guau, gracias maestro RedPic pero sigo aprendiendo de todos vosotros.

Desconectado I3iT

  • PIC12
  • **
  • Mensajes: 50
ummmm

Yo enviaria el 127 como un solo byte, asi "tarda" menos la comunicacion aunque en este caso no influye.

127(d)=7F(h)

Asi pues con enviar el caracter 7F desde el ordenador, al  recibirlo en el PIC lo puedes meter en un char y luego con asignarlo a un int ya tienes el 127...

---------------------------------------------------------

Me a surgido una duda, referente a una aplicación que estube haciendo con comunicaciones RS232.
Yo mandaba al PIC 4 bytes consecutivos desde un programa en VisualBasic, y funciona bien pero de vez en cuando (aleatoriamente) la USART dejaba de recibir datos... si enviaba pero no recibia mas.... y no se muy bien por que??
No empleaba la instrucción kbhit() pero no creo q sea ese el problema.
Es como si se saturara la recepción, eso que ponia la interrupcion de recepción serie la mas prioritaria y solamente guardaba los bytes recibidos en un "char array" y cuando el contador llagaba a 4 activaba un flag para tratar los datos en el main...
A alguien se le ocurre algo?

>Salu2<

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5544
    • Picmania by Redraven
Muy cierto I3iT, sería rapidísimo, un byte en lugar de tres, pero muy dificil de
escribir desde un terminal "tonto" monitor rs232 estilo Hyperterminal de
Windows o similar.

Y si lo escribiesemos en Hex transmitiríamos solo dos, pero tendríamos que
realizar la conversión de dos ASCII-hex a un byte en lugar de tres ASCII-dec
a ese mismo byte.


Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado BINARIO

  • PIC16
  • ***
  • Mensajes: 156
Hola Red pic, yo necesito algo asi pero no tan elaborado puesto que lo que tengo que hacer es muy sencillo tengo un post abierto sobre eso que aun no he podido resolver, quiero leer una trama de datos que me manda un sensor de humedad de suelo, lo manda en RS 485 yo lo convierto a rs232 con un sn75175 y lo leo en la pc y anda bien, la idea es poder procesar esa cadena de caracteres con un pic para hacer un dispositivo movil con baterias y ocupar el sensor de humedad para hacer mediciones en campo, sin embargo me he encontrado con que no me funciona bien lo que hice, por una parte tengo un tranmisor que solo imprime un valor del adc en el puerto serie, lo hace constantemente, la idea es leer ese valor e imprimirlo en un lcd que seria basicamente como funcionaria el dispositivo, este es el codigo:

#include <16F873A.h>
#device adc=10
#FUSES XT,NOWDT
#use delay(clock=4000000)

#use rs232(baud=9600, xmit=pin_c6, rcv=pin_c7))
#include <LCD420.C>

int i=0, flag;
char vector[10]= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};


#int_rda
void serial_isr()
{
 vector=getc();
 printf(lcd_putc, " <%c>", vector);
 i++;
 
 if (i==3) i=0;
 flag=1;
 
 delay_ms(100);
 //disable_interrupts(int_rda);
}

void main()
{
 int16 q;
 float p;
 int i=0;

 
 setup_adc_ports(AN0); //Canal 0 analógico
 setup_adc(ADC_CLOCK_INTERNAL); //Fuente de reloj RC
 
 enable_interrupts(global);
 enable_interrupts(int_rda);

 lcd_init();
 
 for (;;)
 {
 enable_interrupts(global);
 enable_interrupts(int_rda);
 set_adc_channel(0); //Habilitación canal0
 delay_us(10);
 q = read_adc(); //Lectura canal0
 p = 5.0 * q / 1024.0; //Conversión a tensión
 //printf(lcd_putc, "\fADC = %4ld", q);
 printf(lcd_putc, "\nVoltage = %01.2fV", p);
 delay_ms(100);
 
 /*if (flag == 1)
    {
     printf(lcd_putc, "\fDATO = %c", vector);
     delay_ms(1000);
      flag = 0;
     
      if (i==10)
          i=0;
     
    }*/
   
 printf("\r\rT0");
 delay_ms(500);
 printf("\r");
 printf("T3");
 delay_ms(500);
 printf("\r%.2f %.2f %.2f", p, p+1, p+3);
 //printf("\r0.451 0.365 1.25 12.15 13.14 %4ld %01.2f",q, p);
 delay_ms(500);
 

 printf(lcd_putc, "\fVALORES MEDIDOS\n");
 
   
 
 
 //disable_interrupts(global);
 delay_ms(500);
 

 }
}


el tema es que solo me recibe el primer caracter como si la interrupcion de puerto serie solo sucediese una vez en todo el proceso.
000101111 101110000011110 00010 11101 110 1 000111 00010010011010111100 101101001 11110000 001 00 10110 111 00001 01110 0010101 01011110 00 00011111111 0011111 011110001111111111 1011111111101100000000

Desconectado carlos.r.89

  • PIC10
  • *
  • Mensajes: 4
hola compañeros, soy egresado de ing. electronica y tengo un problemita, me eh enpolvado con este tipo de programacion ya que ultimamente solo eh utilizado PLC´s y sistemas industriales, pero trabajo en un aplicacion con un pic 18f2550 y ccs.

RedPic espero puedas ayudarme para hacer mas sencillo tu codigo.

Lo que nececito es leer una cadena de caracteres y mostrarla por un lcd.

Bueno para acabar pronto, nececito leer una cadena de aprox. 20 caracteres ejemplo:    qwertyuiopasdfghjklñ   y esto tenerlo como una sola variable ejemplo: Recibido=qwertyuiopasdfghjklñ;

me pudieras ayudar, o alguien pudiese ayudarme por favor.

tengo software originales que pudiera compartir. por favor compañeros.