#include #include //config #pragma config CP = OFF #pragma config WRT = OFF #pragma config CPD = OFF #pragma config LVP = OFF #pragma config BOREN = OFF #pragma config PWRTE = OFF #pragma config WDTE = OFF #pragma config FOSC = HS //4-20MHz #define _XTAL_FREQ 8000000 //8 MHz clock input #define i2caddress 0x58 //directly above eeprom address space: 0x50 - 0x57, maxium 7 bits //function declaration void setPmtVoltage(uint16_t value); void setHvState(uint8_t onOff); void setGainVoltage(uint16_t value); void setfbGainVoltage(uint16_t value); void setLedVoltage(uint16_t value); unsigned int getPMTlowAmpSignal(void); unsigned int getPMThighAmpSignal(void); unsigned int getPMTvoltage(void); int currentRegister = 0; //i2c registers all, two bytes #define regHighVoltageOnOff 0x10 #define regHighVoltageAdjust 0x11 #define regHighVoltageFeedback 0x12 #define regGainVoltage 0x20 #define regFbGainVoltage 0x30 #define regLowAmpSignal 0x31 #define regHighAmpSignal 0x32 #define regCalibrationLedVoltage 0x40 volatile uint8_t i2c_register = 0; //written in interrupt volatile uint8_t i2c_messageLength = 0; volatile uint16_t i2c_data = 0; void writeI2Cregister(uint16_t value){ switch(i2c_register){ case regHighVoltageOnOff: setHvState((uint8_t)value); break; case regHighVoltageAdjust: setPmtVoltage(value); break; case regGainVoltage: setGainVoltage(value); break; case regFbGainVoltage: setfbGainVoltage(value); break; case regCalibrationLedVoltage: setLedVoltage(value); break; } i2c_register = 0x00; } uint16_t readI2Cregister(void){ switch(i2c_register){ case regHighVoltageFeedback: return getPMTvoltage(); break; case regLowAmpSignal: return getPMTlowAmpSignal(); break; case regHighAmpSignal: return getPMThighAmpSignal(); break; default: return 0xffff; break; } i2c_register = 0x00; } void __interrupt() ISR() { if(PIR1bits.SSPIF){ //interrupt from i2c for each completed byte PORTCbits.RC6 ^= 0x01; //indicate activity with pin RC6 (connected to led in my case) if(SSPSTATbits.R_nW){ //the i2c address is a read address if(SSPSTATbits.D_nA){ //read from master switch(i2c_messageLength){ case 2: SSPBUF = i2c_data & 0xff; //send second byte (lsb)) i2c_messageLength = 0; //completed successfully i2c_register = 0; break; default: i2c_data = 0; i2c_messageLength = 0; i2c_register = 0; break; } }else{ volatile uint8_t discarded = SSPBUF; //discard address i2c_data = readI2Cregister(); SSPBUF = (i2c_data>>8) & 0xff; //send first byte (msb) i2c_messageLength = 2; } SSPCONbits.CKP = 1; //stop clock stretching }else{ //the i2c address is a write address if(SSPSTATbits.D_nA){ //write from master switch(i2c_messageLength){ case 1: //set register i2c_register = SSPBUF; i2c_messageLength = 2; break; case 2: i2c_data += (uint16_t)SSPBUF; i2c_messageLength = 3; break; case 3: i2c_data += ((uint16_t)SSPBUF)<<8; writeI2Cregister(i2c_data); //intended fallthrough, when message complete default: //or on error i2c_data = 0; i2c_messageLength = 0; break; } }else{ volatile uint8_t discarded = SSPBUF; //discard address i2c_messageLength = 1; } } PIR1bits.SSPIF = 0; //reset interrupt flag } } void setup(){ //setup PORTA PORTA = 0x00; TRISA = 0x1F; //all channels input //setup PORTB PORTB = 0xF6; TRISB = 0x09; //INT input, PGM input, all others output //setup PORTC PORTC = 0x20; //set loadDac high TRISC = 0x9B; //all input, but HV_en RC2 and PIC_DAC_Load RC5 //setup I2C SSPADD = i2caddress << 1; SSPSTATbits.SMP = 1; //100KHz mode SSPSTATbits.CKE = 0; //I2C spec SSPCONbits.CKP = 1; SSPCONbits.SSPM = 0x6; //i2c slave, 7bit address SSPCONbits.SSPEN = 1; //I2C interrupt PIR1bits.SSPIF = 0; //reset interrupt flag PIE1bits.SSPIE = 1; //enable i2c interrupt INTCONbits.GIE = 1; //enable global interrupts INTCONbits.PEIE = 1; //enable peripheral interrupts //setup ADC ADCON1bits.ADFM = 1; ADCON1bits.PCFG = 0xC; // RA3 = VREF+, RA2 = VREF-, RA5, RA1 and RA0 inputs; ADCON0bits.ADCS = 2; //clock source for ADC, F_osc/32, good for XTAL > 5MHz ADCON0bits.CHS = 0; //select RA0 as input ADCON0bits.GO = 0; //no conversion in progress ADCON0bits.ADON = 1; //enable ADC peripheral } void main(void) { setup(); while(1){ __delay_ms(1000); PORTCbits.RC6 = 0x00; //reset pin RC6 to off } } void setHvState(uint8_t onOff){ PORTCbits.RC2 = onOff; } //input value of 0xffff corresponds to 4.095V void setPmtVoltage(uint16_t value){ for(uint8_t channelIdx = 0; channelIdx<2; channelIdx++){ uint16_t dacValue = 0x2000; //exclusively load a or b channel PORTBbits.RB6 = 0; //select desired DAC chip dacValue += ((uint16_t)channelIdx<<12); //select channel a=0 or channel b=1 if(channelIdx == 0){ //channel a dacValue |= 0xf00 & (value>>4); }else{ //channel b dacValue |= 0xfff & (value); } for(int8_t bitOffset = 13; bitOffset>=0; bitOffset--){ PORTBbits.RB4 = 0; //drive clock low PORTBbits.RB5 = (uint8_t) ((dacValue&(1<>bitOffset); PORTBbits.RB4 = 1; //drive clock high, } PORTBbits.RB6 = 1; //deselect DAC chip PORTCbits.RC5 = 0; //load value to output PORTCbits.RC5 = 1; } } void setGainVoltage(uint16_t value){ for(uint8_t channelIdx = 0; channelIdx<2; channelIdx++){ uint16_t dacValue = 0x2000; //exclusively load a or b channel PORTBbits.RB1 = 0; //select desired DAC chip dacValue += ((uint16_t)channelIdx<<12); //select channel a=0 or channel b=1 if(channelIdx == 1){ //channel b, FLIPPED! dacValue |= 0xf00 & (value>>4); }else{ //channel a dacValue |= 0xfff & (value); } for(int8_t bitOffset = 13; bitOffset>=0; bitOffset--){ PORTBbits.RB4 = 0; //drive clock low PORTBbits.RB5 = (uint8_t) ((dacValue&(1<>bitOffset); PORTBbits.RB4 = 1; //drive clock high, } PORTBbits.RB1 = 1; //deselect DAC chip PORTCbits.RC5 = 0; //load value to output PORTCbits.RC5 = 1; } } void setLedVoltage(uint16_t value){ uint16_t dacValue = 0x3000; //exclusively load channel b PORTBbits.RB2 = 0; //select desired DAC chip dacValue |= 0xfff & (value); for(int8_t bitOffset = 13; bitOffset>=0; bitOffset--){ PORTBbits.RB4 = 0; //drive clock low PORTBbits.RB5 = (uint8_t) ((dacValue&(1<>bitOffset); PORTBbits.RB4 = 1; //drive clock high, } PORTBbits.RB2 = 1; //deselect DAC chip PORTCbits.RC5 = 0; //load value to output PORTCbits.RC5 = 1; } void setfbGainVoltage(uint16_t value){ uint16_t dacValue = 0x2000; //exclusively load channel a PORTBbits.RB2 = 0; //select desired DAC chip dacValue |= 0xfff & (value); for(int8_t bitOffset = 13; bitOffset>=0; bitOffset--){ PORTBbits.RB4 = 0; //drive clock low PORTBbits.RB5 = (uint8_t) ((dacValue&(1<>bitOffset); PORTBbits.RB4 = 1; //drive clock high, } PORTBbits.RB2 = 1; //deselect DAC chip PORTCbits.RC5 = 0; //load value to output PORTCbits.RC5 = 1; } unsigned int getADCreading(char channel){ ADCON0bits.CHS = channel; __delay_us(100); ADCON0bits.GO = 1; while(ADCON0bits.GO){}; //wait for conversion to finish return ((unsigned int)(ADRESH<<8))+ADRESL; } unsigned int getPMTlowAmpSignal(void){ return getADCreading(0); } unsigned int getPMThighAmpSignal(void){ return getADCreading(1); } unsigned int getPMTvoltage(void){ return getADCreading(5); }