-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- This device needs some work arounds because its clock is completely out of spec for the SERDES it uses. The back channel must be used to configure a PLL on the camera in order to bump the PCLK rate before it will like. For this reason, I mad ConfigureFmcLinkController. CheckLinkState virtual so I could override its behavior with these steps before checking the link state - Added configuraiton and streaming functionality. - There are few outstanding issues: 1. Something is wrong with the DS90UB9x configuration resulting in 1 missing column from each frame and imaging data that "slides" by one column for each image produced 2. The axis map for the BNO needs to be set correctly 3. I was unable to verify that the EWL was functioning. It might be fine, but I was unable to verify.
- Loading branch information
Showing
8 changed files
with
565 additions
and
1 deletion.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
using System.Collections.Generic; | ||
using System.ComponentModel; | ||
|
||
namespace OpenEphys.Onix | ||
{ | ||
public class ConfigureUclaMiniscopeV4 : HubDeviceFactory | ||
{ | ||
PortName port; | ||
readonly ConfigureUclaMiniscopeV4LinkController LinkController = new(); | ||
|
||
public ConfigureUclaMiniscopeV4() | ||
{ | ||
Port = PortName.PortA; | ||
LinkController.HubConfiguration = HubConfiguration.Passthrough; | ||
} | ||
|
||
[Category(ConfigurationCategory)] | ||
[TypeConverter(typeof(HubDeviceConverter))] | ||
public ConfigureUclaMiniscopeV4Camera Imager { get; set; } = new(); | ||
|
||
[Category(ConfigurationCategory)] | ||
[TypeConverter(typeof(HubDeviceConverter))] | ||
public ConfigureUclaMiniscopeV4Bno055 Bno055 { get; set; } = new(); | ||
|
||
public PortName Port | ||
{ | ||
get { return port; } | ||
set | ||
{ | ||
port = value; | ||
var offset = (uint)port << 8; | ||
LinkController.DeviceAddress = (uint)port; | ||
Imager.DeviceAddress = offset + 0; | ||
Bno055.DeviceAddress = offset + 1; | ||
} | ||
} | ||
|
||
[Description("If defined, it will override automated voltage discovery and apply the specified voltage" + | ||
"to the headstage. Warning: this device requires 5.0V to 6.0V for proper operation." + | ||
"Supplying higher voltages may result in damage to the headstage.")] | ||
public double? PortVoltage | ||
{ | ||
get => LinkController.PortVoltage; | ||
set => LinkController.PortVoltage = value; | ||
} | ||
|
||
internal override IEnumerable<IDeviceConfiguration> GetDevices() | ||
{ | ||
yield return LinkController; | ||
yield return Imager; | ||
yield return Bno055; | ||
} | ||
} | ||
} |
69 changes: 69 additions & 0 deletions
69
OpenEphys.Onix/OpenEphys.Onix/ConfigureUclaMiniscopeV4Bno055.cs
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,69 @@ | ||
using System; | ||
using System.ComponentModel; | ||
|
||
namespace OpenEphys.Onix | ||
{ | ||
public class ConfigureUclaMiniscopeV4Bno055 : SingleDeviceFactory | ||
{ | ||
public ConfigureUclaMiniscopeV4Bno055() | ||
: base(typeof(UclaMiniscopeV4Bno055)) | ||
{ | ||
} | ||
|
||
[Category(ConfigurationCategory)] | ||
[Description("Specifies whether the BNO055 device is enabled.")] | ||
public bool Enable { get; set; } = true; | ||
|
||
public override IObservable<ContextTask> Process(IObservable<ContextTask> source) | ||
{ | ||
var enable = Enable; | ||
var deviceName = DeviceName; | ||
var deviceAddress = DeviceAddress; | ||
return source.ConfigureDevice(context => | ||
{ | ||
// configure device via the DS90UB9x deserializer device | ||
var device = context.GetPassthroughDeviceContext(deviceAddress, DS90UB9x.ID); | ||
ConfigureDeserializer(device); | ||
ConfigureBno055(device); | ||
var deviceInfo = new DeviceInfo(context, DeviceType, deviceAddress); | ||
return DeviceManager.RegisterDevice(deviceName, deviceInfo); | ||
}); | ||
} | ||
|
||
static void ConfigureDeserializer(DeviceContext device) | ||
{ | ||
// configure deserializer I2C aliases | ||
var deserializer = new I2CRegisterContext(device, DS90UB9x.DES_ADDR); | ||
uint alias = UclaMiniscopeV4Bno055.BNO055Address << 1; | ||
deserializer.WriteByte((uint)DS90UB9xDeserializerI2CRegister.SlaveID4, alias); | ||
deserializer.WriteByte((uint)DS90UB9xDeserializerI2CRegister.SlaveAlias4, alias); | ||
} | ||
|
||
static void ConfigureBno055(DeviceContext device) | ||
{ | ||
// setup BNO055 device | ||
// TODO: Correct orientation | ||
var i2c = new I2CRegisterContext(device, UclaMiniscopeV4Bno055.BNO055Address); | ||
i2c.WriteByte(0x3E, 0x00); // Power mode normal | ||
i2c.WriteByte(0x07, 0x00); // Page ID address 0 | ||
i2c.WriteByte(0x3F, 0x00); // Internal oscillator | ||
i2c.WriteByte(0x41, 0b00000110); // Axis map config (configured to match hs64; X => Z, Y => -Y, Z => X) | ||
i2c.WriteByte(0x42, 0b000000010); // Axis sign (negate Y) | ||
i2c.WriteByte(0x3D, 8); // Operation mode is NOF | ||
} | ||
} | ||
|
||
static class UclaMiniscopeV4Bno055 | ||
{ | ||
public const int BNO055Address = 0x28; | ||
public const int DataAddress = 0x1A; | ||
|
||
internal class NameConverter : DeviceNameConverter | ||
{ | ||
public NameConverter() | ||
: base(typeof(UclaMiniscopeV4Bno055)) | ||
{ | ||
} | ||
} | ||
} | ||
} |
197 changes: 197 additions & 0 deletions
197
OpenEphys.Onix/OpenEphys.Onix/ConfigureUclaMiniscopeV4Camera.cs
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,197 @@ | ||
using System; | ||
using System.ComponentModel; | ||
using System.Reactive.Disposables; | ||
|
||
namespace OpenEphys.Onix | ||
{ | ||
public class ConfigureUclaMiniscopeV4Camera : SingleDeviceFactory | ||
{ | ||
public ConfigureUclaMiniscopeV4Camera() | ||
: base(typeof(UclaMiniscopeV4)) | ||
{ | ||
} | ||
|
||
[Category(ConfigurationCategory)] | ||
[Description("Specifies whether the camera is enabled.")] | ||
public bool Enable { get; set; } = true; | ||
|
||
[Category(ConfigurationCategory)] | ||
[Description("Only turn on excitation LED during camera exposures.")] | ||
public bool InterleaveLed { get; set; } = false; | ||
|
||
[Category(ConfigurationCategory)] | ||
[Description("Only turn on excitation LED during camera exposures.")] | ||
public UclaMiniscopeV4FramesPerSecond FrameRate { get; set; } = UclaMiniscopeV4FramesPerSecond.Fps30Hz; | ||
|
||
public override IObservable<ContextTask> Process(IObservable<ContextTask> source) | ||
{ | ||
var enable = Enable; | ||
var deviceName = DeviceName; | ||
var deviceAddress = DeviceAddress; | ||
return source.ConfigureDevice(context => | ||
{ | ||
// configure device via the DS90UB9x deserializer device | ||
var device = context.GetPassthroughDeviceContext(deviceAddress, DS90UB9x.ID); | ||
device.WriteRegister(DS90UB9x.ENABLE, enable ? 1u : 0); | ||
|
||
// configure deserializer, chip states, and camera PLL | ||
ConfigureMiniscope(device); | ||
|
||
// configuration properties | ||
var atMega = new I2CRegisterContext(device, UclaMiniscopeV4.AtMegaAddress); | ||
atMega.WriteByte(0x04, (uint)(InterleaveLed ? 0x00 : 0x03)); | ||
|
||
uint shutterWidth = FrameRate switch | ||
{ | ||
UclaMiniscopeV4FramesPerSecond.Fps10Hz => 10000, | ||
UclaMiniscopeV4FramesPerSecond.Fps15Hz => 6667, | ||
UclaMiniscopeV4FramesPerSecond.Fps20Hz => 5000, | ||
UclaMiniscopeV4FramesPerSecond.Fps25Hz => 4000, | ||
UclaMiniscopeV4FramesPerSecond.Fps30Hz => 3300, | ||
_ => 3300 | ||
}; | ||
|
||
WriteCameraRegister(atMega, 200, shutterWidth); | ||
|
||
var deviceInfo = new DeviceInfo(context, DeviceType, deviceAddress); | ||
var disposable = DeviceManager.RegisterDevice(deviceName, deviceInfo); | ||
var shutdown = Disposable.Create(() => | ||
{ | ||
// turn off EWL | ||
var max14574 = new I2CRegisterContext(device, UclaMiniscopeV4.Max14574Address); | ||
max14574.WriteByte(0x03, 0x00); | ||
|
||
// turn off LED | ||
var atMega = new I2CRegisterContext(device, UclaMiniscopeV4.AtMegaAddress); | ||
atMega.WriteByte(1, 0xFF); | ||
}); | ||
return new CompositeDisposable( | ||
shutdown, | ||
disposable); | ||
}); | ||
} | ||
|
||
internal static void ConfigureMiniscope(DeviceContext device) | ||
{ | ||
// configure deserializer | ||
device.WriteRegister(DS90UB9x.TRIGGEROFF, 0); | ||
device.WriteRegister(DS90UB9x.READSZ, UclaMiniscopeV4.SensorColumns); | ||
device.WriteRegister(DS90UB9x.TRIGGER, (uint)DS90UB9xTriggerMode.HsyncEdgePositive); | ||
device.WriteRegister(DS90UB9x.SYNCBITS, 0); | ||
device.WriteRegister(DS90UB9x.DATAGATE, (uint)DS90UB9xDataGate.VsyncPositive); | ||
device.WriteRegister(DS90UB9x.MARK,0); | ||
|
||
// configure deserializer I2C aliases | ||
var deserializer = new I2CRegisterContext(device, DS90UB9x.DES_ADDR); | ||
uint coaxMode = 0x4 + (uint)DS90UB9xMode.Raw12BitLowFrequency; // 0x4 maintains coax mode | ||
deserializer.WriteByte((uint)DS90UB9xDeserializerI2CRegister.PortMode, coaxMode); | ||
|
||
uint alias = UclaMiniscopeV4.AtMegaAddress << 1; | ||
deserializer.WriteByte((uint)DS90UB9xDeserializerI2CRegister.SlaveID1, alias); | ||
deserializer.WriteByte((uint)DS90UB9xDeserializerI2CRegister.SlaveAlias1, alias); | ||
|
||
alias = UclaMiniscopeV4.Tpl0102Address << 1; | ||
deserializer.WriteByte((uint)DS90UB9xDeserializerI2CRegister.SlaveID2, alias); | ||
deserializer.WriteByte((uint)DS90UB9xDeserializerI2CRegister.SlaveAlias2, alias); | ||
|
||
alias = UclaMiniscopeV4.Max14574Address << 1; | ||
deserializer.WriteByte((uint)DS90UB9xDeserializerI2CRegister.SlaveID3, alias); | ||
deserializer.WriteByte((uint)DS90UB9xDeserializerI2CRegister.SlaveAlias3, alias); | ||
|
||
// set up potentiometer | ||
var tpl0102 = new I2CRegisterContext(device, UclaMiniscopeV4.Tpl0102Address); | ||
tpl0102.WriteByte(0x00, 0x72); | ||
tpl0102.WriteByte(0x01, 0x00); | ||
|
||
// turn on EWL | ||
var max14574 = new I2CRegisterContext(device, UclaMiniscopeV4.Max14574Address); | ||
max14574.WriteByte(0x08, 0x7F); | ||
max14574.WriteByte(0x09, 0x02); | ||
|
||
// turn on LED and setup Python480 | ||
var atMega = new I2CRegisterContext(device, UclaMiniscopeV4.AtMegaAddress); | ||
WriteCameraRegister(atMega, 16, 3); // Turn on PLL | ||
WriteCameraRegister(atMega, 32, 0x7007); // Turn on clock managment | ||
WriteCameraRegister(atMega, 199, 666); // Defines granularity (unit = 1/PLL clock) of exposure and reset_length | ||
WriteCameraRegister(atMega, 200, 3300); // Set frame rate to 30 Hz | ||
WriteCameraRegister(atMega, 201, 3000); // Set Exposure | ||
} | ||
|
||
private static void WriteCameraRegister(I2CRegisterContext i2c, uint register, uint value) | ||
{ | ||
// ATMega -> Python480 passthrough protocol | ||
var regLow = register & 0xFF; | ||
var regHigh = (register >> 8) & 0xFF; | ||
var valLow = value & 0xFF; | ||
var valHigh = (value >> 8) & 0xFF; | ||
|
||
i2c.WriteByte(0x05, regHigh); | ||
i2c.WriteByte(regLow, valHigh); | ||
i2c.WriteByte(valLow, 0x00); | ||
} | ||
|
||
internal static void SetLedBrightness(DeviceContext device, double percent) | ||
{ | ||
var des = device.Context.GetPassthroughDeviceContext(device.Address, DS90UB9x.ID); | ||
|
||
var atMega = new I2CRegisterContext(des, UclaMiniscopeV4.AtMegaAddress); | ||
atMega.WriteByte(0x01, (uint)((percent == 0) ? 0xFF : 0x08)); | ||
|
||
var tpl0102 = new I2CRegisterContext(des, UclaMiniscopeV4.Tpl0102Address); | ||
tpl0102.WriteByte(0x01, (uint)(255 * ((100 - percent) / 100.0))); | ||
} | ||
|
||
internal static void SetSensorGain(DeviceContext device, UclaMiniscopeV4SensorGain gain) | ||
{ | ||
var des = device.Context.GetPassthroughDeviceContext(device.Address, DS90UB9x.ID); | ||
|
||
var atMega = new I2CRegisterContext(des, UclaMiniscopeV4.AtMegaAddress); | ||
WriteCameraRegister(atMega, 204, (uint)gain); | ||
} | ||
|
||
internal static void SetLiquidLensVoltage(DeviceContext device, double voltage) | ||
{ | ||
var des = device.Context.GetPassthroughDeviceContext(device.Address, DS90UB9x.ID); | ||
|
||
var max14574 = new I2CRegisterContext(des, UclaMiniscopeV4.Max14574Address); | ||
max14574.WriteByte(0x08, (uint)((voltage - 24.4) / 0.0445) >> 2); | ||
max14574.WriteByte(0x09, 0x02); | ||
} | ||
|
||
} | ||
|
||
static class UclaMiniscopeV4 | ||
{ | ||
public const int AtMegaAddress = 0x10; | ||
public const int Tpl0102Address = 0x50; | ||
public const int Max14574Address = 0x77; | ||
|
||
public const int SensorRows = 608; | ||
public const int SensorColumns = 608; | ||
|
||
internal class NameConverter : DeviceNameConverter | ||
{ | ||
public NameConverter() | ||
: base(typeof(UclaMiniscopeV4)) | ||
{ | ||
} | ||
} | ||
} | ||
|
||
public enum UclaMiniscopeV4SensorGain | ||
{ | ||
Low = 0x00E1, | ||
Medium = 0x00E4, | ||
High = 0x0024, | ||
} | ||
|
||
public enum UclaMiniscopeV4FramesPerSecond | ||
{ | ||
Fps10Hz, | ||
Fps15Hz, | ||
Fps20Hz, | ||
Fps25Hz, | ||
Fps30Hz, | ||
} | ||
|
||
} |
59 changes: 59 additions & 0 deletions
59
OpenEphys.Onix/OpenEphys.Onix/ConfigureUclaMiniscopeV4LinkController.cs
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,59 @@ | ||
using System.Threading; | ||
|
||
namespace OpenEphys.Onix | ||
{ | ||
class ConfigureUclaMiniscopeV4LinkController : ConfigureFmcLinkController | ||
{ | ||
protected override bool ConfigurePortVoltage(DeviceContext device) | ||
{ | ||
const uint MinVoltage = 50; | ||
const uint MaxVoltage = 70; | ||
const uint VoltageOffset = 02; | ||
const uint VoltageIncrement = 02; | ||
|
||
for (uint voltage = MinVoltage; voltage <= MaxVoltage; voltage += VoltageIncrement) | ||
{ | ||
SetPortVoltage(device, voltage); | ||
if (CheckLinkState(device)) | ||
{ | ||
SetPortVoltage(device, voltage + VoltageOffset); | ||
return CheckLinkState(device); | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private void SetPortVoltage(DeviceContext device, uint voltage) | ||
{ | ||
const int WaitUntilVoltageSettles = 200; | ||
device.WriteRegister(FmcLinkController.PORTVOLTAGE, 0); | ||
Thread.Sleep(WaitUntilVoltageSettles); | ||
device.WriteRegister(FmcLinkController.PORTVOLTAGE, voltage); | ||
Thread.Sleep(WaitUntilVoltageSettles); | ||
} | ||
|
||
override protected bool CheckLinkState(DeviceContext device) | ||
{ | ||
try | ||
{ | ||
var ds90ub9x = device.Context.GetPassthroughDeviceContext(DeviceAddress << 8, DS90UB9x.ID); | ||
ConfigureUclaMiniscopeV4Camera.ConfigureMiniscope(ds90ub9x); | ||
} | ||
catch (oni.ONIException ex) | ||
{ | ||
// this can occur if power is too low, so we need to be able to try again | ||
const int FailureToWriteRegister = -6; | ||
if (ex.Number != FailureToWriteRegister) | ||
{ | ||
throw; | ||
} | ||
} | ||
|
||
Thread.Sleep(200); | ||
|
||
var linkState = device.ReadRegister(FmcLinkController.LINKSTATE); | ||
return (linkState & FmcLinkController.LINKSTATE_SL) != 0; | ||
} | ||
} | ||
} |
Oops, something went wrong.