News:

Let's find out together what makes a PIC Tick!

Main Menu

Read unknown number of characters from Serial

Started by crankshaft, Jan 24, 2022, 06:28 AM

Previous topic - Next topic

crankshaft

A simple question, but I cant seem to find an answer.

How can I handle an unknown number of characters being received from RSIN ??

    Dim rsA[10] As Byte
    RsIn {main}, Wait("["),Str rsA,Wait("]",13)
[code]

That waits forever unless I enter all 10 chars ??

JonW

#1
I normally use the timeout function and an array, when a single byte is received the RX_Pntr is incremented so you know how many bytes are in the buffer and decide what to do.  Should also work with RSIN
Just declare the buffer as an array, buffer pointer and buffer size.  Once there is a 100ms quiet period it jumps to the timeout label and inspects the Rx_pntr to see if any or how many bytes are in the buffer.

            Rx_Pntr = 0
; Loop here  and wait for the serial buffer to fill

INCOMMING:                  ' loop here once a byte RX'd so we dont miss one
        HRSIn {100,Check}, Rx_Buff[Rx_Pntr]
        Inc Rx_Pntr                         ' Dropped here as byte has arrived
        If Rx_Pntr > BUFFER_SIZE Then
            Rx_Pntr = 0
            HRSOut "Stack Overflow Error!",13,10
        EndIf
        GoTo INCOMMING
Check:                         
                             
        If Rx_Pntr = 0 Then
           
            GoTo START                 ; NO MESSAGE SO GO BACK TO RECEIVE ROUTINE
        EndIf

; IF WE ARE HERE THEN THERE ARE BYTES IN THE BUFFER

        GoTo  MESSAGE_RXD      ; process commands

shantanu@india

#2
Best to use uart interrupt and read byte by byte. This saves processor time.
Does your data come in a stream or pulses?
Explanation is really simple.... suppose your processor works at 4MHz. This means instruction cycle of 1 microsec. To receive one byte @9600 baud it takes about 1 millisec.
So theoretically the processor can execute 1000 instructions while fetching one single byte
Regards
Shantanu

TimB

As per JONW and shantanu@india

I would use an interrupt RX routine and a time out. I always have an interrupt timer running so just add a dec routine for the RX tmr. When you get data you can load a timer and inc the TX buffer pointer. The Tmr routine decruments the tmr. It can set a flag after it decs the timer and it reaching zero.

The main routine just checks for the flag, When it sees it, it empties the buffer and zeros the pointer, It also clears the tmr flag.


Gamboa

Hi,

Here is an example of an interrupt routine as Tim and Shantanu say. I use it when I don't know the format of the frame.

INT_SerialCom:

  CONTEXT SAVE

'************************************************************************
'******* RECEPCION POR USART 1
'************************************************************************
'Cuando llega el primer caracter al buffer de comunicaciones arranco un proceso de timeout
'Si el siguiente caracter no llega antes del timeout quiere decir que trama se ha acabado
'Velocidad de transmision 9600, tiempo de caracter 1ms
'Como el protocolo no tiene ningun tipo de trama fijo, ni se puede adivinar, solo se puede
'determinar el final de trama por timeout.
'La USART1 se usa para comunicar MCB con las sondas. El protocolo de transmision es de tres
'caracteres y el de recepcion de 1. Excepto con el comando C.

  IF RC1IF = 1 THEN
    ComunicacionU1[ContaCommsU1] = RCREG1 'leo byte y lo guardo en la matriz de recepci n
    TMR2IF = 0 'borro flag
    TMR2 = 0
    TMR2IE = 1 'activo interrupcion
    TMR2ON = 1 'activo el contador (la interrupci n ya est  activada)
    INC ContaCommsU1


  ENDIF 'de IF RC1IF=1

'************************************************************************
'******* INTERRUPCION POR TMR2 PARA TIMEOT DE TRAM POR USART1
'************************************************************************
'Usamos el Timer2 para contar un Timeout de recepción. La máxima longitud del paquete es 1 byte,
'excepto cuando se envia el comando de configuracion C, que se recobem 16 bytes. No hay
'metodo alguno de descifrar si es una trama buena o un ruido. Así que la unica forma de
'determinar el fin de trama es que pase un determinado tiempo desde el último carácter recibido.
'A 9600 baudios un carácter es 1ms. Lo que hacemos es que el siguiente byte debe entrar antes de 4ms.
' Normalmente los paquetes de datos vienen todos seguidos. Damos un margen amplio de más de un paquete de longitud
'para que entre el siguiente byte.
' El Timer 2 con el prescaler máximo 1:16 y con el postescaler máximo 1:16 puede dar un tiempo de 4ms (4096us exactamente).
'
' El reloj del sistema va a 64MHz, y alimenta al timer 0 con una frecuencia de Fosc/4=64/4=16MHz
' y con un prescaler de 16, un contador de 8 y un postcaler de 16 da 4ms. Cada carácter que entra
'al bufer de recepción refrescamos el contador para que no salte el timeout.
'Cuando entra un carácter se arrnaca Timer2 y pasados 4ms salta la interrupción por tiempo, esa será el fin de trama.
'Si la trama es de 16 caracteres, despues del ultimo saltará el timer igualmente.
'

  IF TMR2IF = 1 THEN ' Si el timeout ha pasado entonces es fin de trama
    NumeroCaracteresU1 = ContaCommsU1 - 1
    F_TramaRecibidaU1 = 1 'He recibido una trama completa
    TMR2ON = 0 ' Timer0 On/Off Control bit, timer parado
    TMR2IE = 0 'desactivo interrupcion
    TMR2IF = 0 'Limpio el flag de interrupcion del timer1
    ContaCommsU1 = 0 'Inicializo el indice de recepcion, pierdo la informaci n que ha llegado
  ENDIF 'de TMR1IF = 1


  CONTEXT RESTORE

Sometimes you can't choose and the frame is imposed on you, but when I can decide I prefer a more robust frame with frame start and end, length and CRC. All this gives more work but ensures the quality of the transmission.

Regards,
Gamboa
Long live for you

crankshaft

Thanks so much for helping

My bad, I should have pointed out as this is using soft RSOUT / RSIN and not hardware serial MSSP as the MSSP is being used by I2C.

Anyway, I decided to standardise the length of the RSIN data and that simplified everything.


tumbleweed

I think you're confusing things.

RSIN/RSOUT emulate a serial UART peripheral, not the MSSP peripheral.
The MSSP is used for I2C/SPI.
 

Simon

I have been using a system that works well when receiving periodic data on a serial port and doesn't tie up the micro with any delays.

It is basically a slightly adapted version of the interrupt driven buffer code that Les has written. When the first byte of data is received I start a timer and every time another byte is received the timer is reset. As soon as the transmission stops the timer overflows, sets a flag and resets the interrupts (the timer is usually set to something like 2ms, depending on the baud rate).

I've found this a very good way of receiving data without the pic hanging around waiting and you know as soon as data arrives and it doesn't matter how long the packets of data are.

top204

#8
That's a good idea Simon, and will eliminate the waiting for a timeout if fast moving data is arriving. What I do is set a flag within the interrupt handler when it receives a valid byte, then within the program's flow, it does a simple check of the flag and if it is true, it looks for the serial data that has arrived, so there is no blockage in the program's flow until it is actually receiving the data to do something with it.

When working with an AT parser type mechanism, or ASCII data being received, the received flag is not set until the interrupt buffer sees a LF, CR or Null, then it is known that the full data has arrived in the background and the AT parser routines are called and look at the contents of the serial buffer array for the command and parameters etc, instead of waiting for more data to be received.

Teo

Hello friends
Is it possible to post an example?
Thank you in advance,
Teo