; Bit-Net controller for a microcore walking robot ; PIC16C54 source code for the Parallax assembler ; by Terry Newton, last modified Oct 20, 1997 ; ; Control Circuit... ; ; .------------*---------------*-----------> + ; | 2.2u | | ; | .--||-----*--4.7K-. .---|---< ra1 photo R (+) ; | | | | .-|---< ra0 photo L (+) ; | | .-----. | | | | .22u ; | | -|1 |--|---*-|-|--||--. ; | | -| |--|-----*-|--||--*----> - ; | | -| |--*-||-. | .22u_|_ ; *--|--------| |- 27p_|_ | ; | *--------| PIC |----------' ; | _|_ .---| |---820K----> rb7 Node 4 ; | .-----|---| |---820K----> rb6 Node 3 ; | | .---|---| |---820K----> rb5 Node 2 ; | | | .-|---| |---820K----> rb4 Node 1 ; | | | | | `-----' ; | | | `-|-----*---------------< rb3 Feeler R (-) ; | | `---|---*-|---------------< rb2 Feeler L (-) ; | `-----|---|-|---------------> rb1 Reverse out (-) ; *--100K-|---*-|-100--||-. 47u ; `--100K-|-----*-100--||-* 47u ; .-|<|--2.2K-*---|<|---------------> rb0 Reset (-) ; _|_Status LED _|_ ; ; Microcore walker topology... ; ; NOTE: This is a version of the Nervous Network, invented and ; internationally patented by Mark Tilden. Commercial usage is prohibited ; unless arrangements are made with the patent holder . ; ; Feel L Front Feel R ; -\ +| /- ; \ `-----/--. ; \ photo / Nu- ->- = diodes ; : L R : : M = motors ; : : : *->--: 1-4 = Nv neurons (C input, R to gnd) ; *--:-:--:->--: Nu+ = pos. neuron (C to gnd, R to +) ; ..:..:.:..:.. `-<-. Nu- = neg. neuron (R to gnd, C to +) ; .--: 'brain' :----<-: All neurons made from 1 74C14 chip ; : :...........: : Reverse circuit made from 2 4016 chips ; : :: + M - :: : ; : :`-2 4-': reverse ; : : |\ /| : circuit ; Nu+ : | \ | : ; : : |/ \| : forward: 1-->2-->3-->4-->1 ; `->-`--1 3--' reverse: 4-->3-->2-->1-->4 ; + M - ; ; ; A real-world version of the Bit-Net Algorithm.... ; ; LastBad = 0 ; NetInput = 0 ; NetOutput = 0 ; GetEnvironment (sets badinput flag) ; Loop forever ; BeforeLast = LastBad ; LastBad = BadInput ; BeforeLastE = NetInput ; BeforeLastO = NetOutput ; NetInput = Environment ; For each output bit ; NetOutput(out) = 0 ; For each input bit ; If NetInput(in) ; If Matrix(out,in) ; NetOutput(out) = 1 ; Apply NetOutput to walker for a few seconds ; GetEnvironment ; Train = 0 ; If LastBad = 0 ; If NetOutput contains BadOut bits ; Train = 1 ; If LastBad = 1 or BeforeLast = 1 ; If BadInput ; Train = 1 ; If Train ; TrainNet ; If BeforeLast ; TempSave = NetInput ; NetInput = BeforeLastE ; NetOutput = BeforeLastO ; TrainNet ; NetInput = TempSave ; ; TrainNet: ; For each output bit in NetOutput ; For each input bit in NetInput ; If random chance ; If NetInput(in) = 1 ; Matrix(out,in) = NOT NetOutput(out) ; If out is a BadOut bit ; If in is NOT a BadIn bit ; Matrix(out,in) = 0 ; *** set to PIC device, 54 or 56 *** device PIC16C54, RC_OSC, WDT_ON, PROTECT_OFF reset startup ; behaviour masks... BadInBits = 11000001b ; bad environment bits BadOutBits = 00100001b ; bad out bits with good environment ; drive constants TimeMax = 100 ; max control cycles per action cycle TimeMin = 50 ; minimum control cycles (time out = 0) EffectStart = 100 ; starting counter for each control cycle EffectMax = 18 ; maximum control duty cycle (% if start=100) EffectNorm = 9 ; normal control strength when effect outbit = 0 Discharge = 40 ; ms to discharge photo input caps ; constants for sleep/wake cycling SleepTime = 6 ; time to sleep times about five minutes MinAwake = 10 ; min wake cycles (max wake = 62 cycles) ; port masks PortAMask = 00000011b ; bit 0-1 inputs, bit 2-3 out PortARst = 00000000b ; for clearing both analog ins PortARst1 = 00000010b ; for clearing analog in 1 PortARst2 = 00000001b ; for clearing analog in 2 PortBMask = 00001101b ; bit 1 4-7 outs, bits 2-3 ins, 0=in PortBHiZ = 11111101b ; for hi-z state (no control) PortBStat = 11111100b ; make bit 0 out for status/reset ; pin assignments... ; analog inputs, 8 bit resolution (cell to +, .1u or .2u to gnd) AnaIn_1 = ra.0 ; L Photo AnaIn_2 = ra.1 ; R Photo ; digital inputs, active low (ground) DigIn_1 = rb.2 ; L Feeler DigIn_2 = rb.3 ; R Feeler ; outputs Reverse = rb.1 ; to reversal circuit (active low) Node_1 = rb.4 ; Node_2 = rb.5 ; to microcore in nodes Node_3 = rb.6 ; thru 1M resistors Node_4 = rb.7 ; NetOff = 11110001b ; mask to turn off net StatusLED = rb.0 ; for debugging, hiz or high ResetSig = rb.0 ; double use the pin, low to reset ; RAM assignment... statby = 03h ; PIC status byte TempSave = 07h ; stolen Port C reg Environment = 08h ; current environment buffer NetInput = 09h ; BN input nodes... ; input bit names Feeler_L = 0 ; left whisker Dark_L = 1 ; darkness on left Photo_L = 2 ; more light on left than right Photo_M = 3 ; left about equal to right Photo_R = 4 ; more light on right than left Dark_R = 5 ; darkness on right Feeler_R = 6 ; right whisker Feeler_F = 7 ; forward sensor (really Feeler_L AND Feeler_R) PhotoBitsI = 11100011b ; to mask photo bits (saves a byte...) NetOutput = 0Ah ; BN output nodes... ; output bit names revbit = 0 ; reverse out node1 = 1 ; individual node outs node2 = 2 node3 = 3 node4 = 4 effect = 5 ; increase control effect time = 6 ; increase action cycle time VarA = 0Bh ; GP variables VarB = 0Ch VarC = 0Dh VarD = 0Eh VarE = 0Fh ; Activation bit-matrix Matrix0 = 10h ; reverse Matrix1 = 11h ; node 1 Matrix2 = 12h ; node 2 Matrix3 = 13h ; node 3 Matrix4 = 14h ; node 4 Matrix5 = 15h ; effect Matrix6 = 16h ; time Loops = 17h ; for time delay Loops2 = 18h ; temp delay var CycleCount = 19h ; number of cycles since waking BeforeLastE = 1Ah ; environment before last BeforeLastO = 1Bh ; output before last Temp = 1Ch ; temp variables Temp1 = 1Dh Flags = 1Eh ; collection of bits for... Train = Flags.0 ; Apply training signal BadInput = Flags.1 ; Input contains undesirable bits LastBad = Flags.2 ; bad input flag of last cycle BeforeLast = Flags.3 ; and the cycle before that (maybe 1 more level?) RandBit = Flags.4 ; for random bit generator NetRunning = Flags.5 ; for net watcher TempF = Flags.6 ; temp flag WakeOK = Flags.7 ; signal when it has sat long enough RandShift = 1Fh ; for random number generator ; page 0 subroutines... Wait ; delay about Loops milliseconds (note: VERY approximate!) movlw 150 ; calibrate here if needed movwf Loops2 wait2 nop nop nop decfsz Loops2 goto wait2 decfsz Loops goto wait clr wdt ; no time out ret ; sub for restarting net using reset 'neuron' RestartNet mov !rb, #PortBStat ; make bit 0 out clrb ResetSig ; activate reset circuit mov loops, #255 call wait ; for a bit mov !rb, #PortBHiZ ; and let go ret ; sub for generating random bit in RandBit ; This needs work! (not so bad now but still ???) random movb Temp.0, RandShift.6 movb Temp1.0, RandShift.7 xor Temp, Temp1 movb c, /temp.0 rl RandShift clrb RandBit snb RandShift.0 setb RandBit ret ; sub to flash LED with contents of W, on lsb-msb off ; modified... high/low start, 8 data bits, low stop (long) ; easier to pick off a data cable to a PC LEDflash mov Temp, w mov !rb, #PortBStat setb statusLED mov loops,#100 ; start bit high call wait mov !rb, #PortBHiZ mov loops,#100 ; start bit low call wait mov Temp1, #8 LEDfla2 mov !rb, #PortBHiZ jnb Temp.0, LEDfla3 mov !rb, #PortBStat setb statusLED ; LED bits 0 to 7 LEDfla3 mov loops,#100 call wait rr Temp djnz Temp1, LEDfla2 mov !rb, #PortBHiZ mov loops,#255 call wait ; long stop bit 0 ret GetEnvironment ; Process the environment - read digital and quasi-analog inputs and ; form a collection of feature bits to present to the bit-net clr VarA clr VarB clr Environment ; start all environment bits 0 ; get photo input values one cell at a time ; each two passes, dark flags set to overflow mov !ra, #PortARst1 clrb AnaIn_1 ; discharge cap for photo L mov loops, #Discharge call wait mov !ra, #PortAMask ; back to input state G1loop dec VarA jz G1dark jnb AnaIn_1, G1loop jmp G2start G1dark jb Environment.Dark_L, G2start setb Environment.Dark_L jmp G1loop G2start mov !ra, #PortARst2 clrb AnaIn_2 ; discharge cap for photo R mov loops, #Discharge call wait mov !ra, #PortAMask G2loop dec VarB jz G2dark jnb AnaIn_2, G2loop jmp Gproc G2dark jb Environment.Dark_R, Gproc setb Environment.Dark_R jmp G2loop Gproc mov Temp, VarA mov Temp1, VarB cja Temp, #240, Gproc1 ; if either photo <= 240 cja Temp1, #240, Gproc1 and Temp, #11111000b ; mask off lower bits and Temp1, #11111000b Gproc1 csbe Temp, Temp1 setb Environment.Photo_L ; set photo_L if 1 > 2 csbe Temp1, Temp setb Environment.Photo_R ; set photo_R if 1 < 2 csne Temp1, Temp setb Environment.Photo_M ; set photo_M if 1 = 2 ; limit input space... (no more than two inputs set at once) jnb Environment.Dark_L, noDD ; don't set both sides to dark jnb Environment.Dark_R, noDD clrb Environment.Dark_L clrb Environment.Dark_R noDD jb Environment.Dark_L, noDL0 ; don't set photos if either dark jnb Environment.Dark_R, noDL1 noDL0 and Environment, #PhotoBitsI noDL1 movb Environment.Feeler_L, /DigIn_1 ; set feeler_L to inverted dig1 in movb Environment.Feeler_R, /DigIn_2 ; same for feeler_R jnb Environment.Feeler_L, noFF ; if both feelers, clear L and R jnb Environment.Feeler_R, noFF ; and set feeler_F clrb Environment.Feeler_L clrb Environment.Feeler_R setb Environment.Feeler_F noFF ; analyse inputs and set BadInput flag clrb BadInput mov w, Environment and w, #BadInBits sz setb BadInput ret ; train the net... (subroutine) TrainNet xor RandShift, RTCC ; start 'random' mov FSR, #Matrix0 ; start of bitnet matrix bytes, out loop clr VarB ; for each output bit... mov VarE, #00000001b ; rotating output bit mask Tloop clr VarA ; for each input bit... mov VarD, #00000001b ; rotating input bit mask Tloop1 call random ; get random RandBit mov Temp, 0 ; Temp = matrix byte (out) jnb RandBit, Tloop2 ; if random chance... mov w, NetInput and w, VarD jz Tloop2 ; if NetInput(in) = 1... clrb TempF mov w, NetOutput and w, VarE sz setb TempF ; save NetOutput(out) in TempF mov Temp1, VarD not Temp1 ; Temp1 = inverted input bit mask and Temp, Temp1 ; turn off matrix bit jb TempF, Tloop2 or Temp, VarD ; if TempF not set, set matrix bit Tloop2 mov Temp1, #BadOutBits and Temp1, VarE jz Tupdate ; if output is a badbit... mov Temp1, #BadInBits and Temp1, VarD jnz Tupdate ; if input is NOT a badbit... mov Temp1, VarD not Temp1 ; Temp1 = inverted input bit mask and Temp, Temp1 ; turn off matrix bit Tupdate mov 0, Temp ; update matrix clc rl VarD ; rotate input bit mask left inc VarA cjne VarA, #8, Tloop1 ; next input bit clc rl VarE ; rotate output bit mask left inc FSR inc VarB cjne VarB, #7, Tloop ; next out bit ret ; sleep mode - stop net cycling and wait until disturbed Sleep jb BadInput, NoSleep ; no sleeping if not happy mov !rb, #PortBMask mov rb, #NetOff mov Temp, #15 ; hold high about 3 seconds Sleep1 mov loops, #200 ; to stop cycling call wait djnz Temp, Sleep1 mov !rb, #PortBHiZ ; let go, net stopped ; pattern 10101010 mov w, #10101010b call LEDflash ; dump bitnet to LED... mov FSR, #Matrix0 mov VarC, #7 dump mov w, 0 call LEDflash inc FSR djnz VarC, dump ; pattern 01010101 mov w, #01010101b call LEDflash mov VarE, #SleepTime ; number of sleep cycles clrb WakeOK call GetEnvironment mov VarD, Environment ; save current environment Sleep2 mov option, #00001110b ; about a second sleep ; go to sleep Sleep3 dec CycleCount jnz Sleep4 ; each period about 5 minutes dec VarE ; next cycle snz setb WakeOK ; set WakeOK after cycles expire Sleep4 call GetEnvironment cje Environment, VarD, Sleep2 ; hang until something changes jb WakeOK, Wakeup ; if not time mov VarD, Environment ; update saved environment jnb BadInput, Sleep2 ; keep sleeping unless Bad Input Wakeup setb statby.3 ; set in case wdt times out jmp start0 ; come alive! NoSleep mov CycleCount, #3 jmp mainloop ; 3 more cycles to get somewhere ; ***** code execution starts here ***** startup jnb statby.3, Sleep3 ; timeout from sleep start0 mov OPTION, #00001111b ; int rtc, max wdt (!OPTION for SPASM) mov !ra, #PortAMask ; set the A port direction bits mov ra, #00001100b ; make unused pins high (were eeprom) mov !rb, #PortBHiZ ; and the B port bits to no control setb Reverse ; forward march! call RestartNet ; just in case, my walker doesn't ; always start by itself mov Temp, #15 start1 mov loops, #200 call wait djnz Temp,start1 ; wait a few seconds call GetEnvironment ; look around clrb LastBad ; clear flags and net nodes clr NetInput clr NetOutput mov CycleCount, VarA or CycleCount, VarB ; or-average of light level clc rr CycleCount clc rr CycleCount ; max awake cycles 62 ; limit minimum cycles... cja CycleCount, #MinAwake+1, mainloop mov CycleCount, #MinAwake+1 mainloop dec CycleCount ; go to sleep if cycles run out jz Sleep movb BeforeLast, LastBad ; save lastbad flag movb LastBad, BadInput ; save bad input flag mov BeforeLastE, NetInput ; save env before last mov BeforeLastO, NetOutput ; and the response mov NetInput, Environment ; set the input nodes ; evaluate the bit-net... clr NetOutput mov w, NetInput and w, Matrix0 sz setb NetOutput.0 mov w, NetInput and w, Matrix1 sz setb NetOutput.1 mov w, NetInput and w, Matrix2 sz setb NetOutput.2 mov w, NetInput and w, Matrix3 sz setb NetOutput.3 mov w, NetInput and w, Matrix4 sz setb NetOutput.4 mov w, NetInput and w, Matrix5 sz setb NetOutput.5 mov w, NetInput and w, Matrix6 sz setb NetOutput.6 ; for debugging mov w, NetOutput call LEDflash ; flash the output bits ; control the nervous net with bit-net output... movb Reverse,/NetOutput.revbit ; update reverse out mov VarA, #TimeMax ; cycles per period jb NetOutput.time, PLoop1 mov VarA, #TimeMin ; decrease if time bit not set Ploop1 mov !rb, #PortBHiZ ; start each cycle all hiz mov VarC, #EffectStart ; duty cycle counter ; set duty cycle according to Effect output mov VarB, #EffectMax ; max allowed duty cycle jb NetOutput.effect, PLoop2 mov VarB, #EffectNorm ; decrease if effect bit not set PLoop2 cjne VarC, VarB, PL3 ; go if countdown <> duty cycle mov VarD, #PortBHiZ jnb NetOutput.node1, PL2A ; set out dir bits and VarD, #11101110b ; for each set nodeout bit PL2A jnb NetOutput.node2, PL2B ; (and bit 0 status LED) and VarD, #11011110b PL2B jnb NetOutput.node3, PL2C and VarD, #10111110b PL2C jnb NetOutput.node4, PL2D and VarD, #01111110b PL2D mov !rb, VarD ; set direction bits setb StatusLED clrb Node_1 ; set node outs to low clrb Node_2 clrb Node_3 clrb Node_4 PL3 mov loops, #1 call wait ; delay djnz VarC, Ploop2 ; check for next effect value mov !rb, #PortBHiZ ; back to no control ; exit action loop if anything touches the feelers jb Environment.Feeler_L, PL3A jb Environment.Feeler_R, PL3A jb Environment.Feeler_F, PL3A ; don't check if already was touching jnb DigIn_1, PL3B jnb DigIn_2, PL3B ; exit early if either feeler in goes low PL3A djnz VarA, Ploop1 ; do enough times to have an effect PL3B ; see if net is still running.... mov Temp, #75 ; check for 75 cycles clrb NetRunning ChkNet jnb Node_1, ChkNet1 sb Node_3 setb NetRunning ; if opposing node not on, set flag ChkNet1 jnb Node_2, ChkNet2 sb Node_4 setb NetRunning ChkNet2 mov loops, #40 call wait ; delay each cycle (total about 3 seconds) djnz Temp, ChkNet jb NetRunning, NetOK ; go if running call RestartNet ; restart network NetOK ; evaluate the results of last move (modified to remember back 2 moves) call GetEnvironment ; see what's up clrb Train ; start in a good mood jb LastBad, Eval1 ; if lastbad = 0 (the one in NetInput) mov w, NetOutput and w, #BadOutBits sz ; if bad output setb Train ; set Train flag Eval1 jb LastBad, Eval2 ; if lastbad = 1 jb Beforelast, Eval2 ; or beforelast = 1 jmp Eval3 Eval2 snb BadInput ; if bad input in current environment setb train ; set train flag Eval3 jnb Train, mainloop ; loop if no training to do mov w, #11111111b call LEDflash ; solid flash to indicate training call TrainNet ; train net with current pattern jnb BeforeLast, Eval4 ; if BeforeLast flag set mov TempSave, NetInput ; to restore mov BeforeLastE, NetInput mov BeforeLastO, NetOutput call TrainNet ; train with previous pattern mov NetInput, TempSave ; restore last env Eval4 jmp mainloop ; end of code ; ; note about LED out... this is really the active low ; reset out, highjacked to double as a debugging LED out ; when made high. The pin line is always high except when ; resetting - the reset neuron keeps the line charged, ; not enough to light up the LED but enough to keep ; the reset neuron from firing. ; ; Here's a circuit for extracting the LED activity ; and translating it to a 5V PC signal... ; Printer Port ; LED ; Pin 6 >--*---2.2K---*--|>|--*------------o yel GND (25) ; |e | | .----------o grn Busy(11) ; pnp >|--10K---' .---* *--4.7K--o red D0 (2) ; Q1 | e| _|_ | o blk-nc ; `----10K-----|< gnd | RJ11 ; npn Q2 `-------' connector ; ; ; Version notes... ; 1020 - PIC 54 version, now running on a OTP chip. Unless I find ; something wrong with it, this is the one. Latest changes were ; minor - added SleepTime and MinAwake constants... ; Wake Time - from 10 to 62 cycles depending on light level ; Sleep Time - 6 * 256 timeouts, about 30 minutes ; (but always wakes if a feeler is touched) ;