Ring buffer hardware Usart


Closed Thread
Results 1 to 9 of 9

Hybrid View

  1. #1

    Default Ring buffer hardware Usart

    Thanks to excellent examples on here I now have a ring buffer working on my 16F88 accepting a stream
    of 9600 baud 12 byte data packets and squirting the bytes back out of the TX as quickly as they arrive.

    That's great but i now need to manipulate the bytes in the packets before they are tramsmitted
    and before they are overwritten by fresh incomming data.

    It's general ideas I am after rather than example code.

    Should I have a seperate RX & TX buffer and then move the bytes from one to the other
    when i'm ready to transmit something.

    Or should i just wait until first packet recd and whilst second packet is coming in manipulate the first packet and transmit it?

    I have to drive a serial lcd in the spare time so need the interrupt driven capability.

    So for example 12 byte packet arrives in 24 byte RX buffer. When first 12 byte packet has arrived and whilst second twelve bytes are arriving the first 12 bytes are manipulated as reqd and then transmitted.

    There is some spare time between packets about 12ms in fact. I don't think the incomming data will cathc
    up with the tx data.

    Another issue is the packets have specific header bytes $87 or $AA, how can i make the ring buffer
    RX routine wait until they appear before starting to recieve the following 11 bytes? I need to have th bytes in a consistent places in the buffer to manipulate them so for instance buffer position 0 always contains $87 and position 12 always contains $AA?

    Can you also have an interrupt driven TX routine?

    Thanks Peter

  2. #2
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Hi Peter,

    If it where me and I had the RAM to spare I would probably go with a dual buffer, one for transmitting and one for receiving, and maintain "pointers" into both indicating where to get and put the next byte.

    You can have a 'start of packet flag' which you set in your receive routine if the received byte is either $87 or $AA. Each time you enter the receive routine you check if the flag is set or not, if it is set you write the received byte to the buffer, if it's not set you check the byte for being $87 or $AA.

    Another idea is to always write the received byte to the buffer and have a 'start of packet pointer' - a byte variable containing the packets 'start adress' in the buffer array. You then use that 'adress' as the starting point for your manipulating.

    You can have a interrupt driven transmit routine. When enabled the TX interrupt fires as soon as there's room in the USARTS TX register. In the interrupt rounting you take your character from your ring buffer and stuff it into the TX-reg, increment your 'pointer' so you know which character is next, decide if you've sent everything or not and either disable the interrupt or leave it enabled. Then you exit the interrupt service routine. As soon as the USART is finished sending the character the interrupt fires again and the process repeats.

    Mostly 'general' advice and not much specifics but hopefully you'll get some ideas from it - as always there are several ways to achieve the same thing.

    Good luck!
    /Henrik.

  3. #3
    Join Date
    Nov 2003
    Location
    Greece
    Posts
    3,802


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by HenrikOlsson View Post
    ... As soon as the USART is finished sending the character the interrupt fires again and the process repeats.
    This can be very tricky and lead to an endless loop of interrupts in case you do not have to send something for a while.

    I never had a reliable Tx Interrupt routine although Darrel was more than helpful when I was trying to make a succesful Tx Int Routine.

    Ioannis

  4. #4
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    This can be very tricky and lead to an endless loop of interrupts in case you do not have to send something for a while.
    Correct, that's why I said you need to decide if you should keep the interrupt enabled or disable it when you are in the ISR. You need a way to figure out if the character just loaded into the TX-reg was the last character to send or not. You can have an end of line character, fixed length or you need to have a variable indicating the location of the last character in the circular buffer.

    You can also do it in software. Have a DataToSend flag which you set when there's data to send. The main routine checks this flag each iteration, if set AND there's room in TX-reg you GOSUB your transmit routine. The transmit routine loads one character from the buffer into the TX-reg, checks if all data is sent, if it is it resets the DataToSend flag if not it leaves it set. Then it returns to the main routine.

    This aproach works OK as long as you don't have a lot of pause statements or other commands which "hangs" the MCU in which case the serial output will slow down - which might or might not matter.

    Out of curiosity Ioannis, did you end up in that endless loop or what happend?

    /Henrik.

  5. #5
    Join Date
    Nov 2003
    Location
    Greece
    Posts
    3,802


    Did you find this post helpful? Yes | No

    Default

    It just kept on triggering the INT and after many changes (which led to a spaggeti program) I gave up.

    I just used the good ol' array transmit like this: Hserout [str array\n].

    When I feel like in a good mood I may try it again and see how it goes.

    As for the Rx Buffer, you ideas are good.

    I once tried the index pointer with a long array and no end of fle check. If characters kept coming then just over writen the previous characters in the array.

    Ioannis

  6. #6
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,520


    Did you find this post helpful? Yes | No

    Default

    Hi,
    I just made a quick and dirty test here using DT_Ints. The Main routine waits for PortB.0 going low, when it does it enables the TX interrupt. The TX interrupt service routine extracts characters from the buffer array and puts them in the TX register of the USART. When it sees a CR it disables the interrupt.
    Code:
    '****************************************************************
    '*  Name    : HWTX.BAS                                          *
    '*  Author  : Henrik Olsson                                     *
    '*  Notice  : Copyright (c) 2010 Henrik Olsson 2010             *
    '*          : All Rights NOT Reserved - use as you see fit.     *
    '*  Date    : 2010-11-16                                        *
    '*  Version : 0.0                                               *
    '*  Notes   : Test for interrupt based USART transmit using     *
    '*          : DT_ints-18 on a 18F4520                           *
    '****************************************************************
    DEFINE LOADER_USED 1                ' We're using a bootloader
    DEFINE OSC 20                       ' 20Mhz x-tal is used
    DEFINE HSER_BAUD 38400              ' Baudrate is 38400
    CMCON = 7                           ' Comparators OFF 
    ADCON1 = %00001111                  ' No analog inputs
    TRISC.7 = 1                         ' RxPin is input
    TRISC.6 = 0                         ' TxPin is output
    TRISB.0 = 1                         ' PortB.0 is input
    TxBuffer VAR BYTE[128]              ' Transmit buffer array.
    TxPointer VAR Byte                  ' Pointer into the above array
    INCLUDE "DT_INTS-18.bas"            ' Include Darrels interrupt engine.
    INCLUDE "ReEnterPBP-18.bas"         ' And the stub needed for interrupts in PBP 
     
    ASM                                 ; Set up the USART transmit interrupt.
    INT_LIST  macro     ; IntSource,   Label,    Type, ResetFlag?
            INT_Handler    TX_INT,   _USART_TX,   PBP,    no
        endm
        INT_CREATE                      ; Creates the interrupt processor
    ENDASM
    Boot:
      HSEROUT ["Program start",13]        ' A dummy HSEROUT here to init the USART and show we're alive.
     
      ' Now load the array with something we want to send. The string is, in this case, terminated with a CR (13).
      ArrayWrite TxBuffer,["This is the TX Array Buffer and we're sending it with an interupt based routine",13]
     
    Main: 
      If PortB.0 = 0 then                 ' PortB.0 going low is what
        TxPointer = 0                     ' Initialize pointer to beginning of array
    @   INT_ENABLE   TX_INT               ; Enable USART TX interrupt  
        Pause 100                         ' Simple debounce for the button
      ENDIF
      Goto Main                           ' And do it again.
     
    USART_TX:
      TXREG = TxBuffer[TxPointer]       ' Load character from array into USART TX register
      If TxBuffer[TxPointer] = 13 then  ' If that character was a CR we're done so we.....
    @   INT_DISABLE  TX_INT             ; ...disable the any further USART TX interrupts.
      ELSE                              ' If the character was not a CR we.....
        TxPointer = TxPointer + 1       ' ...increase prepare to send the next character.
      ENDIF
    @ INT_RETURN                        ; And exits the interrupt.
    Seems to work here, try it out if you like.

    /Henrik.

Members who have read this thread : 2

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts