/********************************************************************\ Name: weather_station.c Created by: Stefan Ritt Contents: Application specific (user) part of Midas Slow Control Bus protocol for Mu3e Weather Station \********************************************************************/ #include #include // for atof() #include #include #include "mscbemb.h" extern bit FREEZE_MODE; extern bit DEBUG_MODE; char code node_name[] = "WStation"; /* declare number of sub-addresses to framework */ unsigned char idata _n_sub_addr = 1; bit flush_flag; sbit SCK = P0^0; // Serial Clock (output) sbit MISO = P0^1; // Master In / Slave Out (input) sbit MOSI = P0^2; // Master Out / Slave In (output) sbit NCS_DAC = P0^3; // /CS DAC sbit NHV_ENABLE = P0^7; sbit NCS_ADC = P1^3; // /CS HV ADC // delay for opto-coupler in microseconds #define OPT_DELAY 1 /*---- Define variable parameters returned to CMD_GET_INFO command ----*/ /* data buffer (mirrored in EEPROM) */ struct { float value[16]; float current[16]; float ofs[16]; float rhofs[2]; float rhgain[2]; float o2ofs[2]; float o2gain[2]; } xdata user_data; MSCB_INFO_VAR code vars[] = { 4, UNIT_CELSIUS, 0, 0, MSCBF_FLOAT, "P0T0", &user_data.value[0], 4, UNIT_CELSIUS, 0, 0, MSCBF_FLOAT, "P0T1", &user_data.value[1], 4, UNIT_CELSIUS, 0, 0, MSCBF_FLOAT, "P0T2", &user_data.value[2], 4, UNIT_CELSIUS, 0, 0, MSCBF_FLOAT, "P0T3", &user_data.value[3], 4, UNIT_CELSIUS, 0, 0, MSCBF_FLOAT, "P1T4", &user_data.value[4], 4, UNIT_CELSIUS, 0, 0, MSCBF_FLOAT, "P1T5", &user_data.value[5], 4, UNIT_CELSIUS, 0, 0, MSCBF_FLOAT, "P1T6", &user_data.value[6], 4, UNIT_CELSIUS, 0, 0, MSCBF_FLOAT, "P1T7", &user_data.value[7], 4, UNIT_CELSIUS, 0, 0, MSCBF_FLOAT, "P2T0", &user_data.value[8], 4, UNIT_CELSIUS, 0, 0, MSCBF_FLOAT, "P2T1", &user_data.value[9], 4, UNIT_PERCENT, 0, 0, MSCBF_FLOAT, "P2RH", &user_data.value[10], 4, UNIT_PERCENT, 0, 0, MSCBF_FLOAT, "P2O2", &user_data.value[11], 4, UNIT_CELSIUS, 0, 0, MSCBF_FLOAT, "P3T0", &user_data.value[12], 4, UNIT_CELSIUS, 0, 0, MSCBF_FLOAT, "P3T1", &user_data.value[13], 4, UNIT_PERCENT, 0, 0, MSCBF_FLOAT, "P3RH", &user_data.value[14], 4, UNIT_PERCENT, 0, 0, MSCBF_FLOAT, "P3O2", &user_data.value[15], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I0", &user_data.current[0], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I1", &user_data.current[1], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I2", &user_data.current[2], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I3", &user_data.current[3], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I4", &user_data.current[4], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I5", &user_data.current[5], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I6", &user_data.current[6], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I7", &user_data.current[7], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I8", &user_data.current[8], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I9", &user_data.current[9], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I10", &user_data.current[10], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I11", &user_data.current[11], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I12", &user_data.current[12], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I13", &user_data.current[13], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I14", &user_data.current[14], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I15", &user_data.current[15], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I0Ofs", &user_data.ofs[0], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I1Ofs", &user_data.ofs[1], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I2Ofs", &user_data.ofs[2], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I3Ofs", &user_data.ofs[3], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I4Ofs", &user_data.ofs[4], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I5Ofs", &user_data.ofs[5], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I6Ofs", &user_data.ofs[6], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I7Ofs", &user_data.ofs[7], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I8Ofs", &user_data.ofs[8], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I9Ofs", &user_data.ofs[9], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I10Ofs", &user_data.ofs[10], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I11Ofs", &user_data.ofs[11], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I12Ofs", &user_data.ofs[12], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I13Ofs", &user_data.ofs[13], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I14Ofs", &user_data.ofs[14], 4, UNIT_AMPERE, PRFX_MICRO, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "I15Ofs", &user_data.ofs[15], 4, UNIT_PERCENT, 0, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "RHGain0", &user_data.rhofs[0], 4, UNIT_PERCENT, 0, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "RHGain1", &user_data.rhofs[1], 4, UNIT_FACTOR, 0, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "RHGain0", &user_data.rhgain[0], 4, UNIT_FACTOR, 0, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "RHGain1", &user_data.rhgain[1], 4, UNIT_PERCENT, 0, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "O2Gain0", &user_data.o2ofs[0], 4, UNIT_PERCENT, 0, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "O2Gain1", &user_data.o2ofs[1], 4, UNIT_FACTOR, 0, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "O2Gain0", &user_data.o2gain[0], 4, UNIT_FACTOR, 0, 0, MSCBF_FLOAT | MSCBF_HIDDEN, "O2Gain1", &user_data.o2gain[1], 0 }; MSCB_INFO_VAR *variables = vars; /********************************************************************\ Application specific init and inout/output routines \********************************************************************/ void user_write(unsigned char index) reentrant; void write_gain(void); void set_hv(float value); /*---- User init function ------------------------------------------*/ extern SYS_INFO idata sys_info; void user_init(unsigned char init) { if (init) { user_data.rhgain[0] = 1; user_data.rhgain[1] = 1; user_data.o2gain[0] = 1; user_data.o2gain[1] = 1; } SFRPAGE = LEGACY_PAGE; P0MDOUT = 0x5D; // SCLK, MOSI, CS_DAC, DI, DE_RE push-pull P1MDOUT = 0xFF; } /*---- User write function -----------------------------------------*/ #pragma NOAREGS void user_write(unsigned char index) reentrant { if (index); } /*---- User read function ------------------------------------------*/ unsigned char user_read(unsigned char index) { if (index == 0); return 0; } /*---- User function called vid CMD_USER command -------------------*/ unsigned char user_func(unsigned char *data_in, unsigned char *data_out) { /* echo input data */ data_out[0] = data_in[0]; data_out[1] = data_in[1]; return 2; } /*---- ADC functions ------------------------------------------------*/ unsigned char read_adc() { static unsigned char channel = 0; unsigned char i, cmd, nc; long d; float f; static unsigned int last = 0; unsigned char overflow_flag = 0; // only measure once every 200 ms if (time() < last+20) return 0; last = time(); SCK = 0; MOSI = 1; NCS_ADC = 0; // chip select delay_us(OPT_DELAY); watchdog_refresh(1); if (MISO == 1) { // check /EOC NCS_ADC = 1; // remove chip select delay_us(OPT_DELAY); return 0; } nc = (channel + 1) % 16; cmd = 0xB0 | ((nc & 0x01) << 3) | (nc >> 1); MOSI = (cmd & 0x80) > 0 ? 1 : 0; cmd <<= 1; for (i=0,d=0 ; i<24 ; i++) { delay_us(OPT_DELAY); SCK = 1; d = (d << 1) | MISO; delay_us(OPT_DELAY); SCK = 0; MOSI = (cmd & 0x80) > 0 ? 1 : 0; cmd <<= 1; } SCK = 1; MOSI = 1; NCS_ADC = 1; // remove CS if ((d >> 20) == 0x03) { f = 1.25; overflow_flag = 1; } else if ((d >> 20) == 0x00) { f = -1.25; overflow_flag = 1; } else if ((d >> 21) == 0x01) { d = d & 0x000FFFFF; f = d; f = f / (1l<<20) * 1.25; } else { d = (d | 0xFFF00000); f = d; f = f / (1l<<20) * 1.25; } // convert to current in uA, 2k5 shunt f = f / 2500 * 1E6; // correct current offset f -= user_data.ofs[channel]; // round to two digits f = floor(f * 100 + 0.5) / 100; // store current user_data.current[channel] = f; // RH calibration // I at 0%: 969 uA , I at 100%: 57.6 uA if (channel % 4 == 0 || channel % 4 == 1 || channel < 8) { // AD592 1uA / K f = f - 273.2; if (f < -100) f = -100; } else if (channel %4 == 2) { // HIH-4010, 476 uA at 0%, 28.4 uA at 100% f = (476.0 - f) * 100 / (476 - 28.4); // subtract offset if (channel == 10) f -= user_data.rhofs[0]; else f -= user_data.rhofs[1]; // multiply by gain if (channel == 10) f *= user_data.rhgain[0]; else f *= user_data.rhgain[1]; if (f > 100) f = -100; } else { // O2-A2, 0% at 160uA, 21% at 70 uA f = (160.0 - f) * 21 / (160.0 - 70.0); // subtract offset if (channel == 11) f -= user_data.o2ofs[0]; else f -= user_data.o2ofs[1]; // multiply by gain if (channel == 11) f *= user_data.o2gain[0]; else f *= user_data.o2gain[1]; if (f < -100 || f > 100) f = -100; } // round to three digits f = floor(f * 1000 + 0.5) / 1000; // check overflow flag, and don't set sensible values if so if (overflow_flag == 1) f=-101; // channels are in reverse order user_data.value[channel] = f; // increment channel channel = (channel + 1) % 16; return 1; } /*---- User loop function ------------------------------------------*/ void user_loop(void) { read_adc(); }