Friday, October 15, 2004

Serial Communications (rs232) Without A Level Converter

Continued From: PIC16F84
First Read: Serial Communications (rs232) With A max232 Level Converter

It seems that the pic is capable of driving sufficient current and voltage to pass the logic thresholds of the rs232 protocol without any extra level conversion. This causes only one problem: the rs232 standard uses inverse logic. Consequently, you can use pretty much the exact same circuit and software as above, but exclude the max232 chip, and invert the levels in your code. I'm not sure if it's necessary, but I included 10k resistors on the pic's transmit and receive pins, just to be safe.

The basic layout is: PC > serial cable > pic. The main program is really simple. It prints "Hello: " to the terminal, then echoes whatever you type.

main.c

/* IMPORTANT: I don't know if it's necessary, but I put 10k resistors on
 * the transmit and receive pins of the pic, just to be safe, and it works fine.
 */

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

/*  rs232 interface (serial port communications) without a level converter.
 *  Therefore microcontroller pins are connected DIRECTLY to the serial cable.
 *  Therefore inverted (from standard) logic is used.
 *  
 *  PIC PIN18 PORTA1 - TRANSMIT (receive at PC end of serial cable) white
 *  PIC PIN06 PORTB0 - RECEIVE (transmit at PC end of serial cable) orange
 */

/*  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  = PIC PIN06 PORTB0
 *  2 = white  = rd  = PIC PIN18 PORTA1
 *  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) without a level converter.
 *  Therefore microcontroller pins are connected DIRECTLY to the serial cable.
 *  Therefore inverted (from standard) logic is used.
 *  
 *  PIC PIN18 PORTA1 - TRANSMIT (receive at PC end of serial cable) white
 *  PIC PIN06 PORTB0 - RECEIVE (transmit at PC end of serial cable) orange
 */

/*  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  = PIC PIN06 PORTB0
 *  2 = white  = rd  = PIC PIN18 PORTA1
 *  7 = yellow = rts = unused
 */

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

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

/* Send a single char out the transmit pin.
 */
void rs232_send_char(unsigned char c) 
{
  c = ~c;

  // start bit

  RA1 = 1;
  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 = 0;
  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 == 0) { continue; }
  delay_us(125);

  // 8 data bits

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

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

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

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

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

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

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

  if (RB0 == 0) { 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

:10000000830100308A0004288301282B831290008A
:10001000911B112811088A001008900A0319910AEF
:1000200082008313111883171008900A84000008B7
:100030000800483465346C346C346F343A342034FE
:0200400000348A
:1006140083128510013083121B2B83128C0004304B
:100624008D00FA3083121B238D0B132B8C0B112B93
:10063400080083128C0004308D0053308E008E0B22
:10064400212B8D0B1F2B8C0B1D2B08008316850172
:100654000130860083128501860183120A23193032
:100664008C0000308D0038234B23362B422B0D0891
:1006740091000C088C0A03198D0A062083128A2320
:1006840083120D0891000C08062000380319080095
:10069400392B83128C01061C4D2B29308D008D0BB8
:1006A400512B061C0C1421308D008D0B572B061C6E
:1006B4008C1421308D008D0B5D2B061C0C15213004
:1006C4008D008D0B632B061C8C1521308D008D0B3A
:1006D400692B061C0C1621308D008D0B6F2B061C0C
:1006E4008C1621308D008D0B752B061C0C172130B8
:1006F4008D008D0B7B2B061C8C1721308D008D0BF0
:10070400812B21308D008D0B852B0C0883128A2BB5
:1007140083128E008E09851421308F008F0B902B4D
:100724000E0C031C972B8514982B85100310831231
:100734008E0C21308F008F0B9D2B0E0C031CA42BD1
:100744008514A52B8510031083128E0C21308F0085
:100754008F0BAA2B0E0C031CB12B8514B22B851006
:10076400031083128E0C21308F008F0BB72B0E0CCD
:10077400031CBE2B8514BF2B8510031083128E0C13
:1007840021308F008F0BC42B0E0C031CCB2B851434
:10079400CC2B8510031083128E0C21308F008F0B0D
:1007A400D12B0E0C031CD82B8514D92B85100310C8
:1007B40083128E0C21308F008F0BDE2B0E0C031C4A
:1007C400E52B8514E62B8510031083128E0C213043
:1007D4008F008F0BEB2B0E0C031CF22B8514F32BC9
:1007E4008510031083128E0C21308F008F0BF82B91
:0C07F400851021308F008F0BFD2B0800BA
:02400E00F93F78
:00000001FF
{ "loggedin": false, "owner": false, "avatar": "", "render": "nothing", "trackingID": "UA-36983794-1", "description": "", "page": { "blogIds": [ 230 ] }, "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" }