Updates for V5.0

This commit is contained in:
Agustín Gimenez 2023-07-20 17:07:27 +02:00
parent 7f96804d3e
commit 3a5e9bfee2
25 changed files with 486 additions and 73 deletions

View File

@ -10,6 +10,7 @@
#include "pico/multicore.h" #include "pico/multicore.h"
#include "LogicAnalyzer.pio.h" #include "LogicAnalyzer.pio.h"
#include "LogicAnalyzer_Structs.h" #include "LogicAnalyzer_Structs.h"
#include "tusb.h"
#ifdef WS2812_LED #ifdef WS2812_LED
#include "LogicAnalyzer_W2812.h" #include "LogicAnalyzer_W2812.h"
@ -85,6 +86,8 @@ CAPTURE_REQUEST* req;
#ifdef USE_CYGW_WIFI #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) void storeSettings(WIFI_SETTINGS* settings)
{ {
uint8_t buffer[FLASH_PAGE_SIZE]; uint8_t buffer[FLASH_PAGE_SIZE];
@ -129,6 +132,10 @@ void storeSettings(WIFI_SETTINGS* settings)
} }
#endif #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) void sendResponse(const char* response, bool toWiFi)
{ {
#ifdef USE_CYGW_WIFI #ifdef USE_CYGW_WIFI
@ -146,6 +153,10 @@ void sendResponse(const char* response, bool toWiFi)
printf(response); 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) void processData(uint8_t* data, uint length, bool fromWiFi)
{ {
for(uint pos = 0; pos < length; pos++) 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 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); 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 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 #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. //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) bool processUSBInput(bool skipProcessing)
{ {
//Try to get char //Try to get char
@ -321,11 +370,14 @@ bool processUSBInput(bool skipProcessing)
#ifdef USE_CYGW_WIFI #ifdef USE_CYGW_WIFI
/// @brief Purges any pending data in the USB input
void purgeUSBData() void purgeUSBData()
{ {
while(getchar_timeout_us(0) != PICO_ERROR_TIMEOUT); while(getchar_timeout_us(0) != PICO_ERROR_TIMEOUT);
} }
/// @brief Callback for the WiFi event queue
/// @param event Received event
void wifiEvent(void* event) void wifiEvent(void* event)
{ {
EVENT_FROM_WIFI* wEvent = (EVENT_FROM_WIFI*)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 processWiFiInput(bool skipProcessing)
{ {
bool res = event_has_events(&wifiToFrontend); bool res = event_has_events(&wifiToFrontend);
@ -372,6 +427,7 @@ bool processWiFiInput(bool skipProcessing)
#endif #endif
/// @brief Process input data from the host application if it is available
void processInput() void processInput()
{ {
#ifdef USE_CYGW_WIFI #ifdef USE_CYGW_WIFI
@ -384,6 +440,8 @@ void processInput()
#endif #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() bool processCancel()
{ {
#ifdef USE_CYGW_WIFI #ifdef USE_CYGW_WIFI
@ -397,6 +455,8 @@ bool processCancel()
#endif #endif
} }
/// @brief Main app loop
/// @return Exit code
int main() int main()
{ {
//Overclock Powerrrr! //Overclock Powerrrr!
@ -454,18 +514,10 @@ int main()
event_push(&frontendToWifi, &evt); event_push(&frontendToWifi, &evt);
} }
else else
{ cdc_transfer(lengthPointer, 4);
putchar_raw(lengthPointer[0]);
putchar_raw(lengthPointer[1]);
putchar_raw(lengthPointer[2]);
putchar_raw(lengthPointer[3]);
}
#else #else
putchar_raw(lengthPointer[0]); cdc_transfer(lengthPointer, 4);
putchar_raw(lengthPointer[1]);
putchar_raw(lengthPointer[2]);
putchar_raw(lengthPointer[3]);
#endif #endif
sleep_ms(100); sleep_ms(100);
@ -513,23 +565,24 @@ int main()
} }
else else
{ {
for(int buc = 0; buc < length; buc++) if(first + length > CAPTURE_BUFFER_SIZE)
{ {
putchar_raw(buffer[first++]); cdc_transfer(buffer + first, CAPTURE_BUFFER_SIZE - first);
cdc_transfer(buffer, (first + length) - CAPTURE_BUFFER_SIZE);
if(first >= 131072)
first = 0;
} }
else
cdc_transfer(buffer + first, length);
} }
#else #else
//Send the samples
for(int buc = 0; buc < length; buc++)
{
putchar_raw(buffer[first++]);
if(first >= 131072) if(first + length > CAPTURE_BUFFER_SIZE)
first = 0; {
cdc_transfer(buffer + first, CAPTURE_BUFFER_SIZE - first);
cdc_transfer(buffer, (first + length) - CAPTURE_BUFFER_SIZE);
} }
else
cdc_transfer(buffer + first, length);
#endif #endif
//Done! //Done!
capturing = false; capturing = false;

View File

@ -2,7 +2,9 @@
.program POSITIVE_CAPTURE .program POSITIVE_CAPTURE
pull 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 .wrap_target
@ -16,19 +18,28 @@ POST_CAPTURE:
in pins 32 ;read sample in pins 32 ;read sample
jmp x-- POST_CAPTURE ;loop if more samples needed 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 irq 0 ;notify to the main program that we have finished capturing
LOCK: LOCK:
jmp LOCK ;block the program 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 .program NEGATIVE_CAPTURE
.wrap_target
pull 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: PRE_CAPTURE:
@ -37,15 +48,24 @@ PRE_CAPTURE:
POST_CAPTURE: POST_CAPTURE:
.wrap_target
in pins 32 ;read sample in pins 32 ;read sample
jmp x-- POST_CAPTURE ;loop if more samples needed 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 irq 0 ;notify to the main program that we have finished capturing
LOCK: LOCK:
jmp LOCK ;block the program jmp LOCK ;block the program
LOOP:
mov x, osr ;read loop count
INNER_LOOP:
jmp pin INNER_LOOP ;wait for trigger
.wrap .wrap
;-------------------------------------------------------------------------------------------- ;--------------------------------------------------------------------------------------------
@ -131,6 +151,8 @@ LOCK:
#include "string.h" #include "string.h"
#include "hardware/sync.h" #include "hardware/sync.h"
#define CAPTURE_BUFFER_SIZE (128 * 1024)
typedef enum typedef enum
{ {
MODE_8_CHANNEL, 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 lastTriggerCapture; //Moment where the trigger happened inside the circular pre buffer
static uint32_t lastPreSize; //Pre-trigger buffer size static uint32_t lastPreSize; //Pre-trigger buffer size
static uint32_t lastPostSize; //Post-trigger buffer size static uint32_t lastPostSize; //Post-trigger buffer size
static uint32_t lastLoopCount; //Number of loops
static bool lastTriggerInverted; //Inverted? static bool lastTriggerInverted; //Inverted?
static uint8_t lastTriggerPin; static uint8_t lastTriggerPin;
static uint32_t lastStartPosition; static uint32_t lastStartPosition;
@ -187,7 +210,7 @@ static bool captureProcessed;
#endif #endif
//Main capture buffer, aligned at a 32k boundary, to use the maxixmum ring size supported by DMA channels //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------------------------------------ //--------------Complex trigger PIO program------------------------------------
@ -330,8 +353,8 @@ uint32_t find_capture_tail()
if(busy_channel == 0xFFFFFFFF) if(busy_channel == 0xFFFFFFFF)
return 0xFFFFFFFF; return 0xFFFFFFFF;
//Ok, now we need to know at which transfer the DMA is. The value equals to MAX_TRANSFERS - TRANSFERS_LEFT. //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; int32_t transfer = transferCount - dma_channel_hw_addr(busy_channel)->transfer_count - 1;
//Now compute the last capture position //Now compute the last capture position
transfer = (transfer + busy_offset) - 1; 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 //Store info about the capture
lastPreSize = preLength; lastPreSize = preLength;
lastPostSize = postLength; lastPostSize = postLength;
lastLoopCount = 0;
lastCapturePinCount = capturePinCount; lastCapturePinCount = capturePinCount;
lastCaptureComplexFast = true; lastCaptureComplexFast = true;
lastCaptureMode = captureMode; 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 //Store the PIO units and clear program memory
capturePIO = pio1; //Cannot clear it in PIO1 because the W uses PIO1 to transfer data capturePIO = pio1; //Cannot clear it in PIO1 because the W uses PIO1 to transfer data
triggerPIO = pio0; triggerPIO = pio0;
pio_clear_instruction_memory(triggerPIO); pio_clear_instruction_memory(triggerPIO);
//Configure 24 + 2 IO's to be used by the PIO (24 channels + 2 trigger pins) //Configure 24 + 2 IO's to be used by the PIO (24 channels + 2 trigger pins)
@ -802,6 +826,7 @@ bool startCaptureComplex(uint32_t freq, uint32_t preLength, uint32_t postLength,
//Store info about the capture //Store info about the capture
lastPreSize = preLength; lastPreSize = preLength;
lastPostSize = postLength; lastPostSize = postLength;
lastLoopCount = 0;
lastCapturePinCount = capturePinCount; lastCapturePinCount = capturePinCount;
lastCaptureComplexFast = true; lastCaptureComplexFast = true;
lastCaptureMode = captureMode; lastCaptureMode = captureMode;
@ -910,7 +935,7 @@ bool startCaptureComplex(uint32_t freq, uint32_t preLength, uint32_t postLength,
#endif #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; int maxSamples;
@ -950,6 +975,7 @@ bool startCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength,
//Store info about the capture //Store info about the capture
lastPreSize = preLength; lastPreSize = preLength;
lastPostSize = postLength; lastPostSize = postLength;
lastLoopCount = loopCount;
lastCapturePinCount = capturePinCount; lastCapturePinCount = capturePinCount;
lastTriggerInverted = invertTrigger; lastTriggerInverted = invertTrigger;
lastCaptureComplexFast = false; lastCaptureComplexFast = false;
@ -1028,9 +1054,11 @@ bool startCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength,
//Enable state machine //Enable state machine
pio_sm_set_enabled(capturePIO, sm_Capture, true); 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); pio_sm_put_blocking(capturePIO, sm_Capture, postLength - 1);
//Finally clear capture status, process flags and capture type //Finally clear capture status, process flags and capture type
captureFinished = false; captureFinished = false;
captureProcessed = false; captureProcessed = false;
@ -1048,6 +1076,9 @@ bool IsCapturing()
uint8_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample, CHANNEL_MODE* captureMode) 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 we don't have processed the buffer...
if(!captureProcessed) if(!captureProcessed)
{ {
@ -1066,11 +1097,13 @@ uint8_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample, CHANNEL_MODE* ca
break; break;
} }
//Calculate start position //Calculate start position
if(lastTail < lastPreSize + lastPostSize - 1) if(lastTail < totalSamples - 1)
lastStartPosition = maxSize - ((lastPreSize + lastPostSize) - (lastTail - 1)); lastStartPosition = (maxSize - totalSamples) + lastTail + 1;
else else
lastStartPosition = lastTail - (lastPreSize + lastPostSize) + 1; lastStartPosition = lastTail - totalSamples + 1;
uint32_t currentPos = lastStartPosition; uint32_t currentPos = lastStartPosition;
switch(lastCaptureMode) switch(lastCaptureMode)
{ {
case MODE_24_CHANNEL: case MODE_24_CHANNEL:
@ -1082,7 +1115,7 @@ uint8_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample, CHANNEL_MODE* ca
//Sort channels //Sort channels
//(reorder captured bits based on the channels requested) //(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 oldValue = buffer[currentPos]; //Store current value
newValue = 0; //New value newValue = 0; //New value
@ -1110,7 +1143,7 @@ uint8_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample, CHANNEL_MODE* ca
//Sort channels //Sort channels
//(reorder captured bits based on the channels requested) //(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 oldValue = buffer[currentPos]; //Store current value
newValue = 0; //New value newValue = 0; //New value
@ -1138,7 +1171,7 @@ uint8_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample, CHANNEL_MODE* ca
//Sort channels //Sort channels
//(reorder captured bits based on the channels requested) //(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 oldValue = buffer[currentPos]; //Store current value
newValue = 0; //New value newValue = 0; //New value
@ -1162,7 +1195,7 @@ uint8_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample, CHANNEL_MODE* ca
} }
//Return data //Return data
*captureMode = lastCaptureMode; *captureMode = lastCaptureMode;
*bufferSize = lastPreSize + lastPostSize; *bufferSize = totalSamples;
*firstSample = lastStartPosition; *firstSample = lastStartPosition;
return captureBuffer; return captureBuffer;
} }

View File

@ -1,7 +1,7 @@
#ifndef __BUILD_SETTINGS__ #ifndef __BUILD_SETTINGS__
#define __BUILD_SETTINGS__ #define __BUILD_SETTINGS__
#define FIRMWARE_VERSION "4_5" #define FIRMWARE_VERSION "V5_0"
//Select the board type to build the firmware for //Select the board type to build the firmware for
#define BUILD_PICO #define BUILD_PICO

View File

@ -33,6 +33,8 @@
uint32_t preSamples; uint32_t preSamples;
//Number of samples stored after the trigger //Number of samples stored after the trigger
uint32_t postSamples; uint32_t postSamples;
//Number of capture loops
uint8_t loopCount;
//Capture mode (0 = 8 channel, 1 = 16 channel, 2 = 24 channel) //Capture mode (0 = 8 channel, 1 = 16 channel, 2 = 24 channel)
uint8_t captureMode; uint8_t captureMode;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

After

Width:  |  Height:  |  Size: 2.4 MiB

View File

@ -6,7 +6,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ApplicationIcon>window.ico</ApplicationIcon> <ApplicationIcon>window.ico</ApplicationIcon>
<Version>4.5.1.0</Version> <Version>5.0.0.0</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -14,17 +14,25 @@ namespace CLCapture
{ {
[Value(0, Required = true, HelpText = "Device's serial port or IP address and port.")] [Value(0, Required = true, HelpText = "Device's serial port or IP address and port.")]
public string? AddressPort { get; set; } public string? AddressPort { get; set; }
[Value(1, Required = true, HelpText = "Desired sampling frequency.")] [Value(1, Required = true, HelpText = "Desired sampling frequency.")]
public int SamplingFrequency { get; set; } 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).")] [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; } public string? Channels { get; set; }
[Value(3, Required = true, HelpText = "Number of samples to capture before the trigger.")] [Value(3, Required = true, HelpText = "Number of samples to capture before the trigger.")]
public int PreSamples { get; set; } public int PreSamples { get; set; }
[Value(4, Required = true, HelpText = "Number of samples to capture after the trigger.")] [Value(4, Required = true, HelpText = "Number of samples to capture after the trigger.")]
public int PostSamples { get; set; } 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; } 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; } public string? OutputFile { get; set; }
} }

View File

@ -180,8 +180,7 @@ async Task<int> Capture(CLCaptureOptions opts)
if (opts.Trigger.TriggerType == CLTriggerType.Edge) if (opts.Trigger.TriggerType == CLTriggerType.Edge)
{ {
Console.WriteLine("Starting edge triggered capture..."); Console.WriteLine("Starting edge triggered capture...");
var resStart = driver.StartCapture(opts.SamplingFrequency, opts.PreSamples, opts.PostSamples, 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);
nChannels, opts.Trigger.Channel - 1, opts.Trigger.Value == "0", CaptureFinished);
if (resStart != CaptureError.None) if (resStart != CaptureError.None)
{ {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

After

Width:  |  Height:  |  Size: 2.4 MiB

View File

@ -11,12 +11,18 @@ namespace LogicAnalyzer.Classes
public int Frequency { get; set; } public int Frequency { get; set; }
public int PreTriggerSamples { get; set; } public int PreTriggerSamples { get; set; }
public int PostTriggerSamples { get; set; } public int PostTriggerSamples { get; set; }
public int LoopCount { get; set; }
public CaptureChannel[] CaptureChannels { get; set; } = new CaptureChannel[0]; public CaptureChannel[] CaptureChannels { get; set; } = new CaptureChannel[0];
public int TriggerType { get; set; } public int TriggerType { get; set; }
public int TriggerChannel { get; set; } public int TriggerChannel { get; set; }
public bool TriggerInverted { get; set; } public bool TriggerInverted { get; set; }
public int TriggerBitCount { get; set; } public int TriggerBitCount { get; set; }
public ushort TriggerPattern { get; set; } public ushort TriggerPattern { get; set; }
public CaptureSettings Clone()
{
return (CaptureSettings)MemberwiseClone();
}
} }
public class CaptureChannel public class CaptureChannel

View File

@ -67,7 +67,7 @@ namespace LogicAnalyzer.Controls
newChannelLabel.Text = channels[buc].TextualChannelNumber; newChannelLabel.Text = channels[buc].TextualChannelNumber;
newChannelLabel.Foreground = GraphicObjectsCache.GetBrush(AnalyzerColors.FgChannelColors[buc]); newChannelLabel.Foreground = GraphicObjectsCache.GetBrush(AnalyzerColors.FgChannelColors[buc % 24]);
newChannelGrid.Children.Add(newChannelLabel); newChannelGrid.Children.Add(newChannelLabel);

View File

@ -0,0 +1,7 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="LogicAnalyzer.Controls.SamplePreviewer">
</UserControl>

View File

@ -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));
}
}
}

View File

@ -16,6 +16,7 @@ namespace LogicAnalyzer.Controls
const int MIN_CHANNEL_HEIGHT = 48; const int MIN_CHANNEL_HEIGHT = 48;
public int PreSamples { get; set; } public int PreSamples { get; set; }
public int[]? Bursts { get; set; }
public UInt128[] Samples { get; set; } public UInt128[] Samples { get; set; }
public int ChannelCount { get; set; } public int ChannelCount { get; set; }
public int SamplesInScreen { get; set; } public int SamplesInScreen { get; set; }
@ -32,7 +33,9 @@ namespace LogicAnalyzer.Controls
Color sampleLineColor = Color.FromRgb(60, 60, 60); Color sampleLineColor = Color.FromRgb(60, 60, 60);
Color sampleDashColor = Color.FromArgb(60, 60, 60, 60); Color sampleDashColor = Color.FromArgb(60, 60, 60, 60);
Color triggerLineColor = Colors.White; Color triggerLineColor = Colors.White;
Color burstLineColor = Colors.Azure;
Color userLineColor = Colors.Cyan; Color userLineColor = Colors.Cyan;
public SampleViewer() public SampleViewer()
{ {
InitializeComponent(); InitializeComponent();
@ -101,7 +104,7 @@ namespace LogicAnalyzer.Controls
{ {
//context.FillRectangle(GraphicObjectsCache.GetBrush(AnalyzerColors.BgChannelColors[0]), thisBounds); //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; return;
double channelHeight = thisBounds.Height / (double)ChannelCount; double channelHeight = thisBounds.Height / (double)ChannelCount;
@ -148,6 +151,14 @@ namespace LogicAnalyzer.Controls
if (buc == PreSamples) if (buc == PreSamples)
context.DrawLine(GraphicObjectsCache.GetPen(triggerLineColor, 2), new Point(lineX, 0), new Point(lineX, thisBounds.Height)); 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) if(UserMarker != null && UserMarker == buc)
context.DrawLine(GraphicObjectsCache.GetPen(userLineColor, 2, DashStyle.DashDot), new Point(lineX, 0), new Point(lineX, thisBounds.Height)); context.DrawLine(GraphicObjectsCache.GetPen(userLineColor, 2, DashStyle.DashDot), new Point(lineX, 0), new Point(lineX, thisBounds.Height));

View File

@ -158,7 +158,12 @@
<RadioButton Name="rbTrigger23"></RadioButton> <RadioButton Name="rbTrigger23"></RadioButton>
<RadioButton Name="rbTrigger24"></RadioButton> <RadioButton Name="rbTrigger24"></RadioButton>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal">
<CheckBox Name="ckNegativeTrigger" Margin="10,0,0,10">Negative edge</CheckBox> <CheckBox Name="ckNegativeTrigger" Margin="10,0,0,10">Negative edge</CheckBox>
<CheckBox Name="ckBurst" Margin="20,0,0,10">Burst mode</CheckBox>
<TextBlock Margin="20,10,0,10">Burst count:</TextBlock>
<NumericUpDown IsEnabled="{Binding #ckBurst.IsChecked}" Width="135" Margin="10,0,0,10" Height="35" Minimum="2" Maximum="10000" Value="2" Name="nudBurstCount"></NumericUpDown>
</StackPanel>
</StackPanel> </StackPanel>
<RadioButton Margin="15,0,0,0" Name="rbTriggerTypePattern">Pattern trigger</RadioButton> <RadioButton Margin="15,0,0,0" Name="rbTriggerTypePattern">Pattern trigger</RadioButton>
<StackPanel Name="pnlPatternTrigger" Orientation="Horizontal" VerticalAlignment="Top" Margin="10,0,10,10" Background="#80303030"> <StackPanel Name="pnlPatternTrigger" Orientation="Horizontal" VerticalAlignment="Top" Margin="10,0,10,10" Background="#80303030">

View File

@ -178,6 +178,7 @@ namespace LogicAnalyzer.Dialogs
Frequency = oldset.Frequency, Frequency = oldset.Frequency,
PostTriggerSamples = oldset.PostTriggerSamples, PostTriggerSamples = oldset.PostTriggerSamples,
PreTriggerSamples = oldset.PreTriggerSamples, PreTriggerSamples = oldset.PreTriggerSamples,
LoopCount = 0,
TriggerBitCount = oldset.TriggerBitCount, TriggerBitCount = oldset.TriggerBitCount,
TriggerChannel = oldset.TriggerChannel, TriggerChannel = oldset.TriggerChannel,
TriggerInverted = oldset.TriggerInverted, TriggerInverted = oldset.TriggerInverted,
@ -216,6 +217,8 @@ namespace LogicAnalyzer.Dialogs
triggerChannels[settings.TriggerChannel].IsChecked = true; triggerChannels[settings.TriggerChannel].IsChecked = true;
ckNegativeTrigger.IsChecked = settings.TriggerInverted; ckNegativeTrigger.IsChecked = settings.TriggerInverted;
ckBurst.IsChecked = settings.LoopCount > 0;
nudBurstCount.Value = settings.LoopCount > 0 ? settings.LoopCount : 1;
rbTriggerTypePattern.IsChecked = false; rbTriggerTypePattern.IsChecked = false;
rbTriggerTypeEdge.IsChecked = true; rbTriggerTypeEdge.IsChecked = true;
@ -280,7 +283,9 @@ namespace LogicAnalyzer.Dialogs
int max = driver.GetLimits(channelsToCapture.Select(c => c.ChannelNumber).ToArray()).MaxTotalSamples; 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}."); await this.ShowError("Error", $"Total samples cannot exceed {max}.");
return; return;
@ -385,6 +390,7 @@ namespace LogicAnalyzer.Dialogs
settings.Frequency = (int)nudFrequency.Value; settings.Frequency = (int)nudFrequency.Value;
settings.PreTriggerSamples = (int)nudPreSamples.Value; settings.PreTriggerSamples = (int)nudPreSamples.Value;
settings.PostTriggerSamples = (int)nudPostSamples.Value; settings.PostTriggerSamples = (int)nudPostSamples.Value;
settings.LoopCount = loops;
settings.TriggerInverted = ckNegativeTrigger.IsChecked == true; settings.TriggerInverted = ckNegativeTrigger.IsChecked == true;
settings.CaptureChannels = channelsToCapture.ToArray(); settings.CaptureChannels = channelsToCapture.ToArray();

View File

@ -8,7 +8,7 @@
<BuiltInComInteropSupport>true</BuiltInComInteropSupport> <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationIcon>Assets\Ico40.ico</ApplicationIcon> <ApplicationIcon>Assets\Ico40.ico</ApplicationIcon>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<Version>4.5.1.0</Version> <Version>5.0.0.0</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove=".gitignore" /> <None Remove=".gitignore" />

View File

@ -47,7 +47,9 @@
<controls:SampleMarker Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Foreground="White" Background="Transparent" Name="sampleMarker"></controls:SampleMarker> <controls:SampleMarker Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Foreground="White" Background="Transparent" Name="sampleMarker"></controls:SampleMarker>
</Grid> </Grid>
<ScrollBar VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Orientation="Horizontal" DockPanel.Dock="Bottom" Name="scrSamplePos" AllowAutoHide="False"></ScrollBar> <ScrollBar VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Orientation="Horizontal" DockPanel.Dock="Bottom" Name="scrSamplePos" AllowAutoHide="False"></ScrollBar>
<controls:SamplePreviewer IsVisible="False" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Height="144" ZIndex="1000" Name="samplePreviewer" DockPanel.Dock="Bottom" Margin="0,-144,0,0"></controls:SamplePreviewer>
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ColumnDefinitions="*,240" DockPanel.Dock="Bottom"> <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ColumnDefinitions="*,240" DockPanel.Dock="Bottom">
<ScrollViewer VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> <ScrollViewer VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid ColumnDefinitions="140,*" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> <Grid ColumnDefinitions="140,*" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">

View File

@ -1,5 +1,6 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Threading; using Avalonia.Threading;
using AvaloniaEdit.Utils; using AvaloniaEdit.Utils;
@ -64,16 +65,115 @@ namespace LogicAnalyzer
tkInScreen.PropertyChanged += tkInScreen_ValueChanged; tkInScreen.PropertyChanged += tkInScreen_ValueChanged;
scrSamplePos.Scroll += scrSamplePos_ValueChanged; scrSamplePos.Scroll += scrSamplePos_ValueChanged;
scrSamplePos.PointerEnter += ScrSamplePos_PointerEnter;
scrSamplePos.PointerLeave += ScrSamplePos_PointerLeave;
mnuNew.Click += MnuNew_Click; mnuNew.Click += MnuNew_Click;
mnuOpen.Click += mnuOpen_Click; mnuOpen.Click += mnuOpen_Click;
mnuSave.Click += mnuSave_Click; mnuSave.Click += mnuSave_Click;
mnuExit.Click += MnuExit_Click; mnuExit.Click += MnuExit_Click;
mnuExport.Click += MnuExport_Click; mnuExport.Click += MnuExport_Click;
mnuNetSettings.Click += MnuNetSettings_Click; mnuNetSettings.Click += MnuNetSettings_Click;
AddHandler(InputElement.KeyDownEvent, MainWindow_KeyDown, handledEventsToo: true);
LoadAnalyzers(); LoadAnalyzers();
RefreshPorts(); 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) private async void SampleMarker_ShiftSamples(object? sender, EventArgs e)
{ {
var dlg = new ShiftChannelsDialog(); var dlg = new ShiftChannelsDialog();
@ -120,6 +220,8 @@ namespace LogicAnalyzer
sampleViewer.BeginUpdate(); sampleViewer.BeginUpdate();
sampleViewer.Samples = samples; sampleViewer.Samples = samples;
sampleViewer.EndUpdate(); sampleViewer.EndUpdate();
samplePreviewer.UpdateSamples(samples, sampleViewer.ChannelCount);
samplePreviewer.ViewPosition = sampleViewer.FirstSample;
} }
} }
@ -195,6 +297,9 @@ namespace LogicAnalyzer
sampleViewer.EndUpdate(); sampleViewer.EndUpdate();
samplePreviewer.ViewPosition = sampleViewer.FirstSample;
samplePreviewer.UpdateSamples(samples, sampleViewer.ChannelCount);
sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen; sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen;
sampleMarker.FirstSample = sampleViewer.FirstSample; sampleMarker.FirstSample = sampleViewer.FirstSample;
sampleMarker.ClearRegions(); sampleMarker.ClearRegions();
@ -331,14 +436,6 @@ namespace LogicAnalyzer
var lastSample = e.FirstSample + e.SampleCount - 1; var lastSample = e.FirstSample + e.SampleCount - 1;
var triggerSample = sampleViewer.PreSamples - 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 preDelete = sampleViewer.Samples.Take(e.FirstSample);
var postDelete = sampleViewer.Samples.Skip(e.FirstSample + e.SampleCount + 1); var postDelete = sampleViewer.Samples.Skip(e.FirstSample + e.SampleCount + 1);
@ -412,7 +509,8 @@ namespace LogicAnalyzer
{ {
sampleViewer.BeginUpdate(); sampleViewer.BeginUpdate();
sampleViewer.Samples = finalSamples; sampleViewer.Samples = finalSamples;
sampleViewer.PreSamples = finalPreSamples; sampleViewer.PreSamples = 0;
sampleViewer.Bursts = null;
if (sampleViewer.FirstSample > finalSamples.Length - 1) if (sampleViewer.FirstSample > finalSamples.Length - 1)
sampleViewer.FirstSample = finalSamples.Length - 1; sampleViewer.FirstSample = finalSamples.Length - 1;
@ -425,6 +523,9 @@ namespace LogicAnalyzer
sampleViewer.EndUpdate(); sampleViewer.EndUpdate();
samplePreviewer.UpdateSamples(finalSamples, sampleViewer.ChannelCount);
samplePreviewer.ViewPosition = sampleViewer.FirstSample;
sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen; sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen;
sampleMarker.FirstSample = sampleViewer.FirstSample; sampleMarker.FirstSample = sampleViewer.FirstSample;
sampleMarker.ClearRegions(); sampleMarker.ClearRegions();
@ -569,10 +670,30 @@ namespace LogicAnalyzer
sampleViewer.SamplesInScreen = Math.Min(100, e.Samples.Length / 10); sampleViewer.SamplesInScreen = Math.Min(100, e.Samples.Length / 10);
sampleViewer.FirstSample = Math.Max(e.PreSamples - 10, 0); sampleViewer.FirstSample = Math.Max(e.PreSamples - 10, 0);
if (settings.LoopCount > 0)
{
int pos = e.PreSamples;
List<int> bursts = new List<int>();
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.ClearRegions();
sampleViewer.ClearAnalyzedChannels(); sampleViewer.ClearAnalyzedChannels();
sampleViewer.EndUpdate(); sampleViewer.EndUpdate();
samplePreviewer.UpdateSamples(e.Samples, sampleViewer.ChannelCount);
samplePreviewer.ViewPosition = sampleViewer.FirstSample;
scrSamplePos.Maximum = e.Samples.Length - 1; scrSamplePos.Maximum = e.Samples.Length - 1;
scrSamplePos.Value = sampleViewer.FirstSample; scrSamplePos.Value = sampleViewer.FirstSample;
tkInScreen.Value = sampleViewer.SamplesInScreen; tkInScreen.Value = sampleViewer.SamplesInScreen;
@ -844,7 +965,7 @@ namespace LogicAnalyzer
} }
else 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) if (error != CaptureError.None)
{ {
@ -878,7 +999,15 @@ namespace LogicAnalyzer
return; 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) private void scrSamplePos_ValueChanged(object? sender, ScrollEventArgs e)
{ {
if (sampleViewer.Samples != null) if (sampleViewer.Samples != null)
@ -886,6 +1015,7 @@ namespace LogicAnalyzer
sampleViewer.BeginUpdate(); sampleViewer.BeginUpdate();
sampleViewer.FirstSample = (int)scrSamplePos.Value; sampleViewer.FirstSample = (int)scrSamplePos.Value;
sampleViewer.EndUpdate(); sampleViewer.EndUpdate();
samplePreviewer.ViewPosition = sampleViewer.FirstSample;
sampleMarker.FirstSample = sampleViewer.FirstSample; sampleMarker.FirstSample = sampleViewer.FirstSample;
} }
} }
@ -927,7 +1057,11 @@ namespace LogicAnalyzer
if (string.IsNullOrWhiteSpace(file)) if (string.IsNullOrWhiteSpace(file))
return; 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() })); File.WriteAllText(file, JsonConvert.SerializeObject(ex, new JsonConverter[] { new SampleRegion.SampleRegionConverter() }));
} }
@ -980,6 +1114,7 @@ namespace LogicAnalyzer
Frequency = oldset.Frequency, Frequency = oldset.Frequency,
PostTriggerSamples = oldset.PostTriggerSamples, PostTriggerSamples = oldset.PostTriggerSamples,
PreTriggerSamples = oldset.PreTriggerSamples, PreTriggerSamples = oldset.PreTriggerSamples,
LoopCount = 0,
TriggerBitCount = oldset.TriggerBitCount, TriggerBitCount = oldset.TriggerBitCount,
TriggerChannel = oldset.TriggerChannel, TriggerChannel = oldset.TriggerChannel,
TriggerInverted = oldset.TriggerInverted, TriggerInverted = oldset.TriggerInverted,
@ -1015,6 +1150,9 @@ namespace LogicAnalyzer
sampleViewer.EndUpdate(); sampleViewer.EndUpdate();
samplePreviewer.UpdateSamples(ex.Samples, sampleViewer.ChannelCount);
samplePreviewer.ViewPosition = sampleViewer.FirstSample;
sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen; sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen;
sampleMarker.FirstSample = sampleViewer.FirstSample; sampleMarker.FirstSample = sampleViewer.FirstSample;
sampleMarker.ClearRegions(); sampleMarker.ClearRegions();

View File

@ -62,7 +62,7 @@ namespace SharedDriver
throw new NotSupportedException(); throw new NotSupportedException();
} }
public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int[] Channels, int TriggerChannel, bool TriggerInverted, Action<CaptureEventArgs>? CaptureCompletedHandler = null) public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, int[] Channels, int TriggerChannel, bool TriggerInverted, Action<CaptureEventArgs>? CaptureCompletedHandler = null)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }

View File

@ -13,7 +13,7 @@ namespace SharedDriver
public int Channels { get; } public int Channels { get; }
public event EventHandler<CaptureEventArgs> CaptureCompleted; public event EventHandler<CaptureEventArgs> CaptureCompleted;
public bool SendNetworkConfig(string AccesPointName, string Password, string IPAddress, ushort Port); 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<CaptureEventArgs>? CaptureCompletedHandler = null); public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, int[] Channels, int TriggerChannel, bool TriggerInverted, Action<CaptureEventArgs>? CaptureCompletedHandler = null);
public CaptureError StartPatternCapture(int Frequency, int PreSamples, int PostSamples, int[] Channels, int TriggerChannel, int TriggerBitCount, UInt16 TriggerPattern, bool Fast, Action<CaptureEventArgs>? CaptureCompletedHandler = null); public CaptureError StartPatternCapture(int Frequency, int PreSamples, int PostSamples, int[] Channels, int TriggerChannel, int TriggerBitCount, UInt16 TriggerPattern, bool Fast, Action<CaptureEventArgs>? CaptureCompletedHandler = null);
public bool StopCapture(); public bool StopCapture();
public CaptureLimits GetLimits(int[] Channels); public CaptureLimits GetLimits(int[] Channels);

View File

@ -10,6 +10,7 @@ namespace SharedDriver
{ {
public class LogicAnalyzerDriver : IDisposable, IAnalizerDriver 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]+)"); Regex regAddressPort = new Regex("([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)\\:([0-9]+)");
StreamReader readResponse; StreamReader readResponse;
BinaryReader readData; BinaryReader readData;
@ -74,7 +75,26 @@ namespace SharedDriver
baseStream.ReadTimeout = 10000; baseStream.ReadTimeout = 10000;
DeviceVersion = readResponse.ReadLine(); 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; baseStream.ReadTimeout = Timeout.Infinite;
} }
private void InitNetwork(string AddressPort) private void InitNetwork(string AddressPort)
{ {
@ -138,7 +158,7 @@ namespace SharedDriver
return false; return false;
} }
public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int[] Channels, int TriggerChannel, bool TriggerInverted, Action<CaptureEventArgs>? CaptureCompletedHandler = null) public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, int[] Channels, int TriggerChannel, bool TriggerInverted, Action<CaptureEventArgs>? CaptureCompletedHandler = null)
{ {
if (capturing) if (capturing)
@ -159,25 +179,27 @@ namespace SharedDriver
var captureMode = GetCaptureMode(Channels); var captureMode = GetCaptureMode(Channels);
int requestedSamples = PreSamples + (PostSamples * ((byte)LoopCount + 1));
try try
{ {
switch (captureMode) switch (captureMode)
{ {
case 0: case 0:
if (PreSamples > 98303 || PostSamples > 131069 || PreSamples + PostSamples > 131071) if (PreSamples > 98303 || PostSamples > 131069 || requestedSamples > 131071)
return CaptureError.BadParams; return CaptureError.BadParams;
break; break;
case 1: case 1:
if (PreSamples > 49151 || PostSamples > 65533 || PreSamples + PostSamples > 65535) if (PreSamples > 49151 || PostSamples > 65533 || requestedSamples > 65535)
return CaptureError.BadParams; return CaptureError.BadParams;
break; break;
case 2: case 2:
if (PreSamples > 24576 || PostSamples > 32765 || PreSamples + PostSamples > 32767) if (PreSamples > 24576 || PostSamples > 32765 || requestedSamples > 32767)
return CaptureError.BadParams; return CaptureError.BadParams;
break; break;
} }
@ -197,6 +219,7 @@ namespace SharedDriver
frequency = (uint)Frequency, frequency = (uint)Frequency,
preSamples = (uint)PreSamples, preSamples = (uint)PreSamples,
postSamples = (uint)PostSamples, postSamples = (uint)PostSamples,
loopCount = (byte)LoopCount,
captureMode = captureMode captureMode = captureMode
}; };
@ -217,7 +240,7 @@ namespace SharedDriver
if (result == "CAPTURE_STARTED") if (result == "CAPTURE_STARTED")
{ {
capturing = true; capturing = true;
Task.Run(() => ReadCapture(PreSamples + PostSamples, captureMode)); Task.Run(() => ReadCapture(requestedSamples, captureMode));
return CaptureError.None; return CaptureError.None;
} }
return CaptureError.HardwareError; return CaptureError.HardwareError;
@ -547,6 +570,7 @@ namespace SharedDriver
public UInt32 frequency; public UInt32 frequency;
public UInt32 preSamples; public UInt32 preSamples;
public UInt32 postSamples; public UInt32 postSamples;
public byte loopCount;
public byte captureMode; public byte captureMode;
} }

View File

@ -10,7 +10,7 @@ namespace SharedDriver
{ {
public class MultiAnalizerDriver : IDisposable, IAnalizerDriver 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; LogicAnalyzerDriver[] connectedDevices;
UInt128[][]? tempCapture; UInt128[][]? tempCapture;
@ -79,7 +79,24 @@ namespace SharedDriver
} }
if (ver == null) if (ver == null)
{
ver = mVer.Groups[1].Value; 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 else
{ {
if (ver != mVer.Groups[1].Value) if (ver != mVer.Groups[1].Value)
@ -96,7 +113,7 @@ namespace SharedDriver
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int[] Channels, int TriggerChannel, bool TriggerInverted, Action<CaptureEventArgs>? CaptureCompletedHandler = null) public CaptureError StartCapture(int Frequency, int PreSamples, int PostSamples, int LoopCount, int[] Channels, int TriggerChannel, bool TriggerInverted, Action<CaptureEventArgs>? CaptureCompletedHandler = null)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
@ -163,7 +180,7 @@ namespace SharedDriver
continue; continue;
connectedDevices[buc].Tag = channelsCapturing; 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) if (err != CaptureError.None)
{ {