News:

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

Main Menu

Buffered Serial transmit on 18F27K42

Started by JohnB, Jul 19, 2022, 04:55 PM

Previous topic - Next topic

JohnB

I have been trying to extend the Buffered USART code Les wrote for the K series devices to support buffered TX.

I attach the extended Buffered USART library file.

My interrupt handler for the library is shown below:

' Check the USART2 RX interrupt
'
    If USART2_RX_Flag() = True Then                     ' Was it a USART1 byte receive that triggered the interrupt?
        USART2_bRXByte = U2RXB                          ' Yes. So place the byte received into USART2_bRXByte
        Inc USART2_bIndexIn                             ' Move up the buffer
        If USART2_bIndexIn >= _cUSART2_BufferSize Then  ' End of buffer reached?
            USART2_bIndexIn = 0                         ' Yes. So reset USART2_bIndexIn
        EndIf
        USART2_wFSR1 = AddressOf(USART2_bRingBuffer)    ' Point FSR1L\H to USART1_bRingBuffer
        USART2_wFSR1 = USART2_wFSR1 + USART2_bIndexIn   ' Add the buffer position to FSR1L\H
        INDF1 = USART2_bRXByte                          ' Place the received byte into the buffer
        Global_tByteInBuffer2 = True                    ' Indicate that there is a byte in the buffer
    EndIf

' Check the USART2 TX Interrupt

    If USART2_TX_Flag() = true then                     ' Has the TX buffer triggered
      PIE6bits_U2TXIE = false                           ' disable further Tx interrupt
      Inc USART2_bTxIndexOut                            ' move down the buffer
      USART2_bTxIndexOut = USART2_bIndexOut & $3F       ' Reset if reached ond of buffer
      U2TXB = USART2_bTxBuffer[USART2_bIndexOut]        ' Place next byte in the TX Register
      if USARt2_bTxIndexIn = USART2_BTxIndexIn then     ' if buffer is empty
        global_tByteOutBuffer2 = false                  ' Indicate the buffer is empty
      else
        PIE6bits_U2TXIE = true                          '
      endif
      USART2_TX_Flag() = false
    EndIf

At present all it is doing is transmitting the same character over and over.

Your help would be much appreciated

JohnB

tumbleweed

#1
For uart2 this part needs changing:
$define USART2_RX_IntEnable()  PIE3bits_U1RXIE = 1      ' Enable interrupt on USART2 receive
$define USART2_RX_IntDisable() PIE3bits_U1RXIE = 0      ' Disable interrupt on USART2 receive

'------------------------------------------------------------------------------
$define USART2_TX_IntEnable()  PIE3bits_U1TXIE = 1      ' Enable interrupt on USART2 transmit
$define USART2_TX_IntDisable() PIE3bits_U1TXIE = 0      ' Disable interrupt on USART2 transmit

should be:
$define USART2_RX_IntEnable()  PIE6bits_U2RXIE = 1      ' Enable interrupt on USART2 receive
$define USART2_RX_IntDisable() PIE6bits_U2RXIE = 0      ' Disable interrupt on USART2 receive

'------------------------------------------------------------------------------
$define USART2_TX_IntEnable()  PIE6bits_U2TXIE = 1      ' Enable interrupt on USART2 transmit
$define USART2_TX_IntDisable() PIE6bits_U2TXIE = 0      ' Disable interrupt on USART2 transmit


also:
Proc USART2_InitTxInterrupt()
    Global_tByteOutBuffer2 = False                           ' Reset the byte in buffer flag
    USART2_bTXIndexIn = 0                                     ' Clear the buffer internal pointer
    USART2_bTXIndexOut = 0                                    ' Clear the buffer external pointer
    USART2_RX_IntEnable()                                   ' Enable interrupt on USART2 receive
 EndProc

should be:
Proc USART2_InitTxInterrupt()
    Global_tByteOutBuffer2 = False                           ' Reset the byte in buffer flag
    USART2_bTXIndexIn = 0                                     ' Clear the buffer internal pointer
    USART2_bTXIndexOut = 0                                    ' Clear the buffer external pointer
    ' let output routine enable tx intr
EndProc

You might do better to just add the bytes to the TX buffer...
sub __HRSout2__()
   PP0 = Wreg
   USART2_TX_IntDisable()
   Inc USART2_bTxIndexIn
   USART2_bTxIndexIn = USART2_bTxIndexIn & $3F
   Usart2_bTxBuffer[USART2_bTxIndexIn] = PP0
   Global_tByteOutBuffer2 = true
   USART2_TX_IntEnable()
   WreG = pp0
EndSub

JohnB

Thanks @tumbleweed, I thought I had picked up all the register changes.
I'll try your suggestions and report back.
JohnB

tumbleweed

There are a few other things you'll have to do to get interrupt driven TX working.

First, in the ISR handler qualify the TX IF flag with IE...
if PIR6bits_U2TXIF and PIR6bits_U2TXIE then
  // handle intr
endif
Otherwise you may try to inadvertently transmit a byte when you get an RX intr.

Also, you'll have to add code to the TX byte output routine to account for the fact that there may not be room in the output buffer to add anything else and wait until there is.
Imagine if you have an output buffer of 64 chars and you write an 80 char string... it'll fail.
Likewise if you write two 64 char strings back to back without allowing time for the first transmission it'll fail.

Personally I find interrupt-driven TX not worth all the trouble it causes.

JohnB

I have basically got it working but it is only passing one byte over. I think you are right, too much hassle.
The reason I did this in the first place is that occasionally I get an incorrect message where one bit in a byte is wrong.  I am doing nothing else but just sending alternately one of two messages every 500mS at 115200 baud.  The circuit at present is only a lash-up with probably insufficient decoupling. 
JohnB

keytapper

The baud rate is a kind of complicated standard. The multiple of 30 doesn't fit well on nowadays MCU. There could be much better way on using a multiple of 1000. So the case of using 115200 baud is not the best one, because the error is 3,5%. That's prone to catch spurious characters.
Ignorance comes with a cost

tumbleweed

It's only 3.5% if you don't set the divisor correctly.
You can usually get it much lower than that.

top204

#7
With the recent improvement of the compiler's Baud rate calculations I did, it produces a 0.79 accuracy for 115200 Baud when the device is operating at 64MHz. The improved Baud rate generator is in versions 4.0.2.0 onwards of the Positron8 compiler. In the assembler listing, it always shows the actual Baud rate and the error percentage beside the mnemonics that set up the particular USART, if the appropriate serial commands are used in the program's listing. i.e. HRsout, HSerout, HRsin, HSerin, HRsout2, HRsin2 etc...

; UART1_ACTUALBAUD = 114285.71
; UART1_BAUDERROR = 0.79
    bsf BAUDCON,PP_BRG16
    movlw 0x22
    movwf SPBRG
    clrf SPBRGH
    movlw 0x20
    movwf TXSTA
    movlw 0x90
    movwf RCSTA

Pressing the F2 key after a successful compile will open the assembler listing produced by the compiler, and scroll down to the USART setup section to view them.

The compiler chooses the best Baud generator multiplier by sending the Baud rate to them and comparing which one is more accurate.

With the newer devices that have a different Baud rate multiplier mechanism, the accuracy is even better. For example, on the PIC18F16Q41 device operating at 64MHz, the error percentage is only 0.08 at 115200 Baud:

; UART1_ACTUALBAUD = 115107.92
; UART1_BAUDERROR = 0.08
    movlb high(U1CON0)
    movlw 0x8A
    movwf U1BRGL
    clrf U1BRGH
    movlw 0xB0
    movwf U1CON0
    movlw 0x80
    movwf U1CON1

JohnB

I am using 4.0.2.0 of the complier, the Actual Baudrate is calculated as 115107.92 which results in a baud rate error of 0.08
As I am using short messages typically 16 bytes long is this likely to result in odd bit errors?
JohnB

tumbleweed

Not unless the osc freq is way off.
A uart can tolerate up to 5% error (total of both sides) before bit errors usually occur.

top204

When transmitting via an interrupt, remember that the interrupt will be triggered as soon as a byte is entered into the TX SFR, so the tranmsit subroutine cannot disable/enable the TX interrupt, otherwise pieces will be missing from it via frame errors etc...

You could try a flag method to indicate that something has been placed in the TX buffer for the interrupt, or do as I did with the RX serial and just make a comparison of the buffer index positions within th einterrupt and if they are the same there is nothing in the buffer, but if they are different, there is something in there so access it and transmit it.

Quite a few years ago I created an RX/TX interrupt buffer mechanism, but I have had a look on my drives and cannot find the code.




tumbleweed

Quoteso the tranmsit subroutine cannot disable/enable the TX interrupt, otherwise pieces will be missing from it via frame errors etc...
Perhaps you meant you can't disable the transmitter (TXEN).

TXIF will be set when the TXREG is empty and you can write to it. Disabling the interrupt doesn't effect the byte being transmitted.