A Stamp UART

This is a reprint from the Stamp Project of the Month, June 2000. Check out this month's project. Also, check out our full line of peripheral chips for microcontrollers -- floating point, I/O expansion, PS/2 keyboard interface, PWM, and more. If you want a handy way to connect RS-232 to a breadboard, have a look at http://www.al-williams.com/awce/rs1.htm

Many Stamp projects require RS-232 connections to external devices, instruments, or a PC. The Stamp has several ways to handle RS-232. You can use any pin as a TTL RS-232 input or output. You can also use the programming port as a half-duplex RS-232 port that has level shifting to the RS-232 specification.

The real missing link, however, is buffering. While the Stamp is working, it can't listen to the RS-232 port. If data arrives and you aren't ready, it is lost. Another problem is that only one port has level shifting. If you want to do true RS-232 on another port, you'll have to use a MAX232, a MAX233, or a 1488/1489 combo to get true RS-232 levels.

If you have the tools to program an SX chip (which could be an inexpensive SX-Blitz from Parallax), you can build an SSIB that provides two buffered input channels for the Stamp. You can find the details of the SSIB in my SX course that Parallax produces (download the PDF). Of course, this provides two channels of input buffer, but it doesn't provide level shifting.

Another Solution

Maxim makes a serial UART (the MAX3110E) that uses SPI to communicate with the Stamp. The output is RS-232. In addition to the UART, the chip contains an eight byte buffer and a level shifter similar to a MAX233. The level shifter is independent of the UART and requires no capacitors. There are four shifters, two for the RS232 and two for the RTS and CTS signals from the UART. Maxim will send you free samples of most of their ICs, so you can actually get one or two of the chips to experiment with at no charge.

The MAX3110E requires a crystal, along with two capacitors for the crystal (although you may be able to omit the capacitors if you use a breadboard). Maxim recommends a 1.8432MHz (300 baud to 115.2Kbaud) or 3.6864MHz (600 baud to 230.4Kbaud) crystal. The chip has 28 pins like the Stamp II. Unlike the Stamp II, the MAX3110E is in a .3" package.

You need at least 4 pins to interface  to the UART. You can also gain some efficiency if you use a fifth pin, but it is not strictly necessary.

Usually, with an SPI device you can use ShiftIn and ShiftOut to communicate with it. However, the Maxim UART is a bit different. When you send characters to it, it also simultaneously sends a received character (if available). This helps the host computer keep up, but complicates the programming. Each operation requires 16 bits. You can read and write the configuration register (mainly to set the baud rate), read characters, or write characters. When you write a character, you also read a character, if it is available.

The UART is set up to generate an interrupt when it requires attention. The Stamp doesn't easily support interrupts, so my code can work two different ways. First, with a four-wire interface, it can simply poll to see if characters are available. Second, you can use five wires and poll the IRQ pin instead of sending 16 bits just to check for a character. This is faster, but requires an extra Stamp pin.

The library contains only a few functions:

sendchar - Sends the byte in sbyte via RS232

rcvidle - Polls the UART for input characters (see below)

uartinit - Initialize the UART using the baudrate constant. 

Because the UART can send a character at the same time you are sending a character, your program must be prepared to handle characters at any time. My library requires you to provide a function named rcvchar. This function receives a character in the sbyte variable. If you call sendchar, be careful -- it may cause a recursive call to rcvchar.

In addition to sbyte, the library also uses three bit variables: rts, cts, and err. These reflect the status of RTS, CTS, and if an error is detected.

Keep in mind that the buffer is only 8 characters deep. If you let it fill up, you'll lose characters and there is no error indication that I could find.

The Circuit

Here is a table showing the connections to the MAX3110E:

Pin # Name Connection Pin # Name Connection
1 R2IN N/C 15 DIN Stamp P7
2 R2OUT N/C 16 DOUT Stamp P6
3 T2IN N/C 17 SCLK Stamp P5
4 T1IN Pin 14 18 CS Stamp P4
5 R1OUT Pin 13 19 IRQ Stamp P3
6 R1IN DB9 (RX) 20 SHDN +5V
7 T1OUT DB9 (TX) 21 V+ N/C
8 Vcc +5V 22 C1+ N/C
9 X2 Crystal 23 C1- N/C
10 X1 Crystal 24 C2+ N/C
11 CTS N/C 25 C2- N/C
12 RTS N/C 26 V- N/C
13 RX Pin 5 27 GND Ground
14 TX Pin 4 28 T2OUT N/C

 Pin 6 is the RS-232 RX pin. If you are connecting to a PC or other DTE device, you'll need to connect this to the TX pin. Pin 7 is the TX pin (which would connect to a DTE's RX pin). The exact connection will depend on the device you are connecting to.

You probably need capacitors from pin 9 and 10 to ground. The value of the capacitors should match the load capacitance of the crystal in use. So a 20pF crystal would use two 20pF capacitors. One would connect to pin 9 and ground. The other would connect between pin 10 and ground.

If you are breadboarding the circuit, you probably don't need the capacitors. Also, if you are breadboarding, an ASP-II is ideal for making the connections to the DB9. However you connect, don't forget to connect the ground on the DB connector (pin 5 on a DB9) to your circuit ground.

Source Code

' MAX3110E Driver for Basic Stamp
baudrate con $A   ' 9600, 8, N, 1 -- no interrupts
din con 7
dout con 6
indout var in6
clk con 5
cs con 4
' you can gain a little speed by
' connecting the IRQ line
irq var in3   


uartcfg con $C400  ' use $C400 if using IRQ; $C000 if not
ioword var word
uartin var word
uarti var nib
rts var bit
cts var bit
err var bit
sbyte var byte  ' input byte

gosub uartinit
sbyte="?"
gosub sendchar
idle:
  gosub rcvidle
  goto idle

rcvchar:
  ' do what you want with sbyte here
  ' but be careful... sendchar may reenter
   if sbyte<"a" or sbyte>"z" then s2
   sbyte=sbyte & $DF
s2:
   if sbyte=13 then crlf
   goto sendchar
crlf:
   gosub sendchar
   sbyte=10
   goto sendchar

' Driver code from here down
sendchar:
  ioword=$8000 + sbyte + (rts*$200)
ucheck:
  gosub uart
  cts = uartin >> 9
  if uartin & $200 <> 0 then unoerr
  sbyte="~"
  err=1
  return
unoerr:
  if uartin & $8000 = 0 then ret
  sbyte=uartin & $FF
  goto rcvchar
rcvidle:
' remove next line if not using IRQ
  if irq=1 then ret
  ioword=0
  goto ucheck
uartinit:
err=0
high cs  ' let chip see edge
low din
input dout
low clk
ioword=uartcfg + baudrate
uart:
  uarti=15
  uartin=0
uloop:
    low din
    if (ioword & (1<<uarti)) = 0 then unoset
    high din
unoset:
    low cs   ' really only need this once
    high clk
    if indout=0 then uno1in
    uartin=uartin + (1<<uarti)
uno1in:
   low clk
   uarti=uarti-1
   if uarti<>15 then uloop
   high cs
ret:
   return

This article is copyright 1999, 2000 by AWC. All Rights Reserved.