Timing hserin input - ideas?

johndk

Senior Member
I'm running a time sensitive loop on a 28x2. I'm using a pair of radios to communicate via hserin/out and have just two seconds to exchange information. The incoming information to the "base station" is of indeterminate length, generally multiples of 56 bytes but not always. I'm trying to conserve as much time as possible and thought I had a clever way of knowing when the receive burst was done and ready for processing. This, and several variations, was what I came up with as my solution.

pause 10
do
hserflag = 0
pause 20 ;tried various pause timings
loop until hserflag = 0

And it ALMOST worked. Unfortunately, no matter how I varied this loop, and even this one

pause 10
ptr = sp_flag_status
do
@ptr = hserptr
pause 10
@ptr = @ptr - hserptr
loop until @ptr = 0

It seems to drop bytes on the incoming stream. The dropped bytes seem random, not just being the first few or last few, for instance. Although in the first example, with certain timings, it would seem to drop just the last one or more bytes but I think that's just because I had a long enough initial pause to capture most of the stream before the loop.

I am guessing these loops interfere with the background receive in some way. I thought that background receive was independent of main program activity and hence my BRILLIANT solution(s). Although not sure, I have a feeling that accessing either the flag or the pointer causes a hiccup.

Nevertheless, I would really like to know the microsecond my byte stream receive is finished so I can make the most of the remaining time. An indiscriminate pause would have to accommodate the largest expected burst and will therefore waste time on most exchanges. My other alternative is to begin processing the receive buffer before the entire transmission is finished, but that complicates the programming and I'm also bumping at the limit of my code space.

So, I'd welcome ideas on ways to (simply) know when hserin has reached the end of my byte stream of indeterminate length.

I hope you enjoy my little challenges as much as I do ....(NOT.. This one cost a week of trying to track down the problem.)

John
 

hippy

Ex-Staff (retired)
I recall having used the 'hserflag not changed' trick and believe it worked but I am not at all sure about the second chunk of code.

The best way to check if some number of bytes has been received is to check when hserptr has adjusted by that amount. You might have to wait for some fewer bytes to determine how many bytes there are in the full packet.

I guess your second chunk of code attempts to do that. Not sure why you are using scratchpad in that process. It might work but would depend on where hserptr was when starting and how many actually received.

I would say put aside the project you are currently working on. Develop a test project to get the packet receiving handled reliably then integrate that back into your actual project. It will be much easier for others to understand, analyse and even test your code themselves as a standalone project than a part of something bigger.
 

johndk

Senior Member
If I knew how many bytes were being transmitted, the task would be simple. I could just check the hserptr until the right value is present. But that's my problem. Sometimes I'll get 56 bytes, sometimes a multiple of 56, and sometimes 11. I suppose I could check for all of these values, no that I think of it. But in future iterations of the code, there will be quite a bit more variation in the total burst size. So sooner or later, I'll have to solve the problem.

I'm using the scratchpad in the second code snip simply because I'm short on variables. I actually use the scratchpad quite a bit this way as it eliminates the variable number ceiling.

I've already done this in a small code test bed. This is how I was able to confirm what was happening (or not). Can anyone confirm that touching the hserinflag or the hserptr during a background receive causes problems? If so, it should be added to documentation.

But most of all, I'm interested if anyone has worked out a clever way to determine the temporal end of a byte stream.
 

hippy

Ex-Staff (retired)
Reading hserflag and hserptr should not cause problems. Writing hserflag should only affect usability of its state; for example clearing the flag just after it was set might mean missing that it had been set etc. Writing hserptr will affect where any data received is placed.

When I have written code which has waited for an unknown size packet to come in I have usually looked for hserptr to have changed, then PAUSE for the time during which another byte would have been received and check if it has changed, if so repeat, otherwise take it that all bytes have been received. Something like ...

Code:
WaitForFirstByte:
  ptr = hserPtr
  Do
  Loop Until ptr <> hserptr
WaitUntilAllBytesReceived:
  Do
    w0 = hserPtr
    Pause 10
  Loop Until w0 = hserptr
That PAUSE time can be reduced to near whatever the byte time is if data is sent back-to-back but will have to cater for any inter-byte gap times there may be.

I have used long pause times where the need is only to process the packet before the next one arrives. If a packet arrives once a second, and it takes 250ms to process, one can replace the second loop with a wait up to 750ms and often less.

I would have thought using hserflag rather than change of hserptr would have worked equally well ...

Code:
WaitForFirstByte:
  ptr = hserptr
  hserflag = 0
  Do
  Loop Until hserflag <> 0
WaitUntilAllBytesReceived:
  Do
    hserflag = 0
    Pause 10
  Loop Until hserflag = 0
If you are doing similar and having problems it might be worth posting your test code.
 

johndk

Senior Member
Actually, that was the logic I was using. And I was assuming that the accessing the flag or pointer would not affect the actual bytes captured. I was resetting the flag and then allowing a "guaranteed" pause to allow it to be reset. Alternately, while checking the pointer, you'll notice that I don't reset it. I simply check whether it has changed after a pause. I was having a heck of a time, assuming my radios were not working properly and finally discovered that my "wait for completion" loops were causing the problem ... somehow. I'm going to be traveling for the next few days. But will post my test code for a look.

In the meantime, I need a quick (and dirty, if necessary) 32 bit multiply routine. I've checked the posts I could find and found nothing particularly appealing. I need to multiply a constant 975404 by a number that varies from 0 to 50000. Any ideas where I can find such?
 

BillBWann

Member
In the meantime, I need a quick (and dirty, if necessary) 32 bit multiply routine. I've checked the posts I could find and found nothing particularly appealing. I need to multiply a constant 975404 by a number that varies from 0 to 50000. Any ideas where I can find such?
The code below demonstrates a 32 bit by 32 bit subroutine I wrote some years ago for an 08M2. Its quite compact and quick. This demo simply multiplies your specified constant 975404 by 50000.

I've also included a subroutine which can convert the 64 bit result to decimal and print it out but note the limitations discussed in the code comments.

I hope its useful for you.


Code:
#rem
This is a demo program for the 32 bit by 32 bit multiply subroutine.

It uses all the registers from b13 up to b35 for the multiplication - made up as follows

b20-b23	32 bit no, M1
b24-b27	32 bit no, M2
b28-b35	64 bit result
b13-b19	temporary used variables

With the exception of b13, b18 & b19, all of these registers can't be easily reassigned to other registers without studying the code in detail as they are referenced using bptr or the register is referenced directly in the code by name.

The 64 bit result can be converted back to decimal for debug purposes by using Prt64 but note that it not only destroys the 64 bit result in the process but it also uses up to 10 more registers above b35 to temporarily store the decimal digits. It works by repeatedly dividing the 64 bit result by 10 and pushing the remainder onto the stack until it has reduced the 64 bit result to zero.
	
#endrem

#picaxe 08m2
'#no_data

symbol Tmpb1=b18
symbol Tmpb2=b19
symbol Tmpb3=b13
symbol TempW1=W7
symbol TempW2=W8
symbol LSBpointer=b16
symbol MSBpointer=b17
symbol M1Low=W10
symbol M1High=W11
symbol M2Low=W12
symbol M2High=W13

M1Low=57900
M1High=14  	'M1=975404
M2Low=50000
M2High=0    	'M1=50000 and the product of M1 & M2 should = 48770200000
	
For bptr=28 to 35	'Zero Result (not really required in this case as already zeroed)
	@bptr=0
next bptr
	
gosub Mul32by32
gosub Prt64		'Note that this routine really only used for debug as it destroys the answer in the process of printing it out.

stop

'--------------------------------------------------------------------------------------------
Mul32by32:
'Multiply the 32 bit munmers stored in M1 & M2 & store answer in b28 - b35. Needs to be cleared initially.

For Tmpb1=20 to 23
	bptr=Tmpb1+8	'needs to progress along answer location
	For Tmpb2=24 to 27
		peek tmpb1,b16
		peek tmpb2,b17
		TempW1=b16*b17
		TempW2=@bptr+b14
		tmpb3=bptr
		do
			@bptrinc=b16
			TempW2=TempW2/256
			TempW2=b16+@bptr
		loop while b17>0
		@bptrinc=b16
		
		bptr=tmpb3+1
		TempW2=@bptr+b15
		tmpb3=bptr
		do
			@bptrinc=b16
			TempW2=TempW2/256
			TempW2=b16+@bptr
		loop while b17>0
		@bptrinc=b16
		bptr=tmpb3
	next tmpb2
next tmpb1
return

'-----------------------------------------------------------------------------------------
Prt64:
'Print out the 64 bit number starting at bptr=28 in decimal but destroys it in the process (debug only)

bptr=36		'set bptr to bottom of the remainder stack

MSBpointer=35	'MSB of long 64 bit word
LSBpointer=35-8+1	'LSB of 64 bit word

do
	b15=0	'set initial remainder to zero
	for Tmpb2=MSBpointer to LSBpointer step-1	'step through each digit of the long word
		peek Tmpb2,b14	'get the digit
		Tmpb1=TempW1/10		'calculate the quotient
		poke Tmpb2,Tmpb1		'write quotient back into long word 
		b15=TempW1//10	'carry the remainder onto the next digit
	next Tmpb2
	@bptrinc=b15				'push remainder of long word/10 onto stack
	peek MSBpointer,Tmpb1		'get the MSB of the remaining long word
	if Tmpb1=0 then: MSBpointer=MSBpointer-1:endif	'move MSB pointer down if current digit is zero
loop until MSBpointer<LSBpointer	'Finish when the original long word is reduced to zero

dec bptr
do		'print out the result
	Tmpb1=@bptrdec
	if Tmpb1<10 then
		sertxd(#Tmpb1)
	else
		Tmpb1="A"-10+Tmpb1
		sertxd(Tmpb1)
		endif
loop until bptr<36
sertxd (cr,lf)

Return
 

AllyCat

Senior Member
Hi,

It may depend whether you want the final result in binary (eg for a frequency synthesiser) or decimal. For a decimal result, it might be more efficient to calculate directly in BCD

But since your calculation is only to multiply 20 bits by 16 bits, the maximum result is less than 40 bits (5 bytes) and can be calculated in a few lines of PICaxe Basic. Here's my (largely untested) attempt which uses bill's variables and routine to convert to decimal:

Code:
#picaxe 08M2
symbol M1Low = W10
symbol M1High = W11
symbol M2low = W12

M1Low = 57900
M1High = 14  		'M1=975404
M2Low = 50000		'M2=50000 and the product of M1 & M2 should = 48770200000

w0 = M1Low * M2Low		; Low word
w1 = M1Low ** M2Low		; Partial Middle word
w2 = M1High ** M2Low		; Preliminary High word
w3 = M1High * M2Low		; Partial Middle word
w1 = w1 + w3			; Final Middle word
if w1 < w3 then
	inc w2			; Carry if overflowed
endif	
sertxd(#b4," ",#b3," ",#b2," ",#b1," ",#b0,cr,lf)  

; Now convert to decimal
bptr = 28
for b10 = 0 to 4
	peek b10,@bptrinc
next 
call Prt64
end
Cheers, Alan.
 

hippy

Ex-Staff (retired)
I took the opportunity to test end of packet detection and it all worked okay for me.

First an 08M2 test program which sends out a packet of between 1 and 26 characters ( plus CR and LF ) at a rate of roughly one character every 10ms, and a packet every second. The packets can be observed using the Terminal ...

Code:
#Picaxe 08M2
#Terminal 4800

Do
  For b0 = 1 To 26
    Gosub SendPacket
  Next
Loop

Sendpacket:
  For b1 = 1 To b0
    b2 = "A" - 1 + b1
    SerTxd( b2 )
    Pause 10
  Next
  SerTxd( CR, LF )
  For b1 = b0 To 100
    Pause 10
  Next
  Return
Then a 282 program using background receive to look for the start and end of packet, and dump the packet when received. Using 'hserptr' and 'hserflag' respectively with a 20ms response after last byte received ...

Code:
#Picaxe 28X2
#Terminal 9600

; 08M2 (C.0) Leg 7 ---> Leg 18 (C.7) 28X2

HSerSetup B4800_8, %111
Do
  Gosub WaitForFirstByte
  Gosub WaitForEndOfBytes
  Gosub DumpPacket
Loop

WaitForFirstByte:
  ptr = hSerPtr
  Do : Loop Until ptr <> hSerPtr
  Return

WaitForEndOfBytes:
  Do
    w1 = hSerPtr
    Pause 20
  Loop Until w1 = hSerPtr
  Return

DumpPacket:
  SerTxd( "=" )
  Do
    SerTxd( @ptrInc )
  Loop Until ptr = hSerPtr
  Return
Code:
#Picaxe 28X2
#Terminal 9600

; 08M2 (C.0) Leg 7 ---> Leg 18 (C.7) 28X2

HSerSetup B4800_8, %111
Do
  Gosub WaitForFirstByte
  Gosub WaitForEndOfBytes
  Gosub DumpPacket
Loop

WaitForFirstByte:
  ptr = hSerPtr
  hSerFlag = 0
  Do : Loop Until hSerFlag <> 0
  Return

WaitForEndOfBytes:
  Do
    hSerFlag = 0
    Pause 20
  Loop Until hSerFlag = 0
  Return

DumpPacket:
  SerTxd( "=" )
  Do
    SerTxd( @ptrInc )
  Loop Until ptr = hSerPtr
  Return
No lost or corrupt characters that I noticed.
 

johndk

Senior Member
Thanks all who had suggestions for my 32 bit multiplication.

Below is the code I used to look for end of packet. My code sometimes gave me the correct number of bytes. But mostly it would drop 3 - 10 bytes out of a 55 byte transmission. The dropped bytes were almost always near the beginning of the transmission. When I changed the eot check for just a long pause, I got consistent, and accurate results. Hippy's results seem to contradict mine. The only real difference I can see is that I'm using a 28x2.
 

Attachments

Top