Controlling and application with both IR and a rotary encoder

matherp

Senior Member
Controlling an application with both IR and a rotary encoder

This is code I developed to re-engineer the user input to my amplifier. The idea is that control is either by an amp mounted rotary encoder, or by a infra red remote.

The issue with this is that the IR input routines are blocking so must be run on a separate picaxe and that the encoder debounce routines can be difficult.

The IR input is handled by an 08M2 (08M should work with minor mods)

Code:
#picaxe 08M2
setfreq M8
symbol last=b1
symbol repcount=b2
symbol pauselength=w2
main:
irin c.3,b0
if b0<9 then 
	b0=b0+49 'convert to an ascci number
	gosub rep
	sertxd (b0)
	b0=255
endif
if b0=9 then
	b0=48 'convert to an ascci number
	gosub rep
	sertxd (b0)
	b0=255
endif
if b0>15 and b0<29 then
      b0=b0+49 ' convert to capital A to M
      if b0="C" then: b0=">" :endif
      if b0="D" then: b0="<" :endif
	gosub rep
      sertxd (b0)
	b0=255
endif
if b0>50 and b0<57 then
      b0=b0+27 ' convert to capital N to S
	gosub rep
      sertxd (b0)
	b0=255
endif
if b0>75 and b0<83 then
      b0=b0+8 ' convert to capital T to Z
	gosub rep
      sertxd (b0)
	b0=255
endif
if b0>115 and b0<118 then
      b0=b0-45 ' convert to capital G and H
	gosub rep
      sertxd (b0)
	b0=255
endif
if b0=37 then
	gosub rep
      sertxd ("P")' convert to capital P
	b0=255
endif
if b0=101 then
	gosub rep
      sertxd ("R")' convert to capital R
	b0=255
endif
if b0=96 then
	gosub rep
      sertxd ("X")' convert to capital X
	b0=255
endif
if b0=35 then
	gosub rep
      sertxd ("Y")' convert to capital Y
	b0=255
endif
if b0=99 then
	gosub rep
      sertxd ("C")' convert to C
	b0=255
endif
if b0=40 then
	gosub rep
      sertxd ("D")' convert to D
	b0=255
endif
'sertxd (b0,last,cr,lf)
pauselength=repcount*repcount * 2
pauselength=500-pauselength
pause pauselength
goto main

rep:
	if b0<>last then 
		repcount=0
	else
		repcount=repcount + 1 max 15
	endif
	last=b0
return
This just reads the codes off the input and recodes them to standard ascii characters which it sends out on the serial output. I'm using my standard Sony TV remote to control the amplifier. The code includes an "acceleration" algorithm to separate individual button pushes but also allow continuous presses for scrolling up and down the volume.

The main processor listens to the serial port but also handles the encoder totally at the interrupt level. My original routines for this http://www.picaxeforum.co.uk/showthread.php?20634-08M2-interrupt-issue were blocking whilst the encoder completed a cycle but this would have caused inputs from the IR to be delayed if the encoder was left in a mid cycle state rather than at the detent (easy to do) so I have recoded them to exit all interrupts immediately but still only report the user event on cycle completion. The interrupt routine feeds the encoder output into the hardware serial buffer so that the main code only has one place to look for data and doesn't care where it arrives from.

Code:
#picaxe 28X2
setfreq m8
symbol resetenc=bit0
symbol switchison = bit2
symbol rotatedleft=bit3
symbol rotatedright=bit4
symbol scratch=b1
symbol switched = b2
'
'Connections
' Encoder switch pulled high and connected to A.1 
' Encoder output1 pulled high and connected to A.2 
' Encoder output2 pulled high and connected to A.3 
'
'
symbol interruptmask = %00001110 ' allow interrupts on a.3,a.2, a.1
symbol rotated =       %00000110
symbol clicked       = %00000001 ' mask to test for c.1 high
'
symbol true=1
symbol false=0
'
hsersetup B9600_8,1
dirsa=0
ptr=0
switchison=false
rotatedright=false
rotatedleft=false
resetenc=true
setint OR 0,interruptmask,A
do
	do while ptr<>hserptr
		b10=@ptrinc
		sertxd (b10) 'Do required control actions here
	loop
	'do required background actions here
loop


interrupt:
	switched=pinsa & interruptmask >> 1'look at just the three inputs
	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,A 
goto proc
I1:
	setint OR 12,interruptmask,A 
goto proc
I2:
	setint OR 10,interruptmask,A 
goto proc
I3:
	setint OR 8,interruptmask,A 
goto proc
I4:
	setint OR 6,interruptmask,A 
goto proc
I5:
	setint OR 4,interruptmask,A 
goto proc
I6:
	setint OR 2,interruptmask,A 
goto proc
I7:
	setint OR 0,interruptmask,A 
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
 
Last edited:

mrburnette

Senior Member
Brilliant. Tossing a second PICAXE at a problem is a good way to deal with blocking commands. Future versions of PICAXE firmware really, really need to deal with this entire 'blocking' command / single thread issue. It gets pretty ugly when any serious use of the chips are considered. Perhaps PIC will become capable enough to allow RevEd to install a thread-sequencer in firmware where a thread can be launched with what a priority, a quality-of-service (QOS) identifier. In this scenario, queuing would be FIFO for all similar QOS threads, but priority threads would take priority for the single core. When Microchip Technology releases a two-core PIC (cheaply) this issue somewhat goes away.

- Ray
 

inglewoodpete

Senior Member
For a dual-core 'chip', the 16M (and, by inference the 16M2) has been discussed before. Just mount 2 x 8-pin PICAXEs in a 16-pin DIL socket.
 

wapo54001

Senior Member
This is code I developed to re-engineer the user input to my amplifier. The idea is that control is either by an amp mounted rotary encoder, or by a infra red remote.

The issue with this is that the IR input routines are blocking so must be run on a separate picaxe and that the encoder debounce routines can be difficult.

The IR input is handled by an 08M2 (08M should work with minor mods)
This is really neat code, I'm just getting into the encoder portion of it. It would be very convenient if both the IR receiver and the rotary encoder code could coexist on the same 28X2 chip but in this example an 08M2 is used to offload the IR responsibilities to a second chip.

Since interrupts are used to manage the rotary encoder input, could one not use an interrupt for the IR as well? I'm wondering about this particularly since an IR transmitter typically sends a series of identical codes when a button is pushed so a few milliseconds delay in receiving and reacting to the IR input wouldn't make any significant difference in real life, would it? Or am I missing something here?

Wouldn't it be very unlikely that the encoder and the IR would be sending data at the same time? And if that happens, couldn't the chip just ignore one or the other until the current operation is completed?
 

hippy

Technical Support
Staff member
Code:
interrupt:
	switched=pinsa & interruptmask >> 1'look at just the three inputs
	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,A 
goto proc
I1:
	setint OR 12,interruptmask,A 
goto proc
I2:
	setint OR 10,interruptmask,A 
goto proc
I3:
	setint OR 8,interruptmask,A 
goto proc
I4:
	setint OR 6,interruptmask,A 
goto proc
I5:
	setint OR 4,interruptmask,A 
goto proc
I6:
	setint OR 2,interruptmask,A 
goto proc
I7:
	setint OR 0,interruptmask,A 
proc:
It seems to me you are setting the interrupts so you get an interrupt whenever the pins change from what they currently are. In which case you could compress all that down to -

Code:
interrupt:
	switched = pinsa & interruptmask 
	setint not switched, interruptmask, A
	switched = switched >> 1
proc:
 
Top