DIY custom 2.4ghz RC radio system for robotics

I think I have all of the 7 wires from the receiver going in order from Throttle to Aux2 going from P0-P6 in the order you mention. But it appears like I am receiving them in the order I mention. Of course to an RC person, they probably don’t care what order the receiver updates the different channels.

I also verified in my test program that when I change things on the transmitter, it updates the appropriate value being printed out.

FF:10227:1451 1471 1508 1330 1520 1445 1118 FF:4293:1451 1472 1508 1330 1499 1445 1117 FF:5181:1450 1469 1508 1330 1465 1445 1118 FF:5260:1451 1472 1508 1330 1427 1445 1118 FF:5240:1448 1471 1510 1331 1381 1447 1118 FF:5181:1451 1471 1508 1330 1333 1445 1118 FF:5304:1450 1469 1508 1330 1279 1445 1118 FF:5244:1451 1472 1508 1330 1226 1445 1117 FF:5298:1450 1471 1508 1330 1169 1445 1118 FF:5227:1450 1472 1508 1331 1105 1445 1117 FF:5178:1448 1469 1510 1330 1046 1445 1118 FF:5325:1451 1471 1508 1330 1015 1445 1120 FF:5288:1451 1472 1508 1330 1007 1445 1118 FF:10309:1451 1471 1508 1330 1007 1450 1118 FF:4251:1450 1472 1508 1333 1007 1462 1118 FF:5344:1451 1472 1508 1330 1009 1486 1118 FF:5280:1451 1472 1508 1330 1007 1520 1117 FF:5199:1450 1471 1508 1330 1009 1571 1118 FF:5347:1450 1471 1508 1330 1006 1627 1118 FF:5363:1448 1471 1508 1330 1007 1687 1118 FF:5341:1450 1472 1510 1330 1007 1747 1118 FF:5361:1451 1471 1508 1330 1007 1814 1118 FF:5368:1451 1471 1508 1334 1007 1889 1118 FF:5383:1451 1471 1508 1330 1007 1957 1118 FF:5299:1451 1471 1508 1330 1006 2011 1117 FF:5303:1451 1471 1508 1330 1007 2017 1118 FF:5289:1453 1471 1510 1330 1007 2017 1118 FF:5380:1450 1471 1508 1330 1007 2017 1117 FF:10224:1451 1472 1508 1334 1007 2017 1118 FF:4233:1451 1471 1508 1331 1007 2017 1118 FF:10290:1448 1471 1508 1334 1007 2017 1118 FF:4312:1451 1472 1508 1330 1007 2017 1117 FF:10366:1450 1471 1508 1334 1007 2017 1117 FF:4228:1451 1471 1508 1330 1007 2008 1118 FF:5124:1451 1471 1508 1330 1009 1964 1118 FF:5245:1450 1471 1508 1330 1007 1909 1118 FF:5227:1450 1472 1508 1330 1007 1841 1118 FF:5213:1450 1471 1508 1330 1007 1768 1117 FF:5220:1451 1471 1508 1330 1007 1688 1118 FF:5222:1451 1472 1508 1330 1007 1612 1118 FF:5224:1451 1472 1508 1331 1007 1546 1118 FF:5315:1451 1471 1508 1330 1007 1510 1118 FF:5212:1450 1469 1508 1330 1009 1493 1117 FF:5195:1450 1471 1510 1330 1006 1475 1118 FF:5296:1451 1469 1508 1330 1007 1465 1118 FF:5281:1450 1471 1508 1330 1007 1457 1118 FF:5278:1450 1472 1508 1330 1007 1451 1118 FF:5370:1450 1471 1508 1330 1007 1441 1117 FF:5272:1451 1472 1508 1330 1007 1430 1118 FF:5357:1451 1472 1508 1331 1009 1415 1117 FF:5248:1451 1471 1508 1330 1009 1372 1118 FF:5066:1451 1472 1508 1330 1007 1292 1118 FF:5214:1451 1471 1508 1330 1007 1214 1118 FF:5278:1451 1471 1508 1334 1007 1126 1118 FF:5235:1451 1472 1508 1330 1007 1040 1117 FF:5070:1448 1471 1510 1330 1007 1007 1118 B:10308:1451 1469 1508 1330 1007 1007 1709 B:5119:1451 1472 1508 1334 1009 1007 1709 B:6321:1454 1472 1508 1336 1009 1007 1711 B:6348:1450 1472 1508 1334 1007 1007 1711 B:6351:1454 1469 1508 1334 1007 1007 1711 B:10300:1451 1471 1508 1334 1007 1007 1709 B:10372:1454 1471 1508 1333 1009 1007 1709 B:10305:1454 1474 1510 1334 1007 1007 1709 B:10233:1454 1471 1510 1334 1007 1009 1709 B:10300:1451 1471 1510 1334 1007 1007 1709 B:10303:1454 1471 1510 1336 1007 1007 1711 FF:5254:1450 1472 1508 1330 1009 1007 1117 FF:10236:1448 1471 1508 1331 1007 1007 1120 FF:10308:1450 1472 1508 1330 1007 1007 1117
The first number is my calculate which button is pressed (in hex), which is calculated from the last number. The numbers appear to update to near the right number. Should probably update the transmitter code with the fix you mentioned earlier.

Kurt

Just in case some of you would like to try it out. I think I have some of my read all 7 inputs in one call working. It is currently coded to work with IO pins 0-6. I do have some comments at the top of what it would take to make it work with pins 1-7 or pins 8,10-15, but I have not tried out those configurations.

Decided I would post my test program here for you to play with and just in case my machine should suddenly have problems…

[code];====================================================================
; DIY - Radio Receiver Test Program
;
; Currently this program assumes all 7 inputs are hooked up to P0-P6
; TBD - Make more general purpose, to allow different input pins.
; For P1-P7 change mask from 0x7f to 0xfe and make pulse_values word(8)
; For P8,10-15 change mask to 0xFD, PDR5 to PDR8, and pulse_values to word(8)
; BUGBUG - Need to check how PCR5 and PMR5 are used…

pulse_values var word(7)
pulse_values_prev var word(7)
keypress var byte
i var byte
changed var byte

pulse_slop con 2
;--------------------------------------------------------------------
;[TIMING]
lTimerWOverflowCnt var long ;used in WTimer overflow. Will keep a 16 bit overflow so we have a 32 bit timer
lCurrentTime var long
lTimerStart var long ;Start time of the calculation cycles
lTimerEnd var long ;End time of the calculation cycles
CycleTime var byte ;Total Cycle time

SSCTime var word ;Time for servo updates
PrevSSCTime var word ;Previous time for the servo updates

InputTimeDelay var word ;Delay that depends on the input to get the “sneaking” effect

; Init code
;====================================================================
;[TIMER INTERRUPT INIT]
ONASMINTERRUPT TIMERWINT, Handle_TIMERW

TCRW = 0x30 ;clears TCNT and sets the timer to inc clock cycle / 8
TMRW = 0x80 ;starts the timer counting
lTimerWOverflowCnt = 0
;enable TIMERWINT_OVF

; Make sure all 7 pins are marked for input...
input p0
input p1
input p2
input p3
input p4
input p5
input p6

start:
gosub TimerReset
GOSUB GetCurrentTime], lTimerStart
#if DO_PULSIN
pulsin p3, 0, pulse_values(3)
pulsin p1, 0, pulse_values(1)
pulsin p0, 0, pulse_values(0)
pulsin p6, 0, pulse_values(6)
pulsin p2, 0, pulse_values(2)
pulsin p5, 0, pulse_values(5)
pulsin p4, 0, pulse_values(4)
#else
gosub Pulsein7
#endif
GOSUB GetCurrentTime], lTimerEnd

changed = 0
for i = 0 to 6
	if (pulse_values(i) > (pulse_values_prev(i)+pulse_slop)) or (pulse_values(i) < (pulse_values_prev(i)-pulse_slop)) then
		changed = 0xff
		pulse_values_prev(i) = pulse_values(i)
	endif
next

if changed then
	if pulse_values(6) < 1150 then
		keypress = 0xff
	else
		keypress = (pulse_values(6) - 1150) / 50
	endif

	lTimerEnd = (lTimerEnd - lTimerStart) /2		; convert difference to near MS...
	serout s_out, i9600, [hex keypress, ":", dec lTimerEnd, ":"]
	for i = 0 to 6
		serout s_out, i9600, [dec pulse_values(i)," "]
	next
	serout s_out, i9600,[13] 

	if keypress = 0xA then 
	  low 12 
	else 
	  input p12 
	endif 
	
	if keypress = 0xB then 
	  low 13 
	else 
	  input p13 
	endif 
	
	if keypress = 0xC then 
	  low 14 
	else 
	  input p14 
	endif 
endif

goto start

;-----------------------------------------------------------------------------------
;[Handle TimerW interrupt]
BEGINASMSUB
HANDLE_TIMERW
push.w r1 ; save away register we will use
bclr #7,@TSRW:8 ; clear the overflow bit in the Timer status word
mov.w @LTIMERWOVERFLOWCNT+1:16,r1 ; We will increment the word that is the highword for a clock timer
inc.w #1,r1
mov.w r1, @LTIMERWOVERFLOWCNT+1:16
pop.w r1 ; restore our registers
rte ; and return
return
;-------------------------------------------------------------------------------------
;[Simple function to get the current time and verify that no overflow happened]
GetCurrentTime
lCurrentTime = lTimerWoverflowCnt + TCNT ; calculate the timer
IF lCurrentTime.highword <> lTimerWOverflowcnt.highword THEN
lCurrentTime = lTimerWoverflowCnt + TCNT ; calculate the timer
ENDIF
return lCurrentTIme

;[Simple function to reset the timer to near zero as to not have to worry about overflows…]
TimerReset:
TCNT = 0
lTimerWoverflowCnt = 0
return
;==============================================================================
; Read in all 7 servo values in one pass.
;
;-------------------------------------------------------------------
; locals for pulsein…

Pulsein7:
; Make sure all 7 IOs are set to input.
PMR5 = 0 ; all IO lines are general IO
PCR5 = 0 ; All are input (may want to leave bit 7 alone…

; Ok now lets transisiton to assembly language.
;

; bMask = 0x7f ; set a mask of which bits we are needing…
; ; Mask could be 0xFE for pins 1-7, need to make array 8 not 7
mov.b #0x7f, r1l ; Ok R1l will be our mask for outstanding IO port bytes.

; wait until none of the IO lines are high... 

; while PDR5 & bMask
; ;
; wend

_PI7_WAIT_FOR_ALL_LOW:
mov.b @PDR5:8, r0l
and.b r1l, r0l ; see if any of the IO bits is still on…
bne _PI7_WAIT_FOR_ALL_LOW:8 ; an IO pin is high, keep looping until none are high
; BUGBUG: should put some way in to time out…

; while bMask

_PI7_WAIT_FOR_NEXT_IO_TO_GO_HIGH:
; we still need some 1 or more of the pulses…
; while (PDR5 & bMask) = 0 ; waiting for a pulse to go high.
mov.b @PDR5:8, r0l ;(4)
and.b r1l, r0l ;(*2) see if any of the IO bits is still on…
beq _PI7_WAIT_FOR_NEXT_IO_TO_GO_HIGH:8 ; (*4)none of the IOS are high yet
; wend
; iPin = ???; TBD: convert which bit is high to IO line number 0-6
; see which bit is on in the mask
xor.w r2,r2 ;(*2)
xor.b r0h, r0h ;(*2)
mov.l #PULSE_VALUES,er3 ;(*6)
_P17_WHICH_BIT_LOOP:
shlr.b r0l ;(@2)
bcs _P17_WHICH_BIT_LOOP_DONE:8 ;(@4)
inc.b r0h ;(@2)
inc.l #2, er3 ;(@2) - point to the next place in the array.
add.w #18,r2 ;(@4) - we do 18 clocks for each pass through this loop
bra _P17_WHICH_BIT_LOOP:8 ;(@4)
_P17_WHICH_BIT_LOOP_DONE:
; bMaskBit = 1 << iPin ; get the mask for which pin…
xor.b r1h,r1h ;(*2)
bset.b r0h,r1h ;(*2) ok we have the mask
bclr.b r0h,r1l ;(*2) and clear it from our global mask of ones we are waiting for

										; = (24) - count so far of clocks after went high

; iPinLoopCnt = 0 ; This may be replaced with time calculations…
; while (PDR5 & bMaskBit)
; iPinLoopCnt = iPinLoopCnt + 1 ; how long until it goes low again
; wend
_P17_WAIT_FOR_IO_GO_BACK_LOW:
mov.b @PDR5:8, r0l ;(#4)
and.b r1h, r0l ;(#2)
beq _P17_IO_WENT_BACK_LOW:8 ;(#4)
add.w #18,r2 ;(#4) - number of cyles per loop
bra _P17_WAIT_FOR_IO_GO_BACK_LOW:8 ;(#4)

_P17_IO_WENT_BACK_LOW:
; need to calculate the pulse width in ms… need to divide calculated clocks by 16
add.w #24,r2 ; (4) ; need to add the rest of the overhead(*) in…
shlr.w r2 ; (2)
shlr.w r2 ; (2)
shlr.w r2 ; (2)
shlr.w r2 ; (2) / 16 (for clock speed)

; aPulses(iPin) = iPinLoopCnt ; convert loop count to pulse width…
mov.w r2,@er3 ; Save away the value

; bMask = bMask & ~bMaskBit ; turn off waiting for this one…
or r1l,r1l ; (2) see if we are done or not
; wend
bne _PI7_WAIT_FOR_NEXT_IO_TO_GO_HIGH:16 ;(6)our mask has not gone to zero so wait for the next one.

; finally transisition back to basic and return.
return
[/code]

EDIT: updated code slightly to fix timing for start of pin going high overhead

Hi Jim,

If I understood correctly the current DIY remotes have the following code in them:

[code]wButtonPulseWidth var word
keypress var byte

col1 var bit
col2 var bit
col3 var bit
col4 var bit

cha1 var word
cha2 var word
cha3 var word
cha4 var word
cha5 var word
cha6 var word
cha7 var word

; create variables for averaging code
index var byte
buffer1 var word(8)
buffer2 var word(8)
buffer3 var word(8)
buffer4 var word(8)
buffer5 var word(8)
buffer6 var word(8)
sum1 var word
sum2 var word
sum3 var word
sum4 var word
sum5 var word
sum6 var word

input p4
input p5
input p6
input p7

output p10
output p11
output p12
output p13

; initialize each buffer element, each sum, and then index to 0
for index = 0 to 7
buffer1(index) = 542
buffer2(index) = 542
buffer3(index) = 542
buffer4(index) = 542
buffer5(index) = 542
buffer6(index) = 542
next

sum1 = 4336
sum2 = 4336
sum3 = 4336
sum4 = 4336
sum5 = 4336
sum6 = 4336
index = 0

; initialize unused channels outside the main loop
cha7=1500

; chirpy squeak kinda thing
sound 9, [100\880, 100\988, 100\1046, 100\1175] ;musical notes, A,B,C,D.

; wake up the Matrix Orbital display module
;serout 8,i19200,[254, 66, 0] ;Backlight on, no timeout.
;serout 8,i19200,[254, 64, “The D-I-Y 2.4ghzRobot Radio Set!”] ;startup screen. Do only once…
pause 5000
serout 8,i19200, [254, 88] ;clear screen

; initialize the ppm output signal
low 15

; — top of main loop —
start:

high 11
high 12
high 13
low 10

col1 = in4
col2 = in5
col3 = in6
col4 = in7

if col1 = 0 then
wButtonPulseWidth = 1200; 1
keypress = “1”
elseif col2 = 0
wButtonPulseWidth = 1250; 2
keypress = “2”
elseif col3 = 0
wButtonPulseWidth = 1300; 3
keypress = “3”
elseif col4 = 0
wButtonPulseWidth = 1650; A
keypress = “A”
else
high 10
low 11
col1 = in4
col2 = in5
col3 = in6
col4 = in7
if col1 = 0 then
wButtonPulseWidth = 1350; 4
keypress = “4”
elseif col2 = 0
wButtonPulseWidth = 1400; 5
keypress = “5”
elseif col3 = 0
wButtonPulseWidth = 1450; 6
keypress = “6”
elseif col4 = 0
wButtonPulseWidth = 1700; B
keypress = “B”
else
high 11
low 12
col1 = in4
col2 = in5
col3 = in6
col4 = in7
if col1 = 0 then
wButtonPulseWidth = 1500; 7
keypress = “7”
elseif col2 = 0
wButtonPulseWidth = 1550; 8
keypress = “8”
elseif col3 = 0
wButtonPulseWidth = 1600; 9
keypress = “9”
elseif col4 = 0
wButtonPulseWidth = 1750; C
keypress = “C”
else
high 12
low 13
col1 = in4
col2 = in5
col3 = in6
col4 = in7
if col1 = 0 then
wButtonPulseWidth = 1150; 0
keypress = “0”
elseif col2 = 0
wButtonPulseWidth = 1900; F
keypress = “F”
elseif col3 = 0
wButtonPulseWidth = 1850 ; E
keypress = “E”
elseif col4 = 0
wButtonPulseWidth = 1800; D
keypress = “D”
else
wButtonPulseWidth = 1100; None is pushed
keypress = " "
endif
endif
endif
endif

; averaging expects that the a/d values are < 4096
; for each channel
; read the a/d
; subtract the previous value from 8 samples ago from the sum
; store the new value in the circular buffer
; add the new value to the sum
; divide the sum by 8 to get the average value
; convert joystick values 392 - 692 to servo values 1000uS - 2000uS

adin 2, cha1 ; right vertical 16
sum1 = sum1 - buffer1(index)
buffer1(index) = cha1
sum1 = sum1 + cha1
cha1 = sum1 / 8
cha1 = (((cha1*42)-6500)/10)

adin 3, cha2 ; right horizontal 17
sum2 = sum2 - buffer2(index)
buffer2(index) = cha2
sum2 = sum2 + cha2
cha2 = sum2 / 8
cha2 = (((cha2*42)-6500)/10)

adin 0, cha3 ; left vertical 18
sum3 = sum3 - buffer3(index)
buffer3(index) = cha3
sum3 = sum3 + cha3
cha3 = sum3 / 8
cha3 = (((cha3*42)-6500)/10)

adin 1, cha4 ; left horizontal 19
sum4 = sum4 - buffer4(index)
buffer4(index) = cha4
sum4 = sum4 + cha4
cha4 = sum4 / 8
cha4 = (((cha4*42)-6500)/10)

adin 18, cha5 ; Left slider
sum5 = sum5 - buffer5(index)
buffer5(index) = cha5
sum5 = sum5 + cha5
cha5 = sum5 / 8
cha5 = cha5 + 988

adin 19, cha6 ; Right slider
sum6 = sum6 - buffer6(index)
buffer6(index) = cha6
sum6 = sum6 + cha6
cha6 = sum6 / 8
cha6 = cha6 + 988

; finally increment the index and limit its range to 0 to 7.
index = (index + 1) & 7

; update the display module
branch index, [update1,update2,update3,update4,update5,update6,update7,update8]

update8:
update7:
serout 8, i19200, [254, 71, 11, 1, keypress]
goto makepulses

update6:
if cha6<1000 then update62
serout 8, i19200, [254, 71, 6, 2, dec cha6]
goto makepulses
update62:
serout 8, i19200, [254, 71, 6, 2, " ", dec cha6]
goto makepulses

update5:
if cha5<1000 then update52
serout 8, i19200, [254, 71, 6, 1, dec cha5]
goto makepulses
update52:
serout 8, i19200, [254, 71, 6, 1, " ", dec cha5]
goto makepulses

update4:
if cha4<1000 then update42
serout 8, i19200, [254, 71, 1, 2, dec cha4]
goto makepulses
update42:
serout 8, i19200, [254, 71, 1, 2, " ", dec cha4]
goto makepulses

update3:
if cha3<1000 then update32
serout 8, i19200, [254, 71, 1, 1, dec cha3]
goto makepulses
update32:
serout 8, i19200, [254, 71, 1, 1, " ", dec cha2]
goto makepulses

update2:
if cha2<1000 then update22
serout 8, i19200, [254, 71, 13, 2, dec cha2]
goto makepulses
update22:
serout 8, i19200, [254, 71, 13, 2, " ", dec cha2]
goto makepulses

update1:
if cha1<1000 then update12
serout 8, i19200, [254, 71, 13, 1, dec cha1]
goto makepulses
update12:
serout 8, i19200, [254, 71, 13, 1, " ", dec cha1]
goto makepulses

; build and send the ppm output
makepulses:
cha1 = ((cha12)-800)
cha2 = ((cha2
2)-800)
cha3 = ((cha32)-800)
cha4 = ((cha4
2)-800)
cha5 = ((cha52)-800)
cha6 = ((cha6
2)-800)
cha7 = ((wButtonPulseWidth*2)-800)

high 15 ;pulsout 15,800
pauseus 400
low 15
pauseus cha1
high 15 ;pulsout 15,800
pauseus 400
low 15
pauseus cha2
high 15 ;pulsout 15,800
pauseus 400
low 15
pauseus cha3
high 15 ;pulsout 15,800
pauseus 400
low 15
pauseus cha4
high 15 ;pulsout 15,800
pauseus 400
low 15
pauseus cha5
high 15 ;pulsout 15,800
pauseus 400
low 15
pauseus cha6
high 15 ;pulsout 15,800
pauseus 400
low 15
pauseus cha7
high 15 ;pulsout 15,800
pauseus 400
low 15

; do it again ad infinitum
goto start
[/code]
I went from your last full listing, plus the changes you posted for the MakePulses to have the generated pulses be closer to what you expected.

I believe that your next step was to get the actual pulses even closer. If I remember correctly at a desired pulse width of 1100 you were off by about 25 and at the desired pulse width of 1900 it was off by 15.

So my guess is that you are planning to change the code at about the lines:
cha1 = ((cha1*2)-800)
to maybe something like:
cha1 = ((cha1 - 15 - (1900-cha1)/80) *2) - 800

I believe for 1900 this would substract 15 and at 1100 should subtract 25, which is probably pretty close?

Note: I have not tried this yet.

Edit: Changed to divide by 80 instead of 100 to make the deltas the same as you mentioned.

Kurt

Hi Guys,

I’ve got some days off so it’s play time! :slight_smile:

I did some hardware modifications. I’ve sandblasted the front panel. I love the end effect! It doesn’t leave fingerprints anymore. :slight_smile: The antenna is mounted on the top of the case (not the front panel). I used the antenna adapter shown on page 7 to mount it. Took a saw I removed the rod. Drilled 4 small holes in the bigger disk where the rod was and mounted the piece to the case. Hope it makes sense to you :blush:

Extended the serial connection to the BAP and mounted the connector to the front panel. This way I can make changes to the program without taking everything apart!

I’ve studied the code and made a few changes…
The joysticks weren’t reading 1500 when in the middle position. The new version includes a calibration step on powerup. Be sure that both the joysticks are in the middle position an the left vertical stick is in the total down position.

There was, and still is, a small difference between the pulse length on the send and receive side. I’ve added the -20 in the make pulses which makes it slight better but not perfect. I think the method Kurt described would work better but I didn’t had time to try that out.

The receiver will give the last received values if the remote is switched off (or the battery is empty). This caused my phoenix to just keep walking. I’ve included a Alive system to be sure everything is still working properly. Every 4 cycles I replace the pushed button with a Alive signal (1000). At the receiver the alive signal can reset a timer. If the timer runs out, it can switch off the bot as well.

I hope nobody mind but I’ve also included a version number and a header. It would help us keep all the modifications ordered.

Here’s the code:

[code] ;Project D-I-Y 2.4GHz Radio set
;Description: Lynxmotion costum RC radio
;Software version: V1.1
;Date: 29-12-2008
;Programmer: Jim Frye (aka RobotDude), Kurt (aka Kurte), Jeroen Janssen (aka Xan)
;
;Hardware setup: ABB2 with ATOM 28 Pro, Spectrum DM8, 2 joysticks, 2 sliders, HEX keypath, display
;
;NEW IN V1.0
; - As released
;
;NEW IN V1.1
; - Added calibration sub. Calibrates the joysticks at powerup. (Xan)
; CAUTION: Be sure that the left joystick is in the outer down position by powerup!
; - Added small offset at the pulses to decrease the send error. (Xan)
; - Added Alive meganism. Sends 1000 on the buttons channel every 4 cycles
;
;KNOWN BUGS:
; - None at the moment :wink:
;
;====================================================================

Display con 8
Speaker con 9
Row0 con 10
Row1 con 11
Row2 con 12
Row3 con 13

DM8PPM con 15 ; Spectrum DM8 signal

Calibrated var bit
ChaOffset var word(6)

wButtonPulseWidth var word
keypress var byte

col1 var bit
col2 var bit
col3 var bit
col4 var bit

cha1 var word
cha2 var word
cha3 var word
cha4 var word
cha5 var word
cha6 var word
cha7 var word

Alive var nib

; create variables for averaging code
index var byte
buffer1 var word(8)
buffer2 var word(8)
buffer3 var word(8)
buffer4 var word(8)
buffer5 var word(8)
buffer6 var word(8)
sum1 var word
sum2 var word
sum3 var word
sum4 var word
sum5 var word
sum6 var word

input p4
input p5
input p6
input p7

output p10
output p11
output p12
output p13

; initialize each buffer element, each sum, and then index to 0
for index = 0 to 7
buffer1(index) = 542
buffer2(index) = 542
buffer3(index) = 542
buffer4(index) = 542
buffer5(index) = 542
buffer6(index) = 542
next

sum1 = 4336
sum2 = 4336
sum3 = 4336
sum4 = 4336
sum5 = 4336
sum6 = 4336
index = 0

; initialize unused channels outside the main loop
cha7=1500

; chirpy squeak kinda thing
sound Speaker, [100\880, 100\988, 100\1046, 100\1175] ;musical notes, A,B,C,D.

; wake up the Matrix Orbital display module
serout Display ,i19200, [254, 66, 0] ;Backlight on, no timeout.
serout Display ,i19200, [254, 64, “The D-I-Y 2.4ghzRobot Radio Set!”] ;startup screen. Do only once…
pause 3000

; Mark for calibration
Calibrated = 0

; initialize the ppm output signal
low DM8PPM

;====================================================================
; — top of main loop —
start:

low Row0
high Row1
high Row2
high Row3

col1 = in4
col2 = in5
col3 = in6
col4 = in7

;Read buttons
if col1 = 0 then
wButtonPulseWidth = 1200; 1
keypress = “1”
elseif col2 = 0
wButtonPulseWidth = 1250; 2
keypress = “2”
elseif col3 = 0
wButtonPulseWidth = 1300; 3
keypress = “3”
elseif col4 = 0
wButtonPulseWidth = 1650; A
keypress = “A”
else
high Row0
low Row1
col1 = in4
col2 = in5
col3 = in6
col4 = in7
if col1 = 0 then
wButtonPulseWidth = 1350; 4
keypress = “4”
elseif col2 = 0
wButtonPulseWidth = 1400; 5
keypress = “5”
elseif col3 = 0
wButtonPulseWidth = 1450; 6
keypress = “6”
elseif col4 = 0
wButtonPulseWidth = 1700; B
keypress = “B”
else
high Row1
low Row2
col1 = in4
col2 = in5
col3 = in6
col4 = in7
if col1 = 0 then
wButtonPulseWidth = 1500; 7
keypress = “7”
elseif col2 = 0
wButtonPulseWidth = 1550; 8
keypress = “8”
elseif col3 = 0
wButtonPulseWidth = 1600; 9
keypress = “9”
elseif col4 = 0
wButtonPulseWidth = 1750; C
keypress = “C”
else
high Row2
low Row3
col1 = in4
col2 = in5
col3 = in6
col4 = in7
if col1 = 0 then
wButtonPulseWidth = 1150; 0
keypress = “0”
elseif col2 = 0
wButtonPulseWidth = 1900; F
keypress = “F”
elseif col3 = 0
wButtonPulseWidth = 1850 ; E
keypress = “E”
elseif col4 = 0
wButtonPulseWidth = 1800; D
keypress = “D”
else
wButtonPulseWidth = 1100; None is pushed
keypress = " "
endif
endif
endif
endif

;send alive

if Alive = 0 then
wButtonPulseWidth = 1000
Alive = 4
else
Alive = Alive - 1
endif

; averaging expects that the a/d values are < 4096
; for each channel
; read the a/d
; subtract the previous value from 8 samples ago from the sum
; store the new value in the circular buffer
; add the new value to the sum
; divide the sum by 8 to get the average value
; convert joystick values 392 - 692 to servo values 1000uS - 2000uS

adin 2, cha1 ; right vertical 16
sum1 = sum1 - buffer1(index)
buffer1(index) = cha1
sum1 = sum1 + cha1
cha1 = sum1 / 8
cha1 = (((cha1*42)-6500)/10)
cha1 = cha1 + chaOffset(0)

adin 3, cha2 ; right horizontal 17
sum2 = sum2 - buffer2(index)
buffer2(index) = cha2
sum2 = sum2 + cha2
cha2 = sum2 / 8
cha2 = (((cha2*42)-6500)/10)
cha2 = cha2 + chaOffset(1)

adin 0, cha3 ; left vertical 18
sum3 = sum3 - buffer3(index)
buffer3(index) = cha3
sum3 = sum3 + cha3
cha3 = sum3 / 8
cha3 = (((cha3*42)-6500)/10)
cha3 = cha3 + chaOffset(2)

adin 1, cha4 ; left horizontal 19
sum4 = sum4 - buffer4(index)
buffer4(index) = cha4
sum4 = sum4 + cha4
cha4 = sum4 / 8
cha4 = (((cha4*42)-6500)/10)
cha4 = cha4 + chaOffset(3)

adin 18, cha5 ; Left slider
sum5 = sum5 - buffer5(index)
buffer5(index) = cha5
sum5 = sum5 + cha5
cha5 = sum5 / 8
cha5 = cha5 + 988

adin 19, cha6 ; Right slider
sum6 = sum6 - buffer6(index)
buffer6(index) = cha6
sum6 = sum6 + cha6
cha6 = sum6 / 8
cha6 = cha6 + 988

; finally increment the index and limit its range to 0 to 7.
index = (index + 1) & 7

if Calibrated=1 then
; update the display module
branch index, [update1,update2,update3,update4,update5,update6,update7,update8]

update8: 
update7: 
serout Display, i19200, [254, 71, 11, 1, keypress] 
goto makepulses 

update6: 
if cha6<1000 then update62 
serout Display, i19200, [254, 71, 6, 2, dec cha6] 
goto makepulses 
update62: 
serout Display, i19200, [254, 71, 6, 2, " ", dec cha6] 
goto makepulses 

update5: 
if cha5<1000 then update52 
serout Display, i19200, [254, 71, 6, 1, dec cha5] 
goto makepulses 
update52: 
serout Display, i19200, [254, 71, 6, 1, " ", dec cha5] 
goto makepulses 

update4: 
if cha4<1000 then update42 
serout Display, i19200, [254, 71, 1, 2, dec cha4] 
goto makepulses 
update42: 
serout Display, i19200, [254, 71, 1, 2, " ", dec cha4] 
goto makepulses 

update3: 
if cha3<1000 then update32 
serout Display, i19200, [254, 71, 1, 1, dec cha3] 
goto makepulses 
update32: 
serout Display, i19200, [254, 71, 1, 1, " ", dec cha3] 
goto makepulses 

update2: 
if cha2<1000 then update22 
serout Display, i19200, [254, 71, 13, 2, dec cha2] 
goto makepulses 
update22: 
serout Display, i19200, [254, 71, 13, 2, " ", dec cha2] 
goto makepulses 

update1: 
if cha1<1000 then update12 
serout Display, i19200, [254, 71, 13, 1, dec cha1] 
goto makepulses 
update12: 
serout Display, i19200, [254, 71, 13, 1, " ", dec cha1] 
goto makepulses 


; build and send the ppm output 
makepulses: 
cha1 = (((cha1-20)*2)-800) ; right vertical
cha2 = (((cha2-20)*2)-800) ; right horizontal
cha3 = (((cha3-20)*2)-800) ; left vertical
cha4 = (((cha4-20)*2)-800) ; left horizontal
cha5 = (((cha5-20)*2)-800) 
cha6 = (((cha6-20)*2)-800) 
cha7 = ((wButtonPulseWidth*2)-800) 

high DM8PPM ;pulsout 15,800 
pauseus 400 
low DM8PPM 
pauseus cha1 
high DM8PPM ;pulsout 15,800 
pauseus 400 
low DM8PPM 
pauseus cha2 
high DM8PPM ;pulsout 15,800 
pauseus 400 
low DM8PPM 
pauseus cha3 
high DM8PPM ;pulsout 15,800 
pauseus 400 
low DM8PPM 
pauseus cha4 
high DM8PPM ;pulsout 15,800 
pauseus 400 
low DM8PPM 
pauseus cha5 
high DM8PPM ;pulsout 15,800 
pauseus 400 
low DM8PPM 
pauseus cha6 
high DM8PPM ;pulsout 15,800 
pauseus 400 
low DM8PPM 
pauseus cha7 
high DM8PPM ;pulsout 15,800 
pauseus 400 
low DM8PPM 

endif

if Calibrated=0 & index=0 then
gosub Calibrate
endif

; do it again ad infinitum
goto start
;====================================================================
;Calibrates middle positions of the sticks to 1500
;Calibrates left vertical stick to be total down
Calibrate:
serout Display, i19200, [254, 88] ;clear screen
serout Display, i19200, [254, 0, “Calibrating…”]
pause 1000

ChaOffset(0) = 1500 - cha1 ; right vertical 16
ChaOffset(1) = 1500 - cha2 ; right horizontal 17 
ChaOffset(2) = 1000 - cha3 ; left vertical 18
ChaOffset(3) = 1500 - cha4 ; left horizontal 19

serout Display, i19200, [254, 88] ;clear screen 

; Mark as calibrated
Calibrated=1

return[/code]

Let me know what you think. :slight_smile:

Xan

Hi Xan! I’m glad you’ve got some play time. I’ve to wait for next year, christmas vacation…

Ehm… Bragging about hardware modifications are worthless without any pictures! hint hint… :wink:

Excellent! Good idea, I think we all are going to do something like that.

That’s simply brilliant Xan.

This is just getting better and better… I’ve not experienced this as a problem. I believe this is some sort of fail-safe system. Fail-safe are very useful for RC aircraft, when connection is lost all servos goes to a predefined position. Often center position for all and throttle down.

:laughing: Since you are a pro programmer I was just waiting for this… :wink:

I thought about that as well, but so far have not done so. Did you unsolder the connector and mount it and then put wires between? Or did you have some small extension cable?

Good idea, earlier with the hitec receiver I did this on the receiver side, but it would be nice to have the display update properly on the LCD…

Good idea. I was thinking of doing something similar. The other idea I had was I believe that we can have the hardware do some of this for us. That is I believe that when you bind the transmitter and receiver there is a way to tell it what the failsafe position is. I thought of trying to set this up to go to some known location.

Currently I am working with it on my rover. I am integrating my read in all 7 pulses function and will be testing to make sure that the pulse value read in matches the actual pulses.

If the transmitter code has unacceptable error in the pulses, I may also convert that one to a simple assembly function that generates the whole pulse train in one call… But hopefully that won’t be necessary.

Kurt

Hi,

Well… Guess you’re right… :wink:



You can see the small nuts which hold the antenna adapter in to place.

I used a flat-cable with 2 separate connectors. I had to feed the cable trough the hole before mounting the cable though.

I found out that there is a failsafe position indeed. When the connection is lost all channels will go to 1500. And because I had the offsets in the phoenix with my first tests it started to walk when it went to the failsafe position. This is solved with the calibration function. :slight_smile:

It would be great if we could adapt the failsafe positions to another value. This way we can use a pulse length for the button channel that is not in use. And switch everything off when the signal is lost.

Xan

Nice pictures Xan! 8) Sandblasting the topplate was much better. I think I’ll do the same about the antenna mounting.

Anyone hazard a guess to the ‘all-in’ cost of putting one of these together?

Thanks

Wow you guys don’t wait around. :slight_smile: Kurt, on the receiver order of pulses generated. I have no doubts that you are seeing what you are seeing. :wink: I’m just hoping my dual trace scope foo (or lack thereof) was causing me to see them in the wrong order. Because if the receivers have no consistency it will be difficult for (non logic analyzer having people) to set it up. :frowning:

I’ve not been able to work on stuff for the last few days. Envy…

Hi Jim, yes I am having some fun :smiley: . Soon I should probably get back to figuring out the PS2s, but right now this is more fun.

So far it looks like my read on in one pass is working reasonably well. I will double check again to verify I have all of the wires hooked up in the right order.

Xan, yours looks really nice! I don’t have a sand blaster or an orbital sander, I only have a larger sander which I am afraid to try on this.

Sounds like a good idea to simply build an RS232 extension cable. I still
need to build a charge cable to plug in. I think I have the right plug available, but I need to adapt it to use with the Lynxmotion charger. Looks pretty slick. On My Rover, I bought an extra battery connector at Radio Shack, which sticks out the top of my rover. This is where I plug the battery charger into. I used a SPDT power switch, where I can only charge in the OFF position… But this plug looks a lot neater. Need to find out where to order one…

If I understand the binding process correctly, the state of the signals that it receives when you did the bind is the failsafe locations. So when I get a chance I may update the transmitter software to have a BIND mode, where we can set the condition that we would like and then do the bind again.

Now back to getting the rover to work!

Kurt

Happy New Year everyone!

I am in the process of updating the Transmitter side of the code. I have started off with Xan’s version of the code, which is great. Some of the things I am currently doing:

  1. converted the pulse output functions to assembly language. Hoping to make it very accurate! To facilitate this I have updated some of the code to convert the cha1, cha2… to an array ChaVals, which I ordered from 0-6. I then updated several of the variable names that are used in the generation of the pulses to be zero based instead of 1 based. (buffer, sum…)

  2. I #ifdef around the keep alive pulse.

  3. I am about to add in some BIND support. What I am thinking of doing is if when you first turn on the Radio, if a keypad key is down it will go into bind mode. In this mode I am going to hardwire the pulse widths for all 7 channels. My thoughts are:
    1500, 1500, 1000, 1500, 988, 988, 2000
    The idea is ground the binding signal on the receiver and power it up, you then hold a key down when you power up the transmitter (Computer side), which enters this mode, then you power up the transmitter and wait for the bind to complete. Once the bind is done, turn off, unplug bind wire and run. When the receiver loses signal, it should go to a safe value for the channels. On the receiver side I plan to detect that the keypad value is out of range to tell me that I lost my connection.

Does this all make sense?

Thanks
Kurt

Sounds great, I hope you get it!
I’ve not found any time for playing with my transmitter (just came home from vacation). But I’ve been following your posts :wink:

Sounds great, but I’m not sure if I follow completely here. Is the BIND support a Spectrum function? I probably need to read the documentation :unamused:

The Bind function is, what you need to do to tell the receiver which transmiter to listen to. I believe that Jim (or James or Beth) did that for yours before it was sent. I did that manually on mine. They can probably explain it much better than I can.

As part of the binding process for the real remotes is the ability to set what the values are for all of the channels for the case where the receiver loses the transmission from the transmitter. For example with an RC aircraft what I believe most people want is for the throttle to go to low and maybe the direction to go to center. Or maybe you want your airplane to do a slow bank, maybe lower landing gear, … The values for the different channels in this state is set to the values that transmitter is sending at the time the bind operation happens.

To setup to do a bind, you need to specially wire up the receiver. Normally you use a “Bind Plug” to do this, but when you break it down, what you need to do is to connect the signal part of the power plug to ground. This is the three pin connector that goes horizontal on the receiver. When you power up the receiver in this mode, the LEDS on both receivers will start blinking. Now on the transmitter side you need to turn the power on to the processor (left switch), which will start code which should start generating pulses. Now you need to hold in the BIND button the the transmitter(upper right) while you apply power to it, which will tell it to go into bind mode, LED also blinking. After several seconds the LEDS will hopefully go solid saying the bind completed. Now turn off everything, remove the bind plug and you are ready to go.

What I am simply hopefully going to do is to setup some specific defaults for the bind. Code is done, but currently debugging assembly transmit code…

I am not sure if this makes any sense.

Kurt

Thanks for your good explanation about the BIND function, Kurt.
It made very much sense!

-Use the BIND function to “bind” the receiver to the transmitter and setting the fail-safe positions. I think I got it, thanks!

Happy New Year guys!

This sounds like a good idea! Removing overhead by switching to assembly could improve the accuracy. Sound logic to me :stuck_out_tongue:

Feel free to remove the alive pulse. Writing custom failsafe values sounds like a much better solution.

Good plan! Writing the controller init values will stop the bot and the keypad value will tell us to turn off the bot (free servos in my case). I hope this works because this is way better then my alive idea :wink:

Xan

Hi Xan,

Sounds good to pull out the keep alive.

I now have the assembly working reasonably well. I ran into a few glitches as I forgot that the Basic command pauseus did not acutually pause a us but instead paused in 1/2 us. SO all of my timings were off.

It also suprised me that if you wanted a pulse of 1500 you have a high pulse of 200 followed by low of 1100 followed by high of 200. I would have expected either the pulse width low for 1500 should either be 1500 or incuding the high pulse (1500-200), but that does not appear to be what it is, so…

Also found that when I generated more or less exact values going into the transmitter, that was not what was coming out… That appears to be where the values ramping different comes in. I have a fudge calculation going in, which keeps it pretty close now.

I implemented the Bind function and I rebound my receiver with the transmitter. I used the values I mentioned earlier. I found that my receive all 7 channels at once is hanging when the transmitter is not there. Looking at the logic analyzer it looks like without the transmitter I am not receiving the first channel. The others come in with my defaults, so I need to add in some simple timeout to my function.

That is all for now.

Kurt

Ok, I have done some debugging of the bind values and found some more information in that there are differences of losing signal vs not having one at the begginning.

For example if you turn on the robot without the transmitter, it appears to not pulse the first channel (or throttle). Mine looks like:
http://i416.photobucket.com/albums/pp245/Kurts_Robots/NoTransmitter.png

Note all of the other channels are outputing my bind values.

Now I power up the transmitter and start doing something. In this case I have full throttle. Something like:
http://i416.photobucket.com/albums/pp245/Kurts_Robots/radiorunning.png

The first channell shows the full throttle. Now I lose the transmitter. All of the channels appear as the same as the last transmission, except for the first one, which appears to be the bind value, like:
http://i416.photobucket.com/albums/pp245/Kurts_Robots/Losttransmitter.png

So I am thinking of changing my default bind values for the first channel, maybe down to something like 750 or lower so it is out of the default range of 1000+.

Sound reasnable?

Kurt

Ok, it appears like what I described in the previous post is somewhat expected. It is described in the manual:
spektrumrc.com/ProdInfo/Files/SPM-Air-Module-Manual_LoRes.pdf

I now have the program working with binding, plus a sample test program that prints out the 7 channel values and detects when the receiver is powered up without the transmitter as well as lost the connection later.

In case any one else wants to play with my current code, here first is the updated transmitter code:

[code];Description: Lynxmotion costum RC radio
;Software version: V1.1
;Date: 29-12-2008
;Programmer: Jim Frye (aka RobotDude), Kurt (aka Kurte), Jeroen Janssen (aka Xan)
;
;Hardware setup: ABB2 with ATOM 28 Pro, Spectrum DM8, 2 joysticks, 2 sliders, HEX keypath, display
;
;NEW IN V1.0
; - As released
;
;NEW IN V1.1
; - Added calibration sub. Calibrates the joysticks at powerup. (Xan)
; CAUTION: Be sure that the left joystick is in the outer down position by powerup!
; - Added small offset at the pulses to decrease the send error. (Xan)
; - Added Alive meganism. Sends 1000 on the buttons channel every 4 cycles
;
;New in V1.2
; - More use of arrays and zero index variable names (Kurt)
; - Assembly language output of pulses - should be vary accurate (Kurt)
; - Removed alive function
; - calibrate of left joystick up/down has a little validation.
; - Add Bind function, set pulses to predefined values to bind with,
; Once bound with this, this is the values will use if it loses connection
; with transmitter.
; If Receiver is powered up without transmitter, all channels will go to default
; except for channel 0 (Throttle - by manual). If receiver loses contact with
; receiver channel 0 will go to default.
;
;KNOWN BUGS:
; - None at the moment :wink:
;
;====================================================================

Display con 8
Speaker con 9
Row0 con 10
Row1 con 11
Row2 con 12
Row3 con 13

DM8PPM con 15 ; Spectrum DM8 signal
;DO_IN_BASIC con 1
#ifndef DO_IN_BASIC
PULSE_FIX con 20
#else
PULSE_FIX con 20
#endif

Calibrated var bit
ChaVals var word(7)
ChaOffset var word(6)
iFudge var byte ; used as an array index in our fudge values code
wButtonPulseWidth var word
keypress var byte

col1 var bit
col2 var bit
col3 var bit
col4 var bit

; create variables for averaging code
index var byte
buffer0 var word(8)
buffer1 var word(8)
buffer2 var word(8)
buffer3 var word(8)
buffer4 var word(8)
buffer5 var word(8)
sum0 var word
sum1 var word
sum2 var word
sum3 var word
sum4 var word
sum5 var word

input p4
input p5
input p6
input p7

output p10
output p11
output p12
output p13

; initialize each buffer element, each sum, and then index to 0
for index = 0 to 7
buffer0(index) = 542
buffer1(index) = 542
buffer2(index) = 542
buffer3(index) = 542
buffer4(index) = 542
buffer5(index) = 542
next

sum0 = 4336
sum1 = 4336
sum2 = 4336
sum3 = 4336
sum4 = 4336
sum5 = 4336
index = 0

; chirpy squeak kinda thing
sound Speaker, [100\880, 100\988, 100\1046, 100\1175] ;musical notes, A,B,C,D.

; wake up the Matrix Orbital display module
serout Display ,i19200, [254, 66, 0] ;Backlight on, no timeout.
serout Display ,i19200, [254, 64, “The D-I-Y 2.4ghzRobot Radio Set!”] ;startup screen. Do only once…
pause 3000

; Mark for calibration
Calibrated = 0

; initialize the ppm output signal
low DM8PPM

; check to see if we should do a bind operation
gosub CheckKeypad
if wButtonPulseWidth <> 1100 then
gosub DoRadioBind
endif

;====================================================================
; — top of main loop —
start:

gosub CheckKeypad
ChaVals(6) = wButtonPulseWidth

; averaging expects that the a/d values are < 4096
; for each channel
; read the a/d
; subtract the previous value from 8 samples ago from the sum
; store the new value in the circular buffer
; add the new value to the sum
; divide the sum by 8 to get the average value
; convert joystick values 392 - 692 to servo values 1000uS - 2000uS

adin 2, ChaVals(0) ; right vertical 16
sum0 = sum0 - buffer0(index)
buffer0(index) = ChaVals(0)
sum0 = sum0 + ChaVals(0)
ChaVals(0) = sum0 / 8
ChaVals(0) = (((ChaVals(0)*42)-6500)/10)
ChaVals(0) = ChaVals(0) + chaOffset(0)

adin 3, ChaVals(1) ; right horizontal 17
sum1 = sum1 - buffer1(index)
buffer1(index) = ChaVals(1)
sum1 = sum1 + ChaVals(1)
ChaVals(1) = sum1 / 8
ChaVals(1) = (((ChaVals(1)*42)-6500)/10)
ChaVals(1) = ChaVals(1) + chaOffset(1)

adin 0, ChaVals(2) ; left vertical 18
sum2 = sum2 - buffer2(index)
buffer2(index) = ChaVals(2)
sum2 = sum2 + ChaVals(2)
ChaVals(2) = sum2 / 8
ChaVals(2) = (((ChaVals(2)*42)-6500)/10)
ChaVals(2) = ChaVals(2) + chaOffset(2)

adin 1, ChaVals(3) ; left horizontal 19
sum3 = sum3 - buffer3(index)
buffer3(index) = ChaVals(3)
sum3 = sum3 + ChaVals(3)
ChaVals(3) = sum3 / 8
ChaVals(3) = (((ChaVals(3)*42)-6500)/10)
ChaVals(3) = ChaVals(3) + chaOffset(3)

adin 18, ChaVals(4) ; Left slider
sum4 = sum4 - buffer4(index)
buffer4(index) = ChaVals(4)
sum4 = sum4 + ChaVals(4)
ChaVals(4) = sum4 / 8
ChaVals(4) = ChaVals(4) + 988

adin 19, ChaVals(5) ; Right slider
sum5 = sum5 - buffer5(index)
buffer5(index) = ChaVals(5)
sum5 = sum5 + ChaVals(5)
ChaVals(5) = sum5 / 8
ChaVals(5) = ChaVals(5) + 988

; finally increment the index and limit its range to 0 to 7.
index = (index + 1) & 7

if Calibrated=1 then
; update the display module
branch index, [update1,update2,update3,update4,update5,update6,update7,update8]

update8:
update7:
serout Display, i19200, [254, 71, 11, 1, keypress]
goto makepulses

update6:
if ChaVals(5)<1000 then
serout Display, i19200, [254, 71, 6, 2, " ", dec ChaVals(5)]
else
serout Display, i19200, [254, 71, 6, 2, dec ChaVals(5)]
endif
goto makepulses

update5:
if ChaVals(4)<1000 then
serout Display, i19200, [254, 71, 6, 1, " ", dec ChaVals(4)]
else
serout Display, i19200, [254, 71, 6, 1, dec ChaVals(4)]
endif
goto makepulses

update4:
if ChaVals(3)<1000 then
serout Display, i19200, [254, 71, 1, 2, " ", dec ChaVals(3)]
else
serout Display, i19200, [254, 71, 1, 2, dec ChaVals(3)]
endif
goto makepulses

update3:
if ChaVals(2)<1000 then
serout Display, i19200, [254, 71, 1, 1, " ", dec ChaVals(2)]
else
serout Display, i19200, [254, 71, 1, 1, dec ChaVals(2)]
endif
goto makepulses

update2:
if ChaVals(1)<1000 then
serout Display, i19200, [254, 71, 13, 2, " ", dec ChaVals(1)]
else
serout Display, i19200, [254, 71, 13, 2, dec ChaVals(1)]
endif
goto makepulses

update1:
if ChaVals(0)<1000 then
serout Display, i19200, [254, 71, 13, 1, " ", dec ChaVals(0)]
else
serout Display, i19200, [254, 71, 13, 1, dec ChaVals(0)]
endif
goto makepulses

; build and send the ppm output
makepulses:

gosub GeneratePulses
endif

if Calibrated=0 & index=0 then
gosub Calibrate
endif

; do it again ad infinitum
goto start

;------------------------------------------------------------------------------
; Generate Pulse function
;
; Variables used: ChanVals array has the pulse widths in us

GeneratePulses:
#ifndef DO_IN_BASIC
; fudge the values. It looks like the transmitter may not fully accuratly take
; the signals in and give them out 100% correctly. So try to fudge…
for iFudge = 0 to 6
ChaVals(iFudge) = chaVals(iFudge) - 3 + (ChaVals(iFudge)-988)/70
next

output DM8PPM
; The DM8PPM pin should already be configured for output...
; This is P15 on BAP which is P87 on the underlying H8/3694
; transistion to assembly language.
; fist setup loop counters and the like before we manipulate the IO port
mov.l	#CHAVALS:32, er3		; er3 has pointer to our array of desired values
mov.b	#7, r2l				; r2l has count of how many channels we wish to output
bset.b	#7,@PCR8:8			; make sure set to output, basic should have done earlier!

_MP_LOOP:
bset.b #7,@PDR8:8 ; (L8)set P15 high (Low over head here 26)
mov.w #200, r1 ; (H4) - We want to pause 400 .5us (pauseus does .5 us)
nop ; (H2)
nop ; (H2)
nop ; (H2)
nop ; (H2)
jsr @_MP_PAUSE:24 ; (H8) - Call our wait function.

bclr.b	#7,@PDR8:8			; (H8) Go low again - so high overhead here (26)
mov.w	@er3+, r1			; (L6) Get the wait count for current channel from array and increment array pointer
sub.w	#400, r1			; (L4) subtract off our high pulse widths..
jsr		@_MP_PAUSE:24		; (L8) call our wait function
dec.b	r2l					; (L2) decrement our counter for how many items to output
bne		_MP_LOOP:8			; (L4) still more channels to output

; loop is done, need to do trailing pulse
bset.b #7,@PDR8:8 ; (8)set P15 high
mov.w #200, r1 ; (4) - We want to pause 400
jsr @_MP_PAUSE:24 ; (8) - Call our wait function.
bclr.b #7,@PDR8:8 ; (8) Go low again
bra _MP_DONE:8 ; we are done!

; internal assembly function to wait a number of us
; Overhead of setup and call to here: 28 or 30
; setup and return overhead: 20: so total of 48/16 = 3us
; add/subtract some fudge
_MP_PAUSE:
sub.w #3, r1 ; (4) - subtract call/setup overhead from counter
shal.w r1 ; (2) - will multply by 16 to get clock cycles
shal.w r1 ; (2) - Should be safe max value ~2000
shal.w r1 ; (2)
shal.w r1 ; (2)
nop ; fudge
nop ;

_MP_PAUSE_LOOP:
sub.w #8, r1 ; (4) - decrement the time per loop cycle from loop
bne _MP_PAUSE_LOOP:8 ; (4) - hard spin in the loop. - since round number can simply test for zero

rts							; (8) - return

_MP_DONE:
; will fall through to return and transition back into basic
#else
ChaVals(0) = (((ChaVals(0)-PULSE_FIX)*2)-800) ; right vertical
ChaVals(1) = (((ChaVals(1)-PULSE_FIX)*2)-800) ; right horizontal
ChaVals(2) = (((ChaVals(2)-PULSE_FIX)*2)-800) ; left vertical
ChaVals(3) = (((ChaVals(3)-PULSE_FIX)*2)-800) ; left horizontal
ChaVals(4) = (((ChaVals(4)-PULSE_FIX)*2)-800)
ChaVals(5) = (((ChaVals(5)-PULSE_FIX)*2)-800)
ChaVals(6) = ((ChaVals(6)*2)-800)

high DM8PPM ;pulsout 15,800
pauseus 400
low DM8PPM
pauseus ChaVals(0)
high DM8PPM ;pulsout 15,800
pauseus 400
low DM8PPM
pauseus ChaVals(1)
high DM8PPM ;pulsout 15,800
pauseus 400
low DM8PPM
pauseus ChaVals(2)
high DM8PPM ;pulsout 15,800
pauseus 400
low DM8PPM
pauseus ChaVals(3)
high DM8PPM ;pulsout 15,800
pauseus 400
low DM8PPM
pauseus ChaVals(4)
high DM8PPM ;pulsout 15,800
pauseus 400
low DM8PPM
pauseus ChaVals(5)
high DM8PPM ;pulsout 15,800
pauseus 400
low DM8PPM
pauseus ChaVals(6)
high DM8PPM ;pulsout 15,800
pauseus 400
low DM8PPM
#endif
return

;------------------------------------------------------------------------------
; CheckKeypad function
;
; Variables updated:
; wButtonPulseWidth - The pulse width
; keypress - the ascii value associated with that key.
CheckKeypad:
low Row0
high Row1
high Row2
high Row3

col1 = in4 
col2 = in5 
col3 = in6 
col4 = in7 

;Read buttons 
if col1 = 0 then 
    wButtonPulseWidth  = 1200;   1 
    keypress = "1" 
elseif col2 = 0 
    wButtonPulseWidth  = 1250;   2 
    keypress = "2" 
elseif col3 = 0 
    wButtonPulseWidth  = 1300;   3 
    keypress = "3" 
elseif col4 = 0 
    wButtonPulseWidth  = 1650;   A 
    keypress = "A" 
else 
    high Row0 
    low  Row1 
    col1 = in4 
    col2 = in5 
    col3 = in6 
    col4 = in7 
    if col1 = 0 then 
        wButtonPulseWidth  = 1350;   4 
        keypress = "4" 
    elseif col2 = 0 
        wButtonPulseWidth  = 1400;   5 
        keypress = "5" 
    elseif col3 = 0 
        wButtonPulseWidth  = 1450;   6 
        keypress = "6" 
    elseif col4 = 0 
        wButtonPulseWidth  = 1700;   B 
        keypress = "B" 
    else 
        high Row1 
        low  Row2 
        col1 = in4 
        col2 = in5 
        col3 = in6 
        col4 = in7 
        if col1 = 0 then 
            wButtonPulseWidth  = 1500;   7 
            keypress = "7" 
        elseif col2 = 0 
            wButtonPulseWidth  = 1550;   8 
            keypress = "8" 
        elseif col3 = 0 
            wButtonPulseWidth  = 1600;    9 
            keypress = "9" 
        elseif col4 = 0 
            wButtonPulseWidth  = 1750;   C 
            keypress = "C" 
        else 
            high Row2 
            low  Row3 
            col1 = in4 
            col2 = in5 
            col3 = in6 
            col4 = in7 
            if col1 = 0 then 
                wButtonPulseWidth  = 1150;   0 
                keypress = "0" 
            elseif col2 = 0 
                wButtonPulseWidth  = 1900;   F 
                keypress = "F" 
            elseif col3 = 0 
                wButtonPulseWidth  = 1850 ;   E 
                keypress = "E" 
            elseif col4 = 0 
                wButtonPulseWidth  = 1800;   D 
                keypress = "D" 
            else 
                wButtonPulseWidth = 1100;   None is pushed 
                keypress = " " 
            endif 
        endif 
    endif 
endif 


return

;====================================================================
;Calibrates middle positions of the sticks to 1500
;Calibrates left vertical stick to be total down
Calibrate:
serout Display, i19200, [254, 88] ;clear screen
serout Display, i19200, [254, 0, “Calibrating…”]
pause 1000

ChaOffset(0) = 1500 - ChaVals(0) ; right vertical 16
ChaOffset(1) = 1500 - ChaVals(1) ; right horizontal 17

; special care for this one as user(me) may forget to put down to bottom
; if (chaVals(2) > 975) and (chaVals(2) < 1125) then
ChaOffset(2) = 1000 - ChaVals(2) ; left vertical 18
; else
; ChaOffset(2) = 0
; endif

ChaOffset(3) = 1500 - ChaVals(3) ; left horizontal 19

serout Display, i19200, [254, 88] ;clear screen

; Mark as calibrated
Calibrated=1
return

;------------------------------------------------------------------------------
; DoRadioBind function
;
; This function will set the 7 channels to fixed values that are transmitted
; This is used when the user is trying to bind the transmitter to the receiver
; Once bound, the receiver will use these values whenever it loses contact
; with the transmitter. This will allow us to have code on the receiver side
; that can detect this and put the robot into a safe state.
;
; TBD: Could allow the values to be set by each user, by where the sticks are, or
; could use keypad to enter…
;
; This function does not return.
DoRadioBind:

serout Display, i19200, [254, 88] ;clear screen     
serout Display ,i19200, [254, 0, ">>Radio Bind!<<"] ;startup screen. Do only once... 

while 1
	ChaVals = 1500, 1500, 800, 1500, 988, 988, 2000
	gosub GeneratePulses		; output pulses.
	pause 6					; not sure
wend

return	; will never happen!
[/code]

Here is my sample test program with the assembly level receive function that reads in all 7 channels regardless of the order. I do have some timeouts working in the code as well now.

[code];====================================================================
; DIY - Radio Receiver Test Program
;
; Currently this program assumes all 7 inputs are hooked up to P0-P6
; TBD - Make more general purpose, to allow different input pins.
; For P1-P7 change mask from 0x7f to 0xfe and make pulse_values word(8)
; For P8,10-15 change mask to 0xFD, PDR5 to PDR8, and pulse_values to word(8)
; BUGBUG - Need to check how PCR5 and PMR5 are used…

awPulsesIn var word(7)
bPulseTimeout var byte

awPulsesPrev var word(7)
fPulseChanged var bit ; we had a channel change
fNoTransmitter var bit ; did we detect no transmitter…
fLostTransmitter var bit ; We lost contact with transmitter

i var byte
keypress var byte
pulse_slop con 2

;--------------------------------------------------------------------
; Init code
;====================================================================
; Make sure all 7 pins are marked for input…
input p0
input p1
input p2
input p3
input p4
input p5
input p6

fNoTransmitter = 0		; assume we have a transmitter.
fLostTransmitter = 0

start:
gosub Pulsein7

; if we get a timeout and it is channel 0 then we now the receiver is not on...
if (bPulseTimeout) then
	; we detected no tranmitter at startup...
	if fNoTransmitter = 0 then
		serout s_out, i9600, "No Transmitter at power up(",hex bPulseTimeout, ")", 13 ]
		fNoTransmitter = 1
	endif
elseif awPulsesIn(0) < 950
	; On Bind we set channel 1 to about 800 normal is usually > 1000 so if under 950 
	; we probably lost our connection with the transmitter
	if fLostTransmitter = 0 then
		serout s_out, i9600, "Lost contact with transmitter", 13]
		fLostTransmitter = 1
	endif

else
	; Ok we hamve a message so reset our error codes
 	fNoTransmitter = 0		; assume we have a transmitter.
	fLostTransmitter = 0

	fPulseChanged = 0
	for i = 0 to 6
		if (awPulsesIn(i) > (awPulsesPrev(i)+pulse_slop)) or (awPulsesIn(i) < (awPulsesPrev(i)-pulse_slop)) then
			fPulseChanged = 1
			awPulsesPrev(i) = awPulsesIn(i)
		endif
	next
	
	if fPulseChanged or bPulseTimeout  then
		if awPulsesIn(6) < 1135 then  
			keypress = 0xff
		else
			keypress = (awPulsesIn(6)+15 - 1150) / 50  ; add in some slope as we could be slight
		endif
  
		serout s_out, i9600, [hex bPulseTimeout, ":", hex keypress, ":" ]
		for i = 0 to 6
			serout s_out, i9600, [dec awPulsesIn(i)," "]
		next
		serout s_out, i9600,[13] 

		if keypress = 0xA then 
		  low 12 
		else 
		  input p12 
		endif 
		
		if keypress = 0xB then 
		  low 13 
		else 
		  input p13 
		endif 
		
		if keypress = 0xC then 
		  low 14 
		else 
		  input p14 
		endif 
	endif
endif

goto start

;==============================================================================
; Read in all 7 servo values in one pass.
;
;-------------------------------------------------------------------
Pulsein7:
; Make sure all 7 IOs are set to input.
PMR5 = 0 ; all IO lines are general IO
PCR5 = 0 ; All are input (may want to leave bit 7 alone…

; Ok now lets transisiton to assembly language.
;

; bMask = 0x7f ; set a mask of which bits we are needing…
; ; Mask could be 0xFE for pins 1-7, need to make array 8 not 7
mov.b #0x7f, r1l ; Ok R1l will be our mask for outstanding IO port bytes.

; wait until none of the IO lines are high... 

; while PDR5 & bMask
; ;
; wend
mov.l #250000,er2 ;(4) - setup timeout counter
_PI7_WAIT_FOR_ALL_LOW:
mov.b @PDR5:8, r0l
and.b r1l, r0l ; see if any of the IO bits is still on…
beq _PI7_WAIT_FOR_NEXT_IO_TO_GO_HIGH:8 ; all zero go to wait for one to go high…
dec.l #1,er2 ;(2)
bne _PI7_WAIT_FOR_ALL_LOW:8 ; an IO pin is high and we have not timed out, keep looping until none are high
; We timed out waiting for all inputs to be low, so error out…
bra _P17_RETURN_STATUS:16 ; will return status that all timed out…

; while bMask

_PI7_WAIT_FOR_NEXT_IO_TO_GO_HIGH:
mov.l #250000,er2 ;(4) - setup timeout counter

_PI7_WAIT_FOR_NEXT_IO_TO_GO_HIGH2:
; we still need some 1 or more of the pulses…
; while (PDR5 & bMask) = 0 ; waiting for a pulse to go high.
mov.b @PDR5:8, r0l ;(4)
and.b r1l, r0l ;(*2) see if any of the IO bits is still on…
bne _P17_IO_WENT_HIGH:8 ;(*4) One went high so go process
dec.l #1,er2 ;(2)
bne _PI7_WAIT_FOR_NEXT_IO_TO_GO_HIGH2:8 ; (4) Not a timeout go try again.
; we timed out…
bra _P17_RETURN_STATUS:16 ; will return status of what timed out…

; wend
; iPin = ???; TBD: convert which bit is high to IO line number 0-6
; see which bit is on in the mask
_P17_IO_WENT_HIGH:
xor.w r2,r2 ;(*2)
xor.b r0h, r0h ;(*2)
mov.l #AWPULSESIN,er3 ;(*6)
_P17_WHICH_BIT_LOOP:
shlr.b r0l ;(@2)
bcs _P17_WHICH_BIT_LOOP_DONE:8 ;(@4)
inc.b r0h ;(@2)
inc.l #2, er3 ;(@2) - point to the next place in the array.
add.w #18,r2 ;(@4) - we do 18 clocks for each pass through this loop
bra _P17_WHICH_BIT_LOOP:8 ;(@4)
_P17_WHICH_BIT_LOOP_DONE:
; bMaskBit = 1 << iPin ; get the mask for which pin…
xor.b r1h,r1h ;(*2)
bset.b r0h,r1h ;(*2) ok we have the mask
bclr.b r0h,r1l ;(*2) and clear it from our global mask of ones we are waiting for

										; = (22) - count so far of clocks after went high

; iPinLoopCnt = 0 ; This may be replaced with time calculations…
; while (PDR5 & bMaskBit)
; iPinLoopCnt = iPinLoopCnt + 1 ; how long until it goes low again
; wend
_P17_WAIT_FOR_IO_GO_BACK_LOW:
mov.b @PDR5:8, r0l ;(#4)
and.b r1h, r0l ;(#2)
beq _P17_IO_WENT_BACK_LOW:8 ;(#4)
add.w #18,r2 ;(#4) - number of cyles per loop
bcc _P17_WAIT_FOR_IO_GO_BACK_LOW:8 ;(#4)

; we had a timeout return the status.
bset.b	r0h, r1l						; turn back on the bit we were waiting for...
bra		_P17_RETURN_STATUS:8			;

_P17_IO_WENT_BACK_LOW:
; need to calculate the pulse width in ms… need to divide calculated clocks by 16
add.w #22,r2 ; (4) ; need to add the rest of the overhead(*-1 loop above) in…
shlr.w r2 ; (2)
shlr.w r2 ; (2)
shlr.w r2 ; (2)
shlr.w r2 ; (2) / 16 (for clock speed)

; aPulses(iPin) = iPinLoopCnt ; convert loop count to pulse width…
mov.w r2,@er3 ; Save away the value

; bMask = bMask & ~bMaskBit ; turn off waiting for this one…
or r1l,r1l ; (2) see if we are done or not
; wend
bne _PI7_WAIT_FOR_NEXT_IO_TO_GO_HIGH:16 ;(6)our mask has not gone to zero so wait for the next one.

_P17_RETURN_STATUS:
mov.b r1l,@BPULSETIMEOUT
; finally transisition back to basic and return.
return
[/code]

Have fun
Kurt

You are quite the programmer, Kurte. I know ALL the credit isn’t yours, but thanks, non the less.