# ############################################################################# # # Copyright (c) 1999-2001 by Wayne C. Gramlich. # All rights reserved. # # Permission to use, copy, modify, distribute, and sell this software # for any purpose is hereby granted without fee provided that the above # copyright notice and this permission are retained. The author makes # no representations about the suitability of this software for any purpose. # It is provided "as is" without express or implied warranty. # # This is a test harness for developing Robobricks. Basically it allows # the user to type in an 8-bit number in octal and send it to a RoboBrick. # Any resulting data is read back and printed in octal. All communication # occurs at 2400 baud. See # # http://web.gramlich.net/projects/robobricks/harness/index.html # # for more details. # # ############################################################################# processor pic16f628 cp=off cpd=off lvp=off bowden=off mclre=on pwrte=off wdte=off fosc=xt #processor pic16f84 cp=off dp=off pwrte=disabled wdte=disabled fosc=xt # Some timing constants: constant clock_rate 10000000 constant clock_ticks_per_instruction 4 constant instructions_per_second clock_rate / clock_ticks_per_instruction constant baud_rate 2400 constant instructions_per_bit clock_rate / (clock_ticks_per_instruction * baud_rate) constant delays_per_bit 3 constant instructions_per_delay instructions_per_bit / delays_per_bit # The null pulse that comes back from a clock pulse command is supposed to be # exactly 9 bits long. 9 bits at 2400 baud is 9/2400 = 3.75mS. The number # iterations through the loop is 3.75mS / (number of instructions per iteration.) constant nine_bits_instructions (clock_rate * 9) / (clock_ticks_per_instruction * baud_rate) constant instructions_per_iteration 7 constant iterations_for_nine_bits nine_bits_instructions / instructions_per_iteration constant iterations_high iterations_for_nine_bits / 256 constant iterations_low iterations_for_nine_bits - (iterations_high * 256) # Some character definitions: constant sp 32 constant cr 13 constant lf 10 # Some bit definitions (see schematic): constant rx_slave_bit 5 constant tx_slave_bit 4 constant rx_master_bit 1 constant tx_master_bit 2 constant rx_slave_mask 1 << rx_slave_bit constant tx_slave_mask 1 << tx_slave_bit constant rx_master_mask 1 << rx_master_bit constant tx_master_mask 1 << tx_master_bit # Some port and pin definitions: port porta a unused none port portb b bits_only read_write_static pin tx_master_pin portb tx_master_bit write_only pin rx_master_pin portb rx_master_bit read_only pin tx_slave_pin portb tx_slave_bit write_only pin rx_slave_pin portb rx_slave_bit read_only # Some register and bit declarations: register status 3 bind c status@0 bind z status@2 # Some miscellaneous declarations: constant space 0xff constant buffer_size 5 string_constants { hello = 0s'Harness-B', cr, lf } procedure main { arguments_none returns_nothing # The main procedure prints out a hello message followed by # command loop. The commands are: # # Ns - Send N (in octal) to RoboBrick (no reply expected) # Nw - Send N (in octal) to RoboBrick and print reply bytes # in octal. Octal 376 means no byte. # i - Interrogate the RoboBrick's identification information # c - Perform a clock adjust sequence. variable char byte variable number byte variable count byte variable index byte variable buffer[buffer_size] byte # Print out a welcome message: index := 0 while (index < hello.size) { char := hello[index] call master_send(char, tx_master_mask) index := index + 1 } # Main loop number := 0 loop_forever { # Get a character: tx_slave_pin := 1 char := master_get() # Delay 2/3's of bit make sure that get_byte is done. call delay() call delay() if (0c'0' <= char && char < (0c'9' + 1)) { # Do a multiply by 8 then add in digit: call master_send(char) number := (number << 3) + char - 0c'0' } else_if (char = 0c's') { # Send byte to brick, no wait: # Echo command and send CRLF: call master_send(char) call master_crlf() # Ship the byte down to the brick: call slave_send(number) number := 0 } else_if (char = 0c'w') { # Send byte to brick, wait for results: # Echo command and send CRLF: call master_send(char) call master_crlf() # Ship the byte down to the brick ... call slave_send(number) number := 0 #call delay() # ...and wait for a response: index := 0 while (index < buffer_size) { buffer[index] := slave_get() index := index + 1 } index := 0 while (index < buffer_size) { call master_octal(buffer[index]) index := index + 1 } # Terminate the output list call master_crlf() } else_if (char = 0c'i') { # Interrogate the slave RoboBrick: # Initialize the id index: call slave_send(0xfd) # Get the first 8 bytes: call slave_id8() # Get the next 8 bytes of random numbers: call slave_id8() # Get the next 8 bytes of random numbers: call slave_id8() # Get the slave brick name: call slave_id_string() # Get the vendor name: call slave_id_string() } else_if (char = 0c'c') { call clock_adjust(number) number := 0 } else_if (char != 0xfc) { # Just echo back the current number: call master_send(0c'<') call master_octal(char) call master_send(0c'>') # Send a carriage-return line-feed: call master_crlf() # Output the character in octal: call master_octal(number) # Send a carriage-return line-feed: call master_crlf() # Reset number number := 0 } } } procedure clock_adjust { argument adjust byte returns_nothing # This procedure will adjust the clock to the slave. variable command byte variable count byte variable error byte variable error_minimum byte variable high byte variable low byte variable target byte target := iterations_low - adjust count := 4 error := 0xff error_minimum := 0xf0 # Print out the target: call master_send(0c'T') call master_octal(iterations_high) call master_octal(target) call master_crlf() loop_forever { # Print out the clock value: call master_send(0c'C') call slave_send(0xfa) call master_octal(slave_get()) # Ask for a timing byte: call slave_send(0xfb) low := 0 high := 0 while (rx_slave_pin) { # Do nothing: } while (!rx_slave_pin) { low := low + 1 if (z) { high := high + 1 } } # Print out high and low: call master_send(0c'H') call master_octal(high) call master_send(0c'L') call master_octal(low) # Now think about adjusting clock. if (high > iterations_high) { # Clock pulse is too long; slave clock is too slow: command := 0xf9 error := 0xff - target } else_if (high < iterations_high) { # Clock pulse is too short; slave clock is too fast: command := 0xf8 error := target } else { # The high 8-bits are equal: if (low > target) { # Clock pulse is too long; slave clock is too slow: command := 0xf9 error := low - target } else_if (low < target) { # Clock pulse is too short; slave clock is too fast: command := 0xf8 error := target - low } else { # Exact match; we are done: command := 0 error := 0 error_minimum := 0 } } # Print out the error and error minimum: call master_send(0c'E') call master_octal(error) call master_send(0c'M') call master_octal(error_minimum) if (error = error_minimum) { call master_crlf() return } else_if (error < error_minimum) { error_minimum := error error := error + 1 } else { count := count - 1 if (z) { error_minimum := error_minimum + 1 count := 4 } } # Now adjust the clock: call slave_send(command) call master_crlf() } } procedure master_crlf { arguments_none returns_nothing # This procedure will output a carriage-return line-feed # to the master. call master_send(cr) call master_send(lf) } procedure master_get { arguments_none returns byte # This procedure will get the next byte or return 0xfc if # no byte is forthcoming. return get_byte(rx_master_mask) } procedure master_send { argument character byte returns_nothing # This procedure will output {character} to the master. call send_byte(character, tx_master_mask) } procedure master_octal { argument number byte returns_nothing # This procedure will output {number} in octal to the tx port. # Output the character in octal: call master_send((number>>6) + 0c'0') call master_send(((number>>3) & 7) + 0c'0') call master_send((number & 7) + 0c'0') call master_send(sp) } procedure slave_get { arguments_none returns byte # This procedure will get a byte from the slave or return 0xfc # if no byte is forthcoming. return get_byte(rx_slave_mask) } procedure slave_id8 { arguments_none returns_nothing # This procedure will print out the next 8 bytes of data from # the slave RoboBrick id bytes. variable counter byte count_down counter 8 { call master_octal(slave_id_next()) } call master_crlf() } procedure slave_id_next { arguments_none returns byte # This procedure will return the next byte from the id bytes. call slave_send(0xfc) return slave_get() } procedure slave_id_string { arguments_none returns_nothing # This procedure will return the string for the id bytes. variable size byte size := slave_id_next() call master_octal(size) count_down size size { call master_send(slave_id_next()) } call master_crlf() } procedure slave_send { argument data byte returns_nothing # This procedure will send one byte of {data} to the slave. call send_byte(data, tx_slave_mask) } procedure get_byte { argument mask byte returns byte # Get an 8-bit byte from {mask} bit of {porta} and return it. # If no character shows up in a while 0xfc is returned. variable count byte variable char byte # Wait until a start bit arrives: count := 0 while (portb & mask != 0) { count := count - 1 if (count = 0) { return 0xfc } call delay() } # Skip over the start bit: call delay() call delay() call delay() # Sample in the middle third of each data bit: char := 0 count_down count 8 { call delay() char := char >> 1 if (portb & mask != 0) { #char := char | 0x80 char@7 := 1 } call delay() call delay() } # Skip over 1/3 of the stop bit: call delay() return char } procedure send_byte { argument char byte argument mask byte returns_nothing # Send {char} to {mask} bit of {porta}. variable count byte variable mark byte # Send the start bit: mark := mask ^ space portb := mark call delay() call delay() call delay() # Send the data: count_down count 8 { if (char@0) { portb := space } else { portb := mark } char := char >> 1 call delay() call delay() call delay() } # Send the stop bit: portb := space call delay() call delay() call delay() } procedure delay { arguments_none returns_nothing uniform_delay instructions_per_delay # Delay 1 third of a bit: }