/* This file is part of MANTIS OS, Operating System for Nymph. See http://mantis.cs.colorado.edu/ Copyright (C) 2003 University of Colorado, Boulder */ /** * @File: PAR_ADboard_Niwot.c * @Brief: A file to run the NCAR PAR prototype interface board w/4ea ads1112 adc's * @Author: jwm * @Date: Jul-07 * * Required Files: * sprintff.c formats floating point, and other 'sprintf' forms to buffer * i2c_support.c local, segregated subroutines for running the MOS i2c * uart.c 'Aaron Beech's version that actually allows 'RAW_MODE' without mos header * wandSpecificCals.h NEW: location where the actual PAR wand calibrations are loaded from * NOTE: This file will need to be built prior to making this routine * It will consist of the ..... * * Modifications * 07Aug07,jwm * - Splitting out calibrations, and ads1112_defs.h in preparation for dual * cal requirement of adc itself plus PARs. * 08Aug07,jwm * - moved sampling into a segregated function to save space, and went * back to using a mosthread for the routine. * saved as PAR_ADboard_test.c.080807-1 * - Removed 'resolution' and 'pga' settings in the ads structure * because they will be the same for all par/level channels * see older version to have full ability to have mixed gains,channels * 09Aug07,jwm * - Separated the calibration for the adc's into structure adsCalStuff * so that voltages can be determined accurately, and then the secondary * sensor calibrations applied * - Later moved to the 'wandSpecificCals.h' */ #include "mos.h" #include "command_daemon.h" #include "printf.h" #include "com.h" #include "led.h" #include "dev.h" #include "avr-i2c.h" #include "avr-eeprom.h" //devines the DEV_AVR_EEPROM_SEEK ioctl #include "avr-adc.h" #include "adc.h" // Declarations //int sprintff( char *, const char *, ...); //static char debugbuf[256]; // local buffer for all // LED Masks #define YELLOW 1 #define GREEN 2 #define RED 4 // Calibration Information / Ouput Type Definition // enum { RAW =1, // These must be different bits to allow 'both' output MV =2, LINEAR=4, POLY =8 }; // ---------------------------------------------------- // *** EDIT THESE DEFINITIONS TO CONTROL OPERATIONS *** // ---------------------------------------------------- #define CAL_METHOD LINEAR // include all format desired. ie... RAW|MV|LINEAR|POLY #define POLLING_RATE 1000 // mSeconds between polling cycles #define SAMPLING_TIME_FUDGE 5 // wild guess at mS eaten during i2c sampling // used to adjust the POLLING_RATE sleep time #define LEVEL_SENSOR_LOOP_CYCLE 5 // How many PAR DAQ samples per Level sensors #define LEVELSENSORS 2 // 2 readings per wand: x,y #define WANDSENSORS 3 // 3 pars per wand #define NWANDS 4 #define ADC_RATE 15 // Sampling frequency can be: // 15 (default), 30, 60, 240 hz // see adsRange structure #define ADC_GAIN 0 // ADC internal gain setting can be 0,1,2,3 // see adsPGA structure // 0 = gain1, 0-2.048 (in se mode) // 1 = gain2, 0-1.024 // 2 = gain4, 0-0.512 // 3 = gain8, 0-0.256vdc #define PAR_SENSOR_ENABLE // Comment out to disable the PAR sampling #define LEVEL_SENSOR_ENABLE // Comment out to disable the LEVEL sampling #define DEBUG // Comment out to disable garbage #define CBUFSZ 128 // Buffer size for output message, note 12pars need at least 120chars //---------------------------------------- // ADS1112 characteristics / Calibrations, and // PAR/Level Calibration stuff //---------------------------------------- #include "ads1112_defs.h" /* ADC Calibrations and the structure definition * The 'specific' file contains the actual values * "adsCals[]" that are for the mote to be programmed.... */ struct adsCalStuff{ char *name; // Name on the Wand uint8_t adsAddr; // I2C addr...the way we named them it should be same a i2c-x // Note they all better be different!!! float offset; // measured values: float gain; // per 'Reference / (Reading-Offset) }; // PAR & Level Calibration Definitions.... // Specific for individual sensors // The offsets for pars will be 0, and only taken up in the adc cal. above // The offsets for levels will be specific for the ADC channels struct adsChanStuff{ char *name; // Name, actually number, labeled on the par diode uint8_t adsNum; // Serves both as ADC index and I2C addr. uint8_t channel; // See enum float offset; float gain; float gain2; }; #include "wandSpecificCals.h" // This is where they're at! // Globals //--------- static char cbuf[ CBUFSZ ]; int calType = CAL_METHOD; uint8_t adc_rate, adc_gain; // Cfg masks for the ads uint8_t size1=1; uint8_t waitmS; uint8_t adsSEChan[3] = { 2,3,1 }; // 'inp1/0' bit patterns 0=2,1=3,2=1 // uint8_t adsDFChan[2] = { 0,1 }; // 'inp1/0' bit patterns, differential channels float calibrated, fval; int16_t vraw[NWANDS*WANDSENSORS]; float vadc[NWANDS*WANDSENSORS]; float bitResolution; // All inputs will be the same gain setting, and freq. // so this can be universal also... uint8_t nparSensors = NWANDS*WANDSENSORS; uint8_t nlevelSensors = NWANDS*LEVELSENSORS; // Definitions // ----------- // routines here void parwand_ads1112(); void sample_adcChannels( uint8_t, uint8_t, struct adsChanStuff *); void report_results( char *, uint8_t, struct adsChanStuff * ); void report_ADSconfig( char *, uint8_t, struct adsChanStuff *); void report_ADCcals( char *, uint8_t, struct adsCalStuff *); // routines elsewhere int sprintff( char *, const char *, ...); void i2c_disable_ack(); //======================================================================== void start (void) { mos_thread_new (parwand_ads1112, MOS_COMMANDER_STACK_SIZE + 120, PRIORITY_NORMAL); } void parwand_ads1112() { uint8_t hz; uint16_t maxVal; uint8_t nbytes; // 'sizeof number of bytes to i2c read uint8_t level_sensor_daq_count = LEVEL_SENSOR_LOOP_CYCLE; // PAR DAQ samples per Level sensors uint16_t pollWaitmS; // ---------- // Setups // MOS: turn radio off, and suspend state the lowest possible power. // Establish the resolution values based on gains. These used in vraw calc. mos_thread_set_suspend_state(SUSPEND_STATE_SLEEP); // for lowest power com_mode(IFACE_RADIO, IF_OFF); // to turn off the radio // down to about 10microA nbytes = 3; switch( ADC_RATE ) { // this sets the adc_rate value for it's configuration reg. case 30: hz=30; adc_rate = READ30HZ; maxVal=adsRange[1].maxVal; break; case 60: hz=60; adc_rate = READ60HZ; maxVal=adsRange[2].maxVal; break; case 240: hz=240; adc_rate = READ240HZ; maxVal=adsRange[3].maxVal; break; default: case 15: hz=15; adc_rate = READ15HZ; maxVal=adsRange[0].maxVal; break; } switch( ADC_GAIN ) { // this sets the adc_gain value case 1: adc_gain = 2; break; case 2: adc_gain = 4; break; case 3: adc_gain = 8; break; default: case 0: adc_gain = 1; break; } // Determine sleep times between polling a set of adcs withing the sampling loop, // and between the overall polling rate waitmS = (1000 / hz ) + 7; // How long to wait between sampling ads pollWaitmS = POLLING_RATE; #ifdef PAR_SENSOR_ENABLE pollWaitmS = pollWaitmS - ((waitmS+SAMPLING_TIME_FUDGE)*WANDSENSORS); #endif #ifdef LEVEL_SENSOR_ENABLE pollWaitmS = pollWaitmS - ((waitmS+SAMPLING_TIME_FUDGE)*LEVELSENSORS); #endif if( pollWaitmS <0 ) pollWaitmS=0; // For fastest possible here bitResolution = 2.048 / (float) maxVal; bitResolution /= (float) adc_gain; // I2C Setup dev_ioctl(DEV_AVR_I2C, I2C_SET_BRR, 50); i2c_disable_ack(); // Continue working if 'missing' reports #ifdef DEBUG printf("\n\rPAR Sensor Sampling: %d wands, %d pars, %d levels", NWANDS,WANDSENSORS,LEVELSENSORS); printf("\n\rOutput Calibration Set= %s %s %s %s", calType&RAW? "raw" : "", calType&MV? "mv" : "", calType&LINEAR? "linear" : "", calType&POLY? "poly" : ""); printf("\n\rSampling waitmS = %d mSec (1000/hz + 7)",waitmS); printf("\n\rPolling sleepmS= %d mSec",pollWaitmS); printf("\n\rADC\tSampling Freq = %d, (Cfg Mask = %x),",hz,adc_rate); printf("\n\r\tGain = %d, (Cfg Mask = %x),",adc_gain,ADC_GAIN); sprintff(cbuf,"\n\r\tMaximum ADC counts = %d, bitResolution = %.7f",maxVal,bitResolution); printf("%s",cbuf); report_ADCcals( "adcCals:", (uint8_t) NWANDS, &adsCals[0] ); // short changing not showing levels here #ifdef LEVEL_SENSOR_ENABLE report_ADSconfig( "Levels Config", nlevelSensors, &adsXYCoefs[0] ); #endif #ifdef PAR_SENSOR_ENABLE report_ADSconfig( "PAR Config", nparSensors, &parCoefs[0] ); #endif mos_thread_sleep( 5000 ); // let them read it! #endif // ----------- // Main Loop with sleep delay // while( TRUE ) { // printf("\n\r"); // =================================== // --- PAR SAMPLING --- // See sampling routine.... // #ifdef PAR_SENSOR_ENABLE mos_led_display(GREEN); // green during sampling sample_adcChannels( WANDSENSORS, NWANDS, &parCoefs[0] ); report_results( "PAR", nparSensors, &parCoefs[0] ); #endif // =================================== // --- LEVEL SENSOR SAMPLING --- // #ifdef LEVEL_SENSOR_ENABLE if( !--level_sensor_daq_count ) { // Check skip count mos_led_display(YELLOW); // during Level sampling sample_adcChannels( LEVELSENSORS, NWANDS, &adsXYCoefs[0] ); report_results( "LVL", LEVELSENSORS*NWANDS, &adsXYCoefs[0] ); level_sensor_daq_count = LEVEL_SENSOR_LOOP_CYCLE; } #endif // --- END of SAMPLING LOOP --- mos_led_display(0); // turn off LEDs mos_thread_sleep( pollWaitmS ); //POLLING_RATE); // sleep until next....note some inaccuracy // because RATE isn't perfectly adjusted by time taken // in sampling above..... } } //---------------------------------------- // Send the VRAW + anything else to the serial port // Requires: 'calType' declared above (raw,mv,etc.) // 'vraw[]' declared and loaded above // 'vadc[]' declared and loaded above // void report_results( char *header, uint8_t nchan, struct adsChanStuff *adsArray) { uint8_t ix; char *cp; struct adsChanStuff *ads=adsArray; // Done Sampling all Channels, Report results // ------------------------------------------ // Depending upon what's requested.... // if( calType & RAW ) { for( ix=0, cp=cbuf; ixoffset ) * ads->gain; calibrated = (vadc[ix] *ads->gain ) + ads->offset; sprintff(cp,"%7.4f ",calibrated); cp+=8; } printf("\n\r%s1st:%s",header,cbuf); } if( calType & POLY ) { for( ads=adsArray, ix=0, cp=cbuf; ixoffset) * ads->gain; calibrated = (fval * ads->gain ) + ads->offset; calibrated = fval * ads->gain + ads->offset; calibrated+= fval * fval * ads->gain2; sprintff(cp,"%7.4f",calibrated); cp+=8; } printf("\n\r%s2nd:%s",header,cbuf); } } #ifdef DEBUG //---------------------------------------- // Displays to Console what the ADS structure Settings Are void report_ADSconfig( char *header, uint8_t nchannels, struct adsChanStuff *adsArray) { uint8_t ix; struct adsChanStuff *ads=adsArray; // Report What the ADS Settings Are // printf("\n\r%s",header); printf("\n\rIx Name adcIx i2c ADC-Ch Offset Gain Gain2"); for( ix=0; ixname, ads->adsNum, adsCals[ads->adsNum].adsAddr, ads->channel); sprintff(cbuf, "%.2f %.2f %.2f", ads->offset, ads->gain, ads->gain2); printf("%s",cbuf); } } void report_ADCcals( char *header, uint8_t nadcs, struct adsCalStuff *adsArray) { uint8_t ix; struct adsCalStuff *adccal=adsArray; // Report What the ADS Calibrations are // printf("\n\r%s.....ADC Calibrations:",header); printf("\n\rIx name i2c Offset Gain "); for( ix=0; ixname, adccal->adsAddr); sprintff(cbuf, "%.5f %.5f", adccal->offset, adccal->gain); printf("%s",cbuf); } } #endif //---------------------------------------- void sample_adcChannels( uint8_t sensors_per_wand, uint8_t nwands, struct adsChanStuff *adsArray) { uint8_t ix, adsChan, npolls, setCfg; uint8_t arrayEntry; struct adsChanStuff *ads=adsArray, *ads0=adsArray; uint8_t retval; // Reply from I2C driver: #bytes uint8_t ADSreply[3]; // Reply from ADS1112, output 16bits + config uint8_t i2cAddr; // printf("\n\rSample_adcChannels %dper wand * %dwands = %d arraysz", // sensors_per_wand,nwands,sensors_per_wand*nwands); // double loop goes through each of the sensors at specific level on wand // there are 3: 1 for each possible adsChan, and then for each of the 4 ADC's // This method first loops to command each of the adc's to convert, then // goes back to grab the values, resulting in a bit faster turnaround for( adsChan=0; adsChanadsNum].adsAddr; // Set I2C Address dev_ioctl(DEV_AVR_I2C, I2C_DEST_ADDR, i2cAddr); // Set the cfgCommand ADS Channel, single conversion, // and specific PGA value setCfg = adc_rate | ads->channel | ADC_GAIN | SINGLE_CONVERSION; retval = dev_write(DEV_AVR_I2C, &setCfg, size1); // sprintff(cbuf," %d-%x-%x-%2d-%d",arrayEntry,i2cAddr,setCfg,ads->channel,retval); // printf("%s",cbuf); } // Delay for anticpated sampling time period and then mos_thread_sleep(waitmS); // Read the results // printf("\nReading Loop %d: ",adsChan); for( ads=ads0, ix=0; ixadsNum].adsAddr; // Set I2C Address dev_ioctl(DEV_AVR_I2C, I2C_DEST_ADDR, i2cAddr); npolls=0; do { ++npolls; // keep track of how many reads retval = dev_read(DEV_AVR_I2C, &ADSreply[0], 3); } while (ADSreply[2]&0x80); vraw[arrayEntry]= (ADSreply[0]<<8) + ADSreply[1]; vadc[arrayEntry] = (float)vraw[arrayEntry] * bitResolution; vadc[arrayEntry] = (vadc[arrayEntry] - adsCals[ads->adsNum].offset ) * adsCals[ads->adsNum].gain; // report index, replied cfgreg, and #polls required // sprintff(cbuf," %d-%x-%x-%x-%x-%d",arrayEntry,i2cAddr,ADSreply[2],ADSreply[1],ADSreply[0],npolls); // printf("%s",cbuf); } ++ads0; ++adsChan; } return; } //====================================================================== #ifdef DONT_DO_THIS_NOW_DIFFERENT_METHOD_IS_NOT_NEEDED void report_ADS1config( char *, uint8_t, struct adsChanStuffptr *); report_ADS1config( "Fake Config", (uint8_t) 4, &fakeCoefs[0] ); struct adsCalStuff unityLevelsCal[4] = { { "48", ADCxy0, 0.0, 1.0 }, // Unmodified readings, put all cals in 'levels' { "4A", ADCxy1, 0.0, 1.0 }, { "4C", ADCxy2, 0.0, 1.0 }, { "4E", ADCxy3, 0.0, 1.0 } }; //---------------------------------------- // PAR/Level Calibration stuff //---------------------------------------- struct adsChanStuffptr { struct adsCalStuff *adCal; uint8_t channel; // See enum float offset; float gain; float gain2; } fakeCoefs[8] = { { &unityLevelsCal[0], CHAN_SE0, 0.0, 1.0, 0.0 }, { &unityLevelsCal[0], CHAN_SE1, 0.0, 1.0, 0.0 }, { &unityLevelsCal[1], CHAN_SE0, 0.0, 1.0, 0.0 }, { &unityLevelsCal[1], CHAN_SE1, 0.0, 1.0, 0.0 }, { &unityLevelsCal[2], CHAN_SE0, 0.0, 1.0, 0.0 }, { &unityLevelsCal[2], CHAN_SE1, 0.0, 1.0, 0.0 }, { &unityLevelsCal[3], CHAN_SE0, 0.0, 1.0, 0.0 }, { &unityLevelsCal[3], CHAN_SE1, 0.0, 1.0, 0.0 } }; void report_ADS1config( char *header, uint8_t nchannels, struct adsChanStuffptr *adsArray) { uint8_t ix; struct adsChanStuffptr *ads=adsArray; struct adsCalStuff *adccal; // Report What the ADS Settings Are // printf("\n\r%s.....for the 'adsChanStuffptr' style....",header); printf("\n\rCh name i2c ADC-Ch Offset Gain Gain2"); for( ix=0; ixadCal; printf("\n\r%2d %s %02x %x ", ix,adccal->name, adccal->adsAddr, ads->channel); sprintff(cbuf, "%.2f %.2f %.2f", ads->offset, ads->gain, ads->gain2); printf("%s",cbuf); } } #endif //======================================================================