diff --git a/Firmware/LogicAnalyzer/DigitalSignalGenerator.pio b/Firmware/LogicAnalyzer/DigitalSignalGenerator.pio
deleted file mode 100644
index e69de29..0000000
diff --git a/Firmware/LogicAnalyzer/LogicAnalyzer.c b/Firmware/LogicAnalyzer/LogicAnalyzer.c
index 4760a15..e565d47 100644
--- a/Firmware/LogicAnalyzer/LogicAnalyzer.c
+++ b/Firmware/LogicAnalyzer/LogicAnalyzer.c
@@ -10,6 +10,7 @@
#include "pico/multicore.h"
#include "LogicAnalyzer.pio.h"
#include "LogicAnalyzer_Structs.h"
+#include "tusb.h"
#ifdef WS2812_LED
#include "LogicAnalyzer_W2812.h"
@@ -85,6 +86,8 @@ CAPTURE_REQUEST* req;
#ifdef USE_CYGW_WIFI
+/// @brief Stores a new WiFi configuration in the flash of the device
+/// @param settings Settings to store
void storeSettings(WIFI_SETTINGS* settings)
{
uint8_t buffer[FLASH_PAGE_SIZE];
@@ -129,6 +132,10 @@ void storeSettings(WIFI_SETTINGS* settings)
}
#endif
+
+/// @brief Sends a response message to the host application in string mode
+/// @param response The message to be sent (null terminated)
+/// @param toWiFi If true the message is sent to a WiFi endpoint, else to the USB connection through STDIO
void sendResponse(const char* response, bool toWiFi)
{
#ifdef USE_CYGW_WIFI
@@ -146,6 +153,10 @@ void sendResponse(const char* response, bool toWiFi)
printf(response);
}
+/// @brief Processes data received from the host application
+/// @param data The received data
+/// @param length Length of the data
+/// @param fromWiFi If true the message comes from a WiFi connection
void processData(uint8_t* data, uint length, bool fromWiFi)
{
for(uint pos = 0; pos < length; pos++)
@@ -208,7 +219,7 @@ void processData(uint8_t* data, uint length, bool fromWiFi)
else if(req->triggerType == 2) //start fast trigger capture
started = startCaptureFast(req->frequency, req->preSamples, req->postSamples, (uint8_t*)&req->channels, req->channelCount, req->trigger, req->count, req->triggerValue, req->captureMode);
else //Start simple trigger capture
- started = startCaptureSimple(req->frequency, req->preSamples, req->postSamples, (uint8_t*)&req->channels, req->channelCount, req->trigger, req->inverted, req->captureMode);
+ started = startCaptureSimple(req->frequency, req->preSamples, req->postSamples, req->loopCount, (uint8_t*)&req->channels, req->channelCount, req->trigger, req->inverted, req->captureMode);
#else
@@ -301,6 +312,44 @@ void processData(uint8_t* data, uint length, bool fromWiFi)
//have any data, but the capture request has a CAPTURE_REQUEST struct as data.
}
+/// @brief Transfer a buffer of data through USB using the TinyUSB CDC functions
+/// @param data Buffer of data to transfer
+/// @param len Length of the buffer
+void cdc_transfer(unsigned char* data, int len)
+{
+
+ int left = len;
+ int pos = 0;
+
+ while(left > 0)
+ {
+ int avail = (int) tud_cdc_write_available();
+
+ if(avail > left)
+ avail = left;
+
+ if(avail)
+ {
+ int transferred = (int) tud_cdc_write(data + pos, avail);
+ tud_task();
+ tud_cdc_write_flush();
+
+ pos += transferred;
+ left -= transferred;
+ }
+ else
+ {
+ tud_task();
+ tud_cdc_write_flush();
+ if (!tud_cdc_connected())
+ break;
+ }
+ }
+}
+
+/// @brief Receive and process USB data from the host application
+/// @param skipProcessing If true the received data is not processed (used for cleanup)
+/// @return True if anything is received, false if not
bool processUSBInput(bool skipProcessing)
{
//Try to get char
@@ -321,11 +370,14 @@ bool processUSBInput(bool skipProcessing)
#ifdef USE_CYGW_WIFI
+/// @brief Purges any pending data in the USB input
void purgeUSBData()
{
while(getchar_timeout_us(0) != PICO_ERROR_TIMEOUT);
}
+/// @brief Callback for the WiFi event queue
+/// @param event Received event
void wifiEvent(void* event)
{
EVENT_FROM_WIFI* wEvent = (EVENT_FROM_WIFI*)event;
@@ -353,6 +405,9 @@ void wifiEvent(void* event)
}
}
+/// @brief Receives and processes input from the host application (when connected through WiFi)
+/// @param skipProcessing /// @param skipProcessing If true the received data is not processed (used for cleanup)
+/// @return True if anything is received, false if not
bool processWiFiInput(bool skipProcessing)
{
bool res = event_has_events(&wifiToFrontend);
@@ -372,6 +427,7 @@ bool processWiFiInput(bool skipProcessing)
#endif
+/// @brief Process input data from the host application if it is available
void processInput()
{
#ifdef USE_CYGW_WIFI
@@ -384,6 +440,8 @@ void processInput()
#endif
}
+/// @brief Processes input data from the host application to check if there is any cancel capture request
+/// @return True if there was input data
bool processCancel()
{
#ifdef USE_CYGW_WIFI
@@ -397,6 +455,8 @@ bool processCancel()
#endif
}
+/// @brief Main app loop
+/// @return Exit code
int main()
{
//Overclock Powerrrr!
@@ -454,18 +514,10 @@ int main()
event_push(&frontendToWifi, &evt);
}
else
- {
- putchar_raw(lengthPointer[0]);
- putchar_raw(lengthPointer[1]);
- putchar_raw(lengthPointer[2]);
- putchar_raw(lengthPointer[3]);
- }
+ cdc_transfer(lengthPointer, 4);
#else
- putchar_raw(lengthPointer[0]);
- putchar_raw(lengthPointer[1]);
- putchar_raw(lengthPointer[2]);
- putchar_raw(lengthPointer[3]);
+ cdc_transfer(lengthPointer, 4);
#endif
sleep_ms(100);
@@ -513,23 +565,24 @@ int main()
}
else
{
- for(int buc = 0; buc < length; buc++)
+ if(first + length > CAPTURE_BUFFER_SIZE)
{
- putchar_raw(buffer[first++]);
-
- if(first >= 131072)
- first = 0;
+ cdc_transfer(buffer + first, CAPTURE_BUFFER_SIZE - first);
+ cdc_transfer(buffer, (first + length) - CAPTURE_BUFFER_SIZE);
}
+ else
+ cdc_transfer(buffer + first, length);
}
#else
- //Send the samples
- for(int buc = 0; buc < length; buc++)
- {
- putchar_raw(buffer[first++]);
- if(first >= 131072)
- first = 0;
+ if(first + length > CAPTURE_BUFFER_SIZE)
+ {
+ cdc_transfer(buffer + first, CAPTURE_BUFFER_SIZE - first);
+ cdc_transfer(buffer, (first + length) - CAPTURE_BUFFER_SIZE);
}
+ else
+ cdc_transfer(buffer + first, length);
+
#endif
//Done!
capturing = false;
diff --git a/Firmware/LogicAnalyzer/LogicAnalyzer.pio b/Firmware/LogicAnalyzer/LogicAnalyzer.pio
index f7353ce..60e5e04 100644
--- a/Firmware/LogicAnalyzer/LogicAnalyzer.pio
+++ b/Firmware/LogicAnalyzer/LogicAnalyzer.pio
@@ -2,7 +2,9 @@
.program POSITIVE_CAPTURE
pull
- out x 32 ;read capture length
+ out y 32 ;read loop count
+ pull
+ mov x, osr ;read capture length (use MOV instead of PULL so we can MOV it again on each loop)
.wrap_target
@@ -16,19 +18,28 @@ POST_CAPTURE:
in pins 32 ;read sample
jmp x-- POST_CAPTURE ;loop if more samples needed
+ jmp y-- LOOP ;jump to loop control
+
irq 0 ;notify to the main program that we have finished capturing
LOCK:
jmp LOCK ;block the program
+LOOP:
+ mov x, osr ;read loop count
+INNER_LOOP:
+
+ jmp pin POST_CAPTURE ;wait for trigger
+ jmp INNER_LOOP
+
;--------------------------------------------------------------------------------------------
.program NEGATIVE_CAPTURE
-.wrap_target
-
pull
- out x 32 ;read capture length
+ out y 32 ;read loop count
+ pull
+ mov x, osr ;read capture length (use MOV instead of PULL so we can MOV it again on each loop)
PRE_CAPTURE:
@@ -37,15 +48,24 @@ PRE_CAPTURE:
POST_CAPTURE:
+.wrap_target
+
in pins 32 ;read sample
jmp x-- POST_CAPTURE ;loop if more samples needed
+ jmp y-- LOOP ;jump to loop control
+
irq 0 ;notify to the main program that we have finished capturing
LOCK:
jmp LOCK ;block the program
+LOOP:
+ mov x, osr ;read loop count
+INNER_LOOP:
+ jmp pin INNER_LOOP ;wait for trigger
+
.wrap
;--------------------------------------------------------------------------------------------
@@ -131,6 +151,8 @@ LOCK:
#include "string.h"
#include "hardware/sync.h"
+#define CAPTURE_BUFFER_SIZE (128 * 1024)
+
typedef enum
{
MODE_8_CHANNEL,
@@ -161,6 +183,7 @@ static uint8_t lastCapturePinCount; //Count of captured pins
static uint32_t lastTriggerCapture; //Moment where the trigger happened inside the circular pre buffer
static uint32_t lastPreSize; //Pre-trigger buffer size
static uint32_t lastPostSize; //Post-trigger buffer size
+static uint32_t lastLoopCount; //Number of loops
static bool lastTriggerInverted; //Inverted?
static uint8_t lastTriggerPin;
static uint32_t lastStartPosition;
@@ -187,7 +210,7 @@ static bool captureProcessed;
#endif
//Main capture buffer, aligned at a 32k boundary, to use the maxixmum ring size supported by DMA channels
-static uint8_t captureBuffer[128 * 1024] __attribute__((aligned(32768)));
+static uint8_t captureBuffer[CAPTURE_BUFFER_SIZE] __attribute__((aligned(32768)));
//-----------------------------------------------------------------------------
//--------------Complex trigger PIO program------------------------------------
@@ -330,8 +353,8 @@ uint32_t find_capture_tail()
if(busy_channel == 0xFFFFFFFF)
return 0xFFFFFFFF;
- //Ok, now we need to know at which transfer the DMA is. The value equals to MAX_TRANSFERS - TRANSFERS_LEFT.
- int32_t transfer = transferCount - dma_channel_hw_addr(busy_channel)->transfer_count;
+ //Ok, now we need to know at which transfer the DMA is. The value equals to MAX_TRANSFERS - TRANSFERS_LEFT - 1 (DMA channel decrements transfer_count when it starts :/).
+ int32_t transfer = transferCount - dma_channel_hw_addr(busy_channel)->transfer_count - 1;
//Now compute the last capture position
transfer = (transfer + busy_offset) - 1;
@@ -636,6 +659,7 @@ bool startCaptureFast(uint32_t freq, uint32_t preLength, uint32_t postLength, co
//Store info about the capture
lastPreSize = preLength;
lastPostSize = postLength;
+ lastLoopCount = 0;
lastCapturePinCount = capturePinCount;
lastCaptureComplexFast = true;
lastCaptureMode = captureMode;
@@ -653,8 +677,8 @@ bool startCaptureFast(uint32_t freq, uint32_t preLength, uint32_t postLength, co
//Store the PIO units and clear program memory
capturePIO = pio1; //Cannot clear it in PIO1 because the W uses PIO1 to transfer data
-
triggerPIO = pio0;
+
pio_clear_instruction_memory(triggerPIO);
//Configure 24 + 2 IO's to be used by the PIO (24 channels + 2 trigger pins)
@@ -665,7 +689,7 @@ bool startCaptureFast(uint32_t freq, uint32_t preLength, uint32_t postLength, co
pio_gpio_init(capturePIO, pinMap[i]);
//Configure capture SM
- sm_Capture = pio_claim_unused_sm(capturePIO, true);
+ sm_Capture = pio_claim_unused_sm(capturePIO, true);
pio_sm_clear_fifos(capturePIO, sm_Capture);
pio_sm_restart(capturePIO, sm_Capture);
captureOffset = pio_add_program(capturePIO, &FAST_CAPTURE_program);
@@ -802,6 +826,7 @@ bool startCaptureComplex(uint32_t freq, uint32_t preLength, uint32_t postLength,
//Store info about the capture
lastPreSize = preLength;
lastPostSize = postLength;
+ lastLoopCount = 0;
lastCapturePinCount = capturePinCount;
lastCaptureComplexFast = true;
lastCaptureMode = captureMode;
@@ -910,7 +935,7 @@ bool startCaptureComplex(uint32_t freq, uint32_t preLength, uint32_t postLength,
#endif
-bool startCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPin, bool invertTrigger, CHANNEL_MODE captureMode)
+bool startCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength, uint8_t loopCount, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPin, bool invertTrigger, CHANNEL_MODE captureMode)
{
int maxSamples;
@@ -950,6 +975,7 @@ bool startCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength,
//Store info about the capture
lastPreSize = preLength;
lastPostSize = postLength;
+ lastLoopCount = loopCount;
lastCapturePinCount = capturePinCount;
lastTriggerInverted = invertTrigger;
lastCaptureComplexFast = false;
@@ -1028,8 +1054,10 @@ bool startCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength,
//Enable state machine
pio_sm_set_enabled(capturePIO, sm_Capture, true);
- //Write capture length to post program to start the capture process
+ //Write loop count and capture length to post program to start the capture process
+ pio_sm_put_blocking(capturePIO, sm_Capture, loopCount);
pio_sm_put_blocking(capturePIO, sm_Capture, postLength - 1);
+
//Finally clear capture status, process flags and capture type
captureFinished = false;
@@ -1048,6 +1076,9 @@ bool IsCapturing()
uint8_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample, CHANNEL_MODE* captureMode)
{
+ //Compute total sample count
+ uint32_t totalSamples = lastPreSize + (lastPostSize * (lastLoopCount + 1));
+
//If we don't have processed the buffer...
if(!captureProcessed)
{
@@ -1066,11 +1097,13 @@ uint8_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample, CHANNEL_MODE* ca
break;
}
//Calculate start position
- if(lastTail < lastPreSize + lastPostSize - 1)
- lastStartPosition = maxSize - ((lastPreSize + lastPostSize) - (lastTail - 1));
+ if(lastTail < totalSamples - 1)
+ lastStartPosition = (maxSize - totalSamples) + lastTail + 1;
else
- lastStartPosition = lastTail - (lastPreSize + lastPostSize) + 1;
+ lastStartPosition = lastTail - totalSamples + 1;
+
uint32_t currentPos = lastStartPosition;
+
switch(lastCaptureMode)
{
case MODE_24_CHANNEL:
@@ -1082,7 +1115,7 @@ uint8_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample, CHANNEL_MODE* ca
//Sort channels
//(reorder captured bits based on the channels requested)
- for(int buc = 0; buc < lastPreSize + lastPostSize; buc++)
+ for(int buc = 0; buc < totalSamples; buc++)
{
oldValue = buffer[currentPos]; //Store current value
newValue = 0; //New value
@@ -1110,7 +1143,7 @@ uint8_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample, CHANNEL_MODE* ca
//Sort channels
//(reorder captured bits based on the channels requested)
- for(int buc = 0; buc < lastPreSize + lastPostSize; buc++)
+ for(int buc = 0; buc < totalSamples; buc++)
{
oldValue = buffer[currentPos]; //Store current value
newValue = 0; //New value
@@ -1138,7 +1171,7 @@ uint8_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample, CHANNEL_MODE* ca
//Sort channels
//(reorder captured bits based on the channels requested)
- for(int buc = 0; buc < lastPreSize + lastPostSize; buc++)
+ for(int buc = 0; buc < totalSamples; buc++)
{
oldValue = buffer[currentPos]; //Store current value
newValue = 0; //New value
@@ -1162,7 +1195,7 @@ uint8_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample, CHANNEL_MODE* ca
}
//Return data
*captureMode = lastCaptureMode;
- *bufferSize = lastPreSize + lastPostSize;
+ *bufferSize = totalSamples;
*firstSample = lastStartPosition;
return captureBuffer;
}
diff --git a/Firmware/LogicAnalyzer/LogicAnalyzer_Build_Settings.h b/Firmware/LogicAnalyzer/LogicAnalyzer_Build_Settings.h
index 5b7efe3..fa79bdb 100644
--- a/Firmware/LogicAnalyzer/LogicAnalyzer_Build_Settings.h
+++ b/Firmware/LogicAnalyzer/LogicAnalyzer_Build_Settings.h
@@ -1,7 +1,7 @@
#ifndef __BUILD_SETTINGS__
#define __BUILD_SETTINGS__
- #define FIRMWARE_VERSION "4_5"
+ #define FIRMWARE_VERSION "V5_0"
//Select the board type to build the firmware for
#define BUILD_PICO
diff --git a/Firmware/LogicAnalyzer/LogicAnalyzer_Structs.h b/Firmware/LogicAnalyzer/LogicAnalyzer_Structs.h
index e2cdf3d..572018f 100644
--- a/Firmware/LogicAnalyzer/LogicAnalyzer_Structs.h
+++ b/Firmware/LogicAnalyzer/LogicAnalyzer_Structs.h
@@ -33,6 +33,8 @@
uint32_t preSamples;
//Number of samples stored after the trigger
uint32_t postSamples;
+ //Number of capture loops
+ uint8_t loopCount;
//Capture mode (0 = 8 channel, 1 = 16 channel, 2 = 24 channel)
uint8_t captureMode;
diff --git a/Software/LogicAnalyzer/Artwork/Logo40.png b/Software/LogicAnalyzer/Artwork/Logo40.png
index 721460d..ac9dd9c 100644
Binary files a/Software/LogicAnalyzer/Artwork/Logo40.png and b/Software/LogicAnalyzer/Artwork/Logo40.png differ
diff --git a/Software/LogicAnalyzer/Artwork/Logo40.psd b/Software/LogicAnalyzer/Artwork/Logo40.psd
index c89a935..16ae046 100644
Binary files a/Software/LogicAnalyzer/Artwork/Logo40.psd and b/Software/LogicAnalyzer/Artwork/Logo40.psd differ
diff --git a/Software/LogicAnalyzer/CLCapture/CLCapture.csproj b/Software/LogicAnalyzer/CLCapture/CLCapture.csproj
index 4f6bbef..a41225b 100644
--- a/Software/LogicAnalyzer/CLCapture/CLCapture.csproj
+++ b/Software/LogicAnalyzer/CLCapture/CLCapture.csproj
@@ -6,7 +6,7 @@
enable
enable
window.ico
- 4.5.1.0
+ 5.0.0.0
diff --git a/Software/LogicAnalyzer/CLCapture/CLCaptureOptions.cs b/Software/LogicAnalyzer/CLCapture/CLCaptureOptions.cs
index 074edd2..2526284 100644
--- a/Software/LogicAnalyzer/CLCapture/CLCaptureOptions.cs
+++ b/Software/LogicAnalyzer/CLCapture/CLCaptureOptions.cs
@@ -14,17 +14,25 @@ namespace CLCapture
{
[Value(0, Required = true, HelpText = "Device's serial port or IP address and port.")]
public string? AddressPort { get; set; }
+
[Value(1, Required = true, HelpText = "Desired sampling frequency.")]
public int SamplingFrequency { get; set; }
+
[Value(2, Required = true, HelpText = "List of channels to capture (channels sepparated by comma, can contain a name adding a semicolon after the channel number, no spaces allowed).")]
public string? Channels { get; set; }
+
[Value(3, Required = true, HelpText = "Number of samples to capture before the trigger.")]
public int PreSamples { get; set; }
+
[Value(4, Required = true, HelpText = "Number of samples to capture after the trigger.")]
public int PostSamples { get; set; }
- [Value(5, Required = true, HelpText = "Trigger definition in the form of \"TriggerType:(Edge, Fast or Complex),Channel:(base trigger channel),Value:(string containing 1's and 0's indicating each trigger chanel state)\".")]
+
+ [Value(5, Required = true, HelpText = "Number of bursts to capture (0 or 1 to disable burst mode).")]
+ public int LoopCount { get; set; }
+
+ [Value(6, Required = true, HelpText = "Trigger definition in the form of \"TriggerType:(Edge, Fast or Complex),Channel:(base trigger channel),Value:(string containing 1's and 0's indicating each trigger chanel state)\".")]
public CLTrigger? Trigger { get; set; }
- [Value(6, Required = true, HelpText = "Name of the output file.")]
+ [Value(7, Required = true, HelpText = "Name of the output file.")]
public string? OutputFile { get; set; }
}
diff --git a/Software/LogicAnalyzer/CLCapture/Program.cs b/Software/LogicAnalyzer/CLCapture/Program.cs
index 3ed7d9c..0b8ad7d 100644
--- a/Software/LogicAnalyzer/CLCapture/Program.cs
+++ b/Software/LogicAnalyzer/CLCapture/Program.cs
@@ -180,8 +180,7 @@ async Task Capture(CLCaptureOptions opts)
if (opts.Trigger.TriggerType == CLTriggerType.Edge)
{
Console.WriteLine("Starting edge triggered capture...");
- var resStart = driver.StartCapture(opts.SamplingFrequency, opts.PreSamples, opts.PostSamples,
- nChannels, opts.Trigger.Channel - 1, opts.Trigger.Value == "0", CaptureFinished);
+ var resStart = driver.StartCapture(opts.SamplingFrequency, opts.PreSamples, opts.PostSamples, opts.LoopCount < 2 ? 0 : opts.LoopCount - 1, nChannels, opts.Trigger.Channel - 1, opts.Trigger.Value == "0", CaptureFinished);
if (resStart != CaptureError.None)
{
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Assets/Logo40.png b/Software/LogicAnalyzer/LogicAnalyzer/Assets/Logo40.png
index 721460d..ac9dd9c 100644
Binary files a/Software/LogicAnalyzer/LogicAnalyzer/Assets/Logo40.png and b/Software/LogicAnalyzer/LogicAnalyzer/Assets/Logo40.png differ
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Classes/CaptureSettings.cs b/Software/LogicAnalyzer/LogicAnalyzer/Classes/CaptureSettings.cs
index 4ebbbd8..746db05 100644
--- a/Software/LogicAnalyzer/LogicAnalyzer/Classes/CaptureSettings.cs
+++ b/Software/LogicAnalyzer/LogicAnalyzer/Classes/CaptureSettings.cs
@@ -11,12 +11,18 @@ namespace LogicAnalyzer.Classes
public int Frequency { get; set; }
public int PreTriggerSamples { get; set; }
public int PostTriggerSamples { get; set; }
+ public int LoopCount { get; set; }
public CaptureChannel[] CaptureChannels { get; set; } = new CaptureChannel[0];
public int TriggerType { get; set; }
public int TriggerChannel { get; set; }
public bool TriggerInverted { get; set; }
public int TriggerBitCount { get; set; }
public ushort TriggerPattern { get; set; }
+
+ public CaptureSettings Clone()
+ {
+ return (CaptureSettings)MemberwiseClone();
+ }
}
public class CaptureChannel
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/ChannelViewer.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Controls/ChannelViewer.axaml.cs
index 9edde7f..fdef170 100644
--- a/Software/LogicAnalyzer/LogicAnalyzer/Controls/ChannelViewer.axaml.cs
+++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/ChannelViewer.axaml.cs
@@ -67,7 +67,7 @@ namespace LogicAnalyzer.Controls
newChannelLabel.Text = channels[buc].TextualChannelNumber;
- newChannelLabel.Foreground = GraphicObjectsCache.GetBrush(AnalyzerColors.FgChannelColors[buc]);
+ newChannelLabel.Foreground = GraphicObjectsCache.GetBrush(AnalyzerColors.FgChannelColors[buc % 24]);
newChannelGrid.Children.Add(newChannelLabel);
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SamplePreviewer.axaml b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SamplePreviewer.axaml
new file mode 100644
index 0000000..a74303e
--- /dev/null
+++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SamplePreviewer.axaml
@@ -0,0 +1,7 @@
+
+
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SamplePreviewer.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SamplePreviewer.axaml.cs
new file mode 100644
index 0000000..a3bef3a
--- /dev/null
+++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SamplePreviewer.axaml.cs
@@ -0,0 +1,102 @@
+using Avalonia.Controls;
+using Avalonia.Media;
+using Avalonia.Media.Imaging;
+using LogicAnalyzer.Classes;
+using MessageBox.Avalonia.DTO;
+using SkiaSharp;
+using System;
+
+namespace LogicAnalyzer.Controls
+{
+ public partial class SamplePreviewer : UserControl
+ {
+ Bitmap? bmp;
+ int sampleCount = 0;
+
+ int viewPosition;
+ public int ViewPosition { get { return viewPosition; } set { viewPosition = value; InvalidateVisual(); } }
+
+ public SamplePreviewer()
+ {
+ InitializeComponent();
+ }
+
+ public void UpdateSamples(UInt128[] Samples, int ChannelCount)
+ {
+ if (ChannelCount > 24)
+ ChannelCount = 24;
+
+ int width = Math.Max(Math.Min(Samples.Length, 4096), 1024);
+
+ float cHeight = 144 / (float)ChannelCount;
+ float sWidth = (float)width / (float)Samples.Length;
+ float high = cHeight / 6;
+ float low = cHeight - high;
+
+ using SKBitmap skb = new SKBitmap(width, 144);
+
+ SKPaint[] colors = new SKPaint[ChannelCount];
+
+ for (int buc = 0; buc < ChannelCount; buc++)
+ {
+ var avColor = AnalyzerColors.FgChannelColors[buc];
+
+ colors[buc] = new SKPaint
+ {
+ Style = SKPaintStyle.Stroke,
+ StrokeWidth = 1,
+ Color = new SKColor(avColor.R, avColor.G, avColor.B)
+ };
+ }
+
+ using (var canvas = new SKCanvas(skb))
+ {
+ for (int x = 0; x < Samples.Length; x++)
+ {
+ UInt128 sample = Samples[x];
+ UInt128 prevSample = Samples[x == 0 ? x : x - 1];
+
+ for (int chan = 0; chan < ChannelCount; chan++)
+ {
+ UInt128 curVal = sample & ((UInt128)1 << chan);
+ UInt128 prevVal = prevSample & ((UInt128)1 << chan);
+
+ float y = chan * cHeight + (curVal != 0 ? high : low);
+
+ canvas.DrawLine(x * sWidth, y, (x + 1) * sWidth, y, colors[chan]);
+
+ if (curVal != prevVal)
+ canvas.DrawLine(x * sWidth, chan * cHeight + high, x * sWidth, chan * cHeight + low, colors[chan]);
+ }
+ }
+ }
+
+ using var encoded = skb.Encode(SKEncodedImageFormat.Png, 1);
+ using var stream = encoded.AsStream();
+
+ if (bmp != null)
+ bmp.Dispose();
+
+ bmp = new Bitmap(stream);
+ sampleCount = Samples.Length;
+ }
+
+ public override void Render(DrawingContext context)
+ {
+ //base.Render(context);
+ var bounds = new Avalonia.Rect(0, 0, this.Bounds.Width, this.Bounds.Height);
+
+ context.FillRectangle(GraphicObjectsCache.GetBrush(Color.Parse("#222222")), bounds);
+
+ if (sampleCount == 0 || bmp == null)
+ return;
+
+ (bmp as IImage).Draw(context, new Avalonia.Rect(bmp.Size), bounds, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode.HighQuality);
+
+ float ratio = (float)bounds.Size.Width / (float)sampleCount;
+ float pos = viewPosition * ratio;
+
+ context.DrawLine(GraphicObjectsCache.GetPen(Colors.White, 1, DashStyle.Dash), new Avalonia.Point(pos, 0), new Avalonia.Point(pos, 143));
+ }
+ }
+}
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleViewer.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleViewer.axaml.cs
index c27fd51..4034d5c 100644
--- a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleViewer.axaml.cs
+++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleViewer.axaml.cs
@@ -16,6 +16,7 @@ namespace LogicAnalyzer.Controls
const int MIN_CHANNEL_HEIGHT = 48;
public int PreSamples { get; set; }
+ public int[]? Bursts { get; set; }
public UInt128[] Samples { get; set; }
public int ChannelCount { get; set; }
public int SamplesInScreen { get; set; }
@@ -32,7 +33,9 @@ namespace LogicAnalyzer.Controls
Color sampleLineColor = Color.FromRgb(60, 60, 60);
Color sampleDashColor = Color.FromArgb(60, 60, 60, 60);
Color triggerLineColor = Colors.White;
+ Color burstLineColor = Colors.Azure;
Color userLineColor = Colors.Cyan;
+
public SampleViewer()
{
InitializeComponent();
@@ -101,7 +104,7 @@ namespace LogicAnalyzer.Controls
{
//context.FillRectangle(GraphicObjectsCache.GetBrush(AnalyzerColors.BgChannelColors[0]), thisBounds);
- if (PreSamples == 0 || Samples == null || ChannelCount == 0 || SamplesInScreen == 0 || updating)
+ if (Samples == null || ChannelCount == 0 || SamplesInScreen == 0 || updating)
return;
double channelHeight = thisBounds.Height / (double)ChannelCount;
@@ -148,6 +151,14 @@ namespace LogicAnalyzer.Controls
if (buc == PreSamples)
context.DrawLine(GraphicObjectsCache.GetPen(triggerLineColor, 2), new Point(lineX, 0), new Point(lineX, thisBounds.Height));
+ if (Bursts != null)
+ {
+ if (Bursts.Any(b => b == buc))
+ {
+ context.DrawLine(GraphicObjectsCache.GetPen(burstLineColor, 2, DashStyle.DashDot), new Point(lineX, 0), new Point(lineX, thisBounds.Height));
+ }
+ }
+
if(UserMarker != null && UserMarker == buc)
context.DrawLine(GraphicObjectsCache.GetPen(userLineColor, 2, DashStyle.DashDot), new Point(lineX, 0), new Point(lineX, thisBounds.Height));
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml
index 6f91c08..2b1fd3a 100644
--- a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml
+++ b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml
@@ -158,7 +158,12 @@
- Negative edge
+
+ Negative edge
+ Burst mode
+ Burst count:
+
+
Pattern trigger
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs
index d1e0e06..6df7fce 100644
--- a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs
+++ b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs
@@ -178,6 +178,7 @@ namespace LogicAnalyzer.Dialogs
Frequency = oldset.Frequency,
PostTriggerSamples = oldset.PostTriggerSamples,
PreTriggerSamples = oldset.PreTriggerSamples,
+ LoopCount = 0,
TriggerBitCount = oldset.TriggerBitCount,
TriggerChannel = oldset.TriggerChannel,
TriggerInverted = oldset.TriggerInverted,
@@ -216,6 +217,8 @@ namespace LogicAnalyzer.Dialogs
triggerChannels[settings.TriggerChannel].IsChecked = true;
ckNegativeTrigger.IsChecked = settings.TriggerInverted;
+ ckBurst.IsChecked = settings.LoopCount > 0;
+ nudBurstCount.Value = settings.LoopCount > 0 ? settings.LoopCount : 1;
rbTriggerTypePattern.IsChecked = false;
rbTriggerTypeEdge.IsChecked = true;
@@ -280,7 +283,9 @@ namespace LogicAnalyzer.Dialogs
int max = driver.GetLimits(channelsToCapture.Select(c => c.ChannelNumber).ToArray()).MaxTotalSamples;
- if (nudPreSamples.Value + nudPostSamples.Value > max)
+ int loops = (int)((ckBurst.IsChecked ?? false) ? nudBurstCount.Value - 1 : 0);
+
+ if (nudPreSamples.Value + (nudPostSamples.Value * (loops + 1)) > max)
{
await this.ShowError("Error", $"Total samples cannot exceed {max}.");
return;
@@ -385,6 +390,7 @@ namespace LogicAnalyzer.Dialogs
settings.Frequency = (int)nudFrequency.Value;
settings.PreTriggerSamples = (int)nudPreSamples.Value;
settings.PostTriggerSamples = (int)nudPostSamples.Value;
+ settings.LoopCount = loops;
settings.TriggerInverted = ckNegativeTrigger.IsChecked == true;
settings.CaptureChannels = channelsToCapture.ToArray();
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj b/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj
index 63a1db0..efd9494 100644
--- a/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj
+++ b/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj
@@ -8,7 +8,7 @@
true
Assets\Ico40.ico
True
- 4.5.1.0
+ 5.0.0.0
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml
index 03b1e5f..247caa9 100644
--- a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml
+++ b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml
@@ -46,8 +46,10 @@
+
+
diff --git a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs
index 2ed0239..fabdd77 100644
--- a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs
+++ b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs
@@ -1,5 +1,6 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
+using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Threading;
using AvaloniaEdit.Utils;
@@ -64,16 +65,115 @@ namespace LogicAnalyzer
tkInScreen.PropertyChanged += tkInScreen_ValueChanged;
scrSamplePos.Scroll += scrSamplePos_ValueChanged;
+ scrSamplePos.PointerEnter += ScrSamplePos_PointerEnter;
+ scrSamplePos.PointerLeave += ScrSamplePos_PointerLeave;
mnuNew.Click += MnuNew_Click;
mnuOpen.Click += mnuOpen_Click;
mnuSave.Click += mnuSave_Click;
mnuExit.Click += MnuExit_Click;
mnuExport.Click += MnuExport_Click;
mnuNetSettings.Click += MnuNetSettings_Click;
+
+ AddHandler(InputElement.KeyDownEvent, MainWindow_KeyDown, handledEventsToo: true);
+
LoadAnalyzers();
RefreshPorts();
}
+ private void MainWindow_KeyDown(object? sender, Avalonia.Input.KeyEventArgs e)
+ {
+ if (e.KeyModifiers == Avalonia.Input.KeyModifiers.Control)
+ {
+ switch (e.Key)
+ {
+ case Key.Left:
+ {
+ var currentVal = scrSamplePos.Value;
+ var maxVal = sampleViewer.SamplesInScreen;
+ int newVal = (int)currentVal - (maxVal / 10);
+
+ if (newVal < 0)
+ newVal = 0;
+
+ scrSamplePos.Value = newVal;
+ scrSamplePos_ValueChanged(scrSamplePos, null);
+ }
+ break;
+ case Key.Right:
+ {
+ var currentVal = scrSamplePos.Value;
+ var maxVal = sampleViewer.SamplesInScreen;
+ int newVal = (int)currentVal + (maxVal / 10);
+
+ if (newVal > scrSamplePos.Maximum)
+ newVal = (int)scrSamplePos.Maximum;
+
+ scrSamplePos.Value = newVal;
+ scrSamplePos_ValueChanged(scrSamplePos, null);
+ }
+ break;
+ case Key.Down:
+ {
+ var currentVal = scrSamplePos.Value;
+ var maxVal = sampleViewer.SamplesInScreen;
+ int newVal = (int)currentVal - maxVal;
+
+ if (newVal < 0)
+ newVal = 0;
+
+ scrSamplePos.Value = newVal;
+ scrSamplePos_ValueChanged(scrSamplePos, null);
+ }
+ break;
+ case Key.Up:
+ {
+ var currentVal = scrSamplePos.Value;
+ var maxVal = sampleViewer.SamplesInScreen;
+ int newVal = (int)currentVal + maxVal;
+
+ if (newVal > scrSamplePos.Maximum)
+ newVal = (int)scrSamplePos.Maximum;
+
+ scrSamplePos.Value = newVal;
+ scrSamplePos_ValueChanged(scrSamplePos, null);
+ }
+ break;
+ }
+ }
+ else if (e.KeyModifiers == KeyModifiers.Shift)
+ {
+ switch (e.Key)
+ {
+ case Key.Left:
+ {
+ var currentVal = scrSamplePos.Value;
+ int newVal = (int)currentVal - 1;
+
+ if (newVal < 0)
+ newVal = 0;
+
+ scrSamplePos.Value = newVal;
+ scrSamplePos_ValueChanged(scrSamplePos, null);
+ }
+ break;
+ case Key.Right:
+ {
+ var currentVal = scrSamplePos.Value;
+ int newVal = (int)currentVal + 1;
+
+ if (newVal > scrSamplePos.Maximum)
+ newVal = (int)scrSamplePos.Maximum;
+
+ scrSamplePos.Value = newVal;
+ scrSamplePos_ValueChanged(scrSamplePos, null);
+ }
+ break;
+
+ }
+
+ }
+ }
+
private async void SampleMarker_ShiftSamples(object? sender, EventArgs e)
{
var dlg = new ShiftChannelsDialog();
@@ -81,7 +181,7 @@ namespace LogicAnalyzer
if (await dlg.ShowDialog(this))
{
- var samples = sampleViewer.Samples;
+ var samples = sampleViewer.Samples;
foreach (var channel in dlg.ShiftedChannels)
{
@@ -120,6 +220,8 @@ namespace LogicAnalyzer
sampleViewer.BeginUpdate();
sampleViewer.Samples = samples;
sampleViewer.EndUpdate();
+ samplePreviewer.UpdateSamples(samples, sampleViewer.ChannelCount);
+ samplePreviewer.ViewPosition = sampleViewer.FirstSample;
}
}
@@ -195,6 +297,9 @@ namespace LogicAnalyzer
sampleViewer.EndUpdate();
+ samplePreviewer.ViewPosition = sampleViewer.FirstSample;
+ samplePreviewer.UpdateSamples(samples, sampleViewer.ChannelCount);
+
sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen;
sampleMarker.FirstSample = sampleViewer.FirstSample;
sampleMarker.ClearRegions();
@@ -331,14 +436,6 @@ namespace LogicAnalyzer
var lastSample = e.FirstSample + e.SampleCount - 1;
var triggerSample = sampleViewer.PreSamples - 1;
- var containsTrigger = e.FirstSample <= triggerSample && lastSample >= triggerSample;
-
- if (containsTrigger)
- {
- await this.ShowError("Error", "Cannot delete the trigger sample.");
- return;
- }
-
var preDelete = sampleViewer.Samples.Take(e.FirstSample);
var postDelete = sampleViewer.Samples.Skip(e.FirstSample + e.SampleCount + 1);
@@ -412,7 +509,8 @@ namespace LogicAnalyzer
{
sampleViewer.BeginUpdate();
sampleViewer.Samples = finalSamples;
- sampleViewer.PreSamples = finalPreSamples;
+ sampleViewer.PreSamples = 0;
+ sampleViewer.Bursts = null;
if (sampleViewer.FirstSample > finalSamples.Length - 1)
sampleViewer.FirstSample = finalSamples.Length - 1;
@@ -425,6 +523,9 @@ namespace LogicAnalyzer
sampleViewer.EndUpdate();
+ samplePreviewer.UpdateSamples(finalSamples, sampleViewer.ChannelCount);
+ samplePreviewer.ViewPosition = sampleViewer.FirstSample;
+
sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen;
sampleMarker.FirstSample = sampleViewer.FirstSample;
sampleMarker.ClearRegions();
@@ -569,10 +670,30 @@ namespace LogicAnalyzer
sampleViewer.SamplesInScreen = Math.Min(100, e.Samples.Length / 10);
sampleViewer.FirstSample = Math.Max(e.PreSamples - 10, 0);
+
+ if (settings.LoopCount > 0)
+ {
+ int pos = e.PreSamples;
+ List bursts = new List();
+
+ for (int buc = 0; buc < settings.LoopCount; buc++)
+ {
+ pos = pos + settings.PostTriggerSamples;
+ bursts.Add(pos);
+ }
+
+ sampleViewer.Bursts = bursts.ToArray();
+ }
+ else
+ sampleViewer.Bursts = null;
+
sampleViewer.ClearRegions();
sampleViewer.ClearAnalyzedChannels();
sampleViewer.EndUpdate();
+ samplePreviewer.UpdateSamples(e.Samples, sampleViewer.ChannelCount);
+ samplePreviewer.ViewPosition = sampleViewer.FirstSample;
+
scrSamplePos.Maximum = e.Samples.Length - 1;
scrSamplePos.Value = sampleViewer.FirstSample;
tkInScreen.Value = sampleViewer.SamplesInScreen;
@@ -844,7 +965,7 @@ namespace LogicAnalyzer
}
else
{
- var error = driver.StartCapture(settings.Frequency, settings.PreTriggerSamples, settings.PostTriggerSamples, settings.CaptureChannels.Select(c => c.ChannelNumber).ToArray(), settings.TriggerChannel, settings.TriggerInverted);
+ var error = driver.StartCapture(settings.Frequency, settings.PreTriggerSamples, settings.PostTriggerSamples, settings.LoopCount, settings.CaptureChannels.Select(c => c.ChannelNumber).ToArray(), settings.TriggerChannel, settings.TriggerInverted);
if (error != CaptureError.None)
{
@@ -878,7 +999,15 @@ namespace LogicAnalyzer
return;
}
}
+ private void ScrSamplePos_PointerLeave(object? sender, Avalonia.Input.PointerEventArgs e)
+ {
+ samplePreviewer.IsVisible = false;
+ }
+ private void ScrSamplePos_PointerEnter(object? sender, Avalonia.Input.PointerEventArgs e)
+ {
+ samplePreviewer.IsVisible = true;
+ }
private void scrSamplePos_ValueChanged(object? sender, ScrollEventArgs e)
{
if (sampleViewer.Samples != null)
@@ -886,6 +1015,7 @@ namespace LogicAnalyzer
sampleViewer.BeginUpdate();
sampleViewer.FirstSample = (int)scrSamplePos.Value;
sampleViewer.EndUpdate();
+ samplePreviewer.ViewPosition = sampleViewer.FirstSample;
sampleMarker.FirstSample = sampleViewer.FirstSample;
}
}
@@ -927,7 +1057,11 @@ namespace LogicAnalyzer
if (string.IsNullOrWhiteSpace(file))
return;
- ExportedCapture ex = new ExportedCapture { Settings = settings, Samples = sampleViewer.Samples, SelectedRegions = sampleViewer.SelectedRegions };
+ var sets = settings.Clone();
+ sets.PreTriggerSamples = sampleViewer.PreSamples;
+ sets.LoopCount = sampleViewer.Bursts?.Length ?? 0;
+
+ ExportedCapture ex = new ExportedCapture { Settings = sets, Samples = sampleViewer.Samples, SelectedRegions = sampleViewer.SelectedRegions };
File.WriteAllText(file, JsonConvert.SerializeObject(ex, new JsonConverter[] { new SampleRegion.SampleRegionConverter() }));
}
@@ -980,6 +1114,7 @@ namespace LogicAnalyzer
Frequency = oldset.Frequency,
PostTriggerSamples = oldset.PostTriggerSamples,
PreTriggerSamples = oldset.PreTriggerSamples,
+ LoopCount = 0,
TriggerBitCount = oldset.TriggerBitCount,
TriggerChannel = oldset.TriggerChannel,
TriggerInverted = oldset.TriggerInverted,
@@ -1015,6 +1150,9 @@ namespace LogicAnalyzer
sampleViewer.EndUpdate();
+ samplePreviewer.UpdateSamples(ex.Samples, sampleViewer.ChannelCount);
+ samplePreviewer.ViewPosition = sampleViewer.FirstSample;
+
sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen;
sampleMarker.FirstSample = sampleViewer.FirstSample;
sampleMarker.ClearRegions();
diff --git a/Software/LogicAnalyzer/SharedDriver/EmulatedAnalizerDriver.cs b/Software/LogicAnalyzer/SharedDriver/EmulatedAnalizerDriver.cs
index 49eb1d5..b774344 100644
--- a/Software/LogicAnalyzer/SharedDriver/EmulatedAnalizerDriver.cs
+++ b/Software/LogicAnalyzer/SharedDriver/EmulatedAnalizerDriver.cs
@@ -62,7 +62,7 @@ namespace SharedDriver
throw new NotSupportedException();
}
- public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null)
+ public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null)
{
throw new NotSupportedException();
}
diff --git a/Software/LogicAnalyzer/SharedDriver/IAnalizerDriver.cs b/Software/LogicAnalyzer/SharedDriver/IAnalizerDriver.cs
index da04fe8..cb5dba2 100644
--- a/Software/LogicAnalyzer/SharedDriver/IAnalizerDriver.cs
+++ b/Software/LogicAnalyzer/SharedDriver/IAnalizerDriver.cs
@@ -13,7 +13,7 @@ namespace SharedDriver
public int Channels { get; }
public event EventHandler CaptureCompleted;
public bool SendNetworkConfig(string AccesPointName, string Password, string IPAddress, ushort Port);
- public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null);
+ public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null);
public CaptureError StartPatternCapture(int Frequency, int PreSamples, int PostSamples, int[] Channels, int TriggerChannel, int TriggerBitCount, UInt16 TriggerPattern, bool Fast, Action? CaptureCompletedHandler = null);
public bool StopCapture();
public CaptureLimits GetLimits(int[] Channels);
diff --git a/Software/LogicAnalyzer/SharedDriver/LogicAnalyzerDriver.cs b/Software/LogicAnalyzer/SharedDriver/LogicAnalyzerDriver.cs
index 698d39f..b41ea40 100644
--- a/Software/LogicAnalyzer/SharedDriver/LogicAnalyzerDriver.cs
+++ b/Software/LogicAnalyzer/SharedDriver/LogicAnalyzerDriver.cs
@@ -10,6 +10,7 @@ namespace SharedDriver
{
public class LogicAnalyzerDriver : IDisposable, IAnalizerDriver
{
+ Regex regVersion = new Regex(".*?(V([0-9]+)_([0-9]+))$");
Regex regAddressPort = new Regex("([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)\\:([0-9]+)");
StreamReader readResponse;
BinaryReader readData;
@@ -74,7 +75,26 @@ namespace SharedDriver
baseStream.ReadTimeout = 10000;
DeviceVersion = readResponse.ReadLine();
+
+ var verMatch = regVersion.Match(DeviceVersion ?? "");
+
+ if (verMatch == null || !verMatch.Success || !verMatch.Groups[2].Success)
+ {
+ Dispose();
+ throw new DeviceConnectionException($"Invalid device version V{ (string.IsNullOrWhiteSpace(verMatch?.Value) ? "(unknown)" : verMatch?.Value) }, minimum supported version: V5_0");
+ }
+
+ int majorVer = int.Parse(verMatch.Groups[2].Value);
+
+ if (majorVer < 5)
+ {
+ Dispose();
+ throw new DeviceConnectionException($"Invalid device version V{verMatch.Value}, minimum supported version: V5_0");
+ }
+
baseStream.ReadTimeout = Timeout.Infinite;
+
+
}
private void InitNetwork(string AddressPort)
{
@@ -138,7 +158,7 @@ namespace SharedDriver
return false;
}
- public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null)
+ public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null)
{
if (capturing)
@@ -159,25 +179,27 @@ namespace SharedDriver
var captureMode = GetCaptureMode(Channels);
+ int requestedSamples = PreSamples + (PostSamples * ((byte)LoopCount + 1));
+
try
{
switch (captureMode)
{
case 0:
- if (PreSamples > 98303 || PostSamples > 131069 || PreSamples + PostSamples > 131071)
+ if (PreSamples > 98303 || PostSamples > 131069 || requestedSamples > 131071)
return CaptureError.BadParams;
break;
case 1:
- if (PreSamples > 49151 || PostSamples > 65533 || PreSamples + PostSamples > 65535)
+ if (PreSamples > 49151 || PostSamples > 65533 || requestedSamples > 65535)
return CaptureError.BadParams;
break;
case 2:
- if (PreSamples > 24576 || PostSamples > 32765 || PreSamples + PostSamples > 32767)
+ if (PreSamples > 24576 || PostSamples > 32765 || requestedSamples > 32767)
return CaptureError.BadParams;
break;
}
@@ -197,6 +219,7 @@ namespace SharedDriver
frequency = (uint)Frequency,
preSamples = (uint)PreSamples,
postSamples = (uint)PostSamples,
+ loopCount = (byte)LoopCount,
captureMode = captureMode
};
@@ -217,7 +240,7 @@ namespace SharedDriver
if (result == "CAPTURE_STARTED")
{
capturing = true;
- Task.Run(() => ReadCapture(PreSamples + PostSamples, captureMode));
+ Task.Run(() => ReadCapture(requestedSamples, captureMode));
return CaptureError.None;
}
return CaptureError.HardwareError;
@@ -547,6 +570,7 @@ namespace SharedDriver
public UInt32 frequency;
public UInt32 preSamples;
public UInt32 postSamples;
+ public byte loopCount;
public byte captureMode;
}
diff --git a/Software/LogicAnalyzer/SharedDriver/MultiAnalizerDriver.cs b/Software/LogicAnalyzer/SharedDriver/MultiAnalizerDriver.cs
index 6c116f6..0a26393 100644
--- a/Software/LogicAnalyzer/SharedDriver/MultiAnalizerDriver.cs
+++ b/Software/LogicAnalyzer/SharedDriver/MultiAnalizerDriver.cs
@@ -10,7 +10,7 @@ namespace SharedDriver
{
public class MultiAnalizerDriver : IDisposable, IAnalizerDriver
{
- Regex regVersion = new Regex(".*?(V[0-9]_[0-9])$");
+ Regex regVersion = new Regex(".*?(V([0-9]+)_([0-9]+))$");
LogicAnalyzerDriver[] connectedDevices;
UInt128[][]? tempCapture;
@@ -79,7 +79,24 @@ namespace SharedDriver
}
if (ver == null)
+ {
ver = mVer.Groups[1].Value;
+
+ if (mVer == null || !mVer.Success || !mVer.Groups[2].Success)
+ {
+ Dispose();
+ throw new DeviceConnectionException($"Invalid device version V{(string.IsNullOrWhiteSpace(mVer?.Value) ? "(unknown)" : mVer?.Value)}, minimum supported version: V5_0");
+ }
+
+ int majorVer = int.Parse(mVer.Groups[2].Value);
+
+ if (majorVer < 5)
+ {
+ Dispose();
+ throw new DeviceConnectionException($"Invalid device version V{mVer.Value}, minimum supported version: V5_0");
+ }
+
+ }
else
{
if (ver != mVer.Groups[1].Value)
@@ -96,7 +113,7 @@ namespace SharedDriver
{
throw new NotSupportedException();
}
- public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null)
+ public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, int[] Channels, int TriggerChannel, bool TriggerInverted, Action? CaptureCompletedHandler = null)
{
throw new NotSupportedException();
}
@@ -163,7 +180,7 @@ namespace SharedDriver
continue;
connectedDevices[buc].Tag = channelsCapturing;
- var err = connectedDevices[buc].StartCapture(Frequency, PreSamples + offset, PostSamples - offset, chan, 24, false);
+ var err = connectedDevices[buc].StartCapture(Frequency, PreSamples + offset, PostSamples - offset, 0, chan, 24, false);
if (err != CaptureError.None)
{