TODOPIC

Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado por: Nocturno en 16 de Noviembre de 2009, 15:12:32

Título: Recepción serie por soft usando el Timer
Publicado por: Nocturno en 16 de Noviembre de 2009, 15:12:32
A veces tenemos que utilizar más de una USART y nuestro micro no tiene más que una.
La emulación software de una USART que trae CCS nos ha sacado a todos de más de un apuro pero tiene un inconveniente importante: mientras envías o recibes un byte pierdes el control del micro, y tu programa se queda esperando a que la rutina de envío o recepción finalice.

En mi caso estoy trabajando con un 18F a 12MIPS y he necesitado implementar una recepción serie por software a 1200 baudios. Esto quiere decir que mientras mi micro recibe un byte, tiene que esperar:
1200 baudios -> cada bit dura 833us -> cada byte tiene 1 bit start + 8 bits datos + 1 bit stop = 10 bits -> total espera 8330 us = 8.3ms

En ese tiempo el micro a 12MIPS habría podido ejecutar 100.000 instrucciones, y en según que aplicaciones no te puedes permitir ese derroche de recursos.

Para no tener esa pérdida de tiempo, y puesto que tenía un Timer libre, he escrito esta pequeña rutina que recibe un byte usando cualquier pin. Lógicamente se podría modificar para que la recepción la hiciera sobre un buffer y para distintas velocidades de transmisión pero no era mi propósito hacer una rutina tan compleja:

Código: C
  1. #int_timer1
  2. void RecibeByte() {
  3.    static int8 Capturando,Dato,BitsLeidos;
  4.  
  5.    set_timer1(55535);           // Esta precarga está calculada para 1200 baudios = 833us
  6.  
  7.    if (Capturando) {            // Ya estábamos capturando el byte
  8.       if (++BitsLeidos==9) {    // Si hemos capturado el último bit
  9.          Capturando=0;          // ya no capturamos más
  10.          ByteRecibido=Dato;     // Cargamos el dato sobre una variable global. Aquí se podría almacenar sobre buffer
  11.       } else {
  12.          Dato=Dato*2+input(PIN_D3);     // acumulamos el bit recibido en el pin D3  
  13.         }
  14.    } else {
  15.       if (!input(PIN_D3)) {     // He leído un cero, que es el bit de Start
  16.          BitsLeidos=0;          // Inicializamos las variables
  17.          Dato=0;
  18.          Capturando=1;
  19.       }
  20.    }
  21. }

Como he dicho antes, la rutina se puede mejorar para hacerla más flexible a otras circunstancias, y también se puede escribir una análoga para hacer la transmisión con el Timer, pero esa tarea la dejaré para quien esté interesado en hacerla.

Espero que sirva de ayuda
Título: Re: Recepción serie por soft usando el Timer
Publicado por: RALF2 en 19 de Noviembre de 2009, 08:21:53
Que tal manolo!
Estaba revizando tu rutina porque me parecio interesante tu propuesta de la rutina serie por software  :mrgreen: , pero a medida que la veia me comensaron a surgir varias dudas  :shock:

Tu empleas la interrupcion por el timer1 en tu ejemplo y segun dices esta rutina permite capturar un byte recibido serialmente, pero no logro entender como sincronizas el byte a recibir con la rutina  :(

En otras palabras como configuras la rutina desde el main para poderla emplear?
Podrias colocar un ejemplito por ejemplo donde este suponte parpadeando un led y cuando se envie un dato serial este lo lea para poder asi ver, como sincronizas el byte que llega con la rutina que colocastes?

De antemano gracias!  :mrgreen:
Título: Re: Recepción serie por soft usando el Timer
Publicado por: RALF2 en 19 de Noviembre de 2009, 08:33:58
Que tal manolo!
Otra vez yo   :D
Creo que la respuesta a mi pregunta esta en este pequeño fragmento  :shock:
Citar
#
if (!input(PIN_D3)) {   // He leído un cero, que es el bit de Start
#
        BitsLeidos=0; // Inicializamos las variables
#
        Dato=0;
#
        Capturando=1;
Alli es donde capturas el bit de start y de esa manera sincronizas los bits que van llegando, estare en lo correcto?  :mrgreen:

Saludos
Título: Re: Recepción serie por soft usando el Timer
Publicado por: Nocturno en 19 de Noviembre de 2009, 08:41:28
La sincronización se basa en que el periodo de la interrupción del timer sea el mismo que el de cada bit de la comunicación serie.
De esta manera, cuando tú inicializas el PIC lo único que haces es activar el timer y su interrupción, y desde ese momento cada periodo (833us en mi caso) se asoma a mirar el pin de recepción.

Cuando se lo encuentre a nivel bajo sabrá que ha recibido el bit de start y pone en marcha el mecanismo de captura del byte.

Es así de simple, lo único que te puede aportar mi ejemplo de uso es esto:
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);

P.D.: cuando ya lo tenía escrito veo que lo has averiguado por ti mismo  :D
Título: Re: Recepción serie por soft usando el Timer
Publicado por: RALF2 en 19 de Noviembre de 2009, 16:24:52
 :mrgreen:
Gracias nocturno por la explicacion, probare la rutina!

Saludos
Título: Re: Recepción serie por soft usando el Timer
Publicado por: Nocturno en 19 de Noviembre de 2009, 16:39:16
Pues ya me contarás luego qué tal te ha ido. Espero que te sirva.
Título: Re: Recepción serie por soft usando el Timer
Publicado por: fidodido18 en 20 de Noviembre de 2009, 17:50:54
Pues ya me contarás luego qué tal te ha ido. Espero que te sirva.

Oye no se si yo estoy muy lento pero no me doy cuenta en donde estas recibiendo los bit por rs232! en que parte especificamente... y que estas haciendo con el timer?

Lo que medio alcanzo a entender es que el timer vigila un pin cada 833 uS y si llega un dato lo recibe?
Título: Re: Recepción serie por soft usando el Timer
Publicado por: Nocturno en 20 de Noviembre de 2009, 18:06:01
No es exactamente RS232 porque se supone que ya hemos convertido a niveles TTL, por ejemplo con un MAX232.
El pin por el que lo recibo es el PIN_D3, aunque puedes cambiarlo por el pin que te venga bien.
Título: Re: Recepción serie por soft usando el Timer
Publicado por: RedPic en 20 de Noviembre de 2009, 18:27:44
Un detalle que puede resultar interesante para completar este magnifico post del maestro Nocturno.

En mi hilo sobre los Fundamentos de la Transmisión Síncrona (http://www.todopic.com.ar/foros/index.php?topic=19587.0) os hacía una introducción teórica sobre la Transmisión Asíncrona que es la que utilizamos para transmitir y recibir en RS232 ...

En aquella pequeña introducción definía el etu (elementary time unit) exactamente como el semi-periodo de la frecuencia de transmisión. O sea: el tiempo que dura cada transmisión de un bit.

(http://picmania.garcia-cuervo.net/images/trans_sincrona_01.gif)

De hecho el trabajo de don Nocturno lo que hace es implementar dicho etu con el Timer, de forma tal que cada vez que se dispara, o lo que es lo mismo: se consume un etu, se lee un nuevo bit en la linea de recepción.

El detalle al que me refiero es que el primer bit, el Start, es siempre un bit "1", o sea una bajada de la linea, de HIGH a LOW y su posterior subida, de LOW a HIGH, que dura exactamente un etu.

Si la línea de recepción que utilizamos dispone de una Interrupción Externa es fácil implementar un sistema que mida exactamente la duración del etu de ese primer bit de Start. Con esa información es inmediato el poder ajustar el Timer de recepción para que utilice ese etu a partir de la primera lectura.

En otras palabras: Midiendo el ancho de pulso del primer bit de Start podemos tener un ajuste automático de la velocidad de recepción.  :mrgreen:

Título: Re: Recepción serie por soft usando el Timer
Publicado por: Nocturno en 20 de Noviembre de 2009, 18:49:57
Mmmmm, no lo entiendo, si el primer bit es también a nivel bajo no podrás hacerlo, porque no hay flanco de subida, ¿no?
Título: Re: Recepción serie por soft usando el Timer
Publicado por: MGLSOFT en 20 de Noviembre de 2009, 18:56:04
Estuve buscando, porque crei ver que el CCS tenia la opcion autobaud, pero no lo encuentro... :( :(
Título: Re: Recepción serie por soft usando el Timer
Publicado por: Nocturno en 20 de Noviembre de 2009, 19:03:30
Sí, lo tiene, querido Marcos, aunque sólo en los micros que dispongan de EUART:

Syntax:
 setup_uart(baud, stream)

setup_uart(baud)

 
 
Parameters:
 baud is a constant representing the number of bits per second. A one or zero may also be passed to control the on/off status.  Stream is an optional stream identifier. 

 

Chips with the advanced UART may also use the following constants: 

UART_ADDRESS UART only accepts data with 9th bit=1

UART_DATA UART accepts all data

 

Chips with the EUART H/W may use the following constants:

UART_AUTODETECT Waits for 0x55 character and sets the UART baud rate to match.

UART_AUTODETECT_NOWAIT Same as above function, except returns before 0x55 is received.  KBHIT() will be true when the match is made.  A call to GETC() will clear the character.

UART_WAKEUP_ON_RDA Wakes PIC up out of sleep when RCV goes from high to low

Título: Re: Recepción serie por soft usando el Timer
Publicado por: RedPic en 20 de Noviembre de 2009, 19:05:19
No, Manolo, en una transmisión Asíncrona (sin línea de Clock) el bit de Start siempre es un "1", al igual que el de Stop siempre es un "0".

En reposo la linea que recibes siempre está en HIGH.

El primer bit que recibes no es de data sino el de Start que es una transición de HIGH a LOW y de LOW a HIGH que dura exactamente el etu del que hablamos.

A partir de ahí cada periodo etu que transcurra puedes encontrarte la linea en HIGH y entonces leearás un "0" o en LOW y entonces leerás un "1".

Así tantas veces como bits se haya acordado que se van a transmitir.

Al final se reserva un etu a HIGH que se interpreta como un bit de Stop.

Este bit de Stop de un etu de duración se utiliza para separar dos transmisiones consecutivas de dos bytes y que así nos de tiempo, un etu, para procesar el byte recibido.

A partir de ahí volvemos a empezar: Un bit de Start a "1", n bits segun lo acordado y un bit de Stop a "0" para procesarlo.

Mientras que en una transmisión-recepción RS232 todo esto es mas o menos irrelevante, nota que tanto el emisor como el receptor son ajustados en velocidad de forma absolutamente fija y certera, hay transmisiones en las que el etu no está predefinido, se establece por el transmisor justo en el momento de enviar el primer bit, y es obligación del receptor el leer el periodo de dicho etu para poder "entender" lo que se está recibiendo.

El ejemplo mas usual de este tipo de transmisión es el de las Tarjetas chips bancarias. Cuando las energizas tienes que ponerte en recepción y medir la duración del bit start del primer byte que te manda. Esa duración te indica a qué velocidad te va a transmitir el resto de la información.  :mrgreen:


  

Título: Re: Recepción serie por soft usando el Timer
Publicado por: Nocturno en 20 de Noviembre de 2009, 19:09:02
Pues estoy convencido de que tienes razón, pero sigo sin entenderlo.
Llegué a la rutina que he publicado tras hacer pruebas con el osciloscopio y juraría que cuando el primer bit de datos es un nivel bajo de tensión, enlaza el nivel bajo que venía del bit de start prolongándolo por dos etus sin que haya ningún flanco de subida.
Título: Re: Recepción serie por soft usando el Timer
Publicado por: RedPic en 20 de Noviembre de 2009, 19:12:23
Sí, lo tiene, querido Marcos, aunque sólo en los micros que dispongan de EUART ...

Esa es otra convención que es muy utilizada. Todo dispositivo que tenga auto-ajuste de la velocidad es "sensible" a la recepción como primer byte del valor 0x55.

Si el primer byte que recibe es 0x55 entonces no lo usa como recibido "normal" sino que lo utiliza para "medirlo" y ajustar la velocidad de transmisión.

Se utiliza precisamente el 0x55 por que es el único byte que tiene todos los bits alternados y empieza por "0".

Así al transmitir el Start + 0x55 + Stop tenemos la secuencia de bits: 1010101010

Curioso y simpático ¿verdad?  :mrgreen:
Título: Re: Recepción serie por soft usando el Timer
Publicado por: Nocturno en 21 de Noviembre de 2009, 03:06:14
Mira, esto es lo que te decía:
(http://www.best-microcontroller-projects.com/image-files/how-rs232-works-tx-logic-rs232-diag.png)

Como el primer bit es un 0, no hay flanco de subida entre el start y el B0, por lo que no puedes calcular autobaudios.
Título: Re: Recepción serie por soft usando el Timer
Publicado por: RALF2 en 21 de Noviembre de 2009, 09:17:57
Que tal amigos!
Queria hacer una pequeña acotacion en algo que coloco redpic referente al bit de stop de la señal serial rs232 donde indica:
Citar
en una transmisión Asíncrona (sin línea de Clock) el bit de Start siempre es un "1", al igual que el de Stop siempre es un "0".

En estado de reposo (IDLE) la señal del rs232 se mantiene en 1 logico.
La señal de start se genera por un cambio de 1 a 0, donde el 0  dura un etu.
luego se sacan los datos, los cuales pueden ser 7 u 8 bits comensando por el l.s.b.
Y finalmente se genera la señal de stop la cual consiste en un "1" logico cuya duracion es por lo general un etu (aunque puede ser 1.5 o 2 etu segun sea el caso tambien).  :mrgreen:
Todo lo dicho anteriormente queda reflejado en el grafico de arriba que coloco el amigo nocturno.

Saludos