Introduction
The intent of program BAROM.C is to illustrate how to perform measurements using a Linear LTC1298 dual 12-bit A/D. A command code is passed to function ltc1298_a_d_meas() which returns a 12 bit A/D result. Function ltc1298_a_d_meas_256_avg() performs 256 measurments and returns the average.
In BAROM.C a Motorola MPX4115AP is connected to Channel 0. The atmospheric pressure is then calculated;
P_millibars = 0.272*a_d_band_avg + 105; or P_inches_Hg_100 = 0.797*a_d_avg + 295256 measurements are then made on Ch1 and the quantizing band is displayed in hexadecimal format.
In program WIND_DIR.C, a Fascinating dual wiper potentiometer is used to calculate the angle of the anemometer. Note that this routine was not verified. However, the BASIC Stamp 2 program on which it is based was.
A more complete discussion which was written in the context of the Parallax BASIC Stamp 2 appears elsewhere.
Note that we have a kit which includes the MPX4115, an LTC1298 and a number of capacitors and resistors and a copy of the BASIC Stamp 2 discussion including the figure.
// BAROM.C (LTC1298 A/D) CCS - PCM Compiler
//
// Illustrates interface with LTC1298 A/D on RB0, RB1 and RB2.
// Performs 256 measurements of Ch0 relative to ground, averages and
// displays atmospheric pressure in millibars (or in inches of mercury).
//
// Performs 256 measurments of Ch1 relative to ground, averages and
// displays user A/D.
//
// copyright, Towanda Malone, Baltimore, MD, Apr, '99 (Tested).
#case
#include <16f84.h>
#include <string.h>
#include <defs_f84.h>
#define _1298_CS_DIR trisb0
#define _1298_CLK_DIR trisb1
#define _1298_DAT_DIR trisb2
#define _1298_CS_PIN rb0
#define _1298_CLK_PIN rb1
#define _1298_DAT_PIN rb2
#define TxData 0 // RA.0 - output to serial LCD
#define IN 1
#define OUT 0
#define MILLIBARS // undefine for display in inches of Hg
long ltc1298_a_d_meas_256_avg(int command);
long ltc1298_a_d_meas(int command);
// delay routines
void delay_ms(long t);
void delay_10us(int t);
// LCD routines
void lcd_init(void);
void out_RAM_str(int *s);
void lcd_hex_byte(int val);
void lcd_dec_byte(int val, int digits);
int num_to_char(int val);
void lcd_char(int ch);
void lcd_new_line(void);
void main(void)
{
char const str_invalid[10] = {"Invalid"};
char const str_at[4] = {"AT="};
char const str_ad[4] = {"AD="};
char s[10];
int msdigit, temp;
long a_d_band_avg, pressure;
while (1)
{
lcd_init();
a_d_band_avg = ltc1298_a_d_meas_256_avg(0x0d); // single ended Ch 0
// make 256 readings and average
if (a_d_band_avg == 0xfff)
{
strcpy(s, str_invalid);
out_RAM_str(s);
lcd_new_line();
delay_ms(1000);
}
else
{
#ifdef MILLIBARS // display in millibars
pressure = 2*a_d_band_avg/10 + 7*a_d_band_avg/100 + 105;
// perform the calculation
strcpy(s, str_at);
out_RAM_str(s);
lcd_dec_byte((int)(pressure/100), 2); // and display the result
lcd_dec_byte((int)(pressure%100), 2);
lcd_new_line();
delay_ms(1000);
#else // display in inches of Hg
// calculate 100 times pressure in iches of Hg
pressure = 7*a_d_band_avg/10 + 9*ad_band_avg/100
+ 7*av_band_avg +295;
strcpy(s, str_at);
out_RAM_str(s);
lcd_dec_byte((int)(pressure/100), 2); // and display the result
lcd_char('.');
lcd_dec_byte((int)(pressure%100), 2);
lcd_new_line();
delay_ms(1000);
#endif
} // end of else
// Now channel 1
a_d_band_avg = ltc1298_a_d_meas_256_avg(0x0f); // single ended, Ch 1
if (a_d_band_avg == 0xfff)
{
strcpy(s, str_invalid);
out_RAM_str(s);
lcd_new_line();
delay_ms(1000);
}
else
{
strcpy(s, str_ad);
out_RAM_str(s);
msdigit= num_to_char(a_d_band_avg>>8); // most significant nibble
lcd_char(msdigit);
lcd_hex_byte((int)(a_d_band_avg & 0xff)); // two lowest nibbles
lcd_new_line();
delay_ms(2000);
}
}
}
long ltc1298_a_d_meas_256_avg(int command)
{
// perform 256 a/d measurments and average
struct long32
{
long lo;
long hi;
};
struct long32 sum;
int n;
long a_d_band, avg, temp, temp_1;
sum.hi=0, sum.lo=0;
for(n=0; n<128; n++)
{
a_d_band = ltc1298_a_d_meas(command);
temp = sum.lo + a_d_band;
if (temp> 8));
lcd_hex_byte(avg/256);
lcd_hex_byte(avg%256);
lcd_new_line();
delay_ms(1000);
return(avg);
}
long ltc1298_a_d_meas(int command)
{
int n;
long band=0x0000;
_1298_CS_DIR = OUT;
_1298_CLK_DIR = OUT;
_1298_DAT_DIR = OUT;
_1298_CS_PIN = 1;
_1298_CLK_PIN = 1;
_1298_CS_PIN = 0; // LTC1298 resets
for (n=0; n<4; n++)
{
_1298_DAT_PIN = (command >> (3-n)) & 0x01; // most sig bit first
_1298_CLK_PIN = 0;
_1298_CLK_PIN = 1;
}
_1298_DAT_DIR = IN;
_1298_CLK_PIN = 0; // dummy clock pulse
_1298_CLK_PIN = 1;
for(n=0; n<12; n++)
{
_1298_CLK_PIN = 0; // dummy clock pulse
_1298_CLK_PIN = 1;
band = (band << 1) | _1298_DAT_PIN;
}
_1298_CS_PIN = 1; // CS back high
return(band);
}
// delay routines
void delay_10us(int t)
{
#asm
BCF STATUS, RP0
DELAY_10US_1:
CLRWDT
NOP
NOP
NOP
NOP
NOP
NOP
DECFSZ t, F
GOTO DELAY_10US_1
#endasm
}
void delay_ms(long t) // delays t millisecs
{
do
{
delay_10us(100);
} while(--t);
}
// LCD routines
int num_to_char(int val) // converts val to hex character
{
int ch;
if (val < 10)
{
ch=val+'0';
}
else
{
val=val-10;
ch=val + 'A';
}
return(ch);
}
void lcd_char(int ch) // serial output to PIC-n-LCD, 9600 baud
{
int n, dly;
// start bit + 8 data bits
#asm
BCF STATUS, RP0
MOVLW 9
MOVWF n
BCF STATUS, C
LCD_CHAR_1:
BTFSS STATUS, C
BSF PORTA, TxData
BTFSC STATUS, C
BCF PORTA, TxData
MOVLW 32
MOVWF dly
LCD_CHAR_2:
DECFSZ dly, F
GOTO LCD_CHAR_2
RRF ch, F
DECFSZ n, F
GOTO LCD_CHAR_1
BCF PORTA, TxData
CLRWDT
MOVLW 96
MOVWF dly
LCD_CHAR_3:
DECFSZ dly, F
GOTO LCD_CHAR_3
CLRWDT
#endasm
}
void lcd_init(void) // sets TxData in idle state and resets PIC-n-LCD
{
#asm
BCF STATUS, RP0
BCF PORTA, TxData
BSF STATUS, RP0
BCF TRISA, TxData
BCF STATUS, RP0
#endasm
lcd_char(0x0c);
delay_ms(250);
}
void lcd_new_line(void) // outputs 0x0d, 0x0a
{
lcd_char(0x0d);
delay_ms(10); // give the PIC-n-LCD time to perform the
lcd_char(0x0a); // new line function
delay_ms(10);
}
void out_RAM_str(int s)
{
while(*s)
{
lcd_char(*s);
++s;
}
}
void lcd_hex_byte(int val) // displays val in hex format
{
int ch;
ch = num_to_char((val>>4) & 0x0f);
lcd_char(ch);
ch = num_to_char(val&0x0f);
lcd_char(ch);
}
void lcd_dec_byte(int val, int digits)
// displays byte in decimal as either 1, 2 or 3 digits
{
int d;
int ch;
if (digits == 3)
{
d=val/100;
ch=num_to_char(d);
lcd_char(ch);
}
if (digits >1) // take the two lowest digits
{
val=val%100;
d=val/10;
ch=num_to_char(d);
lcd_char(ch);
}
if (digits == 1) // take the least significant digit
{
val = val%100;
}
d=val % 10;
ch=num_to_char(d);
lcd_char(ch);
}
// WIND_DIR.C (CCS PCM)
//
// Displays wind direction on Serial LCD. A more complete discussion
// appears elsewhere.
//
// Note that this was not verified. However, the BS2 code on which it is
// based was tested.
//
// Copyright, Peter H. Anderson, Baltimore, MD, April, '99
#case
#include <16f84.h>
#include <string.h>
#include <defs_f84.h>
// Note that the terminal assignments might be redefined such that two
// or more LTC1298s might be accommodated. That is, assign a dedicated
// terminal for each chip select (CS) and share CLK and DAT with all
// devices. In this routine, I have not done this.
#define _1298_CS_DIR trisb0
#define _1298_CLK_DIR trisb1
#define _1298_DAT_DIR trisb2
#define _1298_CS_PIN rb0
#define _1298_CLK_PIN rb1
#define _1298_DAT_PIN rb2
#define TxData 0 // RA.0 - output to serial LCD
#define IN 1
#define OUT 0
void display_angle(long angle);
long ltc1298_a_d_meas_256_avg(int command);
long ltc1298_a_d_meas(int command);
// delay routines
void delay_ms(long t);
void delay_10us(int t);
// LCD routines
void lcd_init(void);
void out_RAM_str(int *s);
void lcd_hex_byte(int val);
void lcd_dec_byte(int val, int digits);
int num_to_char(int val);
void lcd_char(int ch);
void lcd_new_line(void);
char const str_invalid[10] = {"Invalid"}; // global as const
char const str_wd[4] = {"WD="}; // array cannot be
// passed to a function
void main(void)
{
char s[10];
long a_d_band_avg_0, a_d_band_avg_1, angle;
while (1)
{
lcd_init();
a_d_band_avg_0 = ltc1298_a_d_meas_256_avg(0x0d); // single ended Ch 0
a_d_band_avg_1 = ltc1298_a_d_meas_256_avg(0x0f); // single ended Ch 1
// make 256 readings and average on both channels
if ((a_d_band_avg_0 > 0x0100) && (a_d_band_avg_0 < 0xf000))
// wiper 0 not in dead zone
{
angle = 6 * a_d_band_avg_0/100 + 6 * a_d_band_avg_0 / 1000;
// angle = band * 270 / 4096
display_angle(angle, s);
}
else if ((a_d_band_avg_1 > 0x0100) && (a_d_band_avg_1 < 0xf000))
{
// wiper 0 in dead zone but wiper 1 not in dead zone
angle = 180 + 6 * a_d_band_avg_0/100 + 6 * a_d_band_avg_0 / 1000;
display_angle(angle, s);
}
else
{
strcpy(s, str_invalid);
out_RAM_str(s);
}
lcd_new_line();
delay_ms(1000); // pause to admire
} // end of while (1)
}
void display_angle(long angle, char *s)
{
// not too efficient as a long mod and long div are each used
// three times.
// note that by passing pointer s, another declaration of 10
// bytes of RAM is avoided.
angle=angle%360; // such that readings are in range 0 to 359
strcpy(s, str_wd);
out_RAM_str(s);
if(angle/100) // leading zero suppression
{
lcd_dec_byte(angle/100, 1) // display the high digit
}
if((angle/10))
{
lcd_dec_byte(angle%100, 2);
}
else
{
lcd_dec_byte(angle%100, 1);
}
}