//--------------------------------------------------------------------- // PMT_Controller_X.c // // 7/24/08 PMT_Controller_b5.c // -Increased delay for PMT_POW from 110ms to 120 // 6/26/08 PMT_Controller_b4.c // -Writes PMT codes to status array // -Writes PMT error counts (all channels) to status array // -Clears status array during initialization // (init to 0x00, not ascii zero 0x30) // -Initialize PMT_POW outputs to low // 6/25/08 PMT_Controller_b3.c // -Checks for errors on all PMT ports, only when type 3 connected // -Increase bytes for USB comm to 54 // 6/24/08 PMT_Controller_b2.c // -Increase bytes for USB comm to 50 // 6/19/08 PMT_Controller_b1.c // - Added PMT Error checking and reset (port 1 only) // 6/13/08 PMT_Controller_b0.c // - Added some comments // 10/08/07 PMT_Controller.c // - Initialize DAC outputs to zero. // - Allow for user to count channel numbers starting from 1 // - Increase Status[] to 38 bytes // 8/23/07 // Used to run the 4-Channel PMT Controller Circuit // // lt222@cornell.edu //--------------------------------------------------------------------- #include <18F4455.h> #DEVICE ADC=8 // Use only the 8 most significant bits from the adc #include #include #fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN, MCLR #use delay(clock=48000000) #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7) //PMT ID Lines - Analog inputs code which PMT module is connected #define PMT1 PIN_A0 #define PMT2 PIN_A1 #define PMT3 PIN_A2 #define PMT4 PIN_A3 //PMT Communication Lines #define PMT1_COM1 PIN_B0 //error #define PMT2_COM1 PIN_B1 #define PMT3_COM1 PIN_B2 #define PMT4_COM1 PIN_B3 #define PMT1_COM2 PIN_B4 //power #define PMT2_COM2 PIN_B5 #define PMT3_COM2 PIN_B6 #define PMT4_COM2 PIN_B7 // DAC - uses a DAC7614 with a 2.5 volt ref - 0.61 mV per bit #define DAC_SDI PIN_A4 #define DAC_CLK PIN_A5 // Black level DAC #define DAC_LOAD_BL PIN_C2 #define DAC_CS_BL PIN_C0 // High Voltage DAC #define DAC_LOAD_HV PIN_E0 #define DAC_CS_HV PIN_C1 // Amp/Mixer Connections #define MIXER1 PIN_D0 #define MIXER2 PIN_D1 #define GAIN1 PIN_D2 #define GAIN2 PIN_D3 #define GAIN3 PIN_D4 #define GAIN4 PIN_D5 // Handunit Connections #define RS232_TX PIN_C6 #define RS232_RX PIN_C7 ///////////////////////////////////////////////////////////////////////////// //In usb.c/h - tells the CCS PIC USB firmware to include HID handling code. #DEFINE USB_HID_DEVICE TRUE //the following defines needed for the CCS USB PIC driver to enable the TX endpoint 1 // and allocate buffer space on the peripheral #define USB_EP1_TX_ENABLE USB_ENABLE_INTERRUPT //turn on EP1 for IN bulk/interrupt transfers #define USB_EP1_TX_SIZE 54 //allocate 50 bytes in the hardware for transmission //the following defines needed for the CCS USB PIC driver to enable the RX endpoint 1 // and allocate buffer space on the peripheral #define USB_EP1_RX_ENABLE USB_ENABLE_INTERRUPT //turn on EP1 for OUT bulk/interrupt transfers #define USB_EP1_RX_SIZE 54 //allocate 50 bytes in the hardware for reception #include //Microchip 18Fxx5x hardware layer for usb.c #include <4ChannelController_desc_54.h> //USB Configuration and Device descriptors for this USB device #include //handles usb setup tokens and get descriptor reports // Messages - USB interface ***************************most of this can get taken out #define PRINTSTR 0x04 #define HIGH_VOLTAGE 0x48 //"H" #define BLACK_LEVEL 0x42 //"B" #define MIXER 0x4D //"M" #define GAIN 0x47 //"G" #define QUERY 0x3F //"?" #define PMT 0x50 //"P" #define ERROR 0x45 //"E" #define HV_BPV 1638 // 1/(2.5v / 4096)Bits Per Volt for HV #define BL_BPV 819 // Bits per volt for BL #define BL_OFFSET 2048 //DAC Channels start at 0 #define Channel_1 0 #define Channel_2 1 #define Channel_3 2 #define Channel_4 3 // Global Variables int8 value[6]; //pmt id voltages (from adc) int8 command[54]; //command received from system (via usb) int8 pmt_on[6]; //used to specify type of pmt on each channel int8 test[6]; //used to return value to system (via usb). ************may not need any more? int8 status[54], *p; //used to store current settings. retrieved by query command int8 pmt_err_count[4] = {0, 0, 0, 0}; //number of times PMT error occurs on each channel int16 PMT_ERR[4] = {PMT1_COM1, PMT2_COM1, PMT3_COM1, PMT4_COM1}; //Digital error output from PMT protection circuit int16 PMT_POW[4] = {PMT1_COM2, PMT2_COM2, PMT3_COM2, PMT4_COM2}; //Digital power output to PMT protection circuit (reset) //--------------------------------------------------------------------------- // SystemInit: Initialization code // // Sets ports as inputs or outputs and to the correct state // Note: in SET_TRIS_X - 1 means input, 0 means output //--------------------------------------------------------------------------- void SystemInit(void) { int8 i; setup_adc(ADC_CLOCK_INTERNAL); setup_adc_ports(AN0_TO_AN3); setup_psp(PSP_DISABLED); setup_spi(FALSE); setup_wdt(WDT_OFF); setup_timer_0(RTCC_INTERNAL); setup_timer_1(T1_DISABLED); setup_timer_2(T2_DISABLED,0,1); setup_comparator(NC_NC_NC_NC); setup_vref(FALSE); setup_oscillator(False); SET_TRIS_A(0b10011111); // Port A = analog data in from PMT ID signal SET_TRIS_B(0b00001111); // Port B = 4 PMT reset outputs, 4 PMT error inputs SET_TRIS_C(0b00000000); // Port C = all outputs SET_TRIS_D(0b00000000); // Port D = all outputs SET_TRIS_E(0b00001000); // Port E = all outputs except for reset *********change this when you set up rs232 // Setup DAC output_high(DAC_CS_BL); output_high(DAC_CS_HV); output_low(DAC_LOAD_BL); output_low(DAC_LOAD_HV); output_high(DAC_CLK); //Initialize Power Outputs to PMT (to zero) for(i=0;i<5;i++){ output_low(PMT_POW[i]); } //Initialize status register to zeros memset(status,0,sizeof(status)); } //--------------------------------------------------------------------------- // SetDAC_HV: sets the voltage output of the high voltage DAC7614 //--------------------------------------------------------------------------- void SetDAC_HV(int16 address, int16 dac_value) { int8 i; output_low(DAC_CS_HV); address = address << 0x0E; dac_value = address|dac_value; for(i=0;i<16;i++) { output_low(DAC_CLK); delay_cycles(100); if(BIT_TEST(dac_value, 15 - i)) output_high(DAC_SDI); else output_low(DAC_SDI); delay_cycles(100); output_high(DAC_CLK); delay_cycles(100); } output_high(DAC_CS_HV); delay_cycles(100); output_low(DAC_LOAD_HV); delay_cycles(100); output_high(DAC_LOAD_HV); } //--------------------------------------------------------------------------- // SetDAC_BL: Sets the voltage output of the Blacklevel DAC7614 //--------------------------------------------------------------------------- void SetDAC_BL(int16 address, int16 dac_value) { int8 i; output_low(DAC_CS_BL); address = address << 0x0E; dac_value = address|dac_value; for(i=0;i<16;i++) { output_low(DAC_CLK); delay_cycles(100); if(BIT_TEST(dac_value, 15 - i)) output_high(DAC_SDI); else output_low(DAC_SDI); delay_cycles(100); output_high(DAC_CLK); delay_cycles(100); } output_high(DAC_CS_BL); delay_cycles(100); output_low(DAC_LOAD_BL); delay_cycles(100); output_high(DAC_LOAD_BL); } //--------------------------------------------------------------------------- // OutputInit: Sets the output of both DACs to zero //--------------------------------------------------------------------------- void OutputInit (void){ SetDAC_HV(Channel_1, 0); SetDAC_HV(Channel_2, 0); SetDAC_HV(Channel_3, 0); SetDAC_HV(Channel_4, 0); SetDAC_BL(Channel_1, BL_OFFSET); SetDAC_BL(Channel_2, BL_OFFSET); SetDAC_BL(Channel_3, BL_OFFSET); SetDAC_BL(Channel_4, BL_OFFSET); } //--------------------------------------------------------------------------- // poll_adc: Determines which PMTs are connected using analog input voltages // checks for error if PMT type 3, resets PMT on error, increment error counter //--------------------------------------------------------------------------- void poll_adc (void) { int8 i, temp_count; ldiv_t lidiv; //Define acceptable voltage ranges for each type int8 no_PMT = 0x19; // 0.5V int8 PMT1_min = 0x26; // 0.75V int8 PMT1_max = 0x3F; // 1.25V int8 PMT2_min = 0x59; // 1.75V int8 PMT2_max = 0x72; // 2.25V int8 PMT3_min = 0x8C; // 2.75V int8 PMT3_max = 0xA5; // 3.25V int8 PMT4_min = 0xBF; // 3.75V int8 PMT4_max = 0xD8; // 4.25V //Record PMT type (1-4) in pmt_on[]. Values in ASCII to be read by system for (i=0; i<4; i++) { set_adc_channel(i); delay_ms(20); value[i] = read_adc(); if (value[i] <= no_PMT){ pmt_on[i] = 0x30;} // No PMT Connected else if ((value[i] >= PMT1_min) && (value[i] <= PMT1_max)) { pmt_on[i] = 0x31;} // HC125 Bialkali PMT module else if ((value[i] >= PMT2_min) && (value[i] <= PMT2_max)) { pmt_on[i] = 0x32;} // Ultra Bialkali PMT module else if ((value[i] >= PMT3_min) && (value[i] <= PMT3_max)) { // H7422P-40MOD type1 pmt_on[i] = 0x33; if (input(PMT_ERR[i])){ //if PMT error output_high(PMT_POW[i]); //pulse PMT power to reset delay_ms(120); output_low(PMT_POW[i]); (pmt_err_count[i])++; //increment error counter } p = status+44+3*i; //point to location in status[] where this channel is stored. (start at element 44, each channel 3 digits) temp_count = pmt_err_count[i]; lidiv = ldiv(temp_count, 10); *p-- = lidiv.rem + 0x30; lidiv = ldiv(lidiv.quot, 10); *p-- = lidiv.rem + 0x30; lidiv = ldiv(lidiv.quot, 10); *p = lidiv.rem + 0x30; } else if ((value[i] >= PMT4_min) && (value[i] <= PMT4_max)) { pmt_on[i] = 0x34;} // H7422P-40MOD type2 else { pmt_on[i] = 0x39;} // Error (Something is connected but outside the specified ranges) } } //--------------------------------------------------------------------------- // execute_cmd: Executes command received from system (via USB) // See PMT Controller Board spec for command details. //--------------------------------------------------------------------------- void execute_cmd (void) { int8 sel, chan, state, temp_count, i, j; int16 voltage, g[4], m[2]; float v = 0.0; ldiv_t lidiv; enum fun {HV=0, BL, MX, GN}; m[0] = MIXER1; //PIN_D0 m[1] = MIXER2; //PIN_D1 g[0] = GAIN1; //PIN_D2 g[1] = GAIN2; //PIN_D3 g[2] = GAIN3; //PIN_D4 g[3] = GAIN4; //PIN_D5 sel = command[0]; chan = (command[1] - 0x30); //convert from ascii to integer state = (command[2] - 0x30); //convert from ascii to integer p = status; //reset pointer switch(chan) { case 1: chan = Channel_1; //0 break; case 2: chan = Channel_2; //1 break; case 3: chan = Channel_3; //2 break; case 4: chan = Channel_4; //3 break; } //Status[38]used to store all current settings. Settings stored after each cmd executed //*p is used to point to location in Status[] allocated to each setting //Mapping out the bytes of Status[] allocated to each setting... //HV-PMT1[0-3], HV-PMT2[4-7], HV-PMT3[8-11], HV-PMT4[12-15] //BL-PMT1[16-19], BL-PMT2[20-23], BL-PMT3[24-27], BL-PMT4[28-31] //MIXER1[32], MIXER2[33], GAIN1[34], GAIN2[35], GAIN3[36], GAIN4[37] switch(sel) { case HIGH_VOLTAGE: //determine voltage i = 5; j = 0; while (i>1){ if (command[i]!=0x23){ //0x23 is used as delimiter (ASCII "#")when value less than 4 digits //for each value in cmd, convert to integer and multiply by 10s place (10^j) v = v+ ((command[i]-0x30)*exp(j*log(10))); i--; j++; } else i--; } voltage = v*HV_BPV/1000; //multiply by bits per volt, scale down 1000 for output to PMT switch(pmt_on[chan]){ //PMT Type 1 & 2 limit at 1.2V case 0x31: case 0x32: if (voltage > 1.2*HV_BPV) {voltage = 1.2*HV_BPV;} break; //PMT Type 3 & 4 limit at 0.9V case 0x33: case 0x34: if (voltage > 0.9*HV_BPV) {voltage = 0.9*HV_BPV;} break; } SetDAC_HV(chan, voltage); //HighVoltage setting stored in test[], value is returned to system via USB test[0] = voltage; //***********no longer used, should remove all ref to test in next rev p = p+4*chan; //point to location in status[] where this channel is stored for(i=2; i<6; i++){ //Stores 4 byte voltage setting from input command into status *p++ = command[i]; } break; //case HIGH_VOLTAGE case BLACK_LEVEL: i = 5; j = 0; while (i>2){ if (command[i]!=0x23){ //0x23 is used as delimiter (ASCII "#")when value less than 3 digits //for each value in cmd, convert to integer and multiply by 10s place (10^j) v = v+ ((command[i]-0x30)*exp(j*log(10))); i--; j++; } else i--; } if (command[2] == 0x2D){ //value may be negative, 2D is ascii "-" voltage = (-v*BL_BPV)/40 + BL_OFFSET; } else voltage = (v*BL_BPV)/40 + BL_OFFSET; SetDAC_BL(chan, voltage); test[0] = voltage; p = p+16+4*chan; //point to location in status[] where this channel is stored. (skip 16bytes where HV data stored) for(i=2; i<6; i++){ //Stores 4 byte voltage setting from input command into status *p++ = command[i]; } break; //case BLACK_LEVEL case MIXER: if (state == 0) {output_low(m[chan]);} //m[] contains the names Mixer1 and Mixer2 which are assigned to output pins using #define above if (state == 1) {output_high(m[chan]);} p = p+32+chan; //point to location in status[] where this channel is stored. (skip 32bytes where HV and BL data stored) *p = state; break; //case MIXER case GAIN: if (state == 0) {output_low(g[chan]);} //g[] contains the names Gain1 through Gain4 which are assigned to output pins using #define above if (state == 1) {output_high(g[chan]);} p = p+34+chan; //point to location in status[] where this channel is stored. (skip 34bytes where HV, BL, and Mixer data stored) *p = state; break; //case GAIN case QUERY: //query will no longer be used - can remove this switch(command[2]){ case HIGH_VOLTAGE: p = p+4*chan; for(i=0; i<4; i++){ test[i] = *p++; //read back 4 bytes of status where voltage setting for this channel was stored } break; case BLACK_LEVEL: p = p+16+4*chan; for(i=0; i<4; i++){ test[i] = *p++; } break; case MIXER: p = p+32+chan; test[0] = *p; break; case GAIN: p = p+34+chan; test[0] = *p; break; case PMT: for(i=0; i<6; i++){test[i] = pmt_on[i];} break; //case QUERY } } //Write PMT code to STATUS for(i=0; i<4; i++){ p = status+38+i; //point to location in status[] where this channel is stored. (start at element 38) *p = pmt_on[i]; } //Write PMT error counts to STATUS //************i moved this code to "poll_adc" but may want to move it back here // for(i=0; i<4; i++){ // p = status+44+3*i; //point to location in status[] where this channel is stored. (start at element 44, each channel 3 digits) // temp_count = pmt_err_count[i]; // lidiv = ldiv(temp_count, 10); // *p-- = lidiv.rem + 0x30; // lidiv = ldiv(lidiv.quot, 10); // *p-- = lidiv.rem + 0x30; // lidiv = ldiv(lidiv.quot, 10); // *p = lidiv.rem + 0x30; // } } //--------------------------------------------------------------------------- // Main Program //--------------------------------------------------------------------------- void main(void) { usb_init(); SystemInit(); OutputInit(); while (TRUE) { poll_adc(); // Check for USB commands if (usb_enumerated()) { if (usb_kbhit(1)) { usb_gets(1, command, 54, 100); execute_cmd(); usb_puts(1, status, 54, 100); } } } }