-
Notifications
You must be signed in to change notification settings - Fork 110
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #548 from nicost/Octopi
Cephla controller: Adds DA and Laser control
- Loading branch information
Showing
13 changed files
with
559 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<SquidHub*>(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); | ||
} | ||
|
||
|
Oops, something went wrong.