/* Daya Bay gas system digital bubbler readout Based on EMP1 Rocket Lab program toss the original Rocket Lab code was written by Vassilios Papathanakos Department of Physics, Princeton University This new program is created on Feb. 5, 2008, C. Lu Use sequence of channels instead of single-channel method to run the ADC. 3/28/2008 C. Lu So far maximum 8 channels can be readout by this mode, couldn't make 16 channels work in this sequence of channels mode. Therefore readout 16 channrls has to be completed in 2 steps, 8 channels/step. This code has been tested with real bubblers. 3/31/2008 C. Lu */ #include #include #include #define __FLASH__ #define __HW_v_2_1__ /* START type definitions for convinience with microcontrollers */ typedef unsigned char BYTE; /* 8 bits */ typedef unsigned short WORD; /* 16 bits */ typedef unsigned long LONGWORD; /* 32 bits */ /* for dividing a WORD into two BYTEs */ typedef union _WORD_BYTE { WORD w; BYTE b[2]; } WORD_BYTE; #define FLASH_DATA 0x2000 // Data copy in Flash starts here 0x5000 #define FLASH_COUNT 0xF000 // counter for the iteration # // The following block of code sets up a "structure" // for storing data from the digital bubbler. #pragma bitfields = reversed #pragma pack(8) struct bubbl { // bubbler data storage allocation unsigned int time; unsigned int chan1; unsigned int chan2; unsigned int chan3; unsigned int chan4; unsigned int chan5; unsigned int chan6; unsigned int chan7; unsigned int chan8; unsigned int chan9; unsigned int chan10; unsigned int chan11; unsigned int chan12; unsigned int chan13; unsigned int chan14; unsigned int chan15; unsigned int chan16; unsigned int time1; }; #define DATA 202 // Number of samples stored #define BYTES_PER_RECORD 36 // 18 one-word data items struct bubbl *init_data(void) { // Initialize data storage struct bubbl *data = (struct bubbl *)malloc(DATA * (size_t) sizeof(struct bubbl)); // Allocate space // Write STX at the start,... data[0].time = 0x2222; data[0].chan1 = 0x2222; data[0].chan2 = 0x2222; data[0].chan3 = 0x2222; data[0].chan4 = 0x2222; data[0].chan5 = 0x2222; data[0].chan6 = 0x2222; data[0].chan7 = 0x2222; data[0].chan8 = 0x2222; data[0].chan9 = 0x2222; data[0].chan10 = 0x2222; data[0].chan11 = 0x2222; data[0].chan12 = 0x2222; data[0].chan13 = 0x2222; data[0].chan14 = 0x2222; data[0].chan15 = 0x2222; data[0].chan16 = 0x2222; data[0].time1 = 0x2222; // ... and ETX at the end data[DATA - 1].time = 0x3333; data[DATA - 1].chan1 = 0x3333; data[DATA - 1].chan2 = 0x3333; data[DATA - 1].chan3 = 0x3333; data[DATA - 1].chan4 = 0x3333; data[DATA - 1].chan5 = 0x3333; data[DATA - 1].chan6 = 0x3333; data[DATA - 1].chan7 = 0x3333; data[DATA - 1].chan8 = 0x3333; data[DATA - 1].chan9 = 0x3333; data[DATA - 1].chan10 = 0x3333; data[DATA - 1].chan11 = 0x3333; data[DATA - 1].chan12 = 0x3333; data[DATA - 1].chan13 = 0x3333; data[DATA - 1].chan14 = 0x3333; data[DATA - 1].chan15 = 0x3333; data[DATA - 1].chan16 = 0x3333; data[DATA - 1].time1 = 0x3333; return data; } // Wait for 6*n+11 cycles void wait(unsigned int n) { for (unsigned int i = n; i > 0; i--) ; return; } //display a value void display(unsigned int value, unsigned int waitTime) { P1OUT = 16*(value & 0xF); wait(waitTime); return; } /* Routines to inerface with the FLASH on a msp430x161x chip Created on 2005-09-16 by Neelesh Arora for the Rocket Lab of Physics Department, Princeton University */ BYTE COUNT; // Following functions courtesy of http://msp430.info /************************************************************************************************* Function : flash_writeByte Parameter : *dst : address within the FLASH page value : BYTE that has to be written to FLASH Date : 08.09.2001 / 17.11.2002 / 22.11.2002 Description : this function writes a byte to an address in FLASH memory warning: in FLASH only zeros can be written. if a bit value needs to be set to one from zero, the whole page has to be erased, thus setting all bits to one. then the value can be written correctly. this function does not perform the necessary FLASH page erase. *************************************************************************************************/ void flash_writeByte(BYTE *dst, BYTE value) { FCTL2 = FWKEY | FSSEL0 | 20; //clock source is MCLK, divisor is 20 do { _NOP(); } while(FCTL3 & 0x0001); // wait for BUSY to reset FCTL3 = FWKEY; // reset the LOCK bit to enable program/erase FCTL1 = FWKEY | WRT; // set WRT for single acces *dst = value; // do the write as a byte return; } /************************************************************************************************* Function : flash_eraseFLASH Parameter : *seg : any address within the FLASH page that is to be erased Date : 08.09.2001 / 19.11.2002 Description : this function erases a FLASH page *************************************************************************************************/ void flash_eraseFLASH(BYTE *seg) { FCTL2 = FWKEY | FSSEL0 | 20; //clock source is MCLK, divisor is 20 do { _NOP(); } while(FCTL3 & 0x0001); // wait for BUSY to reset FCTL3 = FWKEY; // reset the LOCK bit to enable program/erase FCTL1 = FWKEY | ERASE; // set single segment erase function *seg = 0xFF; // do a dummy write to start erase FCTL3 = FWKEY | LOCK; // lock the flash again return; } // End of functions from http://msp430.info // END of FLASH related functions // Data acquisition void take_data(struct bubbl *data) { TACTL = TACTL | TACLR | MC_2; // reset Timer A for (int i = 1; i < DATA - 1; i++) { // Take data for DATA * records // Pull down P4 port P4.0 to 0 V, which controls 2 MAXIM 4674 multiplexers. // 8 switches of 2 MAXIM 4674 connect 8 channels of MSP430F1611 inputs to first 8 channels of bubblers, #1 - #8. TBCCTL0 = TBCCTL0 & 0x0; //P4.0 pin down // TBCCTL1 = TBCCTL1 & 0x0; //P4.1 pin down // TBCCTL2 = TBCCTL2 & 0x0; //P4.2 pin down // TBCCTL3 = TBCCTL3 & 0x0; //P4.3 pin down data[i].time = TAR; // Get time // Choose the input channel for each ADC12MEM. I only can make 8 channels work in sequence of channels mode for now. ADC12MCTL0 = ADC12MCTL0 | INCH_0 | CSTARTADD_0; // start from the first channel ADC12MCTL1 = ADC12MCTL1 | INCH_1; // setup the correlation between input channel and ADC12MCTLx ADC12MCTL2 = ADC12MCTL2 | INCH_2; ADC12MCTL3 = ADC12MCTL3 | INCH_3; ADC12MCTL4 = ADC12MCTL4 | INCH_4; ADC12MCTL5 = ADC12MCTL5 | INCH_5; ADC12MCTL6 = ADC12MCTL6 | INCH_6; ADC12MCTL7 = ADC12MCTL7 | INCH_7 | EOS; ADC12CTL0 = ADC12CTL0 & (0xFFFF - ENC); // Disable conversions ADC12CTL0 = ADC12CTL0 | MSC; // set multiple conversion ADC12CTL1 = ADC12CTL1 | CONSEQ_1; // set sequence-of-channels mode ADC12IE = 0x07; // set the converstion interrupt flag on for the last channel #7 ADC12CTL0 = ADC12CTL0 | ENC; // Enable conversions ADC12CTL0 = ADC12CTL0 | ADC12SC; // Start conversion while (!(ADC12IFG & 1)) ; // Wait for conversion to finish // record them into structure data data[i].chan8 = ADC12MEM0; data[i].chan7 = ADC12MEM1; data[i].chan6 = ADC12MEM2; data[i].chan5 = ADC12MEM3; data[i].chan4 = ADC12MEM4; data[i].chan3 = ADC12MEM5; data[i].chan2 = ADC12MEM6; data[i].chan1 = ADC12MEM7; ADC12IFG = 0x00; // reset the flag // Pull up P4 port P4.0 to 2.75V that controls 2 MAXIM 4674 analog multiplexers. // Connect MSP430F1611 8 inputs to channel 9-16 of bubblers. TBCCTL0 = TBCCTL0 | OUT; //P4.0 pin up // TBCCTL1 = TBCCTL1 | OUT; //P4.1 pin up // TBCCTL2 = TBCCTL2 | OUT; //P4.2 pin up // TBCCTL3 = TBCCTL3 | OUT; //P4.3 pin up // Choose the input channel for each ADC12MEM. I only can make 8 channels work in sequence of channels mode for now. ADC12MCTL0 = ADC12MCTL0 | INCH_0 | CSTARTADD_0; // start from the first channel ADC12MCTL1 = ADC12MCTL1 | INCH_1; // setup the correlation between input channel and ADC12MCTLx ADC12MCTL2 = ADC12MCTL2 | INCH_2; ADC12MCTL3 = ADC12MCTL3 | INCH_3; ADC12MCTL4 = ADC12MCTL4 | INCH_4; ADC12MCTL5 = ADC12MCTL5 | INCH_5; ADC12MCTL6 = ADC12MCTL6 | INCH_6; ADC12MCTL7 = ADC12MCTL7 | INCH_7 | EOS; ADC12CTL0 = ADC12CTL0 & (0xFFFF - ENC); // Disable conversions ADC12CTL0 = ADC12CTL0 | MSC; // set multiple conversion ADC12CTL1 = ADC12CTL1 | CONSEQ_1; // set sequence-of-channels mode ADC12IE = 0x07; // set the converstion interrupt flag on for the last channel #7 ADC12CTL0 = ADC12CTL0 | ENC; // Enable conversions ADC12CTL0 = ADC12CTL0 | ADC12SC; // Start conversion while (!(ADC12IFG & 1)) ; // Wait for conversion to finish data[i].chan16 = ADC12MEM0; data[i].chan15 = ADC12MEM1; data[i].chan14 = ADC12MEM2; data[i].chan13 = ADC12MEM3; data[i].chan12 = ADC12MEM4; data[i].chan11 = ADC12MEM5; data[i].chan10 = ADC12MEM6; data[i].chan9 = ADC12MEM7; ADC12IFG = 0x00; // reset the flag data[i].time1 = TAR; // get time again wait(500); } return; } void arm(void) { // Prepare for counting bubbles when arming button is pressed P6SEL = 0x36; // Setup port P6.4 for A/D ADC12CTL0 = ADC12CTL0 | SHT0_11; // Set sampling time ADC12CTL0 = ADC12CTL0 | ADC12ON; // Turn the A/D converter on ADC12CTL1 = ADC12CTL1 | SHP; // Use the sampling timer TACTL = TASSEL_1; // Select ACLK as the clock source TACTL = TACTL | ID_3; // Divide input clock by 8, in our case divide ACLK by 8 TACCTL0 = CM_1; // Select capture at rising edge TACCTL0 = TACCTL0 | SCS; // Synchronize the capture source TACCTL0 = TACCTL0 | CAP; // Select capture mode TACTL = TACTL | TACLR | MC_2; // Timer_A clear. Setting this bit resets TAR, the TACLK (not ACLK) divider // Continuous mode: the timer counts up to 0FFFFh return; } // Initialize the crystal-oscillator-driven clocks void init_clock(void) { BCSCTL1 = XT2OFF; // Turn XT2 off, select LF for crystal (watch crystal, 2^15Hz), and no divider for ACLK wait(5000) ; // Delay 0.523 s to ensure start of the oscillator return; } // Initialize ports P1 and P4, P1 for ADC, P4 for switch void init_io(void) { P1SEL = 0x0; // Setup P1.x for I/O P1DIR = 0xF0; // Setup P1.5-7 for output P4SEL = 0x7F; // Setup P4.1-7 for I/O P4DIR = 0x7F; // Setup P4.1-7 for output return; } // copy 8KB data (byte-by-byte) from RAM to FLASH, and update FLASH counter void copyData2Flash(int btw, struct bubbl *data) { // erase 16 flash segments (==8KB) BYTE *addr_seg, *addr_byte; for (int i=0; i<16; i++) { addr_seg=(BYTE *)(FLASH_DATA + (0x2000*(COUNT-1)) + (0x0200*i)); if (addr_seg > (BYTE *)0xEFFF) //No more fresh memory above 0xFFFF BYTE { for(int i=0; i<20; i++){ display(6,500); display(9,300); } return; //program stops } else flash_eraseFLASH(addr_seg); //Otherwise it is OK, erase the fresh memory } display(5,100); //flash 1-st and 3-rd lights briefly to indicate flash memory ready. int displayValue = 5; for (int i=0; i= (BYTE)(0x01) && COUNT <= (BYTE)(0x05)) return(1); else {for(int i=0; i<10; i++){ display(15,5000); display(0,3000); } return(0); } } int main(void) { WDTCTL = WDTPW + WDTHOLD ; // Give the password, and stop the watchdog timer __disable_interrupt(); // Disable all maskable interrupts init_io(); // Initialize ports P1.4:7 for I/O and P4.0:3 for switches init_clock(); // Initialize the crystal-oscillator-driven clocks U1TXBUF = 0xB4; // COUNT = 1; // for(int k=0; k<4; k++){ for(int i=0; i<10; i++){ display(15,1000); // flesh all 4 lights display(0,1000); } display(1,5000); arm(); // prepare to acquire data display(4,5000); struct bubbl *data = init_data(); // Allocate and initialize RAM for data storage display(0,1000); display(7,5000); take_data(data); // acquire samples // write fresh memory int chk= checkFlashCount(); // check which iteration is this, deciding whether to proceed with data acquisition & flash copy COUNT = 1; for(int i=0; i<10; i++){ display(COUNT,1000); display(0,1000); } display(3,2000); if (chk==1){ display(4,2000); int nBytesToWrite = DATA*BYTES_PER_RECORD; copyData2Flash(nBytesToWrite,data); // Copy over data to Flash } for(int i=0; i<8; i++){ display(i,20000); display(0,1000); } // } display(15,20000); // Signal PC ready to transfer data via UART port, CGL 2/29/2008 /* volatile unsigned int i; WDTCTL = WDTPW + WDTHOLD; // Stop WDT BCSCTL1 |= XTS; // ACLK= LFXT1 = HF XTAL do { IFG1 &= ~OFIFG; // Clear OSCFault flag for (i = 0xFF; i > 0; i--); // Time for flag to set } while ((IFG1 & OFIFG)); // OSCFault flag still set? */ U1TXBUF = 0xA3; // set U1TXBUF all 8 bits 1 to signal PC the data is ready to go display(1,20000); BCSCTL2 |= SELM_3; // MCLK= LFXT1 (safe) // P5SEL |= 0xA; // P5.1,3 SPI option select // P5DIR |= 0xB; // P5.0,1,3 output direction // P5OUT &= ~0x1; // FS reset ME2 |= USPIE1; // Enable USART1 SPI U1CTL |= CHAR + SYNC + MM; // 8-bit SPI Master **SWRST** U1TCTL = CKPH + CKPL + SSEL0 + STC; // Inv. delayed, ACLK, 3-pin U1BR0 = 0x2; // ACLK/2 for baud rate U1BR1 = 0x0; // ACLK/2 for baud rate U1MCTL = 0x0; // Clear modulation U1CTL &= ~SWRST; // Initialize USART state machine CCTL0 = CCIE; // CCR0 interrupt enabled CCR0 = 111; // Clock period of CCR0 TACTL = TASSEL_1 + MC_1; // ACLK, Up-mode // U1RXBUF = 0xFF; if (U1TXBUF == 0xA3){ display(7,20000); display(0,20000); display(15,20000); } // U1TXBUF is set as 0xFF, waiting for PC to read, CGL 2/29/2008 /* // CGL following two lines 2/29/2008 WDTCTL = WDTPW + WDTNMI ; // Give the password, and select the function for the RST/NMI pin, same as press the buttun RUN __enable_interrupt(); // enable all maskable interrupts */ return 0; }