
Introduction.
This discussion focuses on interfacing a printer with a Basic Stamp 2 using two PCF8574 8-bit I/O Expanders. Applications might be in dumping data from an EEPROM to hard copy without interfacing with a PC.
This code was developed to interface with old Epson dot matrix printers. That is, printers that were modeled on the original IBM dot matrix. There are no sophisticated escape code sequences.
Further, I made no effort to check the printer for trouble conditions. Rather, the Basic Stamp assumes the printer is ready, not busy and the paper is not empty. The Basic Stamp does not check the acknowledgement from the printer. (In fact, six I/O bits on one 8574 remain available and might be used for such status monitoring).
In writing this code my goal was to make it understandable for the reader. The result is a program which probably uses far more precious program memory than is necessary. However, i was quite surprised to note that this code uses less than 25 percent of the program memory in the Basic Stamp 2.
In the following, all terminals are referenced to the DB25 side of a printer port cable.
Printer Basics.
The printer is selected by exerting a logic zero on the /SELECT_IN (term 17). The printer may return error conditions on BUSY, Paper Empty (PE) and /ERROR. The printer brings SLCT high to signal the processor it is selected to perform. (Note that I simply tied /SELECT IN to ground and ignored all of the status indications.)
Normally processor output /INIT (term 16) is high. The printer is initialized by montarily bringing this lead low.
Processor output /STROBE (term 1) is normally a logic one. A character is sent to the printer by bringing DATA 0 through DATA 7 (terms 2-9) to the appropropriate states and then momentarily bringing /STROBE low. On receiving a character, the printer may return a signal on BUSY and the printer acknowledges by sending a momentary low on /ACK. However, in this program I do not check these leads.
The printer does not print each character as it is received. Rather, the characters are stored in a buffer, and on receipt of a "return" or newline, the content of the buffer is printed.
When /AUTOFEED is at logic zero, the printer advances the paper on receipt of the "return". I left ?AUTOFEED open (logic one) and sent a line feed after each return.
Thus, the printer is selected by bringing /SELECT_IN low. /STROBE and /INIT are normally high. The printer is initialized by bringing /INIT momentarily low. A character is sent by outputting the data on Data 0 - Data 7 and then momentarily bringing /STROBE low.
Hardware.
In this arrangement, two leads from the STAMP use the I2C protocl to communicate with two 8574 devices. One 8574, strapped using the A2, A1 and A0 address bits for address "0", controls the eight Data leads. A second, strapped for address "1" controls the /INIT lead on P0 and /STROBE on P1. The other six I/O pins on this device are not used.
Software.
The I2C protocol in controlling the 8574 is treated in another discussion. Recall that subroutines out_byte, in_byte, nack, start and sstop all relate to the interchange of data on the I2C bus.
Subroutine out_patt causes the content of o_patt to be written to the output of the specified device.
In the program which appears below, all outputs on both 8574s are set to a logic one. Note that /STROBE and /INIT are then high.
The printer is initialized in subroutine init by bringing P0 on device 1 momentarily low.
A character in variable o_char is sent to the printer using subroutine out_char. Note that o_char is written to the output of device 0 and /STROBE on P0 of device 1 is momentarily brought low.
Subroutine print_byte prints the byte in p_byte in hexadecimal. The high nibble is isolated, converted to an ASCII character and sent to the printer. This is followed by the low nibble, followed by a space.
Note that in main, the numbers 0x00 - 0xff are printed. After each eight numbers, an additional space is inserted and after each 16 numbers a return and line feed are sent to the printer.
' PRINT1.BS2
'
' Illustrates how to interface with a printer using a two PCF8574
' I/O Expanders.
'
'
' Basic Stamp 2 PCF8574 PCF8574
' Device 1 Device 0
'
' PIN5 (term 10) ----------- SCL (term 14) ----- SCL (term 14) --
' PIN4 (term 9) ----------- SDA (term 15) ----- SDA (term 15) --
'
' PCF8574 Printer
' Device 1
'
' P1 (term 5)-----------/STROBE (term 1)
' P0 (term 4)---------- INIT (term 16)
'
' PCF8574 Printer
' Device 0
'
' P7 (term 12)--------- DATA7 (term 9)
' P6 (term 11)--------- DATA6 (term 8)
' P5 (term 10)--------- DATA5 (term 7)
' P4 (term 9)---------- DATA4 (term 6)
' P3 (term 7)---------- DATA3 (term 5)
' P2 (term 6)---------- DATA2 (term 4)
' P1 (term 5)---------- DATA1 (term 3)
' P0 (term 4)---------- DATA0 (term 2)
'
' NC ---------- /AUTOFD (term 14)
'
' GRD --------- /SELECT (term 17)
'
' GRD (term 8)--------- GRD (term 18-25)
'
' Note that the slave address is determined by A2 (term 3), A1
' (term2) and A0 (term 1) on the 8574.
'
' 10K pull-up resistors to +5VDC are required on the SDA signal lead.
' 10K pull-up resistors are required on all eight printer DATA leads.
'
' Note that various debug statements and pauses may be removed.
'
' copyright Peter H. Anderson, MSU, May 15, '97
device var byte ' device 0-7
p_byte var byte ' byte to print in hex format
o_char var byte ' ASCII character to print
o_patt var byte ' byte to appear on output of 8574
i_patt var byte ' byte read from input of 8574
o_byte var byte ' byte to send to device
i_byte var byte ' byte from device
directions var byte ' defines which bits on 8574 are outputs
i var byte
n var byte ' index
b var bit ' bit
SDA_PIN con 4
SCL_PIN con 5
SDA_OUT var out4
SCL_OUT var out5
SDA_IN var in4
SDA_DIR var dir4
OUT con 1
IN con 0
dirs=$ffff
directions=$00 ' all bits on 8474s to be outputs
main
device=0
o_patt = $ff ' initialize all outputs to logic one
gosub out_patt
device=1
o_patt = $ff
gosub out_patt
gosub init ' intialize the printer
top
for i=0 to 255 ' print $00 - $FF
p_byte = i
gosub print_byte ' print content of p_byte
if ((i+1)//8) = 0 then space
L11
if ((i+1)//16) = 0 then ret
L12
debug hex i
pause 2
next
stop
space
o_char=$20 ' print a space
gosub out_char
goto L11
ret ' print a return and line feed
o_char=13
gosub out_char
o_char=10
gosub out_char
goto L12
init
device=1 ' momentary low on /INIT
o_patt=$02
gosub out_patt
o_patt=$03
gosub out_patt
return
print_byte ' print byte in p_byte in hex format
o_char = (p_byte>>4) & $0f ' isolate high nibble
if o_char > 9 then alpha_hi ' and convert to ASCII
o_char=o_char + "0"
goto L21
alpha_hi
o_char=o_char+"0" + 7
gosub out_char ' print it
L21
o_char=p_byte & $0f ' same for low nibble
if o_char > 9 then alpha_lo
o_char=o_char + "0"
goto L22
alpha_lo
o_char=o_char + "0" + 7
L22
gosub out_char
o_char=" " ' output a space
gosub out_char
return
out_char ' print charcter in o_char
device=1
o_patt=$3 ' init and strobe high
gosub out_patt
device=0
o_patt = o_char ' character to data 7 .. data 0 leads
gosub out_patt
pause 1
device=1
o_patt=$01
gosub out_patt ' bring strobe low
o_patt=$03 ' and then high
gosub out_patt
pause 1
return
in_patt ' fetch input on addressed device
gosub start
o_byte = $40 | (device <<1) | $1
gosub out_byte
gosub nack
gosub in_byte
gosub nack
gosub sstop
i_patt = i_byte
return
out_patt ' output specified patt to addresses device
gosub start
o_byte = $40 | (device <<1)
gosub out_byte
gosub nack
o_byte = o_patt | directions ' bits defined as inputs set
' to logic one
gosub out_byte
gosub nack
gosub sstop
return
in_byte ' fetches 8 bits, most sign bit first
SDA_DIR=IN 'input
i_byte=0
for n=0 to 7
' pause 200
high SCL_PIN ' clock high
' pause 200
i_byte=(i_byte << 1) | SDA_IN 'read bit and or with prev
debug dec SDA_IN
low SCL_PIN
next
SDA_DIR=OUT 'output
return
out_byte ' output o_byte beginning with most sig bit
low SDA_PIN
for n=0 to 7
b= (o_byte >> 7) & 1
if (b=1) then out_one
SDA_DIR=OUT
debug "0"
_clk
high SCL_PIN
' pause 100
low SCL_PIN
' pause 100
o_byte=o_byte << 1
next
return
out_one
SDA_DIR=IN
debug "1"
goto _clk
nack
SDA_DIR=OUT ' bring SDA high and clock
high SCL_PIN
low SCL_PIN
return
start
low SCL_PIN
SDA_DIR=IN ' SDA at logic one
high SCL_PIN
SDA_DIR =OUT ' bring SDA low while clock is high
low SCL_PIN
debug "START"
debug $0d
return
sstop
low SCL_PIN
SDA_DIR=OUT
high SCL_PIN
SDA_DIR=IN ' bring SDA high while clock is high
debug "STOP"
debug $0d
return
