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); } }