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.
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.
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.
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