Return to Section List   Return to Menu

= = = 3. ADD PERIPHERALS = = =

Although your processor is now fully operational, it cannot store a lot of information, and it does not output human friendly text. In this section, you will add flash memory, a keypad, and an alphanumeric screen to your machine.

= BUY THE SECOND ROUND OF COMPONENTS =

Find the list of components on this page. Purchase all of the "second round" components. Be patient; delivery is not instantaneous!

= REMOVE DEBUG PORTS =

If you have been purchasing the components as instructed, you should only have 6 breadboards and 12 flip flops. This means you will not have enough parts or space to keep the debug ports. Unplug the debug input and debug output ports to allow for more exciting ports.

= HOOK UP THE SCREEN =

Plug in the "display data" and "display signal" flip flops as shown on the breadboard layout. You will need to connect the data bus to these display chips and other nearby flip flops. For convenience of wiring, consider plugging in some of the supporting flash flip flops and wiring them to the data bus. Use the schematic when wiring together the flip flops. "display data" should have mapped memory address 000C, while "display signal" should have address 100C.

Next you will need to do a bit of soldering. If you don't know how to solder, please refer to this video:

How and WHY to Solder Correctly

Connect a reasonably long wire to each of the pins on the alphanumeric screen. Be careful not to form bridges between different pins.

Attach the other end of each wire to the display flip flops. Look at the data sheet for both the flip flops and the screen, then follow this connection diagram:

If you don't use this wiring convention, the example programs will not work.

To test the screen connections, try this "hello world" program:

@@@ Move pointer forward by 1.
P incrementPointer /P pointer_ [
N upperByte
upperByte |<< second /pointer_
N lowerByte
lowerByte |<< first /pointer_
lowerByte |<< increment /lowerByte_
ifNot /lowerByte_ [
upperByte |<< increment /upperByte_
]
incrementPointer |<< pair /lowerByte, upperByte_
]

@@@ I/O address for the alphanumeric screen.
P screenDataAddress
P screenSignalAddress
screenDataAddress |<< pair /0, 192_
screenSignalAddress |<< pair /1, 192_

@@@ Send data and signals to the screen.
V outputScreen /N data, N signal1, N signal2_ [
output /screenDataAddress, data_
output /screenSignalAddress, signal1_
output /screenSignalAddress, signal2_
]

@@@ Erase half of the screen.
V clearScreenHalf /_ [
outputScreen /screen#1, 2, 0_
]

@@@ The bytes to send to the display in
@@@ order to wake it up.
C B}7 wakeDataSet
wakeDataSet |<< /screen#48, screen#48, screen#48, screen#56, screen#8, screen#6, screen#12_

@@@ Wake up half of the screen.
V wakeScreenHalf /_ [
P source
source |<< reference /wakeDataSet_
N count
count |<< ?wakeDataSet?
while /count_ [
N data
data |<< read /source_
outputScreen /data, 2, 0_
source |<< incrementPointer /source_
count |<< decrement /count_
]
]

@@@ Actually wake and clear half of the
@@@ screen for the first time.
wakeScreenHalf /_
clearScreenHalf /_

@@@ A test message to display.
C B}20 testMessage
testMessage |<< "HELLO WORLD!"

@@@ Write each character of the test
@@@ message to the screen.
P testMessagePointer
testMessagePointer |<< reference /testMessage_
N count
count |<< ?testMessage?
while /count_ [
N data
data |<< read /testMessagePointer_
outputScreen /data, 6, 4_
testMessagePointer |<< incrementPointer /testMessagePointer_
count |<< decrement /count_
]

Note that this is not machine code; it is Piston code. To compile Piston code into machine code, first navigate to this page to obtain Fabrication code, then use this page to assemble Fabrication code into machine code.

As you have in the past, convert the machine code into EEPROM programmer code, and then use your Arduino to enter the data. Sometimes the programmer code may be separated by line breaks. In this case, install each data block one at a time.

Run the "hello world" program using the slow clock. If everything worked OK, your machine should print "HELLO WORLD!" on the screen. If the program malfunctioned, add 1000 picofarad capacitors between the display flip flop WE pins and source. This will stabilize any noise in the circuit.

= HOOK UP THE KEYPAD =

Plug in and hook up another flip flop for the keypad. Let the keypad have address 900C in mapped memory. Connect the flip flop WE to the clock oscillator. This will ensure that input is frequently copied into the flip flop.

On an empty portion of breadboard, place 8 tactile push buttons. Attach each button to a keypad flip flop input:

Place a 100 kOhm resistor between each flip flop input and ground. This will ensure that the inputs default to a LOW state.

Test the keypad with this Piston program:

@@@ Move pointer forward by 1.
P incrementPointer /P pointer_ [
N upperByte
upperByte |<< second /pointer_
N lowerByte
lowerByte |<< first /pointer_
lowerByte |<< increment /lowerByte_
ifNot /lowerByte_ [
upperByte |<< increment /upperByte_
]
incrementPointer |<< pair /lowerByte, upperByte_
]

@@@ I/O address for the alphanumeric screen.
P screenDataAddress
P screenSignalAddress
screenDataAddress |<< pair /0, 192_
screenSignalAddress |<< pair /1, 192_

@@@ Send data and signals to the screen.
V outputScreen /N data, N signal1, N signal2_ [
output /screenDataAddress, data_
output /screenSignalAddress, signal1_
output /screenSignalAddress, signal2_
]

@@@ Erase half of the screen.
V clearScreenHalf /_ [
outputScreen /screen#1, 2, 0_
]

@@@ The bytes to send to the display in
@@@ order to wake it up.
C B}7 wakeDataSet
wakeDataSet |<< /screen#48, screen#48, screen#48, screen#56, screen#8, screen#6, screen#12_

@@@ Wake up half of the screen.
V wakeScreenHalf /_ [
P source
source |<< reference /wakeDataSet_
N count
count |<< ?wakeDataSet?
while /count_ [
N data
data |<< read /source_
outputScreen /data, 2, 0_
source |<< incrementPointer /source_
count |<< decrement /count_
]
]

@@@ Actually wake and clear half of the
@@@ screen for the first time.
wakeScreenHalf /_
clearScreenHalf /_

@@@ The I/O address of the keypad.
P keyDataAddress
keyDataAddress |<< pair /9, 192_

@@@ The main program loop.
while /1_ [
N keyData
keyData |<< 0
whileNot /keyData_ [
keyData |<< read /keyDataAddress_
]
outputScreen /screen#65, 6, 4_
while /keyData_ [
keyData |<< read /keyDataAddress_
]
]

While the program is running, press any keypad button. Each time you press a button, the processor will print the letter "A" to the screen. If the program does not work, examine the keypad flip flop OE pin with an LED. If the LED does not periodically blink, the program is not reading keypad port data.

= HOOK UP FLASH MEMORY =

The flash memory requires several ports to operate. Plug in a flip flop for each port as shown on the breadboard layout. Attach each flip flop to the data bus and control signals. Feed the clock oscillator into the WE of the flash output port. This will constantly copy data from the flash chip to the flip flop.

Plug in the flash chip, and use this connection diagram for wiring:

This is the convention used by DUO OS.

Here is a test program for the flash chip:

@@@ XOR together the corresponding bits of two bytes.
N xor /N data1, N data2_ [
N temp1
N temp2
temp1 |<< or /data1, data2_
temp2 |<< and /data1, data2_
temp2 |<< not /temp2_
xor |<< and /temp1, temp2_
]

@@@ Returns 1 if value is 0; returns 0 otherwise.
N booleanNot /N value_ [
booleanNot |<< 1
if /value_ [
booleanNot |<< 0
]
]

@@@ Test equality of two bytes.
N equal /N num1, N num2_ [
equal |<< xor /num1, num2_
equal |<< booleanNot /equal_
]

@@@ Move pointer forward by 1.
P incrementPointer /P pointer_ [
N upperByte
upperByte |<< second /pointer_
N lowerByte
lowerByte |<< first /pointer_
lowerByte |<< increment /lowerByte_
ifNot /lowerByte_ [
upperByte |<< increment /upperByte_
]
incrementPointer |<< pair /lowerByte, upperByte_
]

@@@ I/O address for the alphanumeric screen.
P screenDataAddress
P screenSignalAddress
screenDataAddress |<< pair /0, 192_
screenSignalAddress |<< pair /1, 192_

@@@ Send data and signals to the screen.
V outputScreen /N data, N signal1, N signal2_ [
output /screenDataAddress, data_
output /screenSignalAddress, signal1_
output /screenSignalAddress, signal2_
]

@@@ Erase half of the screen.
V clearScreenHalf /_ [
outputScreen /screen#1, 2, 0_
]

@@@ The bytes to send to the display in
@@@ order to wake it up.
C B}7 wakeDataSet
wakeDataSet |<< /screen#48, screen#48, screen#48, screen#56, screen#8, screen#6, screen#12_

@@@ Wake up half of the screen.
V wakeScreenHalf /_ [
P source
source |<< reference /wakeDataSet_
N count
count |<< ?wakeDataSet?
while /count_ [
N data
data |<< read /source_
outputScreen /data, 2, 0_
source |<< incrementPointer /source_
count |<< decrement /count_
]
]

@@@ Actually wake and clear half of the
@@@ screen for the first time.
wakeScreenHalf /_
clearScreenHalf /_

@@@ Pause for a short time.
V sleep /N count_ [
while /count_ [
count |<< decrement /count_
]
]

@@@ I/O address for flash chip.
P flashOutputAddress
P flashInputAddress
P bottomFlashAddressAddress
P middleFlashAddressAddress
P flashSignalAddress
flashOutputAddress |<< pair /8, 192_
flashInputAddress |<< pair /2, 192_
bottomFlashAddressAddress |<< pair /3, 192_
middleFlashAddressAddress |<< pair /4, 192_
flashSignalAddress |<< pair /5, 192_

@@@ Store flash address and signal between
@@@ function calls.
N currentFlashSignal
N currentTopFlashAddress
currentFlashSignal |<< 224
currentTopFlashAddress |<< 0

@@@ Set the selected flash address
@@@ without changing the signal.
V selectFlashAddress /P address, N topAddress_ [
N data
data |<< first /address_
output /bottomFlashAddressAddress, data_
data |<< second /address_
output /middleFlashAddressAddress, data_
currentTopFlashAddress |<< topAddress
data |<< or /currentFlashSignal, currentTopFlashAddress_
output /flashSignalAddress, data_
]

@@@ Send a signal to flash while
@@@ keeping the address constant.
V signalFlash /N signal_ [
currentFlashSignal |<< signal
N data
data |<< or /currentFlashSignal, currentTopFlashAddress_
output /flashSignalAddress, data_
]

@@@ Set all flash operations to
@@@ inactive.
signalFlash /224_

@@@ Output a value to flash. This
@@@ does not actually write a value
@@@ unless you follow the chip
@@@ security protocol.
V outputFlash /P address, N topAddress, N value_ [
signalFlash /96_
output /flashInputAddress, value_
selectFlashAddress /address, topAddress_
signalFlash /64_
signalFlash /96_
]

@@@ Erases a sector in flash. A sector
@@@ is 4096 bytes long.
V eraseFlash /P address, N topAddress_ [
P tempAddress
tempAddress |<< pair /58, 26_
outputFlash /tempAddress, 2, 170_
tempAddress |<< pair /197, 5_
outputFlash /tempAddress, 1, 85_
tempAddress |<< pair /58, 26_
outputFlash /tempAddress, 2, 128_
tempAddress |<< pair /58, 26_
outputFlash /tempAddress, 2, 170_
tempAddress |<< pair /197, 5_
outputFlash /tempAddress, 1, 85_
outputFlash /address, topAddress, 48_
sleep /255_
sleep /255_
]

@@@ Writes a value into flash. The
@@@ sector must be erased first.
V writeFlash /P address, N topAddress, N value_ [
P tempAddress
tempAddress |<< pair /58, 26_
outputFlash /tempAddress, 2, 170_
tempAddress |<< pair /197, 5_
outputFlash /tempAddress, 1, 85_
tempAddress |<< pair /58, 26_
outputFlash /tempAddress, 2, 160_
outputFlash /address, topAddress, value_
]

@@@ Reads a value from flash.
N readFlash /P address, N topAddress_ [
signalFlash /160_
selectFlashAddress /address, topAddress_
readFlash |<< read /flashOutputAddress_
]

@@@ Feedback messages for the test.
C B}5 positiveFeedback
C B}5 negativeFeedback
positiveFeedback |<< "GOOD "
negativeFeedback |<< "BAD  "

@@@ Write positive or negative feedback.
V displayFeedback /N which_ [
P messagePointer
if /which_ [
messagePointer |<< reference /positiveFeedback_
]
ifNot /which_ [
messagePointer |<< reference /negativeFeedback_
]
N count
count |<< 5
while /count_ [
N data
data |<< read /messagePointer_
outputScreen /data, 6, 4_
messagePointer |<< incrementPointer /messagePointer_
count |<< decrement /count_
]
]

@@@ The main program loop.
P testSector
P testAddress
testSector |<< pair /0, 48_
testAddress |<< pair /17, 49_
N count
count |<< 3
while /count_ [
eraseFlash /testSector, 1_
writeFlash /testAddress, 1, count_
N data
data |<< readFlash /testAddress, 1_
N bool
bool |<< equal /data, count_
displayFeedback /bool_
count |<< decrement /count_
]

The program will attempt to write values to a flash sector. If the flash chip reads back the same values, the program will print "GOOD" to the screen; otherwise "BAD". The program must erase the sector each time by entering a security sequence. If the program does not work on your machine, make sure that your wiring matches the convention shown earlier. Incorrect wiring will cause the security sequence to fail.

= RUN AT FULL SPEED =

Now that you have checked all functionality of the CPU, you can use a faster clock oscillator. Create the circuit in the diagram below:

Whenever you are ready to run a program, attach the oscillator to the decade counter clock input. The code should run very quickly. If the program does not work properly, try placing 1000 picofarad capacitors between various WE pins and source. In addition, you can adjust the CPU speed by using different decade counter outputs.

Good job! You have finished constructing your DUO Compact!

Return to Section List   Return to Menu

Return to the Ostracod Pond