gnupic: SDCC sample program for 16F873?


Previous by date: 24 Jun 2002 21:02:56 -0000 PIC16F628, Ronald Mundell
Next by date: 24 Jun 2002 21:02:56 -0000 Re: SDCC sample program for 16F873?, Scott Dattalo
Previous in thread: 24 Jun 2002 21:02:56 -0000 SDCC sample program for 16F873?, Scott Ellis
Next in thread: 24 Jun 2002 21:02:56 -0000 Re: SDCC sample program for 16F873?, Scott Dattalo

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

Previous by date: 24 Jun 2002 21:02:56 -0000 PIC16F628, Ronald Mundell
Next by date: 24 Jun 2002 21:02:56 -0000 Re: SDCC sample program for 16F873?, Scott Dattalo
Previous in thread: 24 Jun 2002 21:02:56 -0000 SDCC sample program for 16F873?, Scott Ellis
Next in thread: 24 Jun 2002 21:02:56 -0000 Re: SDCC sample program for 16F873?, Scott Dattalo


Powered by ezmlm-browse 0.20.