From e86cd5464e0fd1e62fcc2a048a10b9ae7bcf4251 Mon Sep 17 00:00:00 2001 From: alivender <13898766233@163.com> Date: Mon, 4 Aug 2025 14:31:58 +0800 Subject: [PATCH] =?UTF-8?q?add:=20=E9=80=BB=E8=BE=91=E5=88=86=E6=9E=90?= =?UTF-8?q?=E4=BB=AA=E5=8F=AF=E8=AE=BE=E7=BD=AE=E9=87=87=E6=A0=B7=E9=A2=91?= =?UTF-8?q?=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/LogicAnalyzerController.cs | 17 +++-- server/src/Peripherals/LogicAnalyzerClient.cs | 65 ++++++++++++++++- .../Services/HttpHdmiVideoStreamService.cs | 2 +- src/APIClient.ts | 25 ++++++- .../LogicAnalyzer/LogicAnalyzerManager.ts | 62 +++++++++++----- .../LogicAnalyzer/TriggerSettings.vue | 71 +++++++++++++++++++ 6 files changed, 215 insertions(+), 27 deletions(-) diff --git a/server/src/Controllers/LogicAnalyzerController.cs b/server/src/Controllers/LogicAnalyzerController.cs index f3c7343..f54eeeb 100644 --- a/server/src/Controllers/LogicAnalyzerController.cs +++ b/server/src/Controllers/LogicAnalyzerController.cs @@ -58,6 +58,10 @@ public class LogicAnalyzerController : ControllerBase /// public AnalyzerChannelDiv ChannelDiv { get; set; } = AnalyzerChannelDiv.EIGHT; /// + /// 时钟分频系数 + /// + public AnalyzerClockDiv ClockDiv { get; set; } = AnalyzerClockDiv.DIV1; + /// /// 信号触发配置列表 /// public SignalTriggerConfig[] SignalConfigs { get; set; } = Array.Empty(); @@ -248,6 +252,7 @@ public class LogicAnalyzerController : ControllerBase /// 深度 /// 预采样深度 /// 有效通道(0-[1],1-[2],2-[4],3-[8],4-[16],5-[32]) + /// 采样时钟分频系数 /// 操作结果 [HttpPost("SetCaptureParams")] [EnableCors("Users")] @@ -255,7 +260,7 @@ public class LogicAnalyzerController : ControllerBase [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] - public async Task SetCaptureParams(int capture_length, int pre_capture_length, AnalyzerChannelDiv channel_div) + public async Task SetCaptureParams(int capture_length, int pre_capture_length, AnalyzerChannelDiv channel_div, AnalyzerClockDiv clock_div) { try { @@ -269,18 +274,18 @@ public class LogicAnalyzerController : ControllerBase if (analyzer == null) return BadRequest("用户未绑定有效的实验板"); - var result = await analyzer.SetCaptureParams(capture_length, pre_capture_length, channel_div); + var result = await analyzer.SetCaptureParams(capture_length, pre_capture_length, channel_div, clock_div); if (!result.IsSuccessful) { - logger.Error($"设置深度、预采样深度、有效通道失败: {result.Error}"); - return StatusCode(StatusCodes.Status500InternalServerError, "设置深度、预采样深度、有效通道失败"); + logger.Error($"设置深度、预采样深度、有效通道、时钟分频失败: {result.Error}"); + return StatusCode(StatusCodes.Status500InternalServerError, "设置深度、预采样深度、有效通道、时钟分频失败"); } return Ok(result.Value); } catch (Exception ex) { - logger.Error(ex, "设置深度、预采样深度、有效通道失败时发生异常"); + logger.Error(ex, "设置深度、预采样深度、有效通道、时钟分频失败时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试"); } } @@ -332,7 +337,7 @@ public class LogicAnalyzerController : ControllerBase } // 设置深度、预采样深度、有效通道 var paramsResult = await analyzer.SetCaptureParams( - config.CaptureLength, config.PreCaptureLength, config.ChannelDiv); + config.CaptureLength, config.PreCaptureLength, config.ChannelDiv, config.ClockDiv); if (!paramsResult.IsSuccessful) { logger.Error($"设置深度、预采样深度、有效通道失败: {paramsResult.Error}"); diff --git a/server/src/Peripherals/LogicAnalyzerClient.cs b/server/src/Peripherals/LogicAnalyzerClient.cs index 799a57b..342ff5d 100644 --- a/server/src/Peripherals/LogicAnalyzerClient.cs +++ b/server/src/Peripherals/LogicAnalyzerClient.cs @@ -67,6 +67,7 @@ static class AnalyzerAddr public const UInt32 LOAD_NUM_ADDR = BASE + 0x0000_0002; public const UInt32 PRE_LOAD_NUM_ADDR = BASE + 0x0000_0003; public const UInt32 CAHNNEL_DIV_ADDR = BASE + 0x0000_0004; + public const UInt32 CLOCK_DIV_ADDR = BASE + 0x0000_0005; public const UInt32 DMA1_START_WRITE_ADDR = DMA1_BASE + 0x0000_0012; public const UInt32 DMA1_END_WRITE_ADDR = DMA1_BASE + 0x0000_0013; public const UInt32 DMA1_CAPTURE_CTRL_ADDR = DMA1_BASE + 0x0000_0014; @@ -138,6 +139,52 @@ public enum GlobalCaptureMode NOR = 0b11 } +/// +/// 逻辑分析仪采样时钟分频系数 +/// +public enum AnalyzerClockDiv +{ + /// + /// 1分频 + /// + DIV1 = 0x0000_0000, + + /// + /// 2分频 + /// + DIV2 = 0x0000_0001, + + /// + /// 4分频 + /// + DIV4 = 0x0000_0002, + + /// + /// 8分频 + /// + DIV8 = 0x0000_0003, + + /// + /// 16分频 + /// + DIV16 = 0x0000_0004, + + /// + /// 32分频 + /// + DIV32 = 0x0000_0005, + + /// + /// 64分频 + /// + DIV64 = 0x0000_0006, + + /// + /// 128分频 + /// + DIV128 = 0x0000_0007 +} + /// /// 信号M的操作符枚举 /// @@ -387,13 +434,14 @@ public class Analyzer } /// - /// 设置逻辑分析仪的深度、预采样深度、有效通道 + /// 设置逻辑分析仪的深度、预采样深度、有效通道、分频系数 /// /// 深度 /// 预采样深度 /// 有效通道(0-[1],1-[2],2-[4],3-[8],4-[16],5-[32]) + /// 采样时钟分频系数 /// 操作结果,成功返回true,否则返回异常信息 - public async ValueTask> SetCaptureParams(int capture_length, int pre_capture_length, AnalyzerChannelDiv channel_div) + public async ValueTask> SetCaptureParams(int capture_length, int pre_capture_length, AnalyzerChannelDiv channel_div, AnalyzerClockDiv clock_div) { if (capture_length == 0) capture_length = 1; if (pre_capture_length == 0) pre_capture_length = 1; @@ -462,6 +510,19 @@ public class Analyzer return new(new Exception("Failed to set CAHNNEL_DIV_ADDR")); } } + { + var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, AnalyzerAddr.CLOCK_DIV_ADDR, (UInt32)clock_div, this.timeout); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to set CLOCK_DIV_ADDR: {ret.Error}"); + return new(ret.Error); + } + if (!ret.Value) + { + logger.Error("WriteAddr to CLOCK_DIV_ADDR returned false"); + return new(new Exception("Failed to set CLOCK_DIV_ADDR")); + } + } return true; } diff --git a/server/src/Services/HttpHdmiVideoStreamService.cs b/server/src/Services/HttpHdmiVideoStreamService.cs index d1fbbdf..e00879a 100644 --- a/server/src/Services/HttpHdmiVideoStreamService.cs +++ b/server/src/Services/HttpHdmiVideoStreamService.cs @@ -16,7 +16,7 @@ public class HttpHdmiVideoStreamService : BackgroundService { private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private HttpListener? _httpListener; - private readonly int _serverPort = 4322; + private readonly int _serverPort = 6666; private readonly ConcurrentDictionary _hdmiInDict = new(); private bool _isEnabled = true; diff --git a/src/APIClient.ts b/src/APIClient.ts index 474a4b2..e48ad14 100644 --- a/src/APIClient.ts +++ b/src/APIClient.ts @@ -3948,9 +3948,10 @@ export class LogicAnalyzerClient { * @param capture_length (optional) 深度 * @param pre_capture_length (optional) 预采样深度 * @param channel_div (optional) 有效通道(0-[1],1-[2],2-[4],3-[8],4-[16],5-[32]) + * @param clock_div (optional) 采样时钟分频系数 * @return 操作结果 */ - setCaptureParams(capture_length: number | undefined, pre_capture_length: number | undefined, channel_div: AnalyzerChannelDiv | undefined, cancelToken?: CancelToken): Promise { + setCaptureParams(capture_length: number | undefined, pre_capture_length: number | undefined, channel_div: AnalyzerChannelDiv | undefined, clock_div: AnalyzerClockDiv | undefined, cancelToken?: CancelToken): Promise { let url_ = this.baseUrl + "/api/LogicAnalyzer/SetCaptureParams?"; if (capture_length === null) throw new Error("The parameter 'capture_length' cannot be null."); @@ -3964,6 +3965,10 @@ export class LogicAnalyzerClient { throw new Error("The parameter 'channel_div' cannot be null."); else if (channel_div !== undefined) url_ += "channel_div=" + encodeURIComponent("" + channel_div) + "&"; + if (clock_div === null) + throw new Error("The parameter 'clock_div' cannot be null."); + else if (clock_div !== undefined) + url_ += "clock_div=" + encodeURIComponent("" + clock_div) + "&"; url_ = url_.replace(/[?&]$/, ""); let options_: AxiosRequestConfig = { @@ -8233,6 +8238,18 @@ export enum AnalyzerChannelDiv { XXXII = 5, } +/** 逻辑分析仪采样时钟分频系数 */ +export enum AnalyzerClockDiv { + DIV1 = 0, + DIV2 = 1, + DIV4 = 2, + DIV8 = 3, + DIV16 = 4, + DIV32 = 5, + DIV64 = 6, + DIV128 = 7, +} + /** 捕获配置 */ export class CaptureConfig implements ICaptureConfig { /** 全局触发模式 */ @@ -8243,6 +8260,8 @@ export class CaptureConfig implements ICaptureConfig { preCaptureLength!: number; /** 有效通道 */ channelDiv!: AnalyzerChannelDiv; + /** 时钟分频系数 */ + clockDiv!: AnalyzerClockDiv; /** 信号触发配置列表 */ signalConfigs!: SignalTriggerConfig[]; @@ -8264,6 +8283,7 @@ export class CaptureConfig implements ICaptureConfig { this.captureLength = _data["captureLength"]; this.preCaptureLength = _data["preCaptureLength"]; this.channelDiv = _data["channelDiv"]; + this.clockDiv = _data["clockDiv"]; if (Array.isArray(_data["signalConfigs"])) { this.signalConfigs = [] as any; for (let item of _data["signalConfigs"]) @@ -8285,6 +8305,7 @@ export class CaptureConfig implements ICaptureConfig { data["captureLength"] = this.captureLength; data["preCaptureLength"] = this.preCaptureLength; data["channelDiv"] = this.channelDiv; + data["clockDiv"] = this.clockDiv; if (Array.isArray(this.signalConfigs)) { data["signalConfigs"] = []; for (let item of this.signalConfigs) @@ -8304,6 +8325,8 @@ export interface ICaptureConfig { preCaptureLength: number; /** 有效通道 */ channelDiv: AnalyzerChannelDiv; + /** 时钟分频系数 */ + clockDiv: AnalyzerClockDiv; /** 信号触发配置列表 */ signalConfigs: SignalTriggerConfig[]; } diff --git a/src/components/LogicAnalyzer/LogicAnalyzerManager.ts b/src/components/LogicAnalyzer/LogicAnalyzerManager.ts index baec10d..c830a7a 100644 --- a/src/components/LogicAnalyzer/LogicAnalyzerManager.ts +++ b/src/components/LogicAnalyzer/LogicAnalyzerManager.ts @@ -10,6 +10,7 @@ import { SignalTriggerConfig, SignalValue, AnalyzerChannelDiv, + AnalyzerClockDiv, } from "@/APIClient"; import { AuthManager } from "@/utils/AuthManager"; import { useAlertStore } from "@/components/Alert"; @@ -30,16 +31,8 @@ export type Channel = { // 全局模式选项 const globalModes = [ - { - value: GlobalCaptureMode.AND, - label: "AND", - description: "所有条件都满足时触发", - }, - { - value: GlobalCaptureMode.OR, - label: "OR", - description: "任一条件满足时触发", - }, + {value: GlobalCaptureMode.AND,label: "AND",description: "所有条件都满足时触发",}, + {value: GlobalCaptureMode.OR,label: "OR",description: "任一条件满足时触发",}, { value: GlobalCaptureMode.NAND, label: "NAND", description: "AND的非" }, { value: GlobalCaptureMode.NOR, label: "NOR", description: "OR的非" }, ]; @@ -76,12 +69,23 @@ const channelDivOptions = [ { value: 32, label: "32通道", description: "启用32个通道 (CH0-CH31)" }, ]; +const ClockDivOptions = [ + { value: AnalyzerClockDiv.DIV1, label: "120MHz", description: "采样频率120MHz" }, + { value: AnalyzerClockDiv.DIV2, label: "60MHz", description: "采样频率60MHz" }, + { value: AnalyzerClockDiv.DIV4, label: "30MHz", description: "采样频率30MHz" }, + { value: AnalyzerClockDiv.DIV8, label: "15MHz", description: "采样频率15MHz" }, + { value: AnalyzerClockDiv.DIV16, label: "7.5MHz", description: "采样频率7.5MHz" }, + { value: AnalyzerClockDiv.DIV32, label: "3.75MHz", description: "采样频率3.75MHz" }, + { value: AnalyzerClockDiv.DIV64, label: "1.875MHz", description: "采样频率1.875MHz" }, + { value: AnalyzerClockDiv.DIV128, label: "937.5KHz", description: "采样频率937.5KHz" }, +]; + // 捕获深度限制常量 const CAPTURE_LENGTH_MIN = 1024; // 最小捕获深度 1024 const CAPTURE_LENGTH_MAX = 0x10000000 - 0x01000000; // 最大捕获深度 // 预捕获深度限制常量 -const PRE_CAPTURE_LENGTH_MIN = 0; // 最小预捕获深度 0 +const PRE_CAPTURE_LENGTH_MIN = 2; // 最小预捕获深度 2 // 默认颜色数组 const defaultColors = [ @@ -95,9 +99,8 @@ const defaultColors = [ "#8C33FF", ]; -// 添加逻辑分析仪频率常量 -const LOGIC_ANALYZER_FREQUENCY = 125_000_000; // 125MHz -const SAMPLE_PERIOD_NS = 1_000_000_000 / LOGIC_ANALYZER_FREQUENCY; // 采样周期,单位:纳秒 +// 添加逻辑分析仪基础频率常量 +const BASE_LOGIC_ANALYZER_FREQUENCY = 120_000_000; // 120MHz基础频率 const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState( () => { @@ -112,6 +115,7 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState( const currentChannelDiv = ref(8); // 默认启用8个通道 const captureLength = ref(CAPTURE_LENGTH_MIN); // 捕获深度,默认为最小值 const preCaptureLength = ref(PRE_CAPTURE_LENGTH_MIN); // 预捕获深度,默认0 + const currentclockDiv = ref(AnalyzerClockDiv.DIV1); // 默认时钟分频为1 const isApplying = ref(false); const isCapturing = ref(false); // 添加捕获状态标识 @@ -152,6 +156,17 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState( channels.filter((channel) => channel.enabled), ); + // 计算属性:根据当前时钟分频获取实际采样频率 + const currentSampleFrequency = computed(() => { + const divValue = Math.pow(2, currentclockDiv.value); + return BASE_LOGIC_ANALYZER_FREQUENCY / divValue; + }); + + // 计算属性:获取当前采样周期(纳秒) + const currentSamplePeriodNs = computed(() => { + return 1_000_000_000 / currentSampleFrequency.value; + }); + // 转换通道数字到枚举值 const getChannelDivEnum = (channelCount: number): AnalyzerChannelDiv => { switch (channelCount) { @@ -252,9 +267,16 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState( alert?.info(`全局触发模式已设置为 ${modeOption?.label}`, 2000); }; + const setClockDiv = (mode: AnalyzerClockDiv) => { + currentclockDiv.value = mode; + const modeOption = ClockDivOptions.find((m) => m.value === mode); + alert?.info(`时钟分频已设置为 ${modeOption?.label}`, 2000); + }; + const resetConfiguration = () => { currentGlobalMode.value = GlobalCaptureMode.AND; currentChannelDiv.value = 8; // 重置为默认的8通道 + currentclockDiv.value = AnalyzerClockDiv.DIV1; // 重置为默认采样频率 setChannelDiv(8); // 重置为默认的8通道 signalConfigs.forEach((signal) => { @@ -285,7 +307,7 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState( // 根据当前通道数量解析数据 const channelCount = currentChannelDiv.value; - const timeStepNs = SAMPLE_PERIOD_NS; + const timeStepNs = currentSamplePeriodNs.value; let sampleCount: number; let x: number[]; @@ -528,6 +550,7 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState( channelDiv: getChannelDivEnum(currentChannelDiv.value), captureLength: captureLength.value, preCaptureLength: preCaptureLength.value, + clockDiv: currentclockDiv.value, signalConfigs: allSignals, }); @@ -666,13 +689,13 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState( // 添加生成测试数据的方法 const generateTestData = () => { - const sampleRate = LOGIC_ANALYZER_FREQUENCY; // 使用实际的逻辑分析仪频率 + const sampleRate = currentSampleFrequency.value; // 使用当前设置的采样频率 const duration = 0.001; // 1ms的数据 const points = Math.floor(sampleRate * duration); const x = Array.from( { length: points }, - (_, i) => (i * SAMPLE_PERIOD_NS) / 1000, // 时间轴,单位:微秒 + (_, i) => (i * currentSamplePeriodNs.value) / 1000, // 时间轴,单位:微秒 ); // Generate 8 channels with different digital patterns @@ -745,6 +768,7 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState( currentChannelDiv, // 导出当前通道组状态 captureLength, // 导出捕获深度 preCaptureLength, // 导出预捕获深度 + currentclockDiv, // 导出当前采样频率状态 isApplying, isCapturing, // 导出捕获状态 isOperationInProgress, // 导出操作进行状态 @@ -753,12 +777,15 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState( enabledChannelCount, channelNames, enabledChannels, + currentSampleFrequency, // 导出当前采样频率 + currentSamplePeriodNs, // 导出当前采样周期 // 选项数据 globalModes, operators, signalValues, channelDivOptions, // 导出通道组选项 + ClockDivOptions, // 导出采样频率选项 // 捕获深度常量和验证 CAPTURE_LENGTH_MIN, @@ -772,6 +799,7 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState( // 触发设置方法 setChannelDiv, // 导出设置通道组方法 setGlobalMode, + setClockDiv, // 导出设置采样频率方法 resetConfiguration, setLogicData, startCapture, diff --git a/src/components/LogicAnalyzer/TriggerSettings.vue b/src/components/LogicAnalyzer/TriggerSettings.vue index 7f6b577..fc73036 100644 --- a/src/components/LogicAnalyzer/TriggerSettings.vue +++ b/src/components/LogicAnalyzer/TriggerSettings.vue @@ -81,6 +81,44 @@ {{ currentChannelDivDescription }}

+
+ +
+ + + +
+
+ {{ option.label }} +
+
+
+

+ {{ currentClockDivDescription }} +

+