Friday, October 15, 2004

Serial Communications (rs232) With A max232 Level Converter

Continued From: PIC16F84

max232 is a chip used to convert voltage levels from the rs232 standard (+-12) to microcontroller levels (+-5). It also inverts the logic, because rs232 is inverted to the norm. You don't need to use the max232 for serial communications with a pic, but I went the trouble of figuring it out, so I thought I'd post it. If you're going to use Serial communications without the max232 then read this discussion first, because I don't repeat it.

rs232 is dead simple. You have a start bit, then eight data bits then a stop bit. For 9600 baud, each bit needs a duration of 100us. That's all there is to it. Of course, there are many options, like handshaking etcetera that make it complicated. I used hyperterminal, that comes with every version of windows I've ever seen (Start > All Programs > Accessories > Communications > Hyperterminal). You have to set up the connection correctly: COM1, 9600 baud, 8 data bits, no parity, 1 stop bit, no flow control.

So to send a byte, all you do is hold your pin low for 100us (the start bit), then put each byte on the pin for 100us, then put your pin back to high for at least 100us (the stop bit). This of course requires that when you're not sending anything, your transmit pin must be high; therefore make sure it's initialized to high at the beginning of the program for at least one complete send cycle. To receive bytes, simply watch the receive pin until it goes low (the start bit), then wait for about 125us (which definitely gets you into the first data bit. Then all you do is read the pin every 100us to get your eight bits. When receiving, you don't really care about the stop bit.

Below are the source files. At the top of main.c, there are pinouts for the whole board. I used a mouse cord for my serial connection. I just grabbed an old serial mouse for free, busted it open and ripped the end of the cord off the mouse board. The pinout for the serial cable is given as best I could at the top of main.c. You only use three wires, so you should be able to figure it out. The basic layout is: PC > serial cable > max232 > pic.

The main program is really simple. It prints "Hello: " to the terminal, then echoes whatever you type.

main.c

/*                            PIC16F84
 *                          .-----------.
 *                         -|RA2     RA1|- TRANSMIT (MAX232 PIN 11)
 *                         -|RA3     RA0|- 
 *                         -|RA4    OSC1|- XT CLOCK pin1, 27pF to GND
 *             PUSH BUTTON -|MCLR   OSC2|- XT CLOCK pin2, 27pF to GND
 *                     GND -|Vss     Vdd|- +5v
 * RECEIVE (MAX232 PIN 12) -|RB0     RB7|- 
 *                         -|RB1     RB6|- 
 *                         -|RB2     RB5|- 
 *                         -|RB3     RB4|- 
 *                          '-----------'       
 *
 *  PUSH BUTTON = PIN - BUTTON - GND
 *                PIN - 10k - +5v  
 */

/*                                 MAX232
 *                      .-----------------.
 *               cap1+ -|1 C1+      Vcc 16|- +5v
 *        GND -- cap3+ -|2 Vs+      GND 15|- GND
 *               cap1- -|3 C1-    T1out 14|- SERIAL COARD white
 *               cap2+ -|4 C2+     R1in 13|- SERIAL COARD orange
 *               cap2- -|5 C2-    R1out 12|- PIC RB0
 *        GND -- cap4- -|6 Vs-     T1in 11|- PIC RA1
 *                     -|7 T2out   T2in 10|- 
 *                     -|8 R2in    R2out 9|- 
 *                      '-----------------'    
 *
 *  Note the need for four 10uF capacitors.
 *  Capacitors 1 and 2 go from pin to pin,
 *  and capacitors 3 and 4 go from pin to GND (watch the polarities).
 */

/*  looking head on at the female connector of a serial mouse coard:
 *  i.e. the end that plugs into your pc
 *
 *  5 4 3 2 1      =      blue    null    orange      white    null
 *   9 8 7 6       =          null    null      yellow     null
 *
 *  the following are loose wires on the other end of the coard.
 *  i.e. where it would have been soldered to the mouse circuit board.
 *
 *  5 = blue   = gnd = GND
 *  3 = orange = td  = MAX232 PIN13 R1IN
 *  2 = white  = rd  = MAX232 PIN14 T1OUT
 *  7 = yellow = rts = unused
 */

#include <pic.h>
#include "rs232.c"

__CONFIG(WDTDIS & XT & UNPROTECT);

main(void) 
{
  TRISA = 0b00000000;
  TRISB = 0b00000001;
  PORTA = 0;
  PORTB = 0;

  rs232_initialize();

  rs232_send_string("Hello: ");
  for (;;) 
  {
    rs232_echo_char();
  }
}

rs232.c

/*  rs232 interface (serial port communications) using MAX232 level converter.
 *  
 *  PIC PIN18 PORTA1 - MAX232 PIN11 T1IN
 *  PIC PIN06 PORTB0 - MAX232 PIN12 R1OUT
 */

#include <pic.h>
#include "delay.c"

/* Initialize the transmit pin, 
 * by ensuring it is high for an entire message cycle.
 */
void rs232_initialize()
{
  RA1 = 1;
  delay_ms(1);
}

/* Send a single char out the transmit pin.
 */
void rs232_send_char(unsigned char c) 
{
  // start bit

  RA1 = 0;
  delay_us(100);

  // 8 data bits

  RA1 = (c & 1);
  c = c >> 1;
  delay_us(98);

  RA1 = (c & 1);
  c = c >> 1;
  delay_us(98);

  RA1 = (c & 1);
  c = c >> 1;
  delay_us(98);

  RA1 = (c & 1);
  c = c >> 1;
  delay_us(98);

  RA1 = (c & 1);
  c = c >> 1;
  delay_us(98);

  RA1 = (c & 1);
  c = c >> 1;
  delay_us(98);

  RA1 = (c & 1);
  c = c >> 1;
  delay_us(98);

  RA1 = (c & 1);
  c = c >> 1;
  delay_us(98);

  // stop bit

  RA1 = 1;
  delay_us(100);
}

/* Send a string of chars out the transmit pin.
 */
void rs232_send_string(const char * s)
{
  while(*s)
  { rs232_send_char(*s++); }
}

/* Get a single char from the receive pin.
 * Then send that char over the transmit pin.
 */
void rs232_echo_char() 
{
  unsigned char c = 0;

  // start bit

  while (RB0 == 1) { continue; }
  delay_us(125);

  // 8 data bits

  if (RB0 == 1) { c = c | 0b00000001; }
  delay_us(97);

  if (RB0 == 1) { c = c | 0b00000010; }
  delay_us(97);

  if (RB0 == 1) { c = c | 0b00000100; }
  delay_us(97);

  if (RB0 == 1) { c = c | 0b00001000; }
  delay_us(97);

  if (RB0 == 1) { c = c | 0b00010000; }
  delay_us(97);

  if (RB0 == 1) { c = c | 0b00100000; }
  delay_us(97);

  if (RB0 == 1) { c = c | 0b01000000; }
  delay_us(97);

  if (RB0 == 1) { c = c | 0b10000000; }
  delay_us(97);

  // stop bit

  delay_us(100);

  rs232_send_char(c);
}

delay.h

#define delay_us(x) { unsigned char cnt; cnt = (x)/(3)|1; while(--cnt != 0) continue; }

extern void delay_ms(unsigned char);

extern void delay_s(unsigned char);

delay.c

/* These routines assume a 4MHz clock.
 */

#include "delay.h"

/* Delay for the specified number of miliseconds.
 */
void delay_ms(unsigned char cnt)
{
  unsigned char i;
  do 
  {
    i = 4;
    do { delay_us(250); } while(--i);
  } 
  while(--cnt);
}

/* Delay for the specified number of seconds.
 */
void delay_s(unsigned char cnt)
{
  unsigned char i;
  do 
  {
    i = 4;
    do { delay_ms(250); } while(--i);
  } 
  while(--cnt);
}

MAIN.HEX

:10000000830100308A0004288301292B8312900089
:10001000911B112811088A001008900A0319910AEF
:1000200082008313111883171008900A84000008B7
:100030000800483465346C346C346F343A342034FE
:0200400000348A
:1006160083128514013083121C2B83128C00043044
:100626008D00FA3083121C238D0B142B8C0B122B8E
:10063600080083128C0004308D0053308E008E0B20
:10064600222B8D0B202B8C0B1E2B0800831685016D
:100656000130860083128501860183120B2319302F
:100666008C0000308D0039234C23372B432B0D088B
:1006760091000C088C0A03198D0A062083128B231D
:1006860083120D0891000C08062000380319080093
:100696003A2B83128C0106184E2B29308D008D0BB8
:1006A600522B06180C1421308D008D0B582B061872
:1006B6008C1421308D008D0B5E2B06180C15213005
:1006C6008D008D0B642B06188C1521308D008D0B3B
:1006D6006A2B06180C1621308D008D0B702B061810
:1006E6008C1621308D008D0B762B06180C172130B9
:1006F6008D008D0B7C2B06188C1721308D008D0BF1
:10070600822B21308D008D0B862B0C0883128B2BB0
:1007160083128E00851021308F008F0B902B0E0CCC
:10072600031C972B8514982B8510031083128E0CAF
:1007360021308F008F0B9D2B0E0C031CA42B8514D0
:10074600A52B8510031083128E0C21308F008F0B82
:10075600AA2B0E0C031CB12B8514B22B851003108B
:1007660083128E0C21308F008F0BB72B0E0C031CBF
:10077600BE2B8514BF2B8510031083128E0C2130DF
:100786008F008F0BC42B0E0C031CCB2B8514CC2B8C
:100796008510031083128E0C21308F008F0BD12B06
:1007A6000E0C031CD82B8514D92B8510031083122D
:1007B6008E0C21308F008F0BDE2B0E0C031CE52BCD
:1007C6008514E62B8510031083128E0C21308F00C2
:1007D6008F0BEB2B0E0C031CF22B8514F32B8510C1
:1007E600031083128E0C21308F008F0BF82B85148B
:0A07F60021308F008F0BFD2B08004F
:02400E00F93F78
:00000001FF
{ "loggedin": false, "owner": false, "avatar": "", "render": "nothing", "trackingID": "UA-36983794-1", "description": "", "page": { "blogIds": [ 229 ] }, "domain": "holtstrom.com", "base": "\/michael", "url": "https:\/\/holtstrom.com\/michael\/", "frameworkFiles": "https:\/\/holtstrom.com\/michael\/_framework\/_files.4\/", "commonFiles": "https:\/\/holtstrom.com\/michael\/_common\/_files.3\/", "mediaFiles": "https:\/\/holtstrom.com\/michael\/media\/_files.3\/", "tmdbUrl": "http:\/\/www.themoviedb.org\/", "tmdbPoster": "http:\/\/image.tmdb.org\/t\/p\/w342" }