diff --git a/DeviceAdapters/Octopi-Research/Octopi-Research.vcxproj b/DeviceAdapters/Cephla/Cephla.vcxproj similarity index 96% rename from DeviceAdapters/Octopi-Research/Octopi-Research.vcxproj rename to DeviceAdapters/Cephla/Cephla.vcxproj index 8b6d3c99c..0f0860c28 100644 --- a/DeviceAdapters/Octopi-Research/Octopi-Research.vcxproj +++ b/DeviceAdapters/Cephla/Cephla.vcxproj @@ -15,6 +15,7 @@ Octopi-Research Win32Proj 10.0 + Cephla @@ -91,7 +92,8 @@ - + + @@ -106,6 +108,9 @@ {b8c95f39-54bf-40a9-807b-598df2821d55} + + + diff --git a/DeviceAdapters/Octopi-Research/Octopi-Research.vcxproj.filters b/DeviceAdapters/Cephla/Cephla.vcxproj.filters similarity index 84% rename from DeviceAdapters/Octopi-Research/Octopi-Research.vcxproj.filters rename to DeviceAdapters/Cephla/Cephla.vcxproj.filters index 64909eb13..b2629f281 100644 --- a/DeviceAdapters/Octopi-Research/Octopi-Research.vcxproj.filters +++ b/DeviceAdapters/Cephla/Cephla.vcxproj.filters @@ -21,7 +21,7 @@ Source Files - + Source Files @@ -30,6 +30,9 @@ Source Files + + Source Files + @@ -39,4 +42,9 @@ Header Files + + + Resource Files + + \ No newline at end of file diff --git a/DeviceAdapters/Cephla/Cephla_Controller_Docs.md b/DeviceAdapters/Cephla/Cephla_Controller_Docs.md new file mode 100644 index 000000000..3266d63d5 --- /dev/null +++ b/DeviceAdapters/Cephla/Cephla_Controller_Docs.md @@ -0,0 +1,11 @@ + +# The Cephla Squid Controller +The Squid Micro-controller by the company Cephla controls multiple microscope components such as motorized XY stages, motorized Z stages, LED and laser light source, and it provides analog (0-5V) and digital outputs. Internally, the controller consists of an Arduino (Due in earlier versions, Teensy 4.1 in newer versions) board that communicates to several other boards, such as motor controllers ([TMC4361](https://www.analog.com/en/products/tmc4361.html), and [TMC2660](https://www.analog.com/en/products/TMC2660.html#part-details)), and DAC boards ([DAC 80508](https://www.ti.com/product/DAC80508) from TI). The design and firmware for the controller are [open source,](https://github.com/hongquanli/octopi-research) hence can be modified for everyone's needs. + +## Cabling +The unit needs a ?V power supply with ?A minimum. Most connections are straight forward (XY stage cable, Z Stage cable). The USB connection to the computer is unconventional, i.e. you need a USB cable with ? on both ends. Communication is through a (virtual) serial port, and the device will show up as a COM port in the Windows Device Manager. Baud rate is ignored and communication will take place at highest speeds allowed by the USB connection (i.e. you can choose whatever number for the baud rate). + +## Communication protocol + + + diff --git a/DeviceAdapters/Octopi-Research/Squid.h b/DeviceAdapters/Cephla/Squid.h similarity index 84% rename from DeviceAdapters/Octopi-Research/Squid.h rename to DeviceAdapters/Cephla/Squid.h index aacfecd37..b1693b802 100644 --- a/DeviceAdapters/Octopi-Research/Squid.h +++ b/DeviceAdapters/Cephla/Squid.h @@ -12,9 +12,10 @@ #define ERR_NO_PORT_SET 21002 extern const char* g_HubDeviceName; -extern const char* g_LEDShutterName; +extern const char* g_ShutterName; extern const char* g_XYStageName; extern const char* g_ZStageName; +extern const char* g_DAName; const unsigned char CMD_MOVE_X = 0; const unsigned char CMD_MOVE_Y = 1; @@ -116,12 +117,15 @@ class SquidHub : public HubBase std::string port_; - int assignXYStageDevice(SquidXYStage* xyStageDevice); - int assignZStageDevice(SquidZStage* zStageDevice); + int AssignXYStageDevice(SquidXYStage* xyStageDevice); + int AssignZStageDevice(SquidZStage* zStageDevice); bool XYStageBusy(); bool ZStageBusy(); + int SetDacGain(uint8_t dacNr, bool gain); + + private: bool initialized_; bool autoHome_; @@ -138,15 +142,19 @@ class SquidHub : public HubBase std::atomic_bool yStageBusy_; std::atomic_bool zStageBusy_; std::atomic_bool busy_; + std::atomic dac_div_; // 0: 1x gain: 2.5V, 1: 1x gain: 1.25V + std::atomic dac_gains_; // bit mask with gains. 0 multiples dac_div with 1, 1 multiplies dac_div with 2 + // i.e. dac_div_ 1 and dac_gains_ 1 for a given output results in 0-5V range. + std::mutex mutex_; }; -class SquidLEDShutter : public CShutterBase +class SquidShutter : public CShutterBase { public: - SquidLEDShutter(); - ~SquidLEDShutter(); + SquidShutter(); + ~SquidShutter(); int Initialize(); int Shutdown(); @@ -167,12 +175,16 @@ class SquidLEDShutter : public CShutterBase int OnRed(MM::PropertyBase* pProp, MM::ActionType eAct); int OnGreen(MM::PropertyBase* pProp, MM::ActionType eAct); int OnBlue(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnHasLasers(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnLaserIntensity(MM::PropertyBase* pProp, MM::ActionType eAct, long laserLine); private: int sendIllumination(uint8_t pattern, uint8_t intensity, uint8_t red, uint8_t green, uint8_t blue); + int sendLaserIllumination(uint8_t pattern, uint16_t intensity); SquidHub* hub_; bool initialized_; + bool hasLasers_; std::string name_; MM::MMTime changedTime_; uint8_t pattern_; @@ -180,6 +192,7 @@ class SquidLEDShutter : public CShutterBase uint8_t red_; uint8_t green_; uint8_t blue_; + uint16_t iLaser_[5]; bool isOpen_; uint8_t cmdNr_; }; @@ -322,6 +335,49 @@ class SquidZStage : public CStageBase }; +class SquidDA : public CSignalIOBase +{ +public: + SquidDA(uint8_t dacNr); + ~SquidDA(); + + // MMDevice API + // ------------ + int Initialize(); + int Shutdown(); + + void GetName(char* pszName) const; + bool Busy(); + + // DA API + int SetGateOpen(bool open); + int GetGateOpen(bool& open); + int SetSignal(double volts); + int GetSignal(double& volts); + int GetLimits(double& minVolts, double& maxVolts); + + int IsDASequenceable(bool& isSequenceable) const; + + // action interface + // ---------------- + int OnVolts(MM::PropertyBase* pProp, MM::ActionType eAct); + int OnVoltRange(MM::PropertyBase* pProp, MM::ActionType eAct); + +private: + int SendVoltage(double volt); + SquidHub* hub_; + + bool initialized_; + bool busy_; + double maxV_; + double volts_; + double gatedVolts_; + long dacNr_; + bool gateOpen_; + std::string name_; +}; + + class SquidMessageParser { public: SquidMessageParser(unsigned char* inputStream, long inputStreamLength); diff --git a/DeviceAdapters/Cephla/SquidDA.cpp b/DeviceAdapters/Cephla/SquidDA.cpp new file mode 100644 index 000000000..85e57bab7 --- /dev/null +++ b/DeviceAdapters/Cephla/SquidDA.cpp @@ -0,0 +1,174 @@ +#include "Squid.h" + +const char* g_DAName = "DA"; +const char* g_Volts = "Volts"; +const char* g_VoltRange = "Volt-Range"; +const char* g_0_5V = "0-5V"; +const char* g_0_2_5V = "0-2.5V"; + + +SquidDA::SquidDA(uint8_t dacNr) : + hub_(0), + initialized_(false), + busy_(false), + volts_(0.0), + maxV_(5.0), + gatedVolts_(0.0), + gateOpen_(true), + dacNr_(dacNr) +{ + CPropertyAction* pAct = new CPropertyAction(this, &SquidDA::OnVoltRange); + CreateStringProperty(g_VoltRange, g_0_5V, false, pAct, true); + AddAllowedValue(g_VoltRange, g_0_2_5V); + AddAllowedValue(g_VoltRange, g_0_5V); +} + + +SquidDA::~SquidDA() +{ + if (initialized_) + { + Shutdown(); + } +} + + +int SquidDA::Shutdown() { + initialized_ = false; + return DEVICE_OK; +} + + +void SquidDA::GetName(char* pszName) const +{ + std::ostringstream os; + os << g_DAName << "_" << ((int)dacNr_ + 1); + CDeviceUtils::CopyLimitedString(pszName, os.str().c_str()); +} + + +int SquidDA::Initialize() +{ + hub_ = static_cast(GetParentHub()); + if (!hub_ || !hub_->IsPortAvailable()) { + return ERR_NO_PORT_SET; + } + char hubLabel[MM::MaxStrLength]; + hub_->GetLabel(hubLabel); + + bool setGain = maxV_ == 5.0;; + int ret = hub_->SetDacGain((uint8_t) dacNr_, setGain); + if (ret != DEVICE_OK) + return ret; + + CPropertyAction* pAct = new CPropertyAction(this, &SquidDA::OnVolts); + ret = CreateFloatProperty(g_Volts, 0.0, false, pAct); + if (ret != DEVICE_OK) + return ret; + SetPropertyLimits(g_Volts, 0.0, maxV_); + + return DEVICE_OK; +} + + +bool SquidDA::Busy() { return hub_->Busy(); } + + +int SquidDA::SetGateOpen(bool open) +{ + gateOpen_ = open; + if (open) + { + SendVoltage(volts_); + } + else + { + SendVoltage(0.0); + } + return DEVICE_OK; +} + +int SquidDA::GetGateOpen(bool& open) +{ + open = gateOpen_; + return DEVICE_OK; +}; + + +int SquidDA::SetSignal(double volts) +{ + volts_ = volts; + if (gateOpen_) + { + return SendVoltage(volts_); + } + return DEVICE_OK; +} + +int SquidDA::GetSignal(double& volts) +{ + volts_ = volts; + return DEVICE_OK; +} + + +int SquidDA::GetLimits(double& minVolts, double& maxVolts) +{ + minVolts = 0; + maxVolts = maxV_; + return DEVICE_OK; +} + + +int SquidDA::IsDASequenceable(bool& isSequenceable) const { isSequenceable = false; return DEVICE_OK; } + + +// action interface +// ---------------- +int SquidDA::OnVoltRange(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set(maxV_ == 2.5 ? g_0_2_5V : g_0_5V); + } + else if (eAct == MM::AfterSet) + { + std::string response; + pProp->Get(response); + maxV_ = response == g_0_2_5V ? 2.5 : 5.0; + } + return DEVICE_OK; +} + + +int SquidDA::OnVolts(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set(volts_); + } + else if (eAct == MM::AfterSet) + { + pProp->Get(volts_); + return SetSignal(volts_); + } + return DEVICE_OK; +} + + +int SquidDA::SendVoltage(double volts) +{ + const unsigned cmdSize = 8; + unsigned char cmd[cmdSize]; + for (unsigned i = 0; i < cmdSize; i++) { + cmd[i] = 0; + } + cmd[1] = CMD_ANALOG_WRITE_ONBOARD_DAC; + cmd[2] = (uint8_t) dacNr_; + uint16_t value = (uint16_t) (volts / maxV_ * 65535); + cmd[3] = (value >> 8) & 0xff; + cmd[4] = value & 0xff; + return hub_->SendCommand(cmd, cmdSize); +} + + diff --git a/DeviceAdapters/Octopi-Research/SquidHub.cpp b/DeviceAdapters/Cephla/SquidHub.cpp similarity index 80% rename from DeviceAdapters/Octopi-Research/SquidHub.cpp rename to DeviceAdapters/Cephla/SquidHub.cpp index b64d5426d..9b885fd85 100644 --- a/DeviceAdapters/Octopi-Research/SquidHub.cpp +++ b/DeviceAdapters/Cephla/SquidHub.cpp @@ -20,9 +20,15 @@ const char* g_Max_Velocity = "Max Velocity(mm/s)"; MODULE_API void InitializeModuleData() { RegisterDevice(g_HubDeviceName, MM::HubDevice, g_HubDeviceName); - RegisterDevice(g_LEDShutterName, MM::ShutterDevice, "LEDs"); + RegisterDevice(g_ShutterName, MM::ShutterDevice, "Light-Control"); RegisterDevice(g_XYStageName, MM::XYStageDevice, "XY-Stage"); RegisterDevice(g_ZStageName, MM::StageDevice, "Z-Stage"); + for (int i = 1; i < 9; i++) + { + std::ostringstream os; + os << g_DAName << "_" << i; + RegisterDevice(os.str().c_str(), MM::SignalIODevice, os.str().c_str()); + } } @@ -33,9 +39,9 @@ MODULE_API MM::Device* CreateDevice(const char* deviceName) { return new SquidHub(); } - else if (strcmp(deviceName, g_LEDShutterName) == 0) + else if (strcmp(deviceName, g_ShutterName) == 0) { - return new SquidLEDShutter(); + return new SquidShutter(); } else if (strcmp(deviceName, g_XYStageName) == 0) { @@ -45,6 +51,14 @@ MODULE_API MM::Device* CreateDevice(const char* deviceName) { return new SquidZStage(); } + else { + std::string deviceNameString = deviceName; + if (deviceNameString.rfind(g_DAName) == 0) { + char c = deviceNameString.back(); + int dacNr = std::atoi(&c) - 1; + return new SquidDA((uint8_t) dacNr); + } + } // ...supplied name not recognized return 0; @@ -74,6 +88,8 @@ SquidHub::SquidHub() : x_ = 0l; y_ = 0l; z_ = 0l; + dac_div_ = 0; + dac_gains_ = 0; xStageBusy_ = false; yStageBusy_ = false; zStageBusy_ = false; @@ -86,9 +102,13 @@ SquidHub::SquidHub() : } -SquidHub::~SquidHub() +SquidHub::~SquidHub() { - LogMessage("Destructor called"); + if (initialized_) + { + Shutdown(); + } + LogMessage("SquidHub destructor called"); } @@ -120,6 +140,8 @@ int SquidHub::Initialize() { cmd[1] = 254; // CMD_INITIALIZE_DRIVERS ret = SendCommand(cmd, cmdSize); if (ret != DEVICE_OK) { + delete (monitoringThread_); + monitoringThread_ = 0; return ret; } @@ -140,7 +162,10 @@ int SquidHub::Initialize() { int SquidHub::Shutdown() { if (initialized_) { - delete(monitoringThread_); + if (monitoringThread_ != 0) + { + delete(monitoringThread_); + } initialized_ = false; } return DEVICE_OK; @@ -150,7 +175,6 @@ int SquidHub::Shutdown() { bool SquidHub::Busy() { return busy_; - //return false; } @@ -174,9 +198,15 @@ int SquidHub::DetectInstalledDevices() { std::vector peripherals; peripherals.clear(); - peripherals.push_back(g_LEDShutterName); + peripherals.push_back(g_ShutterName); peripherals.push_back(g_XYStageName); peripherals.push_back(g_ZStageName); + for (int i = 1; i < 9; i++) + { + std::ostringstream os; + os << g_DAName << "_" << i; + peripherals.push_back(os.str().c_str()); + } for (size_t i = 0; i < peripherals.size(); i++) { MM::Device* pDev = ::CreateDevice(peripherals[i].c_str()); @@ -211,14 +241,14 @@ int SquidHub::OnPort(MM::PropertyBase* pProp, MM::ActionType eAct) } -int SquidHub::assignXYStageDevice(SquidXYStage* xyStageDevice) +int SquidHub::AssignXYStageDevice(SquidXYStage* xyStageDevice) { xyStageDevice_ = xyStageDevice; return DEVICE_OK; } -int SquidHub::assignZStageDevice(SquidZStage* zStageDevice) +int SquidHub::AssignZStageDevice(SquidZStage* zStageDevice) { zStageDevice_ = zStageDevice; return DEVICE_OK; @@ -227,6 +257,7 @@ int SquidHub::assignZStageDevice(SquidZStage* zStageDevice) int SquidHub::SendCommand(unsigned char* cmd, unsigned cmdSize) { + std::lock_guard lck(mutex_); cmd[0] = ++cmdNrSend_; cmd[cmdSize - 1] = crc8ccitt(cmd, cmdSize - 1); if (true) { @@ -422,3 +453,33 @@ int SquidHub::Home() cmd[4] = int((STAGE_MOVEMENT_SIGN_Y + 1) / 2); // "move backward" if SIGN is 1, "move forward" if SIGN is - 1 return SendCommand(cmd, cmdSize); } + + +/** + * Needed to initialize the DAC. + * Will be called by each DAC device, hence may be executed multiple times. + * dac_div_ == 0 sets range to 0-1.125V, dac_div_ == 1 to 0-2.5V. + * dac_gains_ is a bit mask, each dac for which the mask is true (1), the output + * voltage will be multiplied by 2, hence dav_div_==1 and dac_gains_==true for + * a given channel results in 0-5V range. + */ +int SquidHub::SetDacGain(uint8_t dacNr, bool gain) +{ + if (gain) + { + dac_gains_ = dac_gains_ | 1 << dacNr; + } + else + { + dac_gains_ = dac_gains_ &~((unsigned char)1 << dacNr); + } + const unsigned cmdSize = 8; + unsigned char cmd[cmdSize]; + for (unsigned i = 0; i < cmdSize; i++) { + cmd[i] = 0; + } + cmd[1] = CMD_SET_DAC80508_REFDIV_GAIN; + cmd[2] = dac_div_; + cmd[3] = dac_gains_; + return SendCommand(cmd, cmdSize); +} diff --git a/DeviceAdapters/Octopi-Research/SquidMonitoringThread.cpp b/DeviceAdapters/Cephla/SquidMonitoringThread.cpp similarity index 100% rename from DeviceAdapters/Octopi-Research/SquidMonitoringThread.cpp rename to DeviceAdapters/Cephla/SquidMonitoringThread.cpp diff --git a/DeviceAdapters/Octopi-Research/SquidLEDShutter.cpp b/DeviceAdapters/Cephla/SquidShutter.cpp similarity index 55% rename from DeviceAdapters/Octopi-Research/SquidLEDShutter.cpp rename to DeviceAdapters/Cephla/SquidShutter.cpp index dc06073e8..021e3b3fe 100644 --- a/DeviceAdapters/Octopi-Research/SquidLEDShutter.cpp +++ b/DeviceAdapters/Cephla/SquidShutter.cpp @@ -1,14 +1,16 @@ -#include "squid.h" +#include "Squid.h" #include -const char* g_LEDShutterName = "LEDs"; +const char* g_ShutterName = "CephlaShutter"; const char* g_OnOff = "OnOff"; const char* g_Pattern = "Pattern"; const char* g_Intensity = "Intensity"; const char* g_Red = "Red"; const char* g_Green = "Green"; const char* g_Blue = "Blue"; +const char* g_HasLasers = "Has Lasers"; +const char* g_488 = "488nm"; extern const int CMD_TURN_ON_ILLUMINATION; extern const int CMD_TURN_OFF_ILLUMINATION; @@ -23,51 +25,70 @@ extern const int ILLUMINATION_SOURCE_LED_ARRAY_LEFT_DOT; extern const int ILLUMINATION_SOURCE_LED_ARRAY_RIGHT_DOT; const std::string ILLUMINATIONS[7] = { - "Full", - "Left_Half", - "Right_Half", - "Left-Blue_Right-Red", - "Low_NA", - "Left_Dot", - "Right_Dot" + "LED-Full", + "LED-Left_Half", + "LED-Right_Half", + "LED-Left-Blue_Right-Red", + "LED-Low_NA", + "LED-Left_Dot", + "LED-Right_Dot" +}; + +// laser IDs start at 11 +const uint8_t NR_LASERS = 5; +const std::string LASERS[NR_LASERS] = { + "405nm", + "488nm", + "638nm", + "561nm", + "730nm" }; const int illumination_source = 1; // presumably this is the lED, with lasers something else -SquidLEDShutter::SquidLEDShutter() : +SquidShutter::SquidShutter() : hub_(0), initialized_(false), - name_(g_LEDShutterName), + hasLasers_(false), + name_(g_ShutterName), pattern_(0), changedTime_(), intensity_ (1), red_(255), green_(255), blue_(255), + iLaser_(), isOpen_(false), cmdNr_(0) { + for (int i = 0; i < NR_LASERS; i++) + iLaser_[i] = 0; + InitializeDefaultErrorMessages(); EnableDelay(); SetErrorText(ERR_NO_PORT_SET, "Hub Device not found. The Squid Hub device is needed to create this device"); // Name - int ret = CreateProperty(MM::g_Keyword_Name, g_LEDShutterName, MM::String, true); + int ret = CreateProperty(MM::g_Keyword_Name, g_ShutterName, MM::String, true); assert(DEVICE_OK == ret); // Description - ret = CreateProperty(MM::g_Keyword_Description, "Squid LED-shutter driver", MM::String, true); + ret = CreateProperty(MM::g_Keyword_Description, "Squid Light Control", MM::String, true); assert(DEVICE_OK == ret); + CPropertyAction* pAct = new CPropertyAction(this, &SquidShutter::OnHasLasers); + ret = CreateStringProperty(g_HasLasers, g_No, false, pAct, true); + AddAllowedValue(g_HasLasers, g_No); + AddAllowedValue(g_HasLasers, g_Yes); + // parent ID display CreateHubIDProperty(); - } -SquidLEDShutter::~SquidLEDShutter() +SquidShutter::~SquidShutter() { if (initialized_) { @@ -76,7 +97,7 @@ SquidLEDShutter::~SquidLEDShutter() } -int SquidLEDShutter::Shutdown() +int SquidShutter::Shutdown() { if (initialized_) { initialized_ = false; @@ -85,13 +106,13 @@ int SquidLEDShutter::Shutdown() } -void SquidLEDShutter::GetName(char* pszName) const +void SquidShutter::GetName(char* pszName) const { - CDeviceUtils::CopyLimitedString(pszName, g_LEDShutterName); + CDeviceUtils::CopyLimitedString(pszName, g_ShutterName); } -int SquidLEDShutter::Initialize() +int SquidShutter::Initialize() { hub_ = static_cast(GetParentHub()); if (!hub_ || !hub_->IsPortAvailable()) { @@ -102,7 +123,7 @@ int SquidLEDShutter::Initialize() // OnOff // ------ - CPropertyAction* pAct = new CPropertyAction(this, &SquidLEDShutter::OnOnOff); + CPropertyAction* pAct = new CPropertyAction(this, &SquidShutter::OnOnOff); int ret = CreateProperty("OnOff", "0", MM::Integer, false, pAct); if (ret != DEVICE_OK) return ret; @@ -119,7 +140,7 @@ int SquidLEDShutter::Initialize() // Pattern // ------ - pAct = new CPropertyAction(this, &SquidLEDShutter::OnPattern); + pAct = new CPropertyAction(this, &SquidShutter::OnPattern); ret = CreateProperty(g_Pattern, ILLUMINATIONS[0].c_str(), MM::String, false, pAct); if (ret != DEVICE_OK) return ret; @@ -128,10 +149,16 @@ int SquidLEDShutter::Initialize() { AddAllowedValue(g_Pattern, ILLUMINATIONS[i].c_str()); } + if (hasLasers_) { + for (uint8_t i = 0; i < 5; i++) + { + AddAllowedValue(g_Pattern, LASERS[i].c_str()); + } + } // Intensity // ------ - pAct = new CPropertyAction(this, &SquidLEDShutter::OnIntensity); + pAct = new CPropertyAction(this, &SquidShutter::OnIntensity); ret = CreateProperty(g_Intensity, "1", MM::Integer, false, pAct); if (ret != DEVICE_OK) return ret; @@ -139,7 +166,7 @@ int SquidLEDShutter::Initialize() // Red // ------ - pAct = new CPropertyAction(this, &SquidLEDShutter::OnRed); + pAct = new CPropertyAction(this, &SquidShutter::OnRed); ret = CreateProperty(g_Red, "255", MM::Integer, false, pAct); if (ret != DEVICE_OK) return ret; @@ -147,7 +174,7 @@ int SquidLEDShutter::Initialize() // Green // ------ - pAct = new CPropertyAction(this, &SquidLEDShutter::OnGreen); + pAct = new CPropertyAction(this, &SquidShutter::OnGreen); ret = CreateProperty(g_Green, "255", MM::Integer, false, pAct); if (ret != DEVICE_OK) return ret; @@ -155,17 +182,37 @@ int SquidLEDShutter::Initialize() // Blue // ------ - pAct = new CPropertyAction(this, &SquidLEDShutter::OnBlue); + pAct = new CPropertyAction(this, &SquidShutter::OnBlue); ret = CreateProperty(g_Blue, "255", MM::Integer, false, pAct); if (ret != DEVICE_OK) return ret; SetPropertyLimits(g_Blue, 0, 255); + + if (hasLasers_) + { + // Create Laser intensity properties for the known laser lines + for (uint8_t i = 0; i < NR_LASERS; i++) + { + CPropertyActionEx* pActEx = new CPropertyActionEx(this, &SquidShutter::OnLaserIntensity, i); + ret = CreateProperty(LASERS[i].c_str(), "0", MM::Integer, false, pActEx); + if (ret != DEVICE_OK) + return ret; + SetPropertyLimits(LASERS[i].c_str(), 0, 65535); + // Set the DACs to 0-2.5V range + ret = hub_->SetDacGain(i, false); + if (ret != DEVICE_OK) + return ret; + } + + } + + SetOpen(isOpen_); // we can not read the state from the device, at least get it in sync with us - ret = UpdateStatus(); - if (ret != DEVICE_OK) - return ret; + //ret = UpdateStatus(); + //if (ret != DEVICE_OK) + // return ret; changedTime_ = GetCurrentMMTime(); initialized_ = true; @@ -175,7 +222,7 @@ int SquidLEDShutter::Initialize() // TODO: figure out how to get a real Busy signal -bool SquidLEDShutter::Busy() +bool SquidShutter::Busy() { return false; //return hub_->IsCommandPending(cmdNr_); @@ -183,7 +230,7 @@ bool SquidLEDShutter::Busy() -int SquidLEDShutter::SetOpen(bool open) +int SquidShutter::SetOpen(bool open) { std::ostringstream os; os << "Request " << open; @@ -195,7 +242,7 @@ int SquidLEDShutter::SetOpen(bool open) return SetProperty("OnOff", "0"); } -int SquidLEDShutter::GetOpen(bool& open) +int SquidShutter::GetOpen(bool& open) { char buf[MM::MaxStrLength]; int ret = GetProperty("OnOff", buf); @@ -207,7 +254,7 @@ int SquidLEDShutter::GetOpen(bool& open) return DEVICE_OK; } -int SquidLEDShutter::Fire(double /*deltaT*/) +int SquidShutter::Fire(double /*deltaT*/) { return DEVICE_UNSUPPORTED_COMMAND; } @@ -215,7 +262,7 @@ int SquidLEDShutter::Fire(double /*deltaT*/) // action interface -int SquidLEDShutter::OnOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) +int SquidShutter::OnOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) { if (eAct == MM::BeforeGet) { @@ -248,28 +295,58 @@ int SquidLEDShutter::OnOnOff(MM::PropertyBase* pProp, MM::ActionType eAct) } -int SquidLEDShutter::OnPattern(MM::PropertyBase* pProp, MM::ActionType eAct) +int SquidShutter::OnPattern(MM::PropertyBase* pProp, MM::ActionType eAct) { if (eAct == MM::BeforeGet) { - pProp->Set(ILLUMINATIONS[pattern_].c_str()); + if (pattern_ < 7) + { + pProp->Set(ILLUMINATIONS[pattern_].c_str()); + } + else if (pattern_ > 10 && pattern_ < 16)// Laser + { + pProp->Set(LASERS[pattern_ - 11].c_str()); + } + else { + return DEVICE_INVALID_PROPERTY_VALUE; + } } else if (eAct == MM::AfterSet) { - std::string pattern; - pProp->Get(pattern); + std::string illumination; + pProp->Get(illumination); + bool isOpen = isOpen_; for (uint8_t i = 0; i < 7; i++) { - if (ILLUMINATIONS[i] == pattern) { + if (ILLUMINATIONS[i] == illumination) { pattern_ = i; - return sendIllumination(pattern_, intensity_, red_, green_, blue_); + if (isOpen) + SetOpen(false); + int ret = sendIllumination(pattern_, intensity_, red_, green_, blue_); + if (isOpen) + SetOpen(true); + return ret; + } + } + for (uint8_t i = 0; i < NR_LASERS; i++) + { + if (LASERS[i] == illumination) + { + pattern_ = i + 11; + if (isOpen) + SetOpen(false); + int ret = sendLaserIllumination(pattern_, iLaser_[i]); + if (isOpen) + SetOpen(true); + return ret; } } } return DEVICE_OK; } -int SquidLEDShutter::OnIntensity(MM::PropertyBase* pProp, MM::ActionType eAct) + +int SquidShutter::OnIntensity(MM::PropertyBase* pProp, MM::ActionType eAct) { if (eAct == MM::BeforeGet) { @@ -288,7 +365,7 @@ int SquidLEDShutter::OnIntensity(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; } -int SquidLEDShutter::OnRed(MM::PropertyBase* pProp, MM::ActionType eAct) +int SquidShutter::OnRed(MM::PropertyBase* pProp, MM::ActionType eAct) { if (eAct == MM::BeforeGet) { @@ -307,7 +384,7 @@ int SquidLEDShutter::OnRed(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; } -int SquidLEDShutter::OnGreen(MM::PropertyBase* pProp, MM::ActionType eAct) +int SquidShutter::OnGreen(MM::PropertyBase* pProp, MM::ActionType eAct) { if (eAct == MM::BeforeGet) { @@ -325,7 +402,8 @@ int SquidLEDShutter::OnGreen(MM::PropertyBase* pProp, MM::ActionType eAct) } return DEVICE_OK; } -int SquidLEDShutter::OnBlue(MM::PropertyBase* pProp, MM::ActionType eAct) + +int SquidShutter::OnBlue(MM::PropertyBase* pProp, MM::ActionType eAct) { if (eAct == MM::BeforeGet) { @@ -344,8 +422,42 @@ int SquidLEDShutter::OnBlue(MM::PropertyBase* pProp, MM::ActionType eAct) return DEVICE_OK; } +int SquidShutter::OnLaserIntensity(MM::PropertyBase* pProp, MM::ActionType eAct, long i) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set((long(iLaser_[i]))); + } + else if (eAct == MM::AfterSet) + { + long pos; + pProp->Get(pos); + iLaser_[i] = (uint16_t)pos; + if (pattern_ == i + 11) + { + return sendLaserIllumination(pattern_, iLaser_[i]); + } + } + return DEVICE_OK; +} + +int SquidShutter::OnHasLasers(MM::PropertyBase* pProp, MM::ActionType eAct) +{ + if (eAct == MM::BeforeGet) + { + pProp->Set(hasLasers_ ? "Yes" : "No"); + } + else if (eAct == MM::AfterSet) + { + std::string ans; + pProp->Get(ans); + hasLasers_ = ans == "Yes"; + } + return DEVICE_OK; +} + -int SquidLEDShutter::sendIllumination(uint8_t pattern, uint8_t intensity, uint8_t red, uint8_t green, uint8_t blue) +int SquidShutter::sendIllumination(uint8_t pattern, uint8_t intensity, uint8_t red, uint8_t green, uint8_t blue) { const unsigned cmdSize = 8; unsigned char cmd[cmdSize]; @@ -364,4 +476,26 @@ int SquidLEDShutter::sendIllumination(uint8_t pattern, uint8_t intensity, uint8_ changedTime_ = GetCurrentMMTime(); return DEVICE_OK; -} \ No newline at end of file +} + +int SquidShutter::sendLaserIllumination(uint8_t pattern, uint16_t intensity) +{ + const unsigned cmdSize = 8; + unsigned char cmd[cmdSize]; + for (unsigned i = 0; i < cmdSize; i++) { + cmd[i] = 0; + } + cmd[1] = CMD_SET_ILLUMINATION; + cmd[2] = pattern; + cmd[3] = (intensity >> 8) & 0xff; + cmd[4] = intensity & 0xff; + + int ret = hub_->SendCommand(cmd, cmdSize); + if (ret != DEVICE_OK) + return ret; + changedTime_ = GetCurrentMMTime(); + + return DEVICE_OK; +} + + diff --git a/DeviceAdapters/Octopi-Research/SquidXYStage.cpp b/DeviceAdapters/Cephla/SquidXYStage.cpp similarity index 99% rename from DeviceAdapters/Octopi-Research/SquidXYStage.cpp rename to DeviceAdapters/Cephla/SquidXYStage.cpp index a3e79ee00..19451de3b 100644 --- a/DeviceAdapters/Octopi-Research/SquidXYStage.cpp +++ b/DeviceAdapters/Cephla/SquidXYStage.cpp @@ -125,7 +125,7 @@ int SquidXYStage::Initialize() if (!hub_ || !hub_->IsPortAvailable()) { return ERR_NO_PORT_SET; } - int ret = hub_->assignXYStageDevice(this); + int ret = hub_->AssignXYStageDevice(this); if (ret != DEVICE_OK) return ret; char hubLabel[MM::MaxStrLength]; diff --git a/DeviceAdapters/Octopi-Research/SquidZStage.cpp b/DeviceAdapters/Cephla/SquidZStage.cpp similarity index 98% rename from DeviceAdapters/Octopi-Research/SquidZStage.cpp rename to DeviceAdapters/Cephla/SquidZStage.cpp index d4163c298..f84ae2a5f 100644 --- a/DeviceAdapters/Octopi-Research/SquidZStage.cpp +++ b/DeviceAdapters/Cephla/SquidZStage.cpp @@ -49,7 +49,7 @@ int SquidZStage::Initialize() } char hubLabel[MM::MaxStrLength]; hub_->GetLabel(hubLabel); - int ret = hub_->assignZStageDevice(this); + int ret = hub_->AssignZStageDevice(this); if (ret != DEVICE_OK) return ret; diff --git a/DeviceAdapters/Octopi-Research/controller-command-set.txt b/DeviceAdapters/Cephla/controller-command-set.txt similarity index 90% rename from DeviceAdapters/Octopi-Research/controller-command-set.txt rename to DeviceAdapters/Cephla/controller-command-set.txt index 465216cf8..cc7124a3b 100644 --- a/DeviceAdapters/Octopi-Research/controller-command-set.txt +++ b/DeviceAdapters/Cephla/controller-command-set.txt @@ -89,6 +89,26 @@ def send_command(self,command): self.last_command_timestamp = time.time() self.retry = 0 + def set_piezo_um(self, z_piezo_um): + dac = int(65535 * (z_piezo_um / OBJECTIVE_PIEZO_RANGE_UM)) + dac = 65535 - dac if OBJECTIVE_PIEZO_FLIP_DIR else dac + self.microcontroller.analog_write_onboard_DAC(7, dac) + +def analog_write_onboard_DAC(self,dac,value): + cmd = bytearray(self.tx_buffer_length) + cmd[1] = CMD_SET.ANALOG_WRITE_ONBOARD_DAC + cmd[2] = dac + cmd[3] = (value >> 8) & 0xff + cmd[4] = value & 0xff + self.send_command(cmd) + +def configure_dac80508_refdiv_and_gain(self, div, gains): + cmd = bytearray(self.tx_buffer_length) + cmd[1] = CMD_SET.SET_DAC80508_REFDIV_GAIN + cmd[2] = div + cmd[3] = gains + self.send_command(cmd) + def read_received_packet(self): while self.terminate_reading_received_packet_thread == False: # wait to receive data @@ -164,6 +184,27 @@ def read_received_packet(self): from: https://github.com/hongquanli/octopi-research/blob/master/software/control/_def.py + +class TriggerMode: + SOFTWARE = 'Software Trigger' + HARDWARE = 'Hardware Trigger' + CONTINUOUS = 'Continuous Acquisition' + +class Acquisition: + CROP_WIDTH = 3000 + CROP_HEIGHT = 3000 + NUMBER_OF_FOVS_PER_AF = 3 + IMAGE_FORMAT = 'bmp' + IMAGE_DISPLAY_SCALING_FACTOR = 0.3 + DX = 0.9 + DY = 0.9 + DZ = 1.5 + NX = 1 + NY = 1 + +class PosUpdate: + INTERVAL_MS = 25 + class MicrocontrollerDef: MSG_LENGTH = 24 CMD_LENGTH = 8 @@ -173,6 +214,9 @@ class Microcontroller2Def: MSG_LENGTH = 4 CMD_LENGTH = 8 N_BYTES_POS = 4 + +USE_SEPARATE_MCU_FOR_DAC = False + class MCU_PINS: PWM1 = 5 PWM2 = 4 diff --git a/DeviceAdapters/Octopi-Research/crc8.h b/DeviceAdapters/Cephla/crc8.h similarity index 100% rename from DeviceAdapters/Octopi-Research/crc8.h rename to DeviceAdapters/Cephla/crc8.h diff --git a/micromanager.sln b/micromanager.sln index 6ada40b11..8c01740a7 100644 --- a/micromanager.sln +++ b/micromanager.sln @@ -490,7 +490,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PlayerOne", "DeviceAdapters EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DahengGalaxy", "DeviceAdapters\DahengGalaxy\DahengGalaxy.vcxproj", "{DD3A2820-F54C-42F3-AA0E-DC95D57481B7}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Octopi-research", "DeviceAdapters\Octopi-Research\Octopi-research.vcxproj", "{FE305987-2B15-4EDA-9FC8-32D81AAE1543}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Cephla", "DeviceAdapters\Cephla\Cephla.vcxproj", "{FE305987-2B15-4EDA-9FC8-32D81AAE1543}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PyDevice", "DeviceAdapters\PyDevice\PyDevice.vcxproj", "{36CF524A-8214-404C-8E6B-B5DEC1FDADF9}" EndProject