Autor Tema: Teoría y praxis de las comunicaciones serie TTL con los PIC's 16F/18F en CCS C  (Leído 11771 veces)

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

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5362
    • Picmania by Redraven
Introducción

Una y otra vez vemos consultas, consultas y consultas sobre este tema y leemos respuestas, respuestas y respuestas a esas consultas, tan variadas como variados son los amigos que responden y las características concretas que se plantan en la consulta:

Desde la mas simple emisión o recepción de un sólo carácter por el PIC, con o sin el uso de interrupciones, hasta el tratamiento de búferes lineales o cíclicos, comandos de control simples o complejos, unidireccionales o interactivos, y que incluyen parámetros de todo tipo, color y sabor. Y planteado para comunicar PIC's con PC's, PIC's con PIC's ó PIC's con casi cualquier otro tipo de dispositivo, usando niveles TTL, RS232, RS485 ...

Sobre este tema hay innumerables hilos abiertos con absolutamente todo lo necesario para realizar casi cualquier cosa que podamos imaginar, usando el buscador del Foro y teniendo la paciencia necesaria para leer y rebuscar entre los distintos post este artículo sería totalmente innecesario.

Exclusiones

Lo que no voy a tratar son los hardware's necesarios para comunicar distintos dispositivos según los distintos protocolos y/o niveles. Desde el punto de vista de un PIC es totalmente indiferente si se comunica con un PC o con otro PIC y es absolutamente igual si lo hace en RS232 con el uno que en TTL con el otro.

Propósito

Abro este hilo en un intento por clarificar conceptos, organizarlos y mostrarlos de la forma mas clara y sencilla posible, unificarlos y crear una jerarquía de menos a más según sea lo que necesitemos creciendo en complejidad.

Pero sin quedarme sólo en la teoría sino que además deseo proponer ejemplos prácticos listos para usar, profusamente comentados línea a línea para que sea posible "leer" el programa, seguir su ejecución, mediante los comentarios añadidos. Con todos los ejemplos escritos, compilados y probados sobre un PIC para certificar su correcto funcionamiento.

En este artículo voy a desarrollar, en lenguaje C variante CCS, las técnicas y conceptos necesarios para emitir y recibir con un PIC de las series 16F y 18F (por la gran similitud que hay entre ellas, tanta que solo va a ser necesario cambiar el #include del modelo de PIC concreto y los #defines de los pines reales conecatados a la USART). Vamos a presentar desde el mas simple echo hasta un completo tratamiento de comandos con parámetros de distinto tipo.

Metodología

Voy a ir escribiendo post cerrados y completos. Cada uno con sus respectivos Objetivos, Implementación en C y su Programa Fuente completo funcionando.

Y como he descrito anteriormente avanzando en complejidad e intentando que estos "saltos" de lo sencillo a lo complejo se vayan haciendo paulatinamente, paso a paso, y así poder describir esos avances de forma que sean acumulables para los amigos que empiezan y no se vean en la obligación de "tragar" mas de lo que son capaces de comprender.

Condiciones del Test

Todos los programas los voy a escribir en CCS C, versión 3.242, y van a ser probados sobre un PIC 18F4550 con un cristal de 20 Mhz con el PLL activado para generar un ciclo de reloj de 48 Mhz, conectado a un PC mediante una interface hardware RS232 construida alrededor de un MAX232, transmitiendo y recibiendo a 115200 baudios, 8 bits, 1 bit de Stop y sin Paridad (none) . Por ello los fuses que apareceran an los ejemplos y la definición CCS del canal Serie va a ser:

Código: C#
  1. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2. // Definiciones de configuración
  3. ///////////////////////////////////////////////////////////////////////////////////////////////////
  4.  
  5. #include <18f4550.h>
  6.  
  7. ///////////////////////////////////////////////////////////////////////////////////////////////////
  8. // Fuses y ajuste de Clock
  9. ///////////////////////////////////////////////////////////////////////////////////////////////////
  10.  
  11. #fuses HSPLL, NOMCLR, PUT, BROWNOUT, BORV43, NOWDT, NOPROTECT, NOLVP
  12. #fuses NODEBUG, USBDIV, PLL5, CPUDIV1, VREGEN, CCP2B3
  13. #use delay(clock=48000000)
  14.  
  15. ///////////////////////////////////////////////////////////////////////////////////////////////////
  16. // Canal de Comunicación : usart
  17. ///////////////////////////////////////////////////////////////////////////////////////////////////
  18.  
  19. #define TTL_TX PIN_C6
  20. #define TTL_RX PIN_C7
  21.  
  22. #use rs232(baud=115200, xmit=TTL_TX, rcv=TTL_RX)
  23.  
  24.  

Esta parte va a ser común a todos los ejemplos y es la única, salvo que expresamente se diga lo contrario, que es necesario adaptar a cada uno de los distintos modelos y condiciones de vuestro PIC en particular. Si esta cabecera corresponde con vuestro PIC todos los ejemplos que tratemos deben funcionar correctamente desde el principio.

Así si nuestro PIC es un 16F876A, con un cristal de 4 Mhz y transmitiendo y recibiendo a 9600 baudios la cabecera debería ser:
Código: C#
  1. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2. // Definiciones de configuración
  3. ///////////////////////////////////////////////////////////////////////////////////////////////////
  4.  
  5. #include <16f876a.h>
  6.  
  7. ///////////////////////////////////////////////////////////////////////////////////////////////////
  8. // Fuses y ajuste de Clock
  9. ///////////////////////////////////////////////////////////////////////////////////////////////////
  10.  
  11. #fuses XT,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT
  12. #use delay(clock=4000000)
  13.  
  14. ///////////////////////////////////////////////////////////////////////////////////////////////////
  15. // Canal de Comunicación : usart
  16. ///////////////////////////////////////////////////////////////////////////////////////////////////
  17.  
  18. #define TTL_TX PIN_C6
  19. #define TTL_RX PIN_C7
  20.  
  21. #use rs232(baud=9600, xmit=TTL_TX, rcv=TTL_RX)
  22.  
  23.  

Como véis las modificaciones son mínimas. El resto lo dejamos para cada uno de los artículos que tratemos.

Espero que os guste y os sirva.  :mrgreen:

« Última modificación: 28 de Septiembre de 2008, 15:33:32 por RedPic »
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado stk500

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 4735
Re: Teoría y praxis de las comunicaciones serie TTL con los PIC's 16F/18F en CCS C
« Respuesta #1 en: 28 de Septiembre de 2008, 06:02:25 »
Muchisimas gracias Amigo Diego, yo me apunto como lector de tu grande obras como siempre, seguire tus Teoria.
 Saludo

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5362
    • Picmania by Redraven
Teoría y praxis de las comunicaciones serie TTL : 1.- El mas simple "echo"
« Respuesta #2 en: 28 de Septiembre de 2008, 06:21:18 »
Título : 1.- El mas simple "echo"

Objetivos

Lo mas simple que se puede hacer con un PIC en esto de las comunicaciones es recibir un carácter y devolverlo a modo de eco. Sin manos, sin pies y casi sin bicicleta, esperamos a recibir algo e inmediatamente lo reenviamos a su destino.

Implementación en C

Para implementarlo en C lo que vamos a hacer es montar nuestro sempiterno bucle infinito del While(true) y dentro de él vamos a utilizar las funciones built-in de C llamadas kbhit() que devuelve true cuando hay algo pendiente de recibir (comprueba si está en alto el bit 5 RCIF del registro PIR1), getc() que obtiene, "lee", el byte recibido (descarga RCREG) y putc() que emite un byte por la USART (escribe TXREG).

Podemos describir lo que vamos a hacer de la forma: Espera mientras te avisen que hay algo pendiente de recibir, que cuando llegue lo lees y lo vuelves a enviar, y así hasta el infinito y mas allá. 

Programa Fuente

Recuerda que la cabecera es la misma para todos los programas y la tienes descrita en el primer post de esta serie.

Código: C#
  1. void main() {
  2.  
  3.    char rec;
  4.  
  5.    printf("TyP_Serie_TTL\r\n");   // Al inicio escribe para ver que emite correctamente
  6.    do{                            // Bucle ...
  7.       if(kbhit()){                //  Si hay algo pendiente de recibir ....
  8.          rec=getc();              //  recibe el caracter ...
  9.          putc(rec);               //  ... y lo escribes ...
  10.       }                           // ... después continúa ...
  11.    }while(TRUE);                  // ... hasta el infinito.
  12. }
  13.  

Al inicio he incluido una línea de la forma  printf("TyP_Serie_TTL\r\n"); para ver que emite correctamente nada mas darle alimentación al PIC.



Realmente la variable rec de tipo char que hemos escrito no es necesaria ya que las líneas :

Código: C#
  1.          rec=getc();              //  recibe el carácter ...
  2.          putc(rec);               //  ... y lo escribes ...
  3.  

las podemos escribir en una sola de la forma:

Código: C#
  1.          putc(getc());              //  escribe lo que recibas ...
  2.  

Continuará.  :mrgreen:
« Última modificación: 28 de Septiembre de 2008, 15:04:36 por RedPic »
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5362
    • Picmania by Redraven
Re: Teoría y praxis de las comunicaciones serie TTL : 2.- Un "echo" interrupto
« Respuesta #3 en: 28 de Septiembre de 2008, 10:39:16 »
Título : 2.- Un  "echo" interrupto  mediante la interrupción por recepción serie

Objetivos

No tan simple como nuestro anterior ejemplo pero casi. Esta vez vamos a hacer que el eco se produzca de igual forma que anteriormente pero haciendo uso de la interrupción por recepción serie, INT_RDA, que se nos va a disparar cuando nuestro PIC reciba un carácter por dicho canal.

Implementación en C

Para implementarlo en C lo que vamos a hacer es simplemente mover la recepción y reenvio del carácter desde el bucle infinito en el main(), que es donde lo teníamos antes, a una rutina especial que solo se ejecutará cada vez que se reciba un carácter por la USART. Recordad que la función escrita a continuación de la directiva #int_rda será la que se ejecute cuando se dispare dicha interrupción. De ahí que le haya llamado rda_handler o manejador de la interrupción RDA.

Podemos describir lo que vamos a hacer de la forma: Haz lo que te de la gana en el main() porque cuando te avisen de que hay algo pendiente de recibir saldrás de allí y en la rutina de la interrupción lo lees y lo vuelves a enviar, devolviéndote adonde estabas en cuanto termines. 

Programa Fuente

Recuerda que la cabecera es la misma para todos los programas y la tienes descrita en el primer post de esta serie.

Código: C#
  1. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2. // INTERRUPCIONES : RDA Recepción USART
  3. ///////////////////////////////////////////////////////////////////////////////////////////////////
  4. #int_rda
  5. void rda_handler(void){
  6.       putc(getc());              //  recibe el caracter y lo reenvía...
  7. }
  8. ///////////////////////////////////////////////////////////////////////////////////////////////////
  9. // MAIN
  10. ///////////////////////////////////////////////////////////////////////////////////////////////////
  11. void main() {
  12.  
  13.    printf("TyP_Serie_TTL\r\n");   // Al inicio escribe para ver que emite correctamente
  14.    printf("Method : Interrupt\r\n");
  15.  
  16.    enable_interrupts(int_rda);    // Habilitamos la interrupción por recepción serie
  17.    enable_interrupts(global);     // Habilitamos las interrupciones
  18.  
  19.    do{                            // Bucle ...
  20.  
  21.       // Aqui no tenemos nada que hacer ...
  22.       // pero podemos hacer lo que queramos.
  23.  
  24.  
  25.    }while(TRUE);                  // ... hasta el infinito.
  26. }
  27.  

Como podéis ver los resultados son idénticos al ejemplo anterior, pero con la inmensa diferencia de que en el bucle infinito del main() podemos escribir cualquier otro código que haga cualquier otra cosa con la seguridad de que al recibir un carácter lo tendremos disponible..



Continuará.  :mrgreen:

« Última modificación: 28 de Septiembre de 2008, 15:32:21 por RedPic »
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5362
    • Picmania by Redraven
Re: Teoría y praxis de las com serie TTL : 3.- Comandos de un solo carácter
« Respuesta #4 en: 28 de Septiembre de 2008, 15:30:59 »
Título : 3.- Comandos de un solo carácter

Objetivos

Entramos ya en terrenos con una utilidad directa. En este tercer ejemplo tenemos la intención de recibir comandos desde fuera del PIC y ejecutar distintas funciones según sea lo recibido. Pero además vamos a tener un pequeño "feed-back" para poder saber qué es lo que está recibiendo el PIC, de forma que el PIC nos devuelva el comando recibido y los resultados de la función que se ejecuta, o algo que nos indique que el comando es erróneo.

Implementación en C

Lo que vamos a hacer es declarar una variable de tipo char en la RAM a la que llamaremos Command, que inicializaremos a cero 0x00 en el main() y sobre la que recogeremos lo que recibamos por el canal serie con la interrupción INT_RDA, tal como vimos en el ejemplo anterior. En el main(), dentro del bucle infinito While(true), comprobaremos en cada vuelta si el contenido de Command es distinto de de 0x00 (que cordad que lo inicializamos con este valor) ya que entonces significaría que si hemos recibido algo. Entonces lo primero que haremos será monitorizar esta recepción mediante un printf y después usando un switch ejecutaremos la función seleccionada. En caso de no ser uno de los que hemos contemplado en el switch devolveremos una señal en tal sentido. En cualquier caso volveremos a inicializar a 0x00 la variable Command para no volver a ejecutar la misma función hasta no recibir de nuevo el comando correspondiente.

Programa Fuente

Recuerda que la cabecera es la misma para todos los programas y la tienes descrita en el primer post de esta serie.

Código: C#
  1. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2. // RAM
  3. ///////////////////////////////////////////////////////////////////////////////////////////////////
  4.  
  5. char Command;
  6.  
  7. ///////////////////////////////////////////////////////////////////////////////////////////////////
  8. // INTERRUPCIONES : RDA Recepción USART
  9. ///////////////////////////////////////////////////////////////////////////////////////////////////
  10.  
  11. #int_rda
  12. void rda_handler(void){
  13.  
  14.    Command=getc();              //  recibe el comando ...
  15.  
  16. }
  17.  
  18. // Funciones a ejecutar según el comando recibido /////////////////////////////////////////////////
  19.  
  20. void first_function(void){
  21.  
  22.    printf(" > Exec first_function()\r\n");
  23. }
  24.  
  25. void second_function(void){
  26.  
  27.    printf(" > Exec second_function()\r\n");
  28. }
  29.  
  30. ///////////////////////////////////////////////////////////////////////////////////////////////////
  31.  
  32. void main() {
  33.  
  34.    printf("TyP_Serie_TTL\r\n");   // Al inicio escribe para ver que emite correctamente
  35.    printf("Method : Interrupt\r\n");
  36.    printf("Commands mono-character\r\n");
  37.  
  38.    enable_interrupts(int_rda);    // Habilitamos la interrupción por recepción serie
  39.    enable_interrupts(global);     // Habilitamos las interrupciones
  40.  
  41.    Command=0x00;                  // Inicializamos Command para estar seguros de que
  42.                                   // su contenido es 0x00
  43.  
  44.    do{                            // inicio del Bucle infinito
  45.  
  46.       if(Command!=0x00){          // Si hemos recibido un comando ...
  47.          printf(" < %c\r\n",Command);  // Lo mostramos de vuelta para saber que ha sido recibido ...
  48.          switch(Command){
  49.             case '1':             // y si éste es el caracter ASCII '1' entonces ...
  50.                first_function();  // llamamos a la primera funcion.
  51.                break;
  52.             case '2':             // pero si éste es el caracter ASCII '2' entonces ...
  53.                second_function(); // llamamos a la segunda funcion.
  54.                break;
  55.             default:
  56.                printf(" < ?\r\n");// y si no es ninguno de los anteriores protestamos ...
  57.                break;
  58.          }
  59.          Command=0x00;            // y volvemos a inicializar Command para que no
  60.                                   // se vuelva a ejecutar hasta volver a recibirlo.
  61.       }
  62.  
  63.    }while(TRUE);                  // final del Bucle infinito
  64. }
  65.  
  66. // Fin del programa  //////////////////////////////////////////////////////////////////////////////
  67.  

Un detalle importante a tener en cuenta con este modo de tratar los comandos recibidos: Aunque la USART no pierda ningún carácter que pueda enviársele solo se ejecutarán aquellos que sean recibidos fuera de la ejecución de una de las funciones asociadas a ellos, y esto es debido a que tras la ejecución de cualquiera de ellas ponemos a 0x00 la variable Command, por eso cualquier cosa recibida durante la ejecución no se ejecutará.



Continuará.  :mrgreen:

« Última modificación: 28 de Septiembre de 2008, 16:07:38 por RedPic »
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado MLO__

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 4582
Re: Teoría y praxis de las comunicaciones serie TTL con los PIC's 16F/18F en CCS C
« Respuesta #5 en: 28 de Septiembre de 2008, 23:12:54 »
Hola.

Muy explicativo como siempre maestro. Sigo atento a tus post. Gracias

Saludos
El papel lo aguanta todo

Desconectado jeremylf

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1338
Re: Teoría y praxis de las comunicaciones serie TTL con los PIC's 16F/18F en CCS C
« Respuesta #6 en: 06 de Octubre de 2008, 13:52:56 »
Muy pero muy explicativo  :mrgreen:

Muchas gracias.

Desconectado beno

  • PIC10
  • *
  • Mensajes: 1
Re: Teoría y praxis de las comunicaciones serie TTL con los PIC's 16F/18F en CCS C
« Respuesta #7 en: 07 de Octubre de 2008, 00:04:01 »
Gracias por la informacion me ha sido muy util
bye

Desconectado AKENAFAB

  • Colaborador
  • DsPIC30
  • *****
  • Mensajes: 3095
    • Automation Media Lab
Re: Teoría y praxis de las comunicaciones serie TTL con los PIC's 16F/18F en CCS C
« Respuesta #8 en: 07 de Octubre de 2008, 02:11:22 »
Hey muy bueno !

Concreto ,conciso y directo   :mrgreen:


Una pregunta :

1.-¿ Porque me da error el compilador al querer manejar una velocidad superior a los 19200?

Esto es con el pic16F628A,oscilador interno,unas prácticas previas.

No entiendo , pues en el datasheet se va lejos ocn velocidades muy superiores.

Saludos!

Desconectado MLO__

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 4582
Re: Teoría y praxis de las comunicaciones serie TTL con los PIC's 16F/18F en CCS C
« Respuesta #9 en: 07 de Octubre de 2008, 19:07:27 »
Hola.

Eso es porque con la velocidad de oscilacion del reloj interno del PIC no se puede alcanzar esa velocidad de transmision.

PD/: no es recomendable usar comunicacion serial cuando se usa oscilador interno (y mucho menos a altas velocidades) ya que fallan debido al error que hay en esos osciladores. Te recomiendo coloques un cristal para el oscilador, o bajes a unos 300 baudios la comunicacion.

Saludos
El papel lo aguanta todo

Desconectado AKENAFAB

  • Colaborador
  • DsPIC30
  • *****
  • Mensajes: 3095
    • Automation Media Lab
Re: Teoría y praxis de las comunicaciones serie TTL con los PIC's 16F/18F en CCS C
« Respuesta #10 en: 08 de Octubre de 2008, 01:04:12 »

Gracias amigo!

Eso lo tengo en cuenta, pero como habia realizado comunicacion en assembly ,no tenia ese problema, pero regresando al datasheet,será que di una velocidad con un error brutal , que en este caso a 4MHz coincidia con muy pocas velocidades de transmision.El error era de 0.16 -+ el 10% del osc interno.

Ademas, vi el manual y el unico donde daria un error de 0%  es a 250 000 bauds.Los demás no eran soportados.

Mala mia!

Un saludo y muchisimas gracias!



Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5362
    • Picmania by Redraven
Teoría y praxis de las com serie TTL : 4.1.- Recibiendo sobre un Buffer 1ª Parte
« Respuesta #11 en: 23 de Noviembre de 2008, 09:21:48 »
Título : 4.1.- Recibiendo sobre un Buffer. 1ª Parte

Objetivos

Las más de las veces todo lo que hemos visto hasta ahora de Comandos de Un Solo Carácter se nos queda corto, ya sea porque necesitamos comandos mas largos, usando palabras en lugar de solo una letra, ya sea porque necesitamos argumentos, o sea datos añadidos a nuestro comandos que deben ser procesados a la llegada de éstos. Todo esto no es posible montarlo procesando uno a uno los caracteres que le vamos enviando al PIC.

Hay que hacerlo recibiendo y acumulando en la memoria del PIC todo aquello que necesitamos y enviando como último carácter uno especial que dispare su procesado. Esto es lo que se conoce como "recibir sobre un buffer" y es lo que vamos a estudiar aquí y ahora: Recibir caracteres sobre un buffer, uno a uno, y procesarlo cuando se reciba uno en concreto, yo siempre uso 0x0D [Retorno de Carro] o sea al pulsar la telca [Enter].

Implementación en C

El primer recurso que necesitamos es el Buffer. En C nuestro buffer va a ser una matriz (un arreglo) de caracteres, también conocido como string, o sea n caracteres consecutivos en la memoria del PIC, de forma que el primer carácter que recibamos lo guardemos en la primera posición del buffer, la 0, el segundo en la siguiente, la 1, y así sucesivamente hasta que le enviemos el carácter especial de procesado o que se nos acabe el buffer porque hayamos alcanzado su final.

Muy importante es la longitud máxima que vamos a darle a nuestro buffer, o sea el máximo número de caracteres que va a tener, tanto para reservar la memoria RAM correspondiente como para detectar si hemos alcanzado el final del mismo y no podemos seguir recibiendo sobre él so pena de sobrescribir otras zonas de la memoria del PIC. Así que vamos a definir una constante con esta Longitud Máxima del buffer que utilizaremos después para estos dos cometidos: Declarar el buffer en la RAM y controlar la recepción máxima de caracteres sobre él.

Además necesitamos un recurso añadido que nos diga a qué posición debe guardarse el siguiente carácter que se reciba. Esto lo vamos a hacer declarando en RAM una variable de tipo entero que inicialmente pondremos a 0 de forma que al recibir el primer carácter lo guardemos en esa posición e incrementamos nuestro índice, así al llegar el siguiente lo guardaremos en 1 y volveremos a incrementar el índice. ¿Hasta cuando? pues hasta recibir el carácter especial de final o llegar al final físico declarado con la constante anterior.

Esta declaración y definición de nuestro buffer en CCS C se hace de la siguiente forma:

Código: C#
  1. ///////////////////////////////////////
  2. // Constantes
  3. ///////////////////////////////////////
  4.  
  5. int const lenbuff=32; // Longitud máxima del buffer
  6.  
  7. ///////////////////////////////////////
  8. // RAM
  9. ///////////////////////////////////////
  10.  
  11. int  xbuff=0x00;     // Índice: siguiente char en cbuff
  12. char cbuff[lenbuff]; // Buffer de recepción
  13.  

Con lenbuff definimos la máxima longitud del buffer que lo usamos al declarar el propio buffer en cbuff[lenbuff] y utilizaremos xbuff como índice del mismo, poniéndolo a cero para comenzar e incrementando su valor cada vez que guardemos un carácter en el buffer.

Tenemos entonces ya lo fundamental, que es el "receptáculo" donde guardar lo recibido y los recursos para ello. Tenemos ahora que recibirlo y guardarlo efectivamente.

Para ello vamos a utilizar la Interrupción por Recepción Serie que vimos en Comandos de un solo carácter pero que en aquel caso era un simple command=getc() y en el que nos ocupa va a ser un mucho mas complejo add_2_cbuff() que es una función donde vamos ha hacer todo lo necesario para recoger el carácter recibido y dejar el buffer preparado para el siguiente carácter por recibir.

La interrupción RDA y el añadir lo recibido al Buffer podría quedar de esta forma:
Código: C#
  1. ///////////////////////////////////////
  2. // INTERRUPCIONES : RDA Recepción USART
  3. ///////////////////////////////////////
  4.  
  5. #int_rda
  6. void serial_isr() {          // Interrupción recepción serie USART
  7.  
  8.    char rcvchar=0x00;        // último caracter recibido
  9.  
  10.    if(kbhit()){              // Si hay algo pendiente de recibir ...
  11.       rcvchar=getc();        // lo descargo y ...
  12.       add_2_cbuff(rcvchar);  // lo añado al buffer
  13.    }
  14. }
  15.  
  16. void add_2_cbuff(char c){
  17.  
  18.       switch(c){
  19.          case 0x0D:  // Enter -> Habilita Flag para procesar comando
  20.             flagcommand=1;
  21.             break;
  22.          default:    // Añade caracter recibido al Buffer
  23.             cbuff[xbuff++]=c;
  24.       }
  25. }
  26.  

Como veis en el código anterior distinguimos en lo recibido si es cualquier cosa o es el carácter especial 0x0D en cuyo caso ponemos un flag en alto.

Pero le vamos a dar un vuelta de tuerca más.  :mrgreen:

Ya que estamos interactuando con nuestro PIC vamos a hacerlo de forma mas completa. Por un lado vamos a implementar alguna forma de feed-back, algo que nos diga cómo va nuestro buffer llenándose, y algo mas de control sobre él. Por ejemplo una tecla para borrar el último carácter recibido y poder así corregir un error de digitación o incluso una tecla especial para borrar todo el buffer almacenado hasta el momento. Así convertiremos las teclas Backspace y Escape, códigos ASCII 0x08 y 0x1B resprectivamente, en teclas especiales también junto a nuestro anterior 0x0D.

Introducimos entonces una nueva función a la que vamos a llamar echo_sel() por "Eco selectivo" para monitorizar lo que estamos enviándole al PIC y también vamos a modificar nuestra add_2_cbuff() anterior para contemplar las nuevas posibilidades de borrado del buffer.

La función echo_sel() la vamos a llamar después de guardar lo recibido en el buffer y así nos irá informando de cómo está el buffer y de si hemos pulsado alguna de las teclas especiales, mostrándonos el resultado sobre el buffer.

El código queda entonces de la siguiente forma:
Código: C#
  1. ///////////////////////////////////////
  2. // INTERRUPCIONES : RDA Recepción USART
  3. ///////////////////////////////////////
  4.  
  5. #int_rda
  6. void serial_isr() {          // Interrupción recepción serie USART
  7.  
  8.    char rcvchar=0x00;        // último caracter recibido
  9.  
  10.    if(kbhit()){              // Si hay algo pendiente de recibir ...
  11.       rcvchar=getc();        // lo descargo y ...
  12.       add_2_cbuff(rcvchar);  // lo añado al buffer y ...
  13.       echo_sel(rcvchar);     // hago eco selectivo (si procede).
  14.    }
  15. }
  16.  
  17. void add_2_cbuff(char c){
  18.  
  19.       switch(c){
  20.          case 0x0D:  // Enter -> Habilita Flag para procesar comando
  21.             flagcommand=1;
  22.             break;
  23.          case 0x08:  // Del   -> Borra último caracter del Buffer
  24.             if(xbuff>0) cbuff[--xbuff]=0x00;
  25.             break;
  26.          case 0x01B: // Esc   -> Borra el Buffer completamente
  27.             init_cbuff();
  28.             break;
  29.          default:    // Añade caracter recibido al Buffer
  30.             cbuff[xbuff++]=c;
  31.       }
  32. }
  33.  
  34. void echo_sel(char c){
  35.  
  36.    switch(c){
  37.       case 0x0D:     // Si he pulsado la tecla [Intro]
  38.          printf("\r\n[Ent]\r\n");
  39.          break;
  40.       case 0x08:     // Si he pulsado la tecla [Retroceso]
  41.          printf("\r\n[Del]\r\n>%s",cbuff);
  42.          break;
  43.       case 0x1B:     // Si he pulsado la tecla [Escape]
  44.          printf("\r\n[Esc]\r\n>");
  45.          break;
  46.       default:       // Echo de cualquier otro caracter
  47.          putc(c);
  48.    }
  49. }
  50.  

Fijaos que al pulsar la tecla Backspace lo único que hacemos es decrementar la variable índice del buffer y ponemos esa posición a 0x00, mientras que por el contrario cuando pulsamos la tecla Escape lo que hacemos es borrar todo el buffer y poner dicho índice a cero.

Esto último lo hacemos con la función init_cbuff()
Código: C#
  1. void init_cbuff(void){
  2.    int i;
  3.  
  4.    for(i=0;i<lenbuff;i++){// Bucle que pone a 0 todos los
  5.       cbuff[i]=0x00;      // caracteres en el buffer
  6.    }
  7.    xbuff=0x00;            // Inicializo el indice de siguiente caracter
  8. }
  9.  

Con lo que ya lo tenemos casi todo. Solo constatar que el main() es hiper-simple ya que lo único que hacemos en él, aparte de habilitar la correspondiente interrupción es esperar a que el flag para procesar comando se ponga en alto, cosa que solo haremos en add_2_cbuff() cuando recibamos el 0x0D. En ese caso ejecutaremos la función commad_process() donde haremos lo que nos de la gana con lo recibido (es ente ejemplo lo copiamos a otra variable y jugamos con los printf para mostrarlo)

Código: C#
  1. ///////////////////////////////////////
  2. // MAIN
  3. ///////////////////////////////////////
  4.  
  5. void main() {
  6.  
  7.    init_cbuff();               // Borra buffer al inicio
  8.    enable_interrupts(int_rda); // Habilita Interrupción RDA
  9.    enable_interrupts(global);  // Habilita interrupciones
  10.  
  11.    do {
  12.       if(flagcommand) commad_process(); // Hay algo pendiente de procesar y lo procesa.
  13.    } while (TRUE);
  14.  
  15. }
  16.  

El programa completo incluye una presentación previa donde se explica lo que se puede hacer con este programa y alguna cosa más irrelevante para el funcionamiento descrito aquí.

Programa Fuente Completo

Recuerda que la cabecera es la misma para todos los programas y la tienes descrita en el primer post de esta serie.

Código: C#
  1. ///////////////////////////////////////
  2. // Constantes
  3. ///////////////////////////////////////
  4.  
  5. int const lenbuff=32; // Longitud máxima del buffer
  6.  
  7. ///////////////////////////////////////
  8. // RAM
  9. ///////////////////////////////////////
  10.  
  11. int  xbuff=0x00;     // Índice: siguiente char en cbuff
  12. char cbuff[lenbuff]; // Buffer de recepción
  13. int1 flagcommand=0;  // Flag para comando disponible
  14.  
  15. ///////////////////////////////////////
  16. // Funciones de Buffer
  17. ///////////////////////////////////////
  18.  
  19. // Echo selectivo ----------------------
  20.  
  21. void echo_sel(char c){
  22.  
  23.    switch(c){
  24.       case 0x0D:     // Si he pulsado la tecla [Intro]
  25.          printf("\r\n[Ent]\r\n");
  26.          break;
  27.       case 0x08:     // Si he pulsado la tecla [Retroceso]
  28.          printf("\r\n[Del]\r\n>%s",cbuff);
  29.          break;
  30.       case 0x1B:     // Si he pulsado la tecla [Escape]
  31.          printf("\r\n[Esc]\r\n>");
  32.          break;
  33.       default:       // Echo de cualquier otro caracter
  34.          putc(c);
  35.    }
  36. }
  37.  
  38. // Inicia a \0 cbuff -------------------
  39.  
  40. void init_cbuff(void){
  41.    int i;
  42.  
  43.    for(i=0;i<lenbuff;i++){// Bucle que pone a 0 todos los
  44.       cbuff[i]=0x00;      // caracteres en el buffer
  45.    }
  46.    xbuff=0x00;            // Inicializo el indice de siguiente caracter
  47. }
  48.  
  49. // Añade a cbuff -----------------------
  50.  
  51. void add_2_cbuff(char c){
  52.  
  53.       switch(c){
  54.          case 0x0D:  // Enter -> Habilita Flag para procesar comando
  55.             flagcommand=1;
  56.             break;
  57.          case 0x08:  // Del   -> Borra último caracter del Buffer
  58.             if(xbuff>0) cbuff[--xbuff]=0x00;
  59.             break;
  60.          case 0x01B: // Esc   -> Borra el Buffer completamente
  61.             init_cbuff();
  62.             break;
  63.          default:    // Añade caracter recibido al Buffer
  64.             cbuff[xbuff++]=c;
  65.       }
  66. }
  67.  
  68. ///////////////////////////////////////
  69. // Procesador de Comandos
  70. ///////////////////////////////////////
  71.  
  72. void commad_menu(void){
  73.  
  74.    printf("\r\nTyP_Serie_TTL\r\n");
  75.    printf("Método : Recibiendo sobre Buffer\r\n\n");
  76.    printf("[Enter]  Procesa el buffer recibido.\r\n");
  77.    printf("[Escape] Borra todo el buffer.\r\n");
  78.    printf("[Delete] Borra último carácter del buffer.\r\n");
  79.    printf("\r\n\r\n>");
  80.  
  81. }
  82.  
  83. void commad_process(void){
  84.  
  85.    int i;
  86.    char cmd[lenbuff];        // Comando
  87.  
  88.    flagcommand=0;            // Desactivo flag de comando pendiente.
  89.    printf("Procesando ...\r\n");
  90.  
  91.    strcpy(cmd,cbuff);        // Lo copio para procesarlo
  92.    printf("Rec. Buffer <%s>\r\n",cmd); // ... y lo muestro
  93.  
  94.    init_cbuff();             // Borro buffer.
  95.    printf("Procesado.\r\n\r\n>"); // Monitorizo procesado.
  96. }
  97.  
  98. ///////////////////////////////////////
  99. // INTERRUPCIONES : RDA Recepción USART
  100. ///////////////////////////////////////
  101.  
  102. #int_rda
  103. void serial_isr() {          // Interrupción recepción serie USART
  104.  
  105.    char rcvchar=0x00;        // último caracter recibido
  106.  
  107.    if(kbhit()){              // Si hay algo pendiente de recibir ...
  108.       rcvchar=getc();        // lo descargo y ...
  109.       add_2_cbuff(rcvchar);  // lo añado al buffer y ...
  110.       echo_sel(rcvchar);     // hago eco selectivo (si procede).
  111.    }
  112. }
  113.  
  114. ///////////////////////////////////////
  115. // MAIN
  116. ///////////////////////////////////////
  117.  
  118. void main() {
  119.  
  120.    delay_ms(2000);           // Espero a estabilizar antes de actuar
  121.    commad_menu();          // Nos presentamos
  122.    init_cbuff();               // Borra buffer al inicio
  123.    enable_interrupts(int_rda); // Habilita Interrupción RDA
  124.    enable_interrupts(global);  // Habilita interrupciones
  125.  
  126.    do {
  127.       if(flagcommand) commad_process(); // Hay algo pendiente de procesar y lo procesa.
  128.    } while (TRUE);
  129.  
  130. }
  131.  
  132. ///////////////////////////////////////
  133. // Fin de programa
  134. ///////////////////////////////////////
  135.  


Aquí podéis ver un ejemplo de este programa funcionando. Un primera parte donde se presenta nuestro PIC y nos informa de sus posibilidades, es la parte de la función commad_menu() y después un ejemplo de escribir una serie de caracteres pulsando a continuación [Escape] para borrar todo lo escrito, y otro con la eliminación del último caracter escrito y su posterior procesado.



Continuará.  :mrgreen:

« Última modificación: 25 de Julio de 2009, 08:51:41 por RedPic »
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5362
    • Picmania by Redraven
Teoría y praxis de las com serie TTL : 4.2.- Recibiendo sobre un Buffer 2ª Parte
« Respuesta #12 en: 13 de Diciembre de 2008, 19:00:40 »
Título : 4.2.- Recibiendo sobre un Buffer. 2ª Parte : Extrayendo parámetros

Objetivos

Una línea de comando puede constar del comando propiamente dicho y uno o varios parámetros. Un trabajo previo a la del procesado de un comando consiste en aislar cada uno de estos componentes para su tratamiento individualizado. Este es el objetivo del presente artículo, recibir toda una linea de comandos y separar en variables distintas el comando principal y todos y cada uno de los posibles parámetros que nuestro comando pueda necesitar. Para una tercera parte dejaremos el procesado concreto de cada uno de los diferentes tipos de parámetros.

Vamos a conseguir trocear en cada uno de sus componentes toda una lista de comando más parámetros, separados todos ellos entre sí por comas (,).

>comando,param1,param2,param3 ... ,paramn[Intro]

obteniendo

Commando="comando"
Param[1]="param1"
Param[2]="param2"
Param[3]="param3"
...
Param[n]="paramn"


A partir de ahí tanto el Comando como cada uno de los parámetros puede ser procesado por individualizadamente.  :mrgreen:

Implementación en C

Con respecto a nuestro anterior programa, descrito en la primera parte de este Recibiendo sobre un Buffer, solo vamos a intercalar un par de funciones que van a interactúar con el Buffer recibido y que nos va a dar dos tipos de resultado: la una va a decirnos si hay o no comando y el número de parámetros que incluye nuestra línea de comandos y la llamaremos count_param_number(), y la segunda va a extraer el comando y el o los parámetros que lo acompañen y a la que vamos a llamar get_param_by_order().

De esta forma donde antes decíamos (simplemente):
Código: C#
  1. void commad_process(void){
  2.  
  3.    int i;
  4.    char cmd[lenbuff];        // Comando
  5.  
  6.    flagcommand=0;            // Desactivo flag de comando pendiente.
  7.    printf("Procesando ...\r\n");
  8.  
  9.    strcpy(cmd,cbuff);        // Lo copio para procesarlo
  10.    printf("Rec. Buffer <%s>\r\n",cmd); // ... y lo muestro
  11.  
  12.    init_cbuff();             // Borro buffer.
  13.    printf("Procesado.\r\n\r\n>"); // Monitorizo procesado.
  14. }
  15.  

ahora decimos (complicadamente ¡Uf! vaya palabro):
Código: C#
  1. void commad_process(void){
  2.  
  3.    int  n,i;
  4.    char command[lenparam];        // Comando
  5.    char param[5][lenparam];      // Parámetros
  6.  
  7.    flagcommand=0;            // Desactivo flag de comando pendiente.
  8.    printf("\nProcesando ...\r\n\n");
  9.  
  10.    // Primera Parte: Cuantos
  11.    n = count_param_number(); // Cuento el número de parámetros
  12.    if(n==0){
  13.       printf("En el Buffer no hay ni comando ni parámetro alguno\r\n\n");
  14.    }
  15.    else{
  16.       if(n==1){
  17.          printf("En el Buffer hay un comando y ningún parámetro\r\n\n");
  18.       }
  19.       else{
  20.          printf("En el Buffer hay un comando y %u parámetro(s)\r\n\n",n-1);
  21.       }
  22.    }
  23.  
  24.    // Segunda Parte: Cuales
  25.    if(n>0){
  26.       get_param_by_order(0,lenparam,command);
  27.       printf("Comando     <%s>\r\n",command);
  28.       if(n>1){
  29.          for(i=1;i<n;i++){
  30.             get_param_by_order(i,lenparam,&param[i-1][0]);
  31.             printf("Parámetro %u <%s>\r\n",i,param[i-1]);
  32.          }
  33.       }
  34.    }
  35.    init_cbuff();             // Borro buffer.
  36.    printf("\nProcesado.\r\n\n>"); // Monitorizo procesado.
  37. }
  38.  

Así tras recibir el correspondiente carácter de fin de transmisión 0x0D (representado por nuestro [Intro] anterior) pasamos a las dos secciones en las que he dividido el procesado del uffer: Saber Cuantos parámetros se han recibido y Cuales son éstos.

El Cuantos nos lo va a dar la llamada a la función count_param_number()

El Cuales nos los va a facilitar la función get_param_by_order()

La primera, count_param_number(), nos va a servir para saber cuantas veces debemos llamar a la segunda, get_param_by_order(). Estudiemos pues ambas funciones y alcanzaremos nuestro objetivo.

1.- count_param_number() no recibe parámetro alguno pero devuelve como resultado un int8 con el número de "trozos" que hay en cbuff separados por comas ( , ) o sea que lo que realmente hacemos es contar el número de comas que hay en el buffer.

Pero para que cada uno utilice el carácter que le de la gana como separador en lugar de tener que utilizar a la fuerza el carácter coma ( , ) he definido una constante llamada delimiter que en mi caso particular hago igual al carácter coma ( , ) pero cualquiera de ustedes puede hacer igual a punto y coma ( ; ), dos puntos ( : ) o el que deseéis.

Código: C#
  1. char const delimiter = ','; // Delimitador de parametros
  2.  

Pero hay más. ¿Y si deseamos utilizar la coma como delimitador? Pues vamos a definir otro carácter especial para deshabilitar el cómputo de comas, de tal forma que cuando entre el encuentro del primero de ellos y el segundo las comas no se tomen en cuenta. Yo en mi caso he utilizado el carácter comillas (") y le he llamado mi disabler.

Código: C#
  1. char const disabler  = '"'; // Deshabilitador de parametros
  2.  

Con todo esto nuestra count_param_number() no es mas que un bucle que cuenta cuantos delimiter hay en el buffer pero que si se encuentra un disabler deja de contar y que si se encuentra otro sigue contando de nuevo.

La implementación de esta función completa, peinada y con colonia es:
Código: C#
  1. // Cuenta los parametros en el buffer --
  2.  
  3. int8 count_param_number(void){
  4.  
  5.    int8 ret, i;
  6.    int1 active;
  7.    char c;
  8.  
  9.    ret=0;
  10.    active=1;
  11.    // ¿Hay comando?
  12.    c = cbuff[0]; // cargo el primer carácter por si acabo antes de empezar
  13.    if(c != 0x00) ret=1;
  14.    // Cuenta parámetros
  15.    for(i=0;i<lenbuff,c!=0x00;i++){
  16.       c = cbuff[i];
  17.       if(c==disabler && active==1) active=0; else if(c==disabler && active==0) active=1;
  18.       if(active==1) if(c==delimiter) ++ret;
  19.    }
  20.  
  21.    return ret;
  22. }
  23.  

2.- get_param_by_order() por su parte recibe tres parámetros: qué número de orden es el "trozo" que deseamos que devuelva, cuál es la máxima longitud admisible para ese trozo y un puntero a un array de char donde devolver lo deseado. Además de eso get_param_by_order() returna un int1 indicándonos si ha sido posible devolver el parámetro deseado desde el buffer. Si hay un parámetro en la posición adecuada y éste es menor o igual a la máxima longitud deseada entonces get_param_by_order() devuelve 0, en caso contrario nos brinda al sol con un 1.

get_param_by_order() no deja entonces de ser el mismo bucle que en la función anterior con la particularidad de que va a copiar caracteres desde el buffer hacia el array designado cuando se ha contado el número adecuado de delimiters, y se desactiva dicha copia cuando esté número de delimiters sobrepasa al deseado (o se acaba el buffer).

La implementación de esta función también acicalada para vosotros es:
Código: C#
  1. // Extrae un Parámetro dado del buffer ------
  2.  
  3. int1 get_param_by_order(int8 pos, int8 maxlen, char* pP){
  4.  
  5.    int1 ret=0;
  6.    char tmp[lenparam];
  7.    int8 i,j;
  8.    int8 nDelimiter;
  9.    int1 active=1;
  10.  
  11.    for(i=0,j=0,nDelimiter=0;i<lenbuff;i++){
  12.       tmp[j]=0x00;
  13.       if(nDelimiter==pos){
  14.          if(!(j==0 && cbuff[i]==disabler)) tmp[j++]=cbuff[i];
  15.       }
  16.       if(cbuff[i]==disabler && active==1) active=0; else if(cbuff[i]==disabler && active==0) active=1;
  17.       if(active==1) if((cbuff[i]==Delimiter) || (cbuff[i]==0x00)) ++nDelimiter;
  18.       if(nDelimiter>pos){
  19.          j -= 1;
  20.          break;
  21.       }
  22.    }
  23.    if(tmp[j-1]==disabler) --j;
  24.    tmp[j]='\0';
  25.    if(j>maxlen){
  26.       ret=1;
  27.    }else{
  28.       strcpy(pP,tmp);
  29.    }
  30.    return ret;
  31. }
  32.  

Nota que se que no os hace falta pero que os lo digo por si acaso. Si llamo a la función con el parámetro pos igual a cero ... entonces lo que nos devuelve es el primer trozo del buffer, o sea el comando.  :mrgreen:

Y por último solo comentar un poco la salida que produzco en el ejemplo. La primera parte sobre Cuantos genera tres textos con printf() según no haya nada en el buffer, solo haya un comando sin parámetros o un comando y uno o mas parámetros. Y la segunda parte, la extracción de Cuales, realiza primero y separadamente la extracción del Comando y después en un bucle entre 1 y el número de parámetros obtenidos antes los extrae todos sobre sus correspondientes variables.

Programa Fuente Completo

Recuerda que la cabecera es la misma para todos los programas y la tienes descrita en el primer post de esta serie.

Código: C#
  1. ///////////////////////////////////////
  2. // Constantes
  3. ///////////////////////////////////////
  4.  
  5. int  const lenbuff=128;     // Longitud máxima del buffer
  6. char const delimiter = ','; // Delimitador de parametros
  7. char const disabler  = '"'; // Deshabilitador de parametros
  8. int  const lenparam  = 32;  // Máxima longitud por parámetro
  9.  
  10. ///////////////////////////////////////
  11. // RAM
  12. ///////////////////////////////////////
  13.  
  14. int  xbuff=0x00;     // Índice: siguiente char en cbuff
  15. char cbuff[lenbuff]; // Buffer de recepción
  16. int1 flagcommand=0;  // Flag para comando disponible
  17.  
  18. ///////////////////////////////////////
  19. // Funciones de Buffer
  20. ///////////////////////////////////////
  21.  
  22. // Cuenta los parametros en el buffer --
  23.  
  24. int8 count_param_number(void){
  25.  
  26.    int8 ret, i;
  27.    int1 active;
  28.    char c;
  29.  
  30.    ret=0;
  31.    active=1;
  32.    // ¿Hay comando?
  33.    c = cbuff[0];
  34.    if(c != 0x00) ret=1;
  35.    // Cuenta parámetros
  36.    for(i=0;i<lenbuff,c!=0x00;i++){
  37.       c = cbuff[i];
  38.       if(c==disabler && active==1) active=0; else if(c==disabler && active==0) active=1;
  39.       if(active==1) if(c==delimiter) ++ret;
  40.    }
  41.  
  42.    return ret;
  43. }
  44.  
  45. // Extrae un Parámetro del buffer ------
  46.  
  47. int1 get_param_by_order(int8 pos, int8 maxlen, char* pP){
  48.  
  49.    int1 ret=0;
  50.    char tmp[lenparam];
  51.    int8 i,j;
  52.    int8 nDelimiter;
  53.    int1 active=1;
  54.  
  55.  
  56.    for(i=0,j=0,nDelimiter=0;i<lenbuff;i++){
  57.       tmp[j]=0x00;
  58.       if(nDelimiter==pos){
  59.          if(!(j==0 && cbuff[i]==disabler)) tmp[j++]=cbuff[i];
  60.       }
  61.       if(cbuff[i]==disabler && active==1) active=0; else if(cbuff[i]==disabler && active==0) active=1;
  62.       if(active==1) if((cbuff[i]==Delimiter) || (cbuff[i]==0x00)) ++nDelimiter;
  63.       if(nDelimiter>pos){
  64.          j -= 1;
  65.          break;
  66.       }
  67.    }
  68.    if(tmp[j-1]==disabler) --j;
  69.    tmp[j]='\0';
  70.    if(j>maxlen){
  71.       ret=1;
  72.    }else{
  73.       strcpy(pP,tmp);
  74.    }
  75.    return ret;
  76. }
  77.  
  78. // Echo selectivo ----------------------
  79.  
  80. void echo_sel(char c){
  81.  
  82.    switch(c){
  83.       case 0x0D:     // Si he pulsado la tecla [Intro]
  84.          printf("\r\n[Ent]\r\n");
  85.          break;
  86.       case 0x08:     // Si he pulsado la tecla [Retroceso]
  87.          printf("\r\n[Del]\r\n>%s",cbuff);
  88.          break;
  89.       case 0x1B:     // Si he pulsado la tecla [Escape]
  90.          printf("\r\n[Esc]\r\n>");
  91.          break;
  92.       default:       // Echo de cualquier otro caracter
  93.          putc(c);
  94.    }
  95. }
  96.  
  97. // Inicia a \0 cbuff -------------------
  98.  
  99. void init_cbuff(void){
  100.    int i;
  101.  
  102.    for(i=0;i<lenbuff;i++){// Bucle que pone a 0 todos los
  103.       cbuff[i]=0x00;      // caracteres en el buffer
  104.    }
  105.    xbuff=0x00;            // Inicializo el indice de siguiente caracter
  106. }
  107.  
  108. // Añade a cbuff -----------------------
  109.  
  110. void add_2_cbuff(char c){
  111.  
  112.       switch(c){
  113.          case 0x0D:  // Enter -> Habilita Flag para procesar comando
  114.             flagcommand=1;
  115.             break;
  116.          case 0x08:  // Del   -> Borra último caracter del Buffer
  117.             if(xbuff>0) cbuff[--xbuff]=0x00;
  118.             break;
  119.          case 0x01B: // Esc   -> Borra el Buffer completamente
  120.             init_cbuff();
  121.             break;
  122.          default:    // Añade caracter recibido al Buffer
  123.             cbuff[xbuff++]=c;
  124.       }
  125. }
  126.  
  127. ///////////////////////////////////////
  128. // Procesador de Comandos
  129. ///////////////////////////////////////
  130.  
  131. void commad_menu(void){
  132.  
  133.    printf("\r\nTyP_Serie_TTL\r\n");
  134.    printf("Método : Extrayendo parámetros del Buffer\r\n");
  135.    printf("         delimitados por [%c]\r\n\n",delimiter);
  136.    printf("[Enter]  Procesa el buffer recibido.\r\n");
  137.    printf("[Escape] Borra todo el buffer.\r\n");
  138.    printf("[Delete] Borra último carácter del buffer.\r\n");
  139.    printf("\r\n\r\n>");
  140.  
  141. }
  142.  
  143. void commad_process(void){
  144.  
  145.    int  n,i;
  146.    char command[lenparam];        // Comando
  147.    char param[5][lenparam];      // Parámetros
  148.  
  149.    flagcommand=0;            // Desactivo flag de comando pendiente.
  150.    printf("\nProcesando ...\r\n\n");
  151.  
  152.  
  153.  
  154.    // Primera Parte: Cuantos
  155.    n = count_param_number(); // Cuento el número de parámetros
  156.    if(n==0){
  157.       printf("En el Buffer no hay ni comando ni parámetro alguno\r\n\n");
  158.    }
  159.    else{
  160.       if(n==1){
  161.          printf("En el Buffer hay un comando y ningún parámetro\r\n\n");
  162.       }
  163.       else{
  164.          printf("En el Buffer hay un comando y %u parámetro(s)\r\n\n",n-1);
  165.       }
  166.    }
  167.    // Segunda Parte: Cuales
  168.    if(n>0){
  169.       get_param_by_order(0,lenparam,command);
  170.       printf("Comando     <%s>\r\n",command);
  171.       if(n>1){
  172.          for(i=1;i<n;i++){
  173.             get_param_by_order(i,lenparam,&param[i-1][0]);
  174.             printf("Parámetro %u <%s>\r\n",i,param[i-1]);
  175.          }
  176.       }
  177.    }
  178.    init_cbuff();             // Borro buffer.
  179.    printf("\nProcesado.\r\n\n>"); // Monitorizo procesado.
  180. }
  181.  
  182. ///////////////////////////////////////
  183. // INTERRUPCIONES : RDA Recepción USART
  184. ///////////////////////////////////////
  185.  
  186. #int_rda
  187. void serial_isr() {          // Interrupción recepción serie USART
  188.  
  189.    char rcvchar=0x00;        // último caracter recibido
  190.  
  191.    if(kbhit()){              // Si hay algo pendiente de recibir ...
  192.       rcvchar=getc();        // lo descargo y ...
  193.       add_2_cbuff(rcvchar);  // lo añado al buffer y ...
  194.       echo_sel(rcvchar);     // hago eco selectivo (si procede).
  195.    }
  196. }
  197.  
  198. ///////////////////////////////////////
  199. // MAIN
  200. ///////////////////////////////////////
  201.  
  202. void main() {
  203.  
  204.    delay_ms(2000);           // Espero a estabilizar antes de actuar
  205.  
  206.    // HARD ///////////
  207.    output_low(PIN_E2);
  208.    output_low(PIN_C0);
  209.    output_low(PIN_D7);
  210.    output_low(PIN_E1);
  211.    output_low(PIN_E0);
  212.    output_high(PIN_D1);
  213.    output_high(PIN_D0);
  214.    delay_ms(110);
  215.    output_low(PIN_D0);
  216.    ///////////////////
  217.  
  218.    commad_menu();
  219.    init_cbuff();               // Borra buffer al inicio
  220.    enable_interrupts(int_rda); // Habilita Interrupción RDA
  221.    enable_interrupts(global);  // Habilita interrupciones
  222.  
  223.    do {
  224.       if(flagcommand) commad_process(); // Hay algo pendiente de procesar y lo procesa.
  225.    } while (TRUE);
  226.  
  227. }
  228.  
  229. ///////////////////////////////////////
  230. // Fin de programa
  231. ///////////////////////////////////////
  232.  

Aquí podéis ver un ejemplo de este programa funcionando:



Mañana más.  :mrgreen:
« Última modificación: 02 de Octubre de 2009, 15:13:57 por RedPic »
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado jhozate

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1674
Re: Teoría y praxis de las comunicaciones serie TTL con los PIC's 16F/18F en CCS C
« Respuesta #13 en: 15 de Julio de 2009, 19:48:57 »
que bueno este hilo ,,pero por q no continuó?,,,aun asi es un gran aporte..me parece que deberia tener chincheta al entrar a "lenguaje c para microcontroladores" asi cuando se necesite algo relacionado a la comunicacion serie primero nos dirijimos aqui y si no queda algo claro  posteamos la duda...bueno esa es mi opinion.

saludos
Ser Colombiano es un Premio, Saludos desde CALI-COLOMBIA

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: Teoría y praxis de las comunicaciones serie TTL con los PIC's 16F/18F en CCS C
« Respuesta #14 en: 15 de Julio de 2009, 21:46:41 »
Cierto.

No aparece en los índices ni subíndices de...

Indice de Hilos Destacados en el sub-Foro de C para Microcontroladores. 
http://www.todopic.com.ar/foros/index.php?topic=14634.0