// ================================================================================================================================= // Xcontrol // 2009/12/xx // (c) Mark Sadgrove mark@ils.uec.ac.jp or mark.sadgrove@gmail.com // You may use and copy this code under the terms of the Gnu General Public License (GPL) 3.0 // // This project was developed with the support of Professor Ken'ichi Nakagawa // at the Institute for Laser Science, The University of Electro-communications, Chofu, Tokyo, Japan. // osewa ni natte itadaite arigatou gozaimasu. // // PURPOSE: This arduino sketch sets up the Arduino Mega (henceforth "the Mega") (based on the Atmel Atmega 1280 microcontroller) // to act as a configurable Analog 14 + Digital 18 = 32 output controller for experiments. // To provide context, this project was developed specifically to control a Rubidium // magneto optical trap and to run atom-interferometer type experiments. The Mega is set up to provide // both DC operation and experimental sequencing. The sequencing is timed by a Timer overflow interrupt, // about which more is explained in the code below. // Why are only 32 out of the 50 plus outputs used? Partially because I wanted to create a low profile // control box and 32 was the maximum number of BNC connectors I could fit in! Also, the sequencing // part of this project needs to change the output level of possibly all the outputs within about 30 micro-seconds, // which is quite a difficult task with a 16MHz microcontroller, even with low level writes to the output ports. // // HARDWARE: This code was devloped with a specific hardware context in mind! Specifically, the // arduino outputs were connected to opto-couplers to isolate the arduino ground and supply voltages // from those of the various devices you are controlling with the Mega. One particular point to // keep in mind is that the output stage I used inverted the logic, which is why I defined OUTHIGH and OUTLOW. // If your hardware doesn't invert the arduino output, then just change these two variables. // // COMMENTS: If you're a whiz at microcontrollers and can program them in assembly code, skip these comments // and be prepared to sneer at my code and/or comments. I'd be grateful if you could pass any comments and improvements on to me. // If you're a scientist with a modest computer science background and some programming experience, you may // feel in over your head in the register bit setting parts of the code. If you want to understand this // code properly you NEED to download the (rather large) datasheet for the Atmel Atmega 1280 (henceforth "the datasheet") and at least // refer to the pages that I've referenced in the code to appreciate what is going on. The register names // are cryptic and the assignment of functions to bit values in the registers is essentially arbitrary, // so no amount of programming experience will allow you to understand the code without looking at // the datasheet for yourself. // // - Mark Sadgrove // ================================================================================================================================ #include "pins_arduino.h" // If I don't put the include here, some of the functions I need aren't available within this module. //---------------------------------------------------------------------------------- // // Declarations // //---------------------------------------------------------------------------------- // Macros #define clearbit(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // For the register sfr, clears the bit at position "bit" #define setbit(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // For the register sfr, sets the bit at position "bit" // The _BV(bit) macro converts the value given by bit into a string of zeros with a 1 at the location given by the argument "bit", // e.g. BV(2) = 0000 0100 // sbi allows us to set a specific bit of the register sfr by creating the appropriate bit string as above and then bitwise or-ing // the result with the register itself. Here is an example: // sfr = 1001 0010 // say we want to set the bit in position 2: // sbi(sfr,2) = 1001 0010 |0000 0100 = 1001 0110 // You can follow the same reasoning to see how cbi clears the bit at position "bit" // of the register sfr // Uncomment the following line if you would like debug messages, experiment information to be printed to serial //#define TEXTDEBUG 1 // Print debug messages to the serial port (The extra strings take up a lot of space!) // DDS programming definitions #define DDSMAXF 180000000 // For DDS device AD9851, the maximum frequency is the clock freq fclk=180MHz #define DDSFLIM 60000000 // Limit frequencies to 60 MHz. AD9851 can go up to approx fout = fclk/3 #define DPOS 13 // Offset for digital pins in the OutPin array (see below) #define LMAX 4294967295 //=2^32-1 // Data types, etc struct Event { unsigned long etime; // NB: Unsigned long is overkill for times BUT unsigned int is too short! byte Npins; // Changes only occur for 0 13){ // It's a digital pin. uint8_t bit = digitalPinToBitMask(OutPin[pin]); uint8_t port = digitalPinToPort(OutPin[pin]); volatile uint8_t *out; // Yes, the code below is just stripped from wiring_digital.c // (don't worry, it's all GPL!) // with the checking thrown out to save cycles. Since the pin you set is arbitrary, // I can't see a faster way to do this stuff than to use // the macros and functions provided in wiring which // are already included at link time anyway. out = portOutputRegister(port); if (val == LOW) *out &= ~bit; else *out |= bit; } else{ // Analog pin //Serial.println("a"); // The code below is stripped from wiring_analog.c, // with the register setting removed (it's put in in the setup function instead). // Believe it or not, there doesn't seem to be a shorter way to do this! // I tried making an array to convert pins to OC registers and copy the // form of the digital pin setting code above, // but some of the OCRs seem to have different // length address pointers so I couldn't group them into a // single array! If anyone can help shorten the analog // writing routine below, I'd be grateful. byte opin = OutPin[pin]; if (digitalPinToTimer(opin) == TIMER1A) { // Set pwm duty OCR1A = val; } else if (digitalPinToTimer(opin) == TIMER1B) { OCR1B = val; } else if (digitalPinToTimer(opin) == TIMER0A) { //if (val == 0) { // digitalWrite(pin, LOW); //} //else { // set pwm duty OCR0A = val; //} } else if (digitalPinToTimer(opin) == TIMER0B) { //if (val == 0) { // digitalWrite(pin, LOW); //} //else { // set pwm duty OCR0B = val; //} } else if (digitalPinToTimer(opin) == TIMER2A) { // set pwm duty OCR2A = val; } else if (digitalPinToTimer(opin) == TIMER2B) { // set pwm duty OCR2B = val; } else if (digitalPinToTimer(opin) == TIMER3A) { // set pwm duty OCR3A = val; } else if (digitalPinToTimer(opin) == TIMER3B) { // set pwm duty OCR3B = val; } else if (digitalPinToTimer(opin) == TIMER3C) { // set pwm duty OCR3C = val; } else if (digitalPinToTimer(opin) == TIMER4A) { // set pwm duty OCR4A = val; } else if (digitalPinToTimer(opin) == TIMER4B) { // set pwm duty OCR4B = val; } else if (digitalPinToTimer(opin) == TIMER4C) { // set pwm duty OCR4C = val; } else if (digitalPinToTimer(opin) == TIMER5A) { // set pwm duty OCR5A = val; } else if (digitalPinToTimer(opin) == TIMER5B) { // set pwm duty OCR5B = val; } } } myexp.eventcount++; doNextEvent=false; } } else { //--------------------------------------------- // If no experiment is underway, read the serial port and respond to commands //--------------------------------------------- if (Serial.available()) { int opcode = Serial.read(); int b1 = Serial.read(); // Serial.read() returns an int, even though it only grabs 8 bits at a time! int b2 = Serial.read(); // Don't blame me! int b3 = Serial.read(); int b4 = Serial.read(); //#if defined(TEXTDEBUG) //Serial.println("-------"); //Serial.println("Recieved:"); //Serial.println(opcode); //#endif */ //////////////// /////////// (1) DIGITAL WRITE OR EXPERIMENTAL EVENT /////// if (opcode==100){ // 100 = ascii "d" which stands for "digital" byte pin = b1; byte value = b2; for(int k=0;k<2;k++){Serial.println(teststring);} //Serial.println("WWWWWWWWW"); // Do bound checking on input if ((pin < 1)||(pin>18)){ /*#if defined(TEXTDEBUG) Serial.println("Error: Pin out of range. Digital Pin must be between 1-18."); #endif */ return; } // NB: The following error should be impossible because value is type int if (round(value) != value) { /*#if defined(TEXTDEBUG) Serial.println("Warning: Non-integer pin output specified. Pin will be set to rounded value."); #endif */ value=round(value); } if (value < 0) { /*#if defined(TEXTDEBUG) Serial.println("Error: Specified pin output < 0. Pin will not be changed."); #endif */ return; } if (value > 1) { /*#if defined(TEXTDEBUG) Serial.println("Warning: Specified pin output > 1. Pin will be set high."); #endif */ value = 1; } // Digital write. Remember: This code is written for a SPECIFIC // isolation output stage connected to the arduino. Because the opto-isolators // use a "pull up" configuration, the logic is inverted. Thus, we invert the // user specified value so that the actual output is what the user intended. value = 1 - value; // Invert if (!AddEvents){ /*#if defined(TEXTDEBUG) Serial.println("Writing to digital pin..."); Serial.println("---------------------"); #endif */ digitalWrite(OutPin[pin+13], value); } else{ // Create an event in the experiment /*#if defined(TEXTDEBUG) Serial.println("Adding digital pin event..."); Serial.println("---------------------"); #endif */ myexp.events[myexp.Nevents].pins[myexp.events[myexp.Nevents].Npins] = pin+13;//OutPin[pin+13]; //NB: we use the pin index not the physical pin for events myexp.events[myexp.Nevents].vals[myexp.events[myexp.Nevents].Npins] = value; myexp.events[myexp.Nevents].Npins++; /*#if defined(TEXTDEBUG) Serial.print("Number of event: "); Serial.println(myexp.Nevents); Serial.print("Number of pins to change so far: "); Serial.println(myexp.events[myexp.Nevents].Npins); Serial.print("Physical number of pin just added: "); Serial.println(myexp.events[myexp.Nevents].pins[myexp.events[myexp.Nevents].Npins-1]); #endif */ } } //////////////// /////////// (2) ANALOG WRITE OR EXPERIMENTAL EVENT /////// else if (opcode==97){ // 97 = ascii "a"... int pin = b1; int value = b2; for(int k=0;k<2;k++){Serial.println(teststring);}; // Do bound checking on input if ((pin < 1)||(pin>14)){ /*#if defined(TEXTDEBUG) Serial.println("Error: Pin out of range. Analog Pin must be between 1-14."); #endif */ return; } // NB: The following error should be impossible because value is type int if (round(value) != value) { /*#if defined(TEXTDEBUG) Serial.println("Warning: Non-integer pin output specified. Pin will be set to rounded value."); #endif */ value=round(value);} if (value < 0) { /*#if defined(TEXTDEBUG) Serial.println("Error: Specified pin output < 0 Pin will not be changed."); #endif */ return;} if (value > 255) { /*#if defined(TEXTDEBUG) Serial.println("Warning: Specified pin output > 255. Pin will be set high."); #endif */ value = 255;} // Analog write. Once again, because of the output stage logic inversion, we invert // the user value before writing value = 255 - value; // Invert if (!AddEvents){ // Send the value to the appropriate pin /*#if defined(TEXTDEBUG) Serial.println("Writing to analog pin..."); Serial.println("---------------------"); #endif */ analogWrite(OutPin[pin-1],value); } else { // Create an event in the experiment /*#if defined(TEXTDEBUG) Serial.println("Adding analog pin event..."); Serial.println("---------------------"); #endif */ myexp.events[myexp.Nevents].pins[myexp.events[myexp.Nevents].Npins] = pin-1;//; OutPin[pin-1];// NB We use the pin index, not the physical pin number for events myexp.events[myexp.Nevents].vals[myexp.events[myexp.Nevents].Npins] = value; myexp.events[myexp.Nevents].Npins++; /*#if defined(TEXTDEBUG) Serial.print("Number of event: "); Serial.println(myexp.Nevents); Serial.print("Number of pins to change so far: "); Serial.println(myexp.events[myexp.Nevents].Npins); Serial.print("Physical number of pin just added: "); Serial.println(myexp.events[myexp.Nevents].pins[myexp.events[myexp.Nevents].Npins-1]); #endif */ } } //////////////// /////////// (3) ENTER ADD EXPERIMENTAL EVENT MODE /////// else if (opcode==101){ // 101 = ascii "e"... // Set event add flag so that future "a" and "d" opcodes write events // to this time code (e.g. eventtime) rathyer than setting the output // Here's the main problem: Serial.read() only grabs a single byte. // It's very easy to change this in the source code (see hardware/libraries/SoftwareSerial.cpp) // BUT that would break this code for eveyone with a normal arduino install. Adding // in a new library like Messenger would work, but that increases the already bloated // memory overhead of this code! So I do things the hardway: read 4 bytes, stitch together // an unsigned long from that!!! unsigned long ulb1 = b1; unsigned long ulb2 = b2; unsigned long ulb3 = b3; unsigned long ulb4 = b4; //unsigned long eventtime = b4<<24 + b3<<16 + b2<<8 + b1; unsigned long eventtime = ulb4*16777216L + ulb3*65536L + ulb2*256L + ulb1*1L; //#if defined(TEXTDEBUG) Serial.print(">>>>>>>>>> Reconstructed event time: "); Serial.println(eventtime); Serial.println(ulb1); Serial.println(ulb2); Serial.println(ulb3); Serial.println(ulb4); Serial.println(">>>>>>>>>>>>>>>>>"); //#endif */ for(int k=0;k<2;k++){Serial.println(teststring);}; if (eventtime>TimeLim){ /*#if defined(TEXTDEBUG) Serial.println("Error: Specified event time greater than time limit."); #endif */ return; } if (!AddEvents) { // Ignore if we're already adding! myexp.events[myexp.Nevents].etime = eventtime; // Initialise pin count myexp.events[myexp.Nevents].Npins=0; /*#if defined(TEXTDEBUG) Serial.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); Serial.println("Entered event creation mode"); Serial.print("Preliminary number of pins set to "); Serial.println(myexp.events[myexp.Nevents].Npins); #endif */ AddEvents = true; } } //////////////// /////////// (4) FINISH (i.e. LEAVE) ADD EXPERIMENTAL EVENT MODE /////// else if (opcode==102){ // 101 = ascii "f"... for(int k=0;k<2;k++){Serial.println(teststring);}; // Finish the current add event if (AddEvents) { AddEvents = false; myexp.Nevents++; /*#if defined(TEXTDEBUG) Serial.println("Exited event creation mode "); Serial.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); #endif */ } } //////////////// /////////// (5) PRINT INFORMATION ON EXPERIMENT TO SERIAL PORT /////// else if (opcode==112){ // 112 = ascii "p"... for(int k=0;k<2;k++){Serial.println(teststring);}; // Print the experimental contents to serial /*#if defined(TEXTDEBUG) int m = 0; int l = 0; Serial.println("+++++++++++++++++++++++++++++++++++++++"); Serial.println("Current experiment"); Serial.println("+++++++++++++++++++++++++++++++++++++++"); Serial.print("Total number of events: "); Serial.println(myexp.Nevents); Serial.println("Event listing:"); for (l=0;l>>>>>>>>>"); Serial.print("Event: "); Serial.print(l+1); Serial.println(""); Serial.print("Event time: "); Serial.println(myexp.events[l].etime); Serial.print("Number of pins changed: "); Serial.println(myexp.events[l].Npins); for (m=0;m> and << operators don't work properly past // 16 bits! This made shifting the 32bit frequency word into the AD9851 register // annoyingly. // By requiring the user to supply an appropriately scaled 32 bit frequency programming word // for the AD9851 instead of a frequency in Hz, we also get as an added bonus the word already split up // into 4 bytes ulb1...ulb4. This means we can just shift in a byte at a time, which may be // a little clunky, but sure is a lot easier on this fundamentally 8-bit processor! // To convert a frequency fHz given in Hz to a frequency programming word for the AD9851, use the formula: // fprog = round(fHz/180MHz * (2^32-1)) unsigned long ulb1 = b1; unsigned long ulb2 = b2; unsigned long ulb3 = b3; unsigned long ulb4 = b4; //unsigned long eventtime = b4<<24 + b3<<16 + b2<<8 + b1; // The reason that you can't use the method in the commented line above, // instead of the clunky method below, is that x<14! unsigned long ddsfreq = ulb4*16777216L + ulb3*65536L + ulb2*256L + ulb1*1L; //#if defined(TEXTDEBUG) Serial.print(">>>>>>>>>> Reconstructed freq: "); Serial.println(ddsfreq); Serial.println(ulb1); Serial.println(ulb2); Serial.println(ulb3); Serial.println(ulb4); Serial.println(">>>>>>>>>>>>>>>>>"); //#endif */ for(int k=0;k<2;k++){Serial.println(teststring);}; // Now we have to convert the frequency into a 32 bit // binary number which can be used to program the DDS. // The AD9851 has a maximum frequency of 180MHz corresponding // to the maximum frequency word N=(2^32-1). To program a frequency // lower than 180MHz, we find the ratio between the desired frequency and // 180MHz and multiply by 2^32 //if (ddsfreq>DDSFLIM) {ddsfreq = DDSFLIM;} //double temp = (double)ddsfreq/(double)DDSMAXF*LMAX; //unsigned long fprog = round(temp); // This is NOT good practice in principle since the // result can overflow the unsigned long variable! // However, due to the limit applied to ddsfreq, // there should never actually be an overflow here... //Serial.print("fprog: "); //Serial.println(fprog,BIN); byte W0 = 0b00001001; // This is the phase/powerdown/Serial/freq multiplier word. Please see the AD9851 datasheet, page 14 // This setting is for Phase=11.25deg, 6*REFCLK multiplier, Powered up mode // Program the device // STEP 1: RESET // The AD9851 is usually programmed in parallel mode but we only have access to it in serial mode. // This is a design choice made when building the circuit housing the AD9851, but it is a good choice, // since it reduces the number of outputs necessary by a factor of 4. // However, the default programming mode for the AD9851 is parallel and we have to write some bits in // parallel mode in order to enter serial mode. // Luckily, the necessary bits can be set at a hardware level (see Fig. 18, page 15 of AD9851 datasheet) // so that serial mode is always entered after a reset. We rely on this hard-wiring of the initial state // in order for the following code to work. digitalWrite(pRESET,OUTHIGH); delayMicroseconds(10); // We just want the minimum delay. The minimum time for the reset pulse is actually 50ns so even the // shortest delay we can achieve is overkill! Probably we can remove the delay function, since the // arduino clock period is ~60ns digitalWrite(pRESET,OUTLOW); // Assert WCLK high to load the default data digitalWrite(pWCLK,OUTHIGH); delayMicroseconds(10); // We just want the minimum delay. The minimum time for the reset pulse is actually 50ns so even the // shortest delay we can achieve is overkill! Probably we can remove the delay function, since the // arduino clock period is ~60ns digitalWrite(pWCLK,OUTLOW); delayMicroseconds(10); // After the reset we need to assert FQUD high digitalWrite(pFQUD,OUTHIGH); delayMicroseconds(10); // We just want the minimum delay. The minimum time for the reset pulse is actually 50ns so even the // shortest delay we can achieve is overkill! Probably we can remove the delay function, since the // arduino clock period is ~60ns digitalWrite(pFQUD,OUTLOW); delayMicroseconds(10); // STEP 2: LOAD DATA // First frequency block... // Least Significant Byte for(int i=0;i<8;i++){ digitalWrite(pDATA1,((~ulb1 & _BV(i))>>i)); // Write value to pins... // Serial.println((ulb1 & _BV(i))>>i,BIN); digitalWrite(pDATA2,((~ulb1 & _BV(i))>>i)); // Write value to pins... digitalWrite(pDATA3,((~ulb1 & _BV(i))>>i)); // Write value to pins... delayMicroseconds(5); digitalWrite(pWCLK,OUTHIGH); delayMicroseconds(5); digitalWrite(pWCLK,OUTLOW); } // Second byte for(int i=0;i<8;i++){ digitalWrite(pDATA1,((~ulb2 & _BV(i))>>i)); // Write value to pins... //Serial.println((ulb2 & _BV(i))>>i,BIN); digitalWrite(pDATA2,((~ulb2 & _BV(i))>>i)); // Write value to pins... digitalWrite(pDATA3,((~ulb2 & _BV(i))>>i)); // Write value to pins... delayMicroseconds(5); digitalWrite(pWCLK,OUTHIGH); delayMicroseconds(5); digitalWrite(pWCLK,OUTLOW); } // Third byte for(int i=0;i<8;i++){ digitalWrite(pDATA1,((~ulb3 & _BV(i))>>i)); // Write value to pins... //Serial.println((ulb3 & _BV(i))>>i,BIN); digitalWrite(pDATA2,((~ulb3 & _BV(i))>>i)); // Write value to pins... digitalWrite(pDATA3,((~ulb3 & _BV(i))>>i)); // Write value to pins... delayMicroseconds(5); digitalWrite(pWCLK,OUTHIGH); delayMicroseconds(5); digitalWrite(pWCLK,OUTLOW); } // Most Significant Byte for(int i=0;i<8;i++){ digitalWrite(pDATA1,((~ulb4 & _BV(i))>>i)); // Write value to pins... //Serial.println((ulb4 & _BV(i))>>i,BIN); digitalWrite(pDATA2,((~ulb4 & _BV(i))>>i)); // Write value to pins... digitalWrite(pDATA3,((~ulb4 & _BV(i))>>i)); // Write value to pins... delayMicroseconds(5); digitalWrite(pWCLK,OUTHIGH); delayMicroseconds(5); digitalWrite(pWCLK,OUTLOW); } // ... then phase/control block for(int i=0;i<8;i++){ digitalWrite(pDATA1,((~W0 & _BV(i))>>i)); // Write value to pins... digitalWrite(pDATA2,((~W0 & _BV(i))>>i)); // Write value to pins... digitalWrite(pDATA3,((~W0 & _BV(i))>>i)); // Write value to pins... delayMicroseconds(5); digitalWrite(pWCLK,OUTHIGH); delayMicroseconds(5); digitalWrite(pWCLK,OUTLOW); } // STEP 3: Pulse FQUD delayMicroseconds(5); digitalWrite(pFQUD,OUTHIGH); delayMicroseconds(5); digitalWrite(pFQUD,OUTLOW); // Now set data line levels back to zero for sake of cleanliness! digitalWrite(pDATA1,OUTLOW); // Write value to pins... digitalWrite(pDATA2,OUTLOW); // Write value to pins... digitalWrite(pDATA3,OUTLOW); // Write value to pins... } //////////////// /////////// (8) EXECUTE EXPERIMENT /////// else if (opcode==120){ // 120 = ascii "x"... for(int k=0;k<2;k++){Serial.println(teststring);}; if (b1!=1){return;} // Execute the experimental sequence if (!myexp.ExpInProgress){ /*#if defined TEXTDEBUG Serial.println("Experiment started..."); #endif */ Serial.println("x"); myexp.ExpInProgress = true; // Set flag clearbit(TIMSK0,TOIE0); // Disable the Timer0 overflow interrupt. This means that delay() will not function // See page 194 of ATMEGA1280 datasheet for information on the TIMSK2 register and the TOIE2 bit setbit(TIMSK2,TOIE2); // Enable Timer2 overflow interrupt // Register name: TIMSK2 = Timer Interrupt Mask 2 // Bit name: TOIE2 = Timer Overflow Interrupt Enable 2 // We use this interrupt to provide // timing of events in the experiment } } //////////////// /////////// (9) SET PREMULTIPLIER /////// else if (opcode==122){ // 122 = ascii "z"... byte pin = b1; byte value = b2; for(int k=0;k<2;k++){Serial.println(teststring);}; if (value>=1) {value = 1;} else {value = 0;} PinEnable[pin] = value; } } } } //---------------------------------------------------------------------------------- // // Interrupt Service Routine: TIMER 2 OVerFlow intterupt function. // // This function is called with frequency 62.5 kHz as set // by the statement TCCR2B = TCCR2B & 0b11111000 | 0x01 // in the head of the code, which sets the prescaler division to 1. // More specifically, it is called whenever the 8 bit timer/counter register number 2 // of the Mega exceeds is incremented past its maximum of 255 (16MHz/256h = 62.5kHz). // We don't have much time to get things done here. Specifically, if the user wants to change every pin setting // in a given experimental event at the fastest possible rate, we have just 256/32=8 cycles per pin to write // each pin value, which on the AVR architecture is at most 8 instructions. Thus, we need to use // direct writes to the port registers to change the outputs wherever possible. // Note, however, that the actual port writes are offloaded to a separate function doEvent() // defined after the timer interrupt service. // // //---------------------------------------------------------------------------------- ISR(TIMER2_OVF_vect) { exptimer++; // Increment the experiment timer. This is the essential job of the interrupt service if ((exptimer-1) == myexp.events[myexp.eventcount].etime) { doNextEvent=true; } else if ((myexp.eventcount >= myexp.Nevents)||(exptimer >= TimeLim) ){ // I tried putting this clause at the end of the doEvent clause in the loop function, // but unfortunately, it didn't work. Still, not a huge amount of code to // execute here, so it's probably alright leaving it here in the interrupt handler. myexp.ExpInProgress = false; doNextEvent = false; myexp.eventcount=0; exptimer = 0; // Reset the experiment timer of course! /*#if defined(TEXTDEBUG) Serial.println("Experiment over"); #endif */ // For information on the following registers and bits, see the "Execute experiment" if clause // in the main loop function above. clearbit(TIMSK2,TOIE2); // The experiment is over so turn off the timer 2 overflow interrupt setbit(TIMSK0,TOIE0); // Allow timer0 overflow interrupts. This should turn delay() back on } }