Interfacing a BS2 to a Printer Using Two 8574 I/O Expanders

copyright, Peter H. Anderson, Department of Electrical Engineering
Morgan State University, Baltimore, MD, May 15, '97

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