LTC1298, Measuring Barometric Pressure and Wind Speed


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 + 295
256 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);
   }
}