Speed USART

General discussion relating to the library modules supplied with the compiler

Moderators: David Barker, Jerry Messina

Post Reply
AndrF
Posts: 8
Joined: Fri Feb 06, 2015 5:50 am

Speed USART

Post by AndrF » Wed May 06, 2015 12:57 pm

I use pic18f26k20 & usart.bas. Baudrate = br115200. The transfer of an array of 1024 bytes takes approximately 0.3 seconds (controller - PC). It is possible to speed up? Sorry for bad English...

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Speed USART

Post by Jerry Messina » Wed May 06, 2015 1:21 pm

Your English is fine.

At 115200 baud each byte takes approx 86usec to transfer, so it should be possible to send 1024 bytes in 88msec or so... at least a lot faster than 300msec.

A few questions -
1) What's the main clock frequency for the 26K20?
2) How are you coding the array transfer? Could you show a bit of code?
3) I assume you're not using any kind of handshaking, correct?

AndrF
Posts: 8
Joined: Fri Feb 06, 2015 5:50 am

Re: Speed USART

Post by AndrF » Wed May 06, 2015 1:38 pm

Jerry Messina wrote:Your English is fine.
Yandex online translator...
Jerry Messina wrote: A few questions -
1) What's the main clock frequency for the 26K20?
Device = 18F26K20
Clock = 16
...
USART.SetBaudrate(br115200)
Jerry Messina wrote: 2) How are you coding the array transfer? Could you show a bit of code?
packets of 128 bytes + 5 bytes (checksum, length...):

Code: Select all

Sub WriteUartData()
	Dim iCrcM0 As Word, i As Byte

        ' Calc checksum
	iCrcM0 = 0
	For i = 0 To g_UsartBuffer(2) - 1
		If i <> 3 AND i <> 4 Then
			iCrcM0 = iCrcM0 + g_UsartBuffer(i)
		EndIf
	Next
	g_UsartBuffer(3) = iCrcM0.Byte1
	g_UsartBuffer(4) = iCrcM0.Byte0
        ' Send Array
	For i = 0 To g_UsartBuffer(2) - 1
		USART.WriteByte(g_UsartBuffer(i))
	Next
End Sub
Jerry Messina wrote: 3) I assume you're not using any kind of handshaking, correct?
Correct.

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Speed USART

Post by Jerry Messina » Wed May 06, 2015 2:33 pm

For a packet of 133 bytes (128 + 5), WriteUartData() takes approx 12msec to compute the xsum and send the packet.

You could improve on the xsum calculation slightly but that's about it (right now it takes about 920usec).
The rest of the time (~11msec) is determined by the baudrate, so you'd have to run the uart faster to have any big effect on the time.

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: Speed USART

Post by SHughes_Fusion » Mon May 11, 2015 7:54 am

The k22 series will happily run at 64MHz if you enable the PLL - I've got a few running at this speed in various application which are talking to an RN42 bluetooth module at 115200 baud.

You can also set faster baud rates quite easily but you will have to configure the UART yourself to achieve this, or edit the module to add calculations for other baud rates. You can easily get in to the mega-bauds with the clock speed you are running at.

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Speed USART

Post by Jerry Messina » Mon May 11, 2015 9:29 am

That's a good point. Bumping up the clock speed might help a lot.

I should have worded my previous post a bit differently. His packet routine takes about 12ms/128 byte packet, so it should be about 96ms to transfer 8 packets (1K). The OP said it was taking 300ms to transfer 1K of data.

Most of the time is being spent elsewhere, not in the transfer.

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: Speed USART

Post by SHughes_Fusion » Mon May 11, 2015 9:34 am

That is a point, we could do with seeing more of the code to help optimise it further.

I did wonder if, for example, testing I<3 or I>4 would be more efficient than testing i<>3 and I<>4?

I'd also comment that using an interrupt-based buffered routine would allow the last lot of data to be being sent while the checksum was being calculated for the next lot - if you see what I mean. I'd guess that a lot of time is being spent here just waiting for the transmit buffer to empty.

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Speed USART

Post by Jerry Messina » Mon May 11, 2015 10:27 am

>I did wonder if, for example, testing I<3 or I>4 would be more efficient than testing i<>3 and I<>4?

You can speed up the xsum calculation portion of WriteUartData() a good bit, but that isn't really the limiting factor. Even sending the data isn't too bad... you're only ever waiting one char time at worst. This is a case where using transmit interrupts won't help much. Likely it'd actually make it slower unless as you say, you could work on the next packet while transferring the previous. Still only saves a few hundred usec at best.

WriteUartData() isn't the issue. It's elsewhere. 200 of the 300ms is being spent doing something, but not transferring the data.

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Speed USART

Post by Jerry Messina » Mon May 11, 2015 11:28 am

For kicks I put together some examples showing optimizations of the checksum portion of WriteUartData().

Code: Select all

clock=16
dim g_UsartBuffer(128 + 5) as byte

// original routine: 862us
Sub WriteUartData()
   Dim iCrcM0 As Word, i As Byte

   ' Calc checksum
   iCrcM0 = 0
   For i = 0 To g_UsartBuffer(2) - 1
      If i <> 3 AND i <> 4 Then
         iCrcM0 = iCrcM0 + g_UsartBuffer(i)
      EndIf
   Next
   g_UsartBuffer(3) = iCrcM0.Byte1
   g_UsartBuffer(4) = iCrcM0.Byte0
End Sub

// opt 1: remove tests from loop = 670us
Sub WriteUartData1()
   Dim iCrcM0 As Word, i As Byte
   dim len as byte

   ' Calc checksum
   iCrcM0 = 0
    ' set packet xsum bytes to 0 (adding zero doesn't change result)
   g_UsartBuffer(3) = 0
   g_UsartBuffer(4) = 0
   
   ' precompute len
   len = g_UsartBuffer(2) - 1
   ' xsum the packet
   For i = 0 To len
      iCrcM0 = iCrcM0 + g_UsartBuffer(i)
   Next
   ' put xsum 
   g_UsartBuffer(3) = iCrcM0.Byte1
   g_UsartBuffer(4) = iCrcM0.Byte0
End Sub

// opt 2: remove tests from loop and remove index calcs = 303us
Sub WriteUartData2()
   Dim iCrcM0 As Word
   dim len as byte

   ' Calc checksum
   iCrcM0 = 0
    ' set packet xsum bytes to 0 (adding zero doesn't change result)
   g_UsartBuffer(3) = 0
   g_UsartBuffer(4) = 0
   
   ' precompute len
   len = g_UsartBuffer(2)

    FSR0 = addressof(g_UsartBuffer)
    while (len <> 0)
      iCrcM0 = iCrcM0 + POSTINC0
      len = len - 1
    end while
    
   g_UsartBuffer(3) = iCrcM0.Byte1
   g_UsartBuffer(4) = iCrcM0.Byte0
End Sub

// test data
dim i as byte
// fill a packet w/data
for i = 0 to bound(g_UsartBuffer)
    g_UsartBuffer(i) = i
next
// set packet size
g_UsartBuffer(2) = 128 + 5

writeuartdata()     // 862us
writeuartdata1()    // 670us
writeuartdata2()    // 303us
I wasn't sure about the original logic and exactly what data the routine should be checksumming, so I did the entire 133 bytes.

You can get a decent boost just by being smart about what's in the for loop. We gained almost 200us just by recoding the for loop logic.

The last routine shows the benefits you can gain by taking advantage of the PIC18 low-level FSR instructions. The routine is almost three times as fast as the original.

User avatar
octal
Registered User
Registered User
Posts: 586
Joined: Thu Jan 11, 2007 12:49 pm
Location: Paris IDF
Contact:

Re: Speed USART

Post by octal » Mon May 11, 2015 12:50 pm

Be careful with this implementation. The loop variable "i" is a BYTE type variable, that means that if your g_UsartBuffer(2) variable is equal to ZERO, the loop will be executed from 0 to (-1) :wink:

Also, if you send frequently long buffers of data (with more than 1 byte), you better avoid the systematic test "If i <> 3 AND i <> 4 Then" in the loop and prefer to write your code as:

Code: Select all


Sub WriteUartData()
   Dim iCrcM0 As Word, i As Byte

   ' Calc checksum
   iCrcM0 = g_UsartBuffer(0) + g_UsartBuffer(1) + g_UsartBuffer(2)
   For i = 5 To g_UsartBuffer(2) - 1
         iCrcM0 = iCrcM0 + g_UsartBuffer(i)
   Next
   g_UsartBuffer(3) = iCrcM0.Byte1
   g_UsartBuffer(4) = iCrcM0.Byte0
End Sub


Post Reply