gnupic: SDCC sample program for 16F873?
Subject:
Re: SDCC sample program for 16F873?
From:
####@####.#### (Linas Vepstas)
Date:
24 Jun 2002 21:02:56 -0000
Message-Id: <20020624205109.GC15530@backlot.linas.org>
On Mon, Jun 24, 2002 at 08:07:55PM +1000, Scott Ellis was heard to remark:
> I need to write a program that samples the A/D's and sends the results on the serial port. Also has to receive some commands. Does anyone have some sample code for this for Scott's port of SDCC?
>
> (I know I am being lazy, but if some one is feeling generous and has already done this then ... :-) )
/*
* FILE:
* a_to_d.c
*
* FUNCTION:
* Program a PIC16F873 to read five analog inputs, make an
* A to D conversion, and output data on a serial port.
*
* The output on the serial port conists of a series of
* ASCII strings of the form "an m 0xhhh\r\n" where m is
* the number of the analog input (0 to 4), and 0xhhh is
* the 10-bit hexadecimal voltage measurement. The
* output is normalized so that 0x3ff is full scale
* and 0x000 is zero. Note that the 873 only provides
* a 10-bit accuracy, which is why full-scale is 0x3ff.
*
* The goal of the ascii output is to make it as
* human-readable and easy to parse as possible.
*
* Future expansion: if a 12-bit a/d is used in the future,
* the output should be "ad m 0xhhh\r\n" normalized to full
* scale at 0xfff. The two chars "ad" indicate the 12-bit
* output. If an 8-bit output is used, the format should be
* "ab m 0xhh\r\n" where "ab" indicates its only a byte.
* A 16-bit output should be of the form "as m 0xhhhh\r\n"
* where "as" implies a 16-bit short.
*
* This program assumes that hardware flow control is
* used on the serial port, and thus, it listens to
* RTS to determine whether to send or not. Currently,
* it assumes an inverted RTS is wired to the PIC
* port B bit 1.
*
* Note that there were a number of design tradeoffs made.
* In all cases, clarity won and performance lost.
* This is true for both the A-to-D conversion, as well
* as the format of the data string returned on the
* serial port. The goal is to make the serial port
* as human-readable as possible, and performance is
* not at all important.
*
* Note: all spin loop timings assume a 2MHz clock.
*
* HISTORY:
* Linas Vepstas May 2002
*/
#define __16F873
#include "p16f873.h"
#define RTS_BITMASK 0x2;
/* Spin Loop Calibrabtion:
* Assume 2 MHz crystal; calib done with oscilloscope:
* ints: 38.3 uS per loop, 38 uS setup
* shorts: 26.2 uS per loop 26 uS setup
* chars: 15.9 uS per loop, 16 uS setup
*/
#define PAGE1 STATUS |= RP0;
#define PAGE0 STATUS &= ~RP0;
/* ---------------------------------------------------------- */
/* intialize the serial port */
void
serial_port_init_9600_8N1 (void)
{
// set the transmit status and control register
// to asnync 8-bit no parity
PAGE1;
TXSTA = BRGH;
// set serial port to 9600 baud
// our oscillator is 2.0 MHz
SPBRG = 12;
// set receive status control register
// to enable serial port
PAGE0;
RCSTA = SPEN;
// set the transmit status and control register
// transmit enable
PAGE1;
TXSTA |= TXEN;
// set port B pin1 to be input
TRISB |= 0x2;
// set port B pin2 to be output
TRISB &= 0xfb;
PAGE0;
}
/* ---------------------------------------------------------- */
/* output string to the serial port */
void
put_string_to_serial_port (const char * buff)
{
const char * p = buff;
while (1)
{
char x;
if (0 == *p) break;
// loop-poll while xmit buffer is full
x = PIR1; x &= TXIF;
while (1)
{
x = PIR1;
x &= TXIF;
if (0 != x) break;
}
TXREG = *p;
p++;
}
/* reset the PCLATH because the call may be in a different page */
PCLATH = 0;
}
/* ---------------------------------------------------------- */
/* get serial port RTS (for hardware flow control) from PORTB 1 */
char
get_RTS (void)
{
char b;
b = PORTB;
b &= RTS_BITMASK;
b = b ^ RTS_BITMASK; /* MAX232 inverts the meaning */
return b;
}
/* ---------------------------------------------------------- */
/* output character to the serial port */
void
put_char_to_serial_port (const char c)
{
char x;
// loop-poll while xmit buffer is full
x = PIR1; x &= TXIF;
while (1)
{
x = PIR1;
x &= TXIF;
if (0 != x) break;
}
TXREG = c;
}
/* ---------------------------------------------------------- */
/* convert nibble to hex value, presented as ascii char */
char
print_nibble (char nibble)
{
nibble &= 0xf;
if (0xa > nibble) return (nibble + '0');
nibble -= 0xa;
return (nibble + 'a');
}
/* ---------------------------------------------------------- */
/* Initialize the Analog-to-Digital Converter */
void
ad_init (void)
{
/* all units function as a to D,
* Vdd and Vss are the reference voltages */
PAGE1;
ADCON1 = 0;
/* right-justify the results (8 low bits in ADRESL, and
* two remaining high bits in ADRESH) */
ADCON1 |= ADFM;
PAGE0;
}
/* ---------------------------------------------------------- */
/* Perform the Analog-to-Digital Conversion */
/* Note:
* returning the value as a short is hardly the most efficient
* thing one coould do on a PIC, but it makes things conceptually
* much simpler overall.
*/
unsigned char
get_ad_result_lo (void)
{
_asm
BSF STATUS,5
MOVF ADRESL,W
BCF STATUS,5
_endasm;
}
short
ad_convert (char unit)
{
short retval;
char i;
unsigned char uc;
/* select the a/d unit to use */
ADCON0 = (unit << 3) & 0x38;
/* set conversion clock to 8*Tosc */
ADCON0 |= ADCS0;
/* turn the a/d unit on */
ADCON0 |= ADON;
/* spin for the acquisition time */
/* hack alert -- XXX we wing this, set it well above 20 microsecs */
i=0;
while (i<2) i++;
ADCON0 |= GO;
/* conversion takes 36 uS minimum */
i=0;
while (i<3) i++;
/* poll the GO_DONE bit */
while (1)
{
if (0 == (ADCON0 & GO)) break;
}
/* Pack results into a short, right-aligned. */
uc = ADRESH;
retval = uc;
retval <<= 8;
retval &= 0x300;
uc = get_ad_result_lo();
retval |= uc;
return retval;
}
/* ---------------------------------------------------------- */
/* main loop */
main()
{
char i = 0;
char j = 0;
serial_port_init_9600_8N1 ();
ad_init();
PAGE0;
while (1)
{
short volts;
short snib;
unsigned char nibble;
char hex;
char do_send;
char analog = '0' + i;
volts = ad_convert (i);
do_send = get_RTS ();
if (do_send)
{
put_string_to_serial_port ("an ");
put_char_to_serial_port (analog);
put_string_to_serial_port (" 0x");
/* print 3 hex digits, high nibble first */
snib = volts >> 4;
snib >>= 4;
nibble = snib;
hex = print_nibble (nibble);
put_char_to_serial_port (hex);
snib = volts >> 4;
nibble = snib;
hex = print_nibble (nibble);
put_char_to_serial_port (hex);
nibble = volts;
hex = print_nibble (nibble);
put_char_to_serial_port (hex);
put_string_to_serial_port ("\r\n");
}
else
{
// If not sending data, toggle pin2 on port b,
// handy for looking at with oscilloscope.
PORTB |= 0x4;
for (j=0; j<100; j++) {}
PORTB &= 0xfb;
}
i++;
if (5 == i) i = 0;
}
}
/* ---------------------------------------------------------- */
--
pub 1024D/01045933 2001-02-01 Linas Vepstas (Labas!) ####@####.####
PGP Key fingerprint = 8305 2521 6000 0B5E 8984 3F54 64A9 9A82 0104 5933