PIC16f627A with RS232 and sdcc


Programming PICs in assembly can be challenging, but it gets to be a drag after a while. Especially when doing serial communications you start wondering: "wouldn't it be great if all this fiddly crap was in some kind of library ?"

I've been playing with sdcc (Small Device C Compiler) to write code for the PIC. Unfortunately at the time I was playing with it (in the middle of exams), there was no library code to do RS232 for the 16f627a (at least not in a blatantly obvious way). So instead of asking for directions like any sane person would do, I dove right in and wrote it myself.

Now that I've upgraded to Ubuntu Jaunty (which is such a resource hog btw), I wonder if the sdcc people have included some code for the 16f627a but haven't checked.

In any case, I'll post the code here for all of you to laugh at and for me to have a backup for when my harddisk dies (any minute now)

lib.h:


#ifndef __LIB_H
#define __LIB_H

#define USART_TX_INT_ON 0xff
#define USART_TX_INT_OFF 0x7f

#define USART_RX_INT_ON 0xff
#define USART_RX_INT_OFF 0xbf

#define USART_BRGH_HIGH 0xff
#define USART_BRGH_LOW 0xef

#define USART_CONT_RX 0xff
#define USART_SINGLE_RX 0xf7

#define USART_SYNC_MASTER 0xff
#define USART_SYNC_SLAVE 0xfb

#define USART_NINE_BIT 0xff
#define USART_EIGHT_BIT 0xfd

#define USART_SYNCH_MODE 0xff
#define USART_ASYNCH_MODE 0xfe

void usart_open(unsigned char, unsigned int);

void usart_putc(unsigned char);
unsigned char usart_getc(void);

unsigned char usart_busy(void);
unsigned char usart_drdy(void);

unsigned char usart_wait_and_read(void);
void usart_wait_and_write(unsigned char);

#endif /* __LIB_H */


lib.c:

#include <pic/pic16f627a.h>
#include "lib.h"

void usart_open(unsigned char config, unsigned int spbrg) { /* {{{ */
TXSTA = 0; // Reset USART registers to POR state
RCSTA = 0;

if(config & ~USART_ASYNCH_MODE)
SYNC = 1;

if(config & ~USART_EIGHT_BIT) {
TX9 = 1;
RX9 = 1;
}

if(config & ~USART_SYNC_SLAVE)
CSRC = 1;

/*
if(config & ~USART_SINGLE_RX)
CREN = 1;
else
SREN = 1;
*/

CREN = 1;
SREN = 1;

if(config & ~USART_BRGH_LOW)
BRGH = 1;
else
BRGH = 0;

/* TX interrupts */
TXIF = 0;

if(config & ~USART_RX_INT_OFF)
RCIE = 1;
else
RCIE = 0;

/* RX interrupts */
RCIF = 0;

if(config & ~USART_TX_INT_OFF)
TXIE = 1;
else
TXIE = 0;

SPBRG = (char)spbrg;

TXEN = 1;
SPEN = 1;

TRISB1 = 1; // set B1 to transmit
TRISB2 = 0; // set B2 to receive
}
/* }}} */

void usart_putc(unsigned char dat) { /* {{{ */
//if(TX9) {
// TX9D = 0;
// if(USART_Status.TX_NINE)
// TX9D = 1;
//}

TXREG = dat; // Write the data byte to the USART
}
/* }}} */
unsigned char usart_getc(void) { /* {{{ */
return RCREG;
}
/* }}} */

unsigned char usart_busy(void) { /* {{{ */
return !TRMT;
}
/* }}} */
unsigned char usart_drdy(void) { /* {{{ */
return RCIF;
}
/* }}} */

unsigned char usart_wait_and_read(void) { /* {{{ */
while(!usart_drdy());
return usart_getc();
}
/* }}} */
void usart_wait_and_write(unsigned char dat) { /* {{{ */
while(usart_busy());
usart_putc(dat);
}
/* }}} */


test.c :

#include <pic/pic16f627a.h>
#include "lib.h"

#define GPSIM_20Mhz_2400baud_BRGHlow 0x81
#define GPSIM_20Mhz_9600baud_BRGHlow 0x20

#define PIC_4Mhz_2400baud_BRGHlow 0x19
#define PIC_4Mhz_9600baud_BRGHhigh 0x19

static __code unsigned short __at(0x2007) _conf = _INTRC_OSC_NOCLKOUT;

unsigned char data_num[] = {
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f
};

unsigned char data_alpha[] = {
0x77, 0x7c, 0x58, 0x5e, 0x79, 0x71
};

//unsigned char hex[] = "0123456789ABCDEF"; <- this doesn't work yet
unsigned char hex[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};

void main()
{
unsigned char c = 'G';
usart_open(USART_TX_INT_OFF & USART_RX_INT_OFF & USART_ASYNCH_MODE & USART_BRGH_LOW & USART_EIGHT_BIT, GPSIM_20Mhz_2400baud_BRGHlow);

TRISA = 0;

for (;;)
{
c = usart_wait_and_read();

if(c >= 'A' && c <= 'F') {
PORTA = data_alpha[c - 'A'];
} else {
if(c >= '0' && c <= '9')
PORTA = data_num[c - '0'];
}

usart_wait_and_write(c);
usart_wait_and_write(':');
usart_wait_and_write(' ');
usart_wait_and_write('0');
usart_wait_and_write('x');
usart_wait_and_write(hex[(c & 0xf0) >> 4]);
usart_wait_and_write(hex[c & 0x0f]);
usart_wait_and_write('\n');
}
}


env.conf :

# load the gpsim modules library.
module lib /usr/lib/libgpsim_modules.so.0.0.0

# creating an usart and connecting its TX pin to the PICs RX pin, allowing keyboard input
module load usart U1
node nct
node ncr
attach nct pin(portb1) U1.TXPIN
attach ncr pin(portb2) U1.RXPIN
U1.console = true
U1.rxbaud = 2400
U1.txbaud = 2400

# creating a 7 segment led display and hooking it up to PORTA
module load led_7segments L7
node nl0 nl1 nl2 nl3 nl4 nl5 nl6
attach nl0 pin(porta0) L7.seg0
attach nl1 pin(porta1) L7.seg1
attach nl2 pin(porta2) L7.seg2
attach nl3 pin(porta3) L7.seg3
attach nl4 pin(porta4) L7.seg4
attach nl5 pin(porta5) L7.seg5
attach nl6 pin(porta6) L7.seg6

# tell the builtin scope in gpsim to monitor portb2
# (in gpsim: Windows -> Scope)
scope.ch0="portb1"


Makefile :

test.hex: test.o lib.o
gplink \
-c \
-s /usr/share/gputils/lkr/16f627a.lkr \
-o $@ \
-m \
$^ \
-I /usr/share/sdcc/lib/pic \
pic16f627a.lib libsdcc.lib

test.o: test.asm
gpasm -c $<

lib.o: lib.asm
gpasm -c $<

test.asm: test.c
sdcc -S -mpic14 -p16f627a $<

lib.asm: lib.c
sdcc -S -mpic14 -p16f627a $<

run:
gpsim -c env.conf -s test.cod

clean:
rm -f *.cod *.hex *.lst *~ *.o *.asm *.cof *.map

upload:
../K8048/k14 p test.hex
../K8048/k14 v test.hex


The code in test.c does RS232 in both directions. It takes a character and sends out the hex code for it. In addition, if the character is a hex digit (0123456789ABCDEF), it gets displayed on the 7 digit led display.