none
Problemas con recepción de datos puerto serie RRS feed

  • Pregunta

  • El problema con el que me encuentro es que no siempre recibo bien los datos o no los capturo por el puerto serie.
    Tengo un microcontrolador(PIC) conectado al puerto serie para comunicarme con él, le envío un carácter "R" y el me devuelve  "Rx" donde x es 0 o 1 en ASCII, ese es el dato que a mi me interesa capturar, el 0 o 1.

    el código que estoy usando actualmente es el siguiente:

    puertoSerie.write("R")                                                           'enviamos caracter

    dato=Asc(puertoSerie.ReadExisting.Chars(1))                        'leemos y cogemos valor de x

    el problema es que no siempre me lee bien los datos porque hay veces que me aparece un error del tipo "
    index out of range exception", es decir que no ha leido el "Rx", creo que el PC va demasiado rápido para el micro y cuando se pone a leer, no está  todavía los datos en el buffer de lectura del puerto.

    También decir que no puedo realizar otras tareas hasta que no haya leido el 0 o 1.
    Y mejor no usar retardos entre escritura y lectura.

    ¿Alguién sabe como solucionar este problema, mediante algún bucle o algo por el estilo?

    Un saludo.
    Muchas gracias.
    miércoles, 17 de septiembre de 2008 7:46

Todas las respuestas

  • Hola Creams,

    Una de las características de las comunicaciones serie, es que son asíncronas… lo que significa que los ‘Bytes’ se serializan y transmiten sin que el receptor conozca el momento preciso. Es decir el bit de ‘start’ indica al receptor el envio de un ‘byte’ pero en ningún caso y por ausencia de protocolo conocemos de antemano la cantidad de ‘bytes’ que vamos a recibir.

    Como tu bien dices, estas ejecutando la lectura directamente sin esperar al evento de ‘Data Received’, es presumible que la rapidez de proceso del PC salte la excepción de   index out of range exception“ intentando procesar la posicion del segundo caracter que evidentemente aun no has recibido.

    El mandato ReadExisting, lee los bytes recibidos que actualmente permanecen en el buffer y en tu caso y en ocasiones, dependiendo del flujo de ejecucion, debes tener en el buffer, 0, 1 o cuando te funciona bien2 bytes.

    Es una buena práctica formalizar el intercambio de tramas delimitándolas con un carácter de inicio y otro de fin… por ejemplo la mayoría implementan el CR o CHR(13) como fin de trama, algunos fabricantes optan por utilizar como fin de trama secuencias tales como 2 bytes de FCS+ Un Carácter + CR, en fin como siempre hay para todos los gustos. Con este método podrás estar seguro de que la transmisión a finalizado correctamente.

    Deberías implementar el disparo de recepción y procesar los datos una vez se ha completado la recepción.

     

    En todo caso otra opción podria ser algo parecido a :

     'String de recepcion utilizado como buffer   
     Private PortSerie_Recepcion As String = ""    
     'Anadir el manipulador de recepcion en la sub New, Load…
       
     AddHandler
    Serie.DataReceived, AddressOf Rx
     
     
    Sub Rx(ByVal sender As Object, _
            ByVal e As serialDataReceivedEventArgs)       
       Try

         'Añadir la recepcion actual al buffer            
         PortSerie_Recepcion += Serie.ReadExisting
         If PortSerie_Recepcion.Contains(Chr(13)) Then                
             PROCESAR LA INFORMACION
             PortSerie_Recepcion = ""           
        
    End If       
       Catch ex As Exception
        
    'En caso de excepción       
      End Try   
     End Sub

    Si la longitud de tus tramas es de 2 bytes… una idea muy básica puedes ser controlar el numero de caracteres que están en el buffer pendientes de ser leídos y antes de procesarlos con :

    PuertoSerie.BytesToRead

            ' Definitivamente NO ES RECOMENDABLE

            '

            If PuertoSerie.BytesToRead = 2 Then

                dato = Asc(PuertoSerie.ReadExisting.Chars(1))

            End If


    El único problema utilizando este procedimiento es que nunca se procesara el dato si no se reciben exactamente dos caracteres y por ende en intercambios muy rápidos o con interferencias puede perder la lectura de pares de bytes y estar procesando inadecuadamente por desplazamiento de posiciones o peor aun, si BytesToRead es > 2... como mal menor necesitarias PuertoSerie.BytesToRead > 2 , aunque insisto lo mejor es incluir un fin de trama en cada transmision desde el firmware del dispositivo.

    Saludos,
    Pep Lluis,

     

    miércoles, 17 de septiembre de 2008 10:51
    Moderador
  • Buenos días Pep Lluis, ante todo agredecer tú respuesta y la forma en la que lo has hecho.

    He estado probando las opciones que me has comentado, la opción de controlar el tamaño de los
    caracteres que están en el buffer pendientes de ser leídos, pero esta opción no es válida puesto que hay veces que el buffer tiene tamaño 2 pero los byte que existen no son los correctos por una lectura incorrecta, igual si compruebo que lo leido es "R0" o "R1" que es lo único que se debería leer, y volver a leer hasta que  cumpla me soluciona el problema, aunque no se si es una manera adecuada de hacerlo.

    Respecto a la otra opción expuesta, la de utilizar
    el evento de ‘Data Received’, el problema con el que me encuentro es que se ejecuta en un hilo distinto por lo que el programa sigue corriendo, ¿exite alguna forma de hacer que mi programa pare hasta que se ejecute completamente la rutina que recoge el evento?
    ¿Sería una buena idea declarar una variable goblal del tipo boolean que se modifique en la rutina del
    evento ‘Data Received’ cuando esta ha acabado, o existe alguna forma mas elegante de hacerlo?


    Un saludo.
    Muchas gracias.
    jueves, 18 de septiembre de 2008 8:19
  • Creo que esta puede ser una solución de partida… aunque siempre aprovechando el evento ‘DataReceived’.

    Cuando comentas que necesitas que la aplicación no se ejecute hasta recibir los datos, en todo caso podrías montar un ‘Threat’ con la operación de lectura de los dos bytes pero tampoco considero acertado dejar la aplicación a merced de que el dispositivo responda.

    Si la necesidad de esperar la respuesta es para generar otra consulta, puede añadir indicadores que disparen el PuertoSerie.Write a través de un temporizador y siempre cuando no esté pendiente de una respuesta. En todo caso concreta la necesidad y componemos el ejemplo.

        'Anadir el manipulador de recepcion en la sub New, Load…   

        AddHandler PuertoSerie.DataReceived, AddressOf Rx

     

        Sub Rx(ByVal sender As Object, _

               ByVal e As SerialDataReceivedEventArgs)

            Try

                'Añadir la recepcion actual al buffer           

                If PuertoSerie.BytesToRead > 1 Then

                    'Leer dato

                    PuertoSerie.Read(Dato, 0, 2)

                    Select Case Dato

                        Case "R0"

                            'accion 0

                        Case "R1"

                            'accion 1

                        Case Else

                            'descartar el contenido del buffer

                            PuertoSerie.DiscardInBuffer()

                    End Select

                End If

            Catch ex As Exception

                'En caso de excepción       

            End Try

        End Sub

     

    Saludos,
    Pep Lluis,

     

    jueves, 18 de septiembre de 2008 16:33
    Moderador
  • Buenas.

    Lo que necesito es leer el estado de una patilla del microcontrolador, para ello envio la instrucción "R" y me devuelve "Rx" siendo x el estado de la patilla (0/1). Esto lo tengo que hacer unas 1000 veces, y recoger los 0 y 1 que me devuelve,es como si obtuviera una lectura en binario,luego paso esa lectura a hexadecimal y la muestro.

    Lo que estoy haciendo ahora es leer 8 bit y los transformo a hexadecimal, y sigo con otros 8, así hasta que termine la lectura, luego la muestro por pantalla.

    Estoy empezando a pensar que el problema tiene que ser debido a la configuración del puerto, estoy haciendo pruebas con otro programa y no tengo ningún problema en la recepción como yo lo hago.

    Te pongo el código que utilizo con el otro programa, el
    Winexplorer, con visualScript(casi igual que visual basic)

    Puerto.Write(hex(Asc("R")))                 ' ENVIA COMANDO AL PUERTO SERIE
    Sc.delay(1)                                         ' ESPERA LA RESPUESTA
    tam_resp=Puerto.Bytesinbuffer                 ' MIDE LA LONGITUD DE LA RESPUESTA
    Sc.read (tam_resp)      

    luego para hacer la lectura

    lectura=Puerto.getbyte(1)                     'EN LECTURA SE ALMACENA UN CERO / UNO

    antes de que se ejecute todo este codigo en el principio del programa se modifica una de las propiedades del puerto

    Puerto.Bytedelay = 0


    Y la configuración completa del puerto para winexplorer es:

    Data Baud: 115200
    Reset Baud: 115200
    Parity: Even
    Rec Timeout: 200
    Byte Delay: 100
    Reset Delay:100
    Stop Bits: 2

    Byte Convention: Direct
    No Reset
    Ignore Receive Timeouts
    DTR: High
    RTS: Low
    Toggle this line for a Reset: DTR


    Con esta configuración y con el código anterior todo funciona correctamente por muy rápido que vaya.
    Por lo que veo es que hay algunos parametros que no los puedo configura en visual basic, o no se como hacerlo.

    Te agradecería me dijeras como implementar o configurar el puerto correctamente.

    Muchas gracias por todo.
    viernes, 19 de septiembre de 2008 9:35
  • Segun puntos de vista y según las escuelas pueden utilizarse formas tales como:

    Dim PuertoSerie As New IO.Ports.SerialPort
    PuertoSerie =
    My.Computer.Ports.OpenSerialPort("COM1")
    ….

    ….

    PuertoSerie.Write("Peticion")
    System.Threading.Thread.Sleep(100) 
    'Dar tiempo al dispotivo
    Dim Resultado() As Byte(Longitud)
    PuertoSerie.Read(Resultado, 0, Longitud, 10000)

     

      ' donde 10000 es el timepo predefinido para
      ' que el puerto serie dispare una excepcion

      ' por tiempo de expera excedido

    A pesar de que en mi opinión, utilizar ‘Sleeps’ y técnicas de esperar un fuera de tiempo no cumple con las expectativas de un programa que se precie. Pero esto ultimo no deja de ser una opinión muy personal.

    http://msmvps.com/blogs/peplluis/archive/2008/09/19/leer-los-ceros-y-unos-de-un-micro-controlador.aspx

     

    Saludos,

    Pep Lluis.

    viernes, 19 de septiembre de 2008 15:24
    Moderador
  • Buenos días, en primer lugar decirte que yo también soy de la opinión que esta ultima forma no es la adecuada para la comunicación, pero es a la que me tengo que adaptar.

    Decirte que probé el último código que expusiste y un pequeño cambio que realicé en la propiedad "Byte Delays = 0" del serialPort y se ha solucionado el problema, ahora no se produce ningún error en la lectura.

    Todo solucionado.
    Muchas gracias por el interés que te has tomado en contestarme.
    lunes, 22 de septiembre de 2008 8:06
  • Hola:

    Puedes descargar el manual en pdf sobre VB y  puerto serie.
    Saludo.

    http://electronica-pic.blogspot.com
    martes, 26 de mayo de 2009 8:33