Skip to content

Commit

Permalink
TeensyPUlseGenerator: Added option to wait for input signal
Browse files Browse the repository at this point in the history
before sending a trigger.  Modified the firmware to work correctly.
Make it possible to reverse polarity for input and output in the firmware.
  • Loading branch information
nicost committed Dec 27, 2024
1 parent 201f106 commit dd515dd
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 26 deletions.
44 changes: 44 additions & 0 deletions DeviceAdapters/TeensyPulseGenerator/CameraPulser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ const uint32_t g_Max_MMVersion = 1;
const char* g_versionProp = "Version";
const char* g_Undefined = "Undefined";
const char* g_IntervalBeyondExposure = "Interval-ms_on_top_of_exposure";
const char* g_WaitForInputMode = "Wait_for_Input";


const char* g_DeviceNameCameraPulser = "TeensySendsPulsesToCamera";

CameraPulser::CameraPulser() :
pulseDuration_(1.0),
intervalBeyondExposure_(5.0),
waitForInput_(false),
initialized_(false),
version_(0),
nrCamerasInUse_(0),
Expand Down Expand Up @@ -134,6 +136,17 @@ int CameraPulser::Initialize()
pAct = new CPropertyAction(this, &CameraPulser::OnIntervalBeyondExposure);
CreateFloatProperty(g_IntervalBeyondExposure, intervalBeyondExposure_, false, pAct);

// Trigger Mode property
uint32_t waitForInput;
ret = teensyCom_->GetWaitForInput(waitForInput);
if (ret != DEVICE_OK)
return ret;
waitForInput_ = (bool) waitForInput;
pAct = new CPropertyAction(this, &CameraPulser::OnWaitForInput);
CreateProperty(g_WaitForInputMode, waitForInput_ ? "On" : "Off", MM::String, false, pAct);
AddAllowedValue(g_WaitForInputMode, "Off");
AddAllowedValue(g_WaitForInputMode, "On");

initialized_ = true;

return DEVICE_OK;
Expand Down Expand Up @@ -825,3 +838,34 @@ int CameraPulser::OnIntervalBeyondExposure(MM::PropertyBase* pProp, MM::ActionTy
}
return DEVICE_OK;
}

int CameraPulser::OnWaitForInput(MM::PropertyBase* pProp, MM::ActionType eAct)
{

if (eAct == MM::BeforeGet)
{
pProp->Set(waitForInput_ ? "On" : "Off");
}
else if (eAct == MM::AfterSet)
{
std::string waitForInput;
pProp->Get(waitForInput);
waitForInput_ = (waitForInput == "On");

// Send wait for input command if initialized
if (initialized_)
{
uint32_t sp = waitForInput_ ? 1 : 0;
uint32_t param;
int ret = teensyCom_->SetWaitForInput(sp, param);
if (ret != DEVICE_OK)
return ret;
if (param != sp)
{
GetCoreCallback()->LogMessage(this, "WaitforInput sent not the same as echoed back", false);
return ERR_COMMUNICATION;
}
}
}
return DEVICE_OK;
}
2 changes: 2 additions & 0 deletions DeviceAdapters/TeensyPulseGenerator/CameraPulser.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class CameraPulser : public CCameraBase<CameraPulser>
int OnVersion(MM::PropertyBase* pProp, MM::ActionType pAct);
int OnPulseDuration(MM::PropertyBase* pProp, MM::ActionType eAct);
int OnIntervalBeyondExposure(MM::PropertyBase* pProp, MM::ActionType eAct);
int OnWaitForInput(MM::PropertyBase* pProp, MM::ActionType eAct);

private:
bool ImageSizesAreEqual();
Expand All @@ -75,6 +76,7 @@ class CameraPulser : public CCameraBase<CameraPulser>
std::string usedCamera_;
double intervalBeyondExposure_; // Interval beyond exposure time in ms
double pulseDuration_; // Pulse duration in milli-seconds
bool waitForInput_; // Whether to wait sending the pulse for the input to go high
bool initialized_;
unsigned int nrCamerasInUse_;
ImgBuffer img_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@

static const uint32_t version = 1;

uint8_t outputPin = LED_BUILTIN; // LED_BUILTIN;
uint8_t inputPin = 2;
const uint8_t outputPin = LED_BUILTIN; // Modify as desired. Teensy LED: LED_BUILTIN;
const bool activateOn = HIGH; // set to LOW if required by device
const uint8_t inputPin = 2; // MOdify as desired
const bool inputStateDeviceIsBusy = HIGH; // set to LOW if required


// Do not modify below this line
const int activate = activateOn;
const int inActivate = !activateOn;

/**
Note: The pulse pin nr and input pin nr are hard coded.
Expand Down Expand Up @@ -58,27 +65,27 @@ private:
IntervalTimer intervalTimer;

// State tracking
volatile bool isPulseActive = false;
volatile bool isReadyToTrigger = true;
volatile bool countPulses = false;
volatile bool startOnTrigger = false;
volatile uint32_t pulseNumber = 0;

// Static callback methods for timer interrupts
static PulseGenerator* volatile instancePtr;

const int inputEdge = inputStateDeviceIsBusy == LOW ? RISING : FALLING;

// Turn pulse off
static void stopPulseISR() {
if (instancePtr) {
digitalWriteFast(instancePtr->outputPin, LOW);
instancePtr->isPulseActive = false;
digitalWriteFast(instancePtr->outputPin, inActivate);
instancePtr->pulseTimer.end();
}
}

static inline void pulse() {
static inline void pulseISR() {
// Generate pulse
digitalWriteFast(instancePtr->outputPin, HIGH);
instancePtr->isPulseActive = true;
digitalWriteFast(instancePtr->outputPin, activate);
instancePtr->isReadyToTrigger = false;
instancePtr->pulseNumber++;

Expand All @@ -91,18 +98,22 @@ private:
if (instancePtr) {
instancePtr->isReadyToTrigger = true;
// If waiting for trigger and trigger not received, do nothing
if (instancePtr->waitForTrigger && digitalReadFast(instancePtr->triggerPin) == LOW) {
if (instancePtr->waitForTrigger && digitalReadFast(instancePtr->triggerPin) == inputStateDeviceIsBusy) {
return;
}
pulse();
pulseISR();
}
}

// Trigger pin interrupt handler
static void triggerPinISR() {
if (instancePtr && instancePtr->waitForTrigger && instancePtr->isReadyToTrigger) {
pulse();
}
if (instancePtr && instancePtr->startOnTrigger) {
instancePtr->intervalTimer.begin(instancePtr->intervalISR, instancePtr->pulseInterval);
instancePtr->startOnTrigger = false;
}
if (instancePtr && instancePtr->waitForTrigger && instancePtr->isReadyToTrigger) {
pulseISR();
}
}

public:
Expand All @@ -117,11 +128,11 @@ public:
pulseNumber(0) {
instancePtr = this;
pinMode(outputPin, OUTPUT);
digitalWriteFast(outputPin, LOW);
digitalWriteFast(outputPin, inActivate);

if (triggerPin != 255) {
pinMode(triggerPin, INPUT_PULLDOWN);
attachInterrupt(digitalPinToInterrupt(triggerPin), triggerPinISR, RISING);
attachInterrupt(digitalPinToInterrupt(triggerPin), triggerPinISR, inputEdge);
}
}

Expand Down Expand Up @@ -208,17 +219,16 @@ public:
}

void reportNumberOfPulses() {
Serial.write(6);
noInterrupts();
uint32_t number = numberOfPulses;
interrupts();
Serial.write(6);
Serial.write((byte *) &number, 4);
}

void start() {
// Reset state
isReadyToTrigger = true;
isPulseActive = false;
isRunning = true;

// Stop any existing timers
Expand All @@ -228,12 +238,19 @@ public:
stopPulseISR();

// no timers running, so we can set volatile variables without interupting interrupts
noInterrupts(); // Note that triggerPinISR is always running
pulseNumber = 0;
interrupts();

// Start interval timer to manage pulse cycles
// start first pulse here, otherwise we would wait pulseInterval for the first pulse
intervalTimer.begin(intervalISR, pulseInterval);
pulse();

if (waitForTrigger && digitalReadFast(triggerPin) == inputStateDeviceIsBusy) {
startOnTrigger = true;
} else {
intervalTimer.begin(intervalISR, pulseInterval);
pulseISR();
}

// Simplified start confirmation
Serial.write(1);
Expand Down Expand Up @@ -266,8 +283,7 @@ public:

// Reset state
isRunning = false;
isPulseActive = false;
isReadyToTrigger = true;
isReadyToTrigger = false;
}

void stop() {
Expand All @@ -282,14 +298,11 @@ public:
// Only run if active
noInterrupts();
bool active = isRunning;
bool stop = countPulses && pulseNumber == numberOfPulses;
interrupts();
if (!active)
return;

noInterrupts();
bool stop = countPulses && pulseNumber == numberOfPulses;
interrupts();

if (stop) {
stopNoSerialMessage();
return;
Expand Down Expand Up @@ -407,11 +420,14 @@ void setup() {
// Give some time for serial to initialize
delay(1000);

pinMode(inputPin, INPUT_PULLUP);
pinMode(outputPin, OUTPUT);

// Create pulse generator on pin 13 with optional trigger on pin 12
pulsegen = new PulseGenerator(outputPin, inputPin);

// Configure initial defaults
pulsegen->configure(50000, 500000, 0, false);
pulsegen->configure(1000, 100000, 0, false);
}

void loop() {
Expand Down

0 comments on commit dd515dd

Please sign in to comment.