diff --git a/DeviceAdapters/Arduino/Arduino.cpp b/DeviceAdapters/Arduino/Arduino.cpp index a593b16fa..f7b75f07e 100644 --- a/DeviceAdapters/Arduino/Arduino.cpp +++ b/DeviceAdapters/Arduino/Arduino.cpp @@ -29,7 +29,9 @@ const char* g_DeviceNameArduinoDA1 = "Arduino-DAC1"; const char* g_DeviceNameArduinoDA2 = "Arduino-DAC2"; const char* g_DeviceNameArduinoInput = "Arduino-Input"; const char* g_DeviceNameArduinoMagnifier = "Arduino-Magnifier"; - +// Define a new device name +const char* g_DeviceNameArduinoDA3 = "Arduino-DAC3"; +const char* g_DeviceNameArduinoDA4 = "Arduino-DAC4"; // Global info about the state of the Arduino. This should be folded into a class const int g_Min_MMVersion = 1; @@ -56,6 +58,8 @@ MODULE_API void InitializeModuleData() RegisterDevice(g_DeviceNameArduinoShutter, MM::ShutterDevice, "Shutter"); RegisterDevice(g_DeviceNameArduinoDA1, MM::SignalIODevice, "DAC channel 1"); RegisterDevice(g_DeviceNameArduinoDA2, MM::SignalIODevice, "DAC channel 2"); + RegisterDevice(g_DeviceNameArduinoDA3, MM::SignalIODevice, "DAC channel 3"); // add new + RegisterDevice(g_DeviceNameArduinoDA4, MM::SignalIODevice, "DAC channel 4"); // add new RegisterDevice(g_DeviceNameArduinoInput, MM::GenericDevice, "ADC"); RegisterDevice(g_DeviceNameArduinoMagnifier, MM::MagnifierDevice, "Magnifier(needs ADC)"); } @@ -85,6 +89,14 @@ MODULE_API MM::Device* CreateDevice(const char* deviceName) { return new CArduinoDA(2); // channel 2 } + else if (strcmp(deviceName, g_DeviceNameArduinoDA3) == 0) // add new + { + return new CArduinoDA(3); // channel 3 + } + else if (strcmp(deviceName, g_DeviceNameArduinoDA4) == 0) // add new + { + return new CArduinoDA(4); // channel 4 + } else if (strcmp(deviceName, g_DeviceNameArduinoInput) == 0) { return new CArduinoInput; @@ -358,6 +370,8 @@ int CArduinoHub::DetectInstalledDevices() peripherals.push_back(g_DeviceNameArduinoMagnifier); peripherals.push_back(g_DeviceNameArduinoDA1); peripherals.push_back(g_DeviceNameArduinoDA2); + peripherals.push_back(g_DeviceNameArduinoDA3); + peripherals.push_back(g_DeviceNameArduinoDA4); for (size_t i=0; i < peripherals.size(); i++) { MM::Device* pDev = ::CreateDevice(peripherals[i].c_str()); @@ -1120,7 +1134,7 @@ CArduinoDA::CArduinoDA(int channel) : volts_(0.0), gatedVolts_(0.0), channel_(channel), - maxChannel_(2), + maxChannel_(4), gateOpen_(true) { InitializeDefaultErrorMessages(); @@ -1145,8 +1159,25 @@ CArduinoDA::CArduinoDA(int channel) : CPropertyAction* pAct = new CPropertyAction(this, &CArduinoDA::OnMaxVolt); CreateProperty("MaxVolt", "5.0", MM::Float, false, pAct, true); - name_ = channel_ == 1 ? g_DeviceNameArduinoDA1 : g_DeviceNameArduinoDA2; - + switch (channel_) { + case 1: + name_ = g_DeviceNameArduinoDA1; + break; + case 2: + name_ = g_DeviceNameArduinoDA2; + break; + case 3: + name_ = g_DeviceNameArduinoDA3; + break; + case 4: + name_ = g_DeviceNameArduinoDA4; + break; + default: + // 如果channel_不在1到4之间,可以设置一个默认值或处理错误 + name_ = "UnknownChannel"; + break; + } + // Description int nRet = CreateProperty(MM::g_Keyword_Description, "Arduino DAC driver", MM::String, true); assert(DEVICE_OK == nRet); @@ -1960,3 +1991,4 @@ void ArduinoInputMonitorThread::Start() activate(); } + diff --git a/arduino_test_4channe_copy_20241120151555.ino b/arduino_test_4channe_copy_20241120151555.ino new file mode 100644 index 000000000..77e02138b --- /dev/null +++ b/arduino_test_4channe_copy_20241120151555.ino @@ -0,0 +1,509 @@ +/* + * This goal of the application is to set the digital output on pins 8-13 + * This can be accomplished in three ways. First, a serial command can directly set + * the digital output pattern. Second, a series of patterns can be stored in the + * Arduino and TTLs coming in on pin 2 will then trigger to the consecutive pattern (trigger mode). + * Third, intervals between consecutive patterns can be specified and paterns will be + * generated at these specified time points (timed trigger mode). + * + * Interface specifications: + * digital pattern specification: single byte, bit 0 corresponds to pin 8, + * bit 1 to pin 9, etc.. Bits 7 and 8 will not be used (and should stay 0). + * + * Set digital output command: 1p + * Where p is the desired digital pattern. Controller will return 1 to + * indicate succesfull execution. + * + * Get digital output command: 2 + * Controller will return 2p. Where p is the current digital output pattern + * + * Set Analogue output command: 3xvv + * Where x is the output channel (either 1 or 2), and vv is the output in a + * 12-bit significant number. + * Controller will return 3xvv: + * + * Get Analogue output: 4 + * + * + * Set digital patten for triggered mode: 5xd + * Where x is the number of the pattern (currently, 12 patterns can be stored). + * and d is the digital pattern to be stored at that position. Note that x should + * be the real number (i.e., not ASCI encoded) + * Controller will return 5xd + * + * Set the Number of digital patterns to be used: 6x + * Where x indicates how many digital patterns will be used (currently, up to 12 + * patterns maximum). In triggered mode, after reaching this many triggers, + * the controller will re-start the sequence with the first pattern. + * Controller will return 6x + * + * Skip trigger: 7x + * Where x indicates how many digital change events on the trigger input pin + * will be ignored. + * Controller will respond with 7x + * + * Start trigger mode: 8 + * Controller will return 8 to indicate start of triggered mode + * Stop triggered a 9. Trigger mode will supersede (but not stop) + * blanking mode (if it was active) + * + * Stop Trigger mode: 9 + * Controller will return 9x where x is the number of triggers received during the last + * trigger mode run + * + * Set time interval for timed trigger mode: 10xtt + * Where x is the number of the interval (currently, 12 intervals can be stored) + * and tt is the interval (in ms) in Arduino unsigned int format. + * Controller will return 10x + * + * Sets how often the timed pattern will be repeated: 11x + * This value will be used in timed-trigger mode and sets how often the output + * pattern will be repeated. + * Controller will return 11x + * + * Starts timed trigger mode: 12 + * In timed trigger mode, digital patterns as set with function 5 will appear on the + * output pins with intervals (in ms) as set with function 10. After the number of + * patterns set with function 6, the pattern will be repeated for the number of times + * set with function 11. Any input character (which will be processed) will stop + * the pattern generation. + * Controller will retun 12. + * + * Start blanking Mode: 20 + * In blanking mode, zeroes will be written on the output pins when the trigger pin + * is low, when the trigger pin is high, the pattern set with command #1 will be + * applied to the output pins. + * Controller will return 20 + * + * Stop blanking Mode: 21 + * Stops blanking mode. Controller returns 21 + * + * Blanking mode trigger direction: 22x + * Sets whether to blank on trigger high or trigger low. x=0: blank on trigger high, + * x=1: blank on trigger low. x=0 is the default + * Controller returns 22 + * + * + * Get Identification: 30 + * Returns (asci!) MM-Ard\r\n + * + * Get Version: 31 + * Returns: version number (as ASCI string) \r\n + * + * Read digital state of analogue input pins 0-5: 40 + * Returns raw value of PINC (two high bits are not used) + * + * Read analogue state of pint pins 0-5: 41x + * x=0-5. Returns analogue value as a 10-bit number (0-1023) + * + * + * + * Possible extensions: + * Set and Get Mode (low, change, rising, falling) for trigger mode + * Get digital patterm + * Get Number of digital patterns + */ + + unsigned int version_ = 2; + + // pin on which to receive the trigger (2 and 3 can be used with interrupts, although this code does not use interrupts) + int inPin_ = 2; + // to read out the state of inPin_ faster, use + int inPinBit_ = 1 << inPin_; // bit mask + + // pin connected to DIN of TLV5618 + int dataPin = 3; + // pin connected to SCLK of TLV5618 + int clockPin = 4; + // pin connected to CS of TLV5618 + + int CS1 = 5; + int CS2 = 6; + + const int SEQUENCELENGTH = 12; // this should be good enough for everybody;) + byte triggerPattern_[SEQUENCELENGTH] = {0,0,0,0,0,0,0,0,0,0,0,0}; + unsigned int triggerDelay_[SEQUENCELENGTH] = {0,0,0,0,0,0,0,0,0,0,0,0}; + int patternLength_ = 0; + byte repeatPattern_ = 0; + volatile long triggerNr_; // total # of triggers in this run (0-based) + volatile long sequenceNr_; // # of trigger in sequence (0-based) + int skipTriggers_ = 0; // # of triggers to skip before starting to generate patterns + byte currentPattern_ = 0; + const unsigned long timeOut_ = 1000; + bool blanking_ = false; + bool blankOnHigh_ = false; + bool triggerMode_ = false; + boolean triggerState_ = false; + + void setup() { + // Higher speeds do not appear to be reliable + Serial.begin(57600); + + pinMode(inPin_, INPUT); + pinMode (dataPin, OUTPUT); + pinMode (clockPin, OUTPUT); + pinMode (CS1, OUTPUT); + pinMode (CS2, OUTPUT); + pinMode(8, OUTPUT); + pinMode(9, OUTPUT); + pinMode(10, OUTPUT); + pinMode(11, OUTPUT); + pinMode(12, OUTPUT); + pinMode(13, OUTPUT); + + // Set analogue pins as input: + DDRC = DDRC & B11000000; + // Turn on build-in pull-up resistors + PORTC = PORTC | B00111111; + + digitalWrite(CS1, HIGH); + digitalWrite(CS2, HIGH); + } + + void loop() { + if (Serial.available() > 0) { + int inByte = Serial.read(); + switch (inByte) { + + // Set digital output + case 1 : + if (waitForSerial(timeOut_)) { + currentPattern_ = Serial.read(); + // Do not set bits 6 and 7 (not sure if this is needed..) + currentPattern_ = currentPattern_ & B00111111; + if (!blanking_) + PORTB = currentPattern_; + Serial.write( byte(1)); + } + break; + + // Get digital output + case 2: + Serial.write( byte(2)); + Serial.write( PORTB); + break; + + // Set Analogue output (TODO: save for 'Get Analogue output') + case 3: + if (waitForSerial(timeOut_)) { + int channel = Serial.read(); + if (waitForSerial(timeOut_)) { + byte msb = Serial.read(); + msb &= B00001111; + if (waitForSerial(timeOut_)) { + byte lsb = Serial.read(); + analogueOut(channel, msb, lsb); + Serial.write( byte(3)); + Serial.write( channel); + Serial.write(msb); + Serial.write(lsb); + } + } + } + break; + + // Sets the specified digital pattern + case 5: + if (waitForSerial(timeOut_)) { + int patternNumber = Serial.read(); + if ( (patternNumber >= 0) && (patternNumber < SEQUENCELENGTH) ) { + if (waitForSerial(timeOut_)) { + triggerPattern_[patternNumber] = Serial.read(); + triggerPattern_[patternNumber] = triggerPattern_[patternNumber] & B00111111; + Serial.write( byte(5)); + Serial.write( patternNumber); + Serial.write( triggerPattern_[patternNumber]); + break; + } + } + } + Serial.write( "n:");//Serial.print("n:"); + break; + + // Sets the number of digital patterns that will be used + case 6: + if (waitForSerial(timeOut_)) { + int pL = Serial.read(); + if ( (pL >= 0) && (pL <= 12) ) { + patternLength_ = pL; + Serial.write( byte(6)); + Serial.write( patternLength_); + } + } + break; + + // Skip triggers + case 7: + if (waitForSerial(timeOut_)) { + skipTriggers_ = Serial.read(); + Serial.write( byte(7)); + Serial.write( skipTriggers_); + } + break; + + // starts trigger mode + case 8: + if (patternLength_ > 0) { + sequenceNr_ = 0; + triggerNr_ = -skipTriggers_; + triggerState_ = digitalRead(inPin_) == HIGH; + PORTB = B00000000; + Serial.write( byte(8)); + triggerMode_ = true; + } + break; + + // return result from last triggermode + case 9: + triggerMode_ = false; + PORTB = B00000000; + Serial.write( byte(9)); + Serial.write( triggerNr_); + break; + + // Sets time interval for timed trigger mode + // Tricky part is that we are getting an unsigned int as two bytes + case 10: + if (waitForSerial(timeOut_)) { + int patternNumber = Serial.read(); + if ( (patternNumber >= 0) && (patternNumber < SEQUENCELENGTH) ) { + if (waitForSerial(timeOut_)) { + unsigned int highByte = 0; + unsigned int lowByte = 0; + highByte = Serial.read(); + if (waitForSerial(timeOut_)) + lowByte = Serial.read(); + highByte = highByte << 8; + triggerDelay_[patternNumber] = highByte | lowByte; + Serial.write( byte(10)); + Serial.write(patternNumber); + break; + } + } + } + break; + + // Sets the number of times the patterns is repeated in timed trigger mode + case 11: + if (waitForSerial(timeOut_)) { + repeatPattern_ = Serial.read(); + Serial.write( byte(11)); + Serial.write( repeatPattern_); + } + break; + + // starts timed trigger mode + case 12: + if (patternLength_ > 0) { + PORTB = B00000000; + Serial.write( byte(12)); + for (byte i = 0; i < repeatPattern_ && (Serial.available() == 0); i++) { + for (int j = 0; j < patternLength_ && (Serial.available() == 0); j++) { + PORTB = triggerPattern_[j]; + delay(triggerDelay_[j]); + } + } + PORTB = B00000000; + } + break; + + // Blanks output based on TTL input + case 20: + blanking_ = true; + Serial.write( byte(20)); + break; + + // Stops blanking mode + case 21: + blanking_ = false; + Serial.write( byte(21)); + break; + + // Sets 'polarity' of input TTL for blanking mode + case 22: + if (waitForSerial(timeOut_)) { + int mode = Serial.read(); + if (mode==0) + blankOnHigh_= true; + else + blankOnHigh_= false; + } + Serial.write( byte(22)); + break; + + // Gives identification of the device + case 30: + Serial.println("MM-Ard"); + break; + + // Returns version string + case 31: + Serial.println(version_); + break; + + case 40: + Serial.write( byte(40)); + Serial.write( PINC); + break; + + case 41: + if (waitForSerial(timeOut_)) { + int pin = Serial.read(); + if (pin >= 0 && pin <=5) { + int val = analogRead(pin); + Serial.write( byte(41)); + Serial.write( pin); + Serial.write( highByte(val)); + Serial.write( lowByte(val)); + } + } + break; + + case 42: + if (waitForSerial(timeOut_)) { + int pin = Serial.read(); + if (waitForSerial(timeOut_)) { + int state = Serial.read(); + Serial.write( byte(42)); + Serial.write( pin); + if (state == 0) { + digitalWrite(14+pin, LOW); + Serial.write( byte(0)); + } + if (state == 1) { + digitalWrite(14+pin, HIGH); + Serial.write( byte(1)); + } + } + } + break; + + } + } + + // In trigger mode, we will blank even if blanking is not on.. + if (triggerMode_) { + boolean tmp = PIND & inPinBit_; + if (tmp != triggerState_) { + if (blankOnHigh_ && tmp ) { + PORTB = 0; + } + else if (!blankOnHigh_ && !tmp ) { + PORTB = 0; + } + else { + if (triggerNr_ >=0) { + PORTB = triggerPattern_[sequenceNr_]; + sequenceNr_++; + if (sequenceNr_ >= patternLength_) + sequenceNr_ = 0; + } + triggerNr_++; + } + + triggerState_ = tmp; + } + } else if (blanking_) { + if (blankOnHigh_) { + if (! (PIND & inPinBit_)) + PORTB = currentPattern_; + else + PORTB = 0; + } else { + if (! (PIND & inPinBit_)) + PORTB = 0; + else + PORTB = currentPattern_; + } + } +} + + +bool waitForSerial(unsigned long timeOut) +{ + unsigned long startTime = millis(); + while (Serial.available() == 0 && (millis() - startTime < timeOut) ) {} + if (Serial.available() > 0) + return true; + return false; + } + +// Sets analogue output in the TLV5618 +// channel is either 0 ('A') or 1 ('B') +// value should be between 0 and 4095 (12 bit max) +// pins should be connected as described above +void analogueOut(int channel, byte msb, byte lsb) +{ + // Select DAC + + // Configure Channel + msb &= B00001111; + if (channel == 0){ + digitalWrite(CS1, LOW); // Activate DAC 1 + digitalWrite(CS2, HIGH); // + msb |= B10000000; // + } + else if (channel == 1){ + digitalWrite(CS1, LOW); // Activate DAC 2 + digitalWrite(CS2, HIGH); // + msb &= B00001111; // + } + // Alternative Channel Selection Logic + else if (channel == 2){ + digitalWrite(CS1, HIGH); // Activate DAC 3 + digitalWrite(CS2, LOW); // + msb |= B10000000; // + } + else if (channel == 3){ + digitalWrite(CS1, HIGH); // Activate DAC 4 + digitalWrite(CS2, LOW); // + msb &= B00001111; // + } + // send data + shiftOut(dataPin, clockPin, MSBFIRST, msb); + shiftOut(dataPin, clockPin, MSBFIRST, lsb); + + // End Transmission + digitalWrite(clockPin, HIGH); + digitalWrite(clockPin, LOW); + + // Deactivate all DACs + digitalWrite(CS1, HIGH); + digitalWrite(CS2, HIGH); +} + + + +/* + // This function is called through an interrupt +void triggerMode() +{ + if (triggerNr_ >=0) { + PORTB = triggerPattern_[sequenceNr_]; + sequenceNr_++; + if (sequenceNr_ >= patternLength_) + sequenceNr_ = 0; + } + triggerNr_++; +} + + +void blankNormal() +{ + if (DDRD & B00000100) { + PORTB = currentPattern_; + } else + PORTB = 0; +} + +void blankInverted() +{ + if (DDRD & B00000100) { + PORTB = 0; + } else { + PORTB = currentPattern_; + } +} + +*/ +