i think a problem with scratchpad overflow

wapo54001

Senior Member
I have a 20X2 accepting bytes of data via interrupt-monitored hserin with the bytes being placed in the scratchpad.

When I exceeded 127 bytes of data the scratchpad overflows and the program goes out of control, so I added some code that I thought would solve the problem.

Losing some data is OK but I don't want to re-read bytes previously read into the scratchpad; I would like to reset hserptr and ptr to zero and clear the scratchpad whenever hserptr approaches the 127 byte limit.

I've tried to do that with this code


Code:
		IF hserptr > 120 THEN
			hserptr = 1
			ptr = 1
			@PTR = 0
		ENDIF

but it does not seem to work, maybe because I'm only zeroing one byte at ptr. Can anyone suggest a way to make this code work so that the scratchpad never wraps around and the program does not reread bytes of data that were inserted previously? I'm thinking I'd like to zero out the scratchpad entirely but any other solution that works would be fine with me.
 

srnet

Senior Member
The scratchpad is described as a 'circular buffer' meaning that when it overflows it goes back to the start, automatically.

So your program (which we have not seen the code for !!) should not go out of control when the scratchpad overflows.
 

hippy

Technical Support
Staff member
You could handle 'serinflag' by interrupt and disable HSERIN if hserptr gets too far into the buffer. An easier solution to avoid continually reading through the buffer would be to check that you are not doing that, by tracking where you were and where you are or how many bytes you have processed from the scratchpad.
 

Goeytex

Senior Member
It seems to me that any "out of control" situation (whatever that really means?) can be eliminated as hippy suggests above. There are likely other ways as well. However, specific code recommendations/corrections cannot really be made without seeing your complete code, specifically how the interrupt routine or other routines are processing the data. It would also be helpful to know how fast the data is arriving and any other pertinent details of the incoming data.

Is the data a continuous stream or does it come in packets? What is the baud rate?. et al.
 

wapo54001

Senior Member
I have tried a variety of changes to the code, nothing works. You can see in the terminal display when things go wrong. While it's working properly, the terminal displays a single ascii byte code for each command received from the encoder or the IR which arrives by hserin. After about 120~150 commands, the program suddenly takes off and repeats commands on its own and will continue to do so until I reset by shutting off power. When powered back on, it behaves properly again until a certain number of commands have passed through and then it takes off again.

Here is the entire program:


Code:
#Picaxe 20X2
#No_Data
#No_Table
SETFREQ m16

'Adapted from matherp's rotary encoder code on 28X2

'PIN ASSIGNMENTS
' Encoder switch pulled high and connected to C.1 
' Encoder output1 pulled high and connected to C.2 
' Encoder output2 pulled high and connected to C.3 
'
'SYMBOLS

'STORAGE SYMBOLS
'W0
	'b0
		SYMBOL ResetEnc = bit0
		SYMBOL SwitchIsOn = bit2
		SYMBOL RotatedLeft = bit3
		SYMBOL RotatedRight = bit4
	SYMBOL Scratch = b1
'W1	
	SYMBOL Switched = b2
	SYMBOL Ctrl_Mode = b3	'1=Vol, 2=Bal, 3=Cal
	Ctrl_Mode = 1
'W2	
	SYMBOL CmdCode = b4
	'SYMBOL = b5
symbol loopcounter = w13
loopcounter = 0
SYMBOL HoldPot1 = W12
SYMBOL HoldPot2 = W13
SYMBOL Pot1Val = W14	
SYMBOL Pot2Val = W15

'OTHER SYMBOLS
SYMBOL InterruptMask = %00001110 ' allow interrupts on C.3,C.2,C.1
SYMBOL Rotated =       %00000110
SYMBOL Clicked =       %00000001 ' mask to test for c.1 high
'
SYMBOL True = 1
SYMBOL false = 0
'
'INITIALIZE
Pot1Val = 20
Pot2Val = 20

'I2C setup
hi2csetup i2cmaster, %10100000, i2cfast_16, i2cbyte

HSERSETUP B9600_16,5	'CHANGED B,1 TO B,5 TO INVERT RECEPTION
dirsC = 0				'CHANGED DIRSA TO DIRSC
ptr = 0
SwitchIsOn = false
RotatedRight = false
RotatedLeft = false
ResetEnc = True
SETINT OR 0,InterruptMask,C	'CHANGED INTERRUPTMASK,A TO INTERRUPTMASK,C
DO
	DO WHILE ptr<>hserptr
	'	IF hserptr > 1 THEN
	'		hserptr = 0
	'		ptr = 0
	'		@ptrinc = 0
	'	ENDIF
		CmdCode = @ptrinc
 'Do required control actions here
	IF Ctrl_Mode = 1 THEN	'Volume Setting
		IF CmdCode = "<" OR CmdCode = "D" THEN
			Pot1Val = Pot1Val MIN 6 - 5 
			Pot2Val = Pot2Val MIN 6 - 5
		ELSEIF CmdCode = ">" OR CmdCode = "C" THEN
			Pot1Val = Pot1Val MAX 1015 + 5 
			Pot2Val = Pot2Val MAX 1015 + 5
		ELSEIF CmdCode = "E" THEN
			IF Pot1Val > 1 OR Pot2Val > 1 THEN
				HoldPot1 = Pot1Val
				HoldPot2 = Pot2Val
				Pot1Val = 1
				Pot2Val = 1
			ELSE
				Pot1Val = HoldPot1
				Pot2Val = HoldPot2
			ENDIF
		ENDIF	
			
	
	ENDIF
		hi2cout 0,(b28,b29,b30,b31) ' send Pot1Val & Pot2Val in 4 bytes to address 0
		SERTXD (CmdCode) 'output command to terminal
CmdCode = 0
		'Do required control actions here
	LOOP
	'do required background actions here
LOOP

Interrupt:
	Switched = pinsc & InterruptMask >> 1'look at just the three inputs CHANGED FROM PINSA
	on Switched GOTO I0,I1,I2,I3,I4,I5,I6,I7 'whatever the cause set the next interrupt to look for a reverse
I0:
	SETINT OR 14,InterruptMask,C 
GOTO proc
I1:
	SETINT OR 12,InterruptMask,C 
GOTO proc
I2:
	SETINT OR 10,InterruptMask,C 
GOTO proc
I3:
	SETINT OR 8,InterruptMask,C 
GOTO proc
I4:
	SETINT OR 6,InterruptMask,C 
GOTO proc
I5:
	SETINT OR 4,InterruptMask,C 
GOTO proc
I6:
	SETINT OR 2,InterruptMask,C 
GOTO proc
I7:
	SETINT OR 0,InterruptMask,C 
proc:
	Scratch = Switched & Clicked
	IF Scratch = 0 THEN
		SwitchIsOn = True
	ELSE
		IF SwitchIsOn = True THEN
			put hserptr,"A"
			hserptr = hserptr+1  & $03FF
			SwitchIsOn = false
		ENDIF
	ENDIF	
'
	Scratch = Switched & Rotated >>1
	on Scratch GOTO R0,R1,R2,R3
R0:
	IF RotatedLeft = True THEN
		PUT hserptr,"<"
		hserptr = hserptr+1  & $03FF
		RotatedLeft = false
	ENDIF
	IF RotatedRight = True THEN
		put hserptr,">"
		hserptr = hserptr+1  & $03FF
		RotatedRight = false
	ENDIF
	RETURN
R1:
	IF ResetEnc = True THEN
		RotatedLeft = True
		RotatedRight = false
		ResetEnc = false
	ENDIF
	RETURN
R2:
	IF ResetEnc = True THEN
		RotatedRight = True
		RotatedLeft = false
		ResetEnc = false
	ENDIF
	RETURN
R3:
	RotatedLeft = false
	RotatedRight = false
	ResetEnc = True
RETURN
 

hippy

Technical Support
Staff member
The main loop code, where 'ptr' chases 'hserptr', seems to be correct and the background serial receive would be handling incoming data and updating 'hserptr' correctly. That should work okay and not go out of control except if data was coming in faster than you were processing what had been received.

The problem is perhaps in putting bytes to the serial receive buffer whilst serial is being received. Also your "hserptr = hserptr+1 & $03FF" would be wrong for the 128 byte buffer on a 20X2 but might not be a problem.

An alternative approach would be to have two buffers. Leave the scratchpad purely for background serial, take bytes from there and put them in RAM using @bPtrInc. Then have the interrupt routine also update using @bPtrInc.
 

lbenson

Senior Member
We need a description of what is supposed to be happening based on what inputs.

I don't know if this could cause your problem, but I understand that resetting the interrupt should be the last thing you do before returning from the interrupt--to prevent a new interrupt from occurring before you have left the original one (I'm not certain that this is the case--the firmware might not re-enable the interrupt until it sees the return).
 

geoff07

Senior Member
I needed to use the scratchpad for more than one purpose, so needed to turn the circular buffer into a conventional buffer of a fixed length. This is what I did:

1 set up a flow control signal (CTS), that is made high by the receiver when it was ready to receive a message. The sender only sends when it sees this. As soon as any byte is received in the background (hserptr =/= 0) it is reset, allowing the incoming message to complete but no more to be sent.
2 after each received message is dealt with, reset the hserptr to zero (first byte) and then set the CTS line.
3 each message is sent with an eot byte as the last byte, so the receiver knows that it has everything in the current message, with a timeout in case the eot is missing.

I don't use any interrupts for this. It works well and means I only have to use a fixed number of bytes of the scratchpad (the first 20 in my case).
 

wapo54001

Senior Member
To answer the question above, the code only aggregates the IR and encoder inputs and sends them to an associated board via I2C and that board then acts on the value commanded, so this board doesn't "do" anything more than what you see.

This code works, but how klutzy, and there is a barely noticeable pause in operation while the scratchpad is cleared . . .

Code:
IF hserptr > 120 THEN   'Clear scratchpad to all zeros 
FOR ScratchLocation = 0 to 127 
PUT ScratchLocation, 0
NEXT ScratchLocation
hserptr = 0
ptr = 0
ENDIF
If, by looking at this someone sees a better way, I'd love to hear it.
 
Last edited:

wapo54001

Senior Member
But why do you want to 'clear' the scratchpad? If you manage it with pointers, this is not required.
Only because I can't figure out why the program stops working after 127 commands have passed through, and this solves the problem. Obviously something to do with the size of the scratchpad (I think!) but I can't figure it and this makes the program work -- it goes past the 127 byte barrier and keeps working. The original code hits 127 bytes and starts transmitting on I2C and serial all by itself. It seems to run through the same 127 commands and then starts in again. It does this until I turn it off.
 

hippy

Technical Support
Staff member
I would suggest commenting out the interrupt code, only using serial, and likewise, disabling serial and only using interrupts. That should narrow down which is responsible.

You can alter the interrupt to put different data into the scratchpad than the serial does, then, when it's looping, you can dump the scratchpad and better tell where the data within it originated.

Could it be that the interrupt is re-triggering when you are not expecting it to, putting lots of data in the scratchpad ?
 

wapo54001

Senior Member
I would suggest commenting out the interrupt code, only using serial, and likewise, disabling serial and only using interrupts. That should narrow down which is responsible.

You can alter the interrupt to put different data into the scratchpad than the serial does, then, when it's looping, you can dump the scratchpad and better tell where the data within it originated.

Could it be that the interrupt is re-triggering when you are not expecting it to, putting lots of data in the scratchpad ?
@hippy, thanks. I'll try stopping one and then the other though I'm pretty sure it's the encoder because I can run SIRC into the IR continuously and indefinitely and it stays right with it. Only when turning the encoder OR clicking the switch continuously does the problem occur after 120+ commands have been received. The problem, whatever it is seems to not affect serial but does affect both the encoder rotation and the switch click, so is common to all of the encoder code, not just one part of it.

I woke up this morning thinking that something the background serial does automatically isn't getting done with the encoder code, something to do with the pointers, and I'll look at that later today.
 

wapo54001

Senior Member
When you find the answer it often turns out to be obvious.

I changed "hserptr=hserptr+1 & $03FF" to "hserptr = hserptr+1 & $7F" and it now works perfectly.

Thanks to all who offered suggestions.
 
Top