# ############################################################################# # # 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 testing the LED10 RoboBrick. See: # # http://web.gramlich.net/projects/robobricks/led10/index.html # # for more details. # # ############################################################################# processor pic16f84 cp=off dp=off pwrte=disabled wdte=disabled fosc=xt constant clock_rate 4000000 constant instruction_rate clock_rate / 4 constant baud_rate 2400 constant instructions_per_bit instruction_rate / 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 # 9 bits long. At 4Mhz = 1 instruction per microsecond, 9 bits is 9/2400 = # 3.75ms. The count loop below is 7 instruction long. 3.75/.007 = 536 = 0x217. # So we want our timer loop to get as close to 0x217 as possible. constant clock_fudge 4 constant clock_target 0x17 + clock_fudge constant sp 32 constant cr 13 constant lf 10 # Some bit definitions: constant rx_slave_bit 0 constant tx_slave_bit 1 constant tx_master_bit 2 constant rx_master_bit 3 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 register status 3 bind c status@0 bind z status@2 port porta a bits_only read_write_static pin tx_master_pin porta tx_master_bit write_only pin rx_master_pin porta rx_master_bit read_only pin tx_slave_pin porta tx_slave_bit write_only pin rx_slave_pin porta rx_slave_bit read_only pin heart porta 4 write_only constant space 0xff constant buffer_size 5 string_constants { bit_string = 0s'Bit' blink_string = 0s'Blink' clock_string = 0s'Clk' common_string = 0s'Common' done_string = 0s'Done' fail_string = 0s'Fail' hello_string = 0s'LED4_Test' ramps = 0, 1, 0x10, 0x80, 0xff read_string = 0s'Read' register_string = 0s'Reg' } procedure main { arguments_none returns_nothing # Read a byte. variable buffer[buffer_size] byte variable char byte variable count byte variable index byte variable number byte # Print out a welcome message: call master_crlf() call master_string(hello_string) call master_crlf() # 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: call master_send(char) call master_crlf() # Initialize the id index: call slave_send(0xfd) # Get the first 8 bytes: call common_test_id_bytes8() # Get the next 8 bytes of random numbers: call common_test_id_bytes8() # Get the next 8 bytes of random numbers: call common_test_id_bytes8() # Get the slave brick name: call common_test_id_string() # Get the vendor name: call common_test_id_string() } else_if (char = 0c't') { # Do testing: call master_send(char) call master_crlf() call test() } else_if (char = 0c'c') { call master_send(char) call master_crlf() call clock_adjust() } 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 { arguments_none 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 call master_string(clock_string) call master_crlf() count := 4 error := 0xff error_minimum := 0xf0 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) # If high is less than 2, things are probably pretty broken. # We'll try a little fix here, but we are probably pretty # screwed up. if (high < 2) { low := 0 } # Now think about adjusting clock. if (low > clock_target) { #call master_send(0c'+') # Slave clock is too slow (i.e. pulse too long.) error := low - clock_target # Increment clock: command := 0xf9 #call slave_send(0xf9) } else_if (low < clock_target) { #call master_send(0c'-') # Slave clock is too fast (i.e. pulse too short.) error := clock_target - low # Decrement clock: command := 0xf8 #call slave_send(0xf8) } else { #call master_send(0c'x') 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 test { arguments_none returns_nothing # This procedure will test the LED10 RoboBrick: variable bit byte variable leds byte variable temp byte variable pattern byte # Adjust the clock: call clock_adjust() # Verify that the common commands are working: call common_test() # Clear the LEDS: call slave_send(0) call test_prompt(0) # Let's do some simple testing first: leds := 1 while (!(leds@4)) { # Set the bits: call slave_send(leds) call test_prompt(leds) call test_verify(leds) leds := leds << 1 } # Clear the leds: call slave_send(0) # Let's test out the bit twiddle commands: call master_string(bit_string) call master_crlf() bit := 0 leds := 1 while (bit < 4) { # Bit set: call slave_send(0x14 | bit) call test_verify(leds) # Read the bit: call slave_send(0x1c | bit) temp := slave_get() if (temp != 1) { call master_octal(bit) call master_octal(temp) call master_fail(bit_string, 0x30) } # Bit clear: call slave_send(0x10 | bit) call test_verify(0) # Read the bit again: call slave_send(0x1c | bit) temp := slave_get() if (temp != 0) { call master_octal(bit) call master_octal(temp) call master_fail(bit_string, 0x31) } # Bit toggle: call slave_send(0x18 | bit) call test_verify(leds) # Bit toggle again: call slave_send(0x18 | bit) call test_verify(0) leds := leds << 1 bit := bit + 1 } # Now do some blink rate tests: call master_string(blink_string) call master_crlf() # First turn the bits on: call slave_send(0xf) bit := 0 while (bit < 4) { # Set the blink rate: call slave_send(0x30 | (bit << 2) | bit) # Read the blink rate back: call slave_send(0x1c | bit) temp := slave_get() pattern := (bit << 1) | 9 if (temp != pattern) { call master_octal(temp) call master_octal(pattern) call master_fail(blink_string, 0x32) } bit := bit + 1 } # Now turn the bits on and let them blink: call test_prompt(0xf) # Now clear the blink rates: bit := 0 while (bit < 4) { # Clear the blink rate: call slave_send(0x20 | bit) bit := bit + 1 } call slave_send(0) # Now test increment: leds := 0 while (leds < 0x10) { call test_verify(leds) # Now increment the LED's: call slave_send(0x44) leds := leds + 1 } # Now test decrement: leds := 0x10 while (leds != 0) { leds := leds - 1 # Now decrement the LED's: call slave_send(0x48) # Now verify: call test_verify(leds) } # Let's clear the LED's: call slave_send(0) # Announce that we are done: call master_string(done_string) call master_crlf() } procedure test_verify { argument leds byte returns_nothing # This procedure will verify that the registers contain high and low. variable temp byte # Read leds register: call slave_send(0x40) temp := slave_get() if (temp != leds) { call master_octal(temp) call master_octal(leds) call master_fail(read_string, 0x50) } } procedure test_prompt { argument leds byte returns_nothing # This procedure will prompt the user to input a paticular pattern: variable count byte variable char byte # Print high order pattern: count_down count 4 { char := 0c'.' if (leds@3) { char := 0c'*' } call master_send(char) leds := leds << 1 } call master_send(0c':') while (master_get() = 0xfc) { # Do nothing: } call master_crlf() } # The procedures below are used to test the common shared commands: procedure common_test { arguments_none returns_nothing # This procedure will verify that the common shared commands work. variable actual byte # Print the ID information: # ID reset: call slave_send(0xfd) # Read the fixed bytes: call common_test_id_match(1, 0xc0) call common_test_id_match(0, 0xc1) call common_test_id_match(8, 0xc2) call common_test_id_match(0, 0xc3) call common_test_id_match(0, 0xc4) call common_test_id_match(0, 0xc5) call common_test_id_match(0, 0xc6) call common_test_id_match(0, 0xc7) call master_crlf() call common_test_id_bytes8() call common_test_id_bytes8() # Read the brick name: call common_test_id_string() # Read the vendor name: call common_test_id_string() # Verfify that we don't read off the end of the id and crash: call common_test_id_next() # Reset id: call slave_send(0xfd) # Verify that we are still alive: call common_test_id_match(1, 0xc8) # Read glitch: # Clear glitch register: call slave_send(0xfe) actual := slave_get() # Send a couple of glitches: call slave_send(0xff) call slave_send(0xff) # Read the glitch register: call slave_send(0xfe) if (slave_get() != 2) { call master_fail(common_string, 0xc9) } # Do a clock pulse: call slave_send(0xfb) if (slave_get() != 0) { call master_fail(common_string, 0xca) } # Read clock: call slave_send(0xfa) actual := slave_get() # Increment: call slave_send(0xf9) # Decrement: call slave_send(0xf8) # Read clock again: call slave_send(0xfa) if (actual != slave_get()) { call master_fail(common_string, 0xcb) } } procedure common_test_id_match { argument desired byte argument test_number byte returns_nothing # This procedure will verify that the next byte in the id is {desired}. if (desired != common_test_id_next()) { call master_fail(common_string, test_number) } } procedure common_test_id_bytes8 { arguments_none returns_nothing # This procedure will print out the next 8 bytes of the id in octal. variable count byte count_down count 8 { call master_octal(common_test_id_next()) } call master_crlf() } procedure common_test_id_string { arguments_none returns_nothing # This procedure will print out the next id string. variable count byte count_down count (common_test_id_next()) { call master_send(common_test_id_next()) } call master_crlf() } procedure common_test_id_next { arguments_none returns byte # This procedure returns the next byte from the identification string. call slave_send(0xfc) return slave_get() } # The following procedures are used to communicate with the master: 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_fail { argument test_name string argument test_number byte returns_nothing # This procedure will output `fail' followed by a carriage return # and line feed. call master_string(fail_string) call master_string(test_name) call master_octal(test_number) call master_crlf() } 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_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_send { argument data byte returns_nothing # This procedure will send one byte of {data} to the slave. call send_byte(data, tx_slave_mask) } # The last procedures do character sending and receiving: 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 (porta & 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 (porta & mask != 0) { char := char | 0x80 } 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 porta := mark call delay() call delay() call delay() # Send the data: count_down count 8 { if (char@0) { porta := space } else { porta := mark } char := char >> 1 call delay() call delay() call delay() } # Send the stop bit: porta := space call delay() call delay() call delay() } 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_string { argument message string returns_nothing # This procedure will output {message} to the master. variable size byte variable index byte index := 0 while (index < message.size) { call master_send(message[index]) index := index + 1 } call master_send(sp) } procedure delay { arguments_none returns_nothing uniform_delay instructions_per_delay # Delay 1 third of a bit: }