ucl 1.0 # Copyright (c) 2000-2005 by Wayne C. Gramlich # All rights reserved. library $robobricks_pic16f688 package pdip pin 1 = power_supply pin 2 = ra5_in, name=debug pin 3 = ra4_in, name=io2, mask=io2_mask, bit=io2_bit pin 4 = ra3_nc, name=nc1 pin 5 = rx, name=rx pin 6 = tx, name=tx pin 7 = rc3_in, name=io7, mask=io7_mask, bit=io7_bit pin 8 = rc2_in, name=io6, mask=io6_mask, bit=io6_bit pin 9 = rc1_in, name=io5, mask=io5_mask, bit=io5_bit pin 10 = rc0_in, name=io4, mask=io4_mask, bit=io4_bit pin 11 = ra2_in, name=io3, mask=io3_mask, bit=io3_bit pin 12 = ra1_in, name=io1, mask=io1_mask, bit=io1_bit pin 13 = ra0_out, name=io0, mask=io0_mask, bit=io0_bit pin 14 = ground # Pin bindings:6 # Some globals: byte constant state_size = 14 constant state_size2 = state_size << 1 global state[state_size] array[byte] bind command_previous = state[0] bind command_last = state[1] bind sent_last = state[2] bind sent_previous = state[3] bind raw = state[4] bind direction = state[5] bind outputs = state[6] bind complement_mask = state[7] bind flags = state[8] bind low_mask = state[9] bind high_mask = state[10] bind rising_mask = state[11] bind falling_mask = state[12] bind analog_mask = state[13] global analogs_high[8] array[byte] global analogs_low[8] array[byte] global thresholds_high[8] array[byte] global thresholds_low[8] array[byte] bind interrupt_enable = flags@0 bind interrupt_pending = flags@1 global glitch byte global index byte global chan byte global channel byte origin 0 procedure main arguments_none returns_nothing local command byte local temp byte local do_send bit # Switch over to 8MHz: $osccon := 0x71 # Warm up the UART: $txsta := 0x24 $rcsta := 0x90 $baudctl := 0x08 $spbrg := $eusart_2400_low $spbrgh := $eusart_2400_high # Initalize all of the globals: call reset() # Process commands: loop_forever # Wait for command: command := byte_get() # Dispatch on command switch command >> 6 # 00xx xxxx: case 0 # 0000 xxxx: switch command >> 4 case 0 do_send := 1 switch command case_maximum 15 case 0 # 0000 0000 (Read Inputs): temp := raw ^ complement_mask case 1 # 0000 0001 (Read Outputs): temp := outputs ^ complement_mask case 2 # 0000 0010 (Read Direction): temp := complement_mask case 3 # 0000 0011 (Read Raw): temp := direction case 4 # 0000 0100 (Read Read Low Mask): temp := low_mask case 5 # 0000 0101 (Read High Mask): temp := high_mask case 6 # 0000 0110 (Read Rising Mask): temp := rising_mask case 7 # 0000 0111 (Read Falling Mask): temp := falling_mask case 8 # 0000 1000 (Read Inputs Raw): temp := raw case 9 # 0000 1010 (Read Analog Mask): temp := analog_mask case 10 # 0000 1001 (Read Outputs Raw): temp := outputs case 11 # 0000 1011 (Read Analog VRef): temp := 0 if $vcfg temp := temp + 1 default # 0000 101x or 0000 11xx (Do nothing): do_send := 0 if do_send call byte_put(temp) case 1 # 0001 xxxx: switch command & 15 case_maximum 15 case 0 # 0001 0000 (Reset Outputs): outputs := complement_mask case 1 # 0001 0001 (Set Outputs): outputs := byte_get() ^ complement_mask case 2 # 0001 0010 (Set Complement Mask): complement_mask := byte_get() case 3 # 0001 0011 (Set Direction Mask): direction := byte_get() call direction_set() case 4 # 0001 0100 (Set Low Mask): low_mask := byte_get() case 5 # 0001 0101 (Set High Mask): high_mask := byte_get() case 6 # 0001 0110 (Set Rising Mask): rising_mask := byte_get() case 7 # 0001 0111 (Set Falling Mask): falling_mask := byte_get() case 8 # 0001 1000 (Set Outputs Raw): outputs := byte_get() case 9 # 0001 1001 (Set Analog Mask): analog_mask := byte_get() call direction_set() case 10 # 0001 101v (Set Analog Vref): $vcfg := 0 case 11 # 0001 101v (Set Analog Vref): $vcfg := 1 case 15 # 0001 1001 (Reset Everything): call reset() case 2 # 0110 vbbb (Set Output Bit): temp := mask[command & 7] if command@3 outputs := outputs | temp else outputs := outputs & (temp ^ 0xff) case 3 # 0011 xxxx (Do Nothing): do_nothing case 1 # 01xx xxxx: switch (command >> 4) & 3 case 0 # 0100 llll (Set Outputs Low): outputs := outputs & 0xf0 | (command ^ complement_mask) & 0xf case 1 # 0101 hhhh (Set Outputs High): outputs := ((command << 4) ^ (complement_mask & 0xf0)) | outputs & 0xf case 2 # 0110 llll (Set Direction Low): direction := direction & 0xf0 | command & 0xf call direction_set() case 3 # 0111 hhhh (Set Direction High): direction := (command << 4) | direction & 0xf call direction_set() case 2 # 10xx xxxx: temp := command & 7 switch (command >> 3) & 7 case_maximum 7 case 0 # 1000 0bbb (Read Analog 8-bits): call byte_put(analogs_high[temp]) case 1 # 1000 1bbb (Read Analog 10-bits): call byte_put(analogs_high[temp]) call byte_put(analogs_low[temp]) case 2 # 1001 0bbb (Read Low Threshold): call byte_put(thresholds_low[temp]) case 3 # 1001 1bbb (Read High Threshold): call byte_put(thresholds_high[temp]) case 6 # 1011 0bbb (Set Low Threshold): thresholds_low[temp] := byte_get() case 7 # 1011 1bbb (Set High Threshold): thresholds_high[temp] := byte_get() case 3 # 11xx xxxx: switch (command >> 3) & 7 default # 110x xxxx or 1110 0xxx: do_nothing case 5 # 1110 1xxx: switch command & 7 case 6 # 1110 1110 (Reset) call reset() case 7 # 1110 1111 (Read Interrupt Bits): temp := 0 if interrupt_pending temp@0 := 1 if interrupt_enable temp@1 := 1 call byte_put(temp) case 6 # 1111 0xxx: switch command & 7 case 0, 1, 2, 3 # 1111 00ep (Set Interrupt Bits): interrupt_enable := command@1 interrupt_pending := command@0 case 4, 5 # 1111 010p (Set Interrupt Pending): interrupt_pending := command@0 case 6, 7 # 1111 011e (Set Interrupt Enable): interrupt_enable := command@0 case 7 switch command & 7 case 0 # 1111 1000 (Clock Decrement): $osctune := $osctune - $osccal_lsb case 1 # 1111 1001 (Clock Increment): $osctune := $osctune + $osccal_lsb case 2 # 1111 1010 (Clock Read): call byte_put($osctune) case 3 # 1111 1011 (Clock Pulse): call byte_put(0) case 4 # 1111 1100 (ID Next): temp := 0 if index < id.size temp := id[index] index := index + 1 call byte_put(temp) case 5 # 1111 1101 (ID Reset): index := 0 case 6 # 1111 1110 (Glitch Read): call byte_put(glitch) glitch := 0 case 7 # 1111 1111 (Glitch): if glitch != 0xff glitch := glitch + 1 procedure direction_set arguments_none returns_nothing # This procedure will set the direction appropriately. local temp byte # Deal with port C: temp := 0 if direction@4 temp@io4_bit := 1 if direction@5 temp@io5_bit := 1 if direction@6 temp@io6_bit := 1 if direction@7 temp@io7_bit := 1 $trisc := temp # Deal with port A: temp := 0 if direction@0 temp@io0_bit := 1 if direction@1 temp@io1_bit := 1 if direction@2 temp@io2_bit:= 1 if direction@3 temp@io3_bit := 1 $trisa := temp temp := 0 if analog_mask@0 temp@0 := 1 if analog_mask@1 temp@1 := 1 if analog_mask@2 temp@3 := 1 if analog_mask@3 temp@2 := 1 if analog_mask@4 temp@4 := 1 if analog_mask@5 temp@5 := 1 if analog_mask@6 temp@6 := 1 if analog_mask@7 temp@7 := 1 $ansel := temp procedure reset arguments_none returns_nothing # This procedure will initialize all global registers: # Initialize global registertes: index := 0 loop_exactly state_size state[index] := 0 index := index + 1 index := 0 loop_exactly 8 analogs_high[index] := 0 analogs_low[index] := 0 thresholds_high[index] := 0x80 thresholds_low[index] := 0x80 index := index + 1 interrupt_enable := 0 interrupt_pending := 0 direction := 0xff analog_mask := 0 # Initialize the A/D module: # A/D Conversion clock is Fosc/8 (Tad=2uS) and AD is on: $adcon0 := 1 $adcon1 := 0x20 $adcs0 := 1 $adif := 0 $adie := 0 $gie := 0 channel := 0 # This sets TRISA and TRISC: call direction_set() # Initialize remaining registers: glitch := 0 index := 0 procedure delay arguments_none returns_nothing # This procedure delays 1/3 of a bit. local temp byte local previous byte local current byte local not_current byte local changed byte # Perform A/D: chan := chans[channel] # At 8MHz, TAD = 1.6uS (ADCS<2:0>=010). An A/D conversion takes # 11*TAD = 11*1.6uS = 17.1uS # Acquiring the signal takes approximately 20uS. # We'll pad that out a little to 25uS to be safe: $adcon0 := (chan << 2) | 1 delay 25 * microsecond # Kick the dog: watch_dog_reset # Read inputs: raw := 0 if io7 raw@7 := 1 if io6 raw@6 := 1 if io5 raw@5 := 1 if io4 raw@4 := 1 if io3 raw@3 := 1 if io2 raw@2 := 1 if io1 raw@1 := 1 if io0 raw@0 := 1 # Getting the value takes 11 & Tad, where Tad = 2uS = 22uS. # We'll add 5uS for a little pad: $go := 1 delay 28 * microsecond # Write out port C: temp := 0 if outputs@7 temp@io7_bit := 1 if outputs@6 temp@io6_bit := 1 if outputs@5 temp@io5_bit := 1 if outputs@4 temp@io4_bit := 1 $portc := temp # Write out port A: temp := 0 if outputs@3 temp@io3_bit := 1 if outputs@2 temp@io2_bit := 1 if outputs@1 temp@io1_bit := 1 if outputs@0 temp@io0_bit := 1 $porta := temp # A/D result is ready: analogs_high[channel] := $adresh analogs_low[channel] := $adresl # Store the result away: channel := (channel + 1) & 7 # Setup for interrupts: previous := current # Read the I/O port once: current := raw ^ complement_mask not_current := current ^ 0xff changed := current ^ previous # See about triggering the interrupt_pending flag: if (low_mask & not_current) | (high_mask & current) | (changed & current & rising_mask) | (changed & not_current & falling_mask) != 0 interrupt_pending := 1 # Send an interrupt if interrupts are enabled: if interrupt_pending if interrupt_enable # If transmit buffer is empty, send a null: if $trmt $txreg := 0 constant zeros8 = "\0,0,0,0,0,0,0,0\" constant module_name = "\4\IO8A" constant vendor_name = "\7\Gramson" string id = "\1,1,32,2,9,0,0,0\" ~ zeros8 ~ zeros8 ~ module_name ~ vendor_name #string chans = "\0,1,3,2,4,5,6,7\" string chans = "\3,6,5,1,0,4,2,7\" string mask = "\1,2,4,8,16,32,64,128\"