add: 逻辑分析仪可设置采样频率

This commit is contained in:
alivender 2025-08-04 14:31:58 +08:00
parent 04b136117d
commit e86cd5464e
6 changed files with 215 additions and 27 deletions

View File

@ -58,6 +58,10 @@ public class LogicAnalyzerController : ControllerBase
/// </summary> /// </summary>
public AnalyzerChannelDiv ChannelDiv { get; set; } = AnalyzerChannelDiv.EIGHT; public AnalyzerChannelDiv ChannelDiv { get; set; } = AnalyzerChannelDiv.EIGHT;
/// <summary> /// <summary>
/// 时钟分频系数
/// </summary>
public AnalyzerClockDiv ClockDiv { get; set; } = AnalyzerClockDiv.DIV1;
/// <summary>
/// 信号触发配置列表 /// 信号触发配置列表
/// </summary> /// </summary>
public SignalTriggerConfig[] SignalConfigs { get; set; } = Array.Empty<SignalTriggerConfig>(); public SignalTriggerConfig[] SignalConfigs { get; set; } = Array.Empty<SignalTriggerConfig>();
@ -248,6 +252,7 @@ public class LogicAnalyzerController : ControllerBase
/// <param name="capture_length">深度</param> /// <param name="capture_length">深度</param>
/// <param name="pre_capture_length">预采样深度</param> /// <param name="pre_capture_length">预采样深度</param>
/// <param name="channel_div">有效通道(0-[1],1-[2],2-[4],3-[8],4-[16],5-[32])</param> /// <param name="channel_div">有效通道(0-[1],1-[2],2-[4],3-[8],4-[16],5-[32])</param>
/// <param name="clock_div">采样时钟分频系数</param>
/// <returns>操作结果</returns> /// <returns>操作结果</returns>
[HttpPost("SetCaptureParams")] [HttpPost("SetCaptureParams")]
[EnableCors("Users")] [EnableCors("Users")]
@ -255,7 +260,7 @@ public class LogicAnalyzerController : ControllerBase
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<IActionResult> SetCaptureParams(int capture_length, int pre_capture_length, AnalyzerChannelDiv channel_div) public async Task<IActionResult> SetCaptureParams(int capture_length, int pre_capture_length, AnalyzerChannelDiv channel_div, AnalyzerClockDiv clock_div)
{ {
try try
{ {
@ -269,18 +274,18 @@ public class LogicAnalyzerController : ControllerBase
if (analyzer == null) if (analyzer == null)
return BadRequest("用户未绑定有效的实验板"); 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) if (!result.IsSuccessful)
{ {
logger.Error($"设置深度、预采样深度、有效通道失败: {result.Error}"); logger.Error($"设置深度、预采样深度、有效通道、时钟分频失败: {result.Error}");
return StatusCode(StatusCodes.Status500InternalServerError, "设置深度、预采样深度、有效通道失败"); return StatusCode(StatusCodes.Status500InternalServerError, "设置深度、预采样深度、有效通道、时钟分频失败");
} }
return Ok(result.Value); return Ok(result.Value);
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex, "设置深度、预采样深度、有效通道失败时发生异常"); logger.Error(ex, "设置深度、预采样深度、有效通道、时钟分频失败时发生异常");
return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试"); return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试");
} }
} }
@ -332,7 +337,7 @@ public class LogicAnalyzerController : ControllerBase
} }
// 设置深度、预采样深度、有效通道 // 设置深度、预采样深度、有效通道
var paramsResult = await analyzer.SetCaptureParams( var paramsResult = await analyzer.SetCaptureParams(
config.CaptureLength, config.PreCaptureLength, config.ChannelDiv); config.CaptureLength, config.PreCaptureLength, config.ChannelDiv, config.ClockDiv);
if (!paramsResult.IsSuccessful) if (!paramsResult.IsSuccessful)
{ {
logger.Error($"设置深度、预采样深度、有效通道失败: {paramsResult.Error}"); logger.Error($"设置深度、预采样深度、有效通道失败: {paramsResult.Error}");

View File

@ -67,6 +67,7 @@ static class AnalyzerAddr
public const UInt32 LOAD_NUM_ADDR = BASE + 0x0000_0002; public const UInt32 LOAD_NUM_ADDR = BASE + 0x0000_0002;
public const UInt32 PRE_LOAD_NUM_ADDR = BASE + 0x0000_0003; public const UInt32 PRE_LOAD_NUM_ADDR = BASE + 0x0000_0003;
public const UInt32 CAHNNEL_DIV_ADDR = BASE + 0x0000_0004; 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_START_WRITE_ADDR = DMA1_BASE + 0x0000_0012;
public const UInt32 DMA1_END_WRITE_ADDR = DMA1_BASE + 0x0000_0013; public const UInt32 DMA1_END_WRITE_ADDR = DMA1_BASE + 0x0000_0013;
public const UInt32 DMA1_CAPTURE_CTRL_ADDR = DMA1_BASE + 0x0000_0014; public const UInt32 DMA1_CAPTURE_CTRL_ADDR = DMA1_BASE + 0x0000_0014;
@ -138,6 +139,52 @@ public enum GlobalCaptureMode
NOR = 0b11 NOR = 0b11
} }
/// <summary>
/// 逻辑分析仪采样时钟分频系数
/// </summary>
public enum AnalyzerClockDiv
{
/// <summary>
/// 1分频
/// </summary>
DIV1 = 0x0000_0000,
/// <summary>
/// 2分频
/// </summary>
DIV2 = 0x0000_0001,
/// <summary>
/// 4分频
/// </summary>
DIV4 = 0x0000_0002,
/// <summary>
/// 8分频
/// </summary>
DIV8 = 0x0000_0003,
/// <summary>
/// 16分频
/// </summary>
DIV16 = 0x0000_0004,
/// <summary>
/// 32分频
/// </summary>
DIV32 = 0x0000_0005,
/// <summary>
/// 64分频
/// </summary>
DIV64 = 0x0000_0006,
/// <summary>
/// 128分频
/// </summary>
DIV128 = 0x0000_0007
}
/// <summary> /// <summary>
/// 信号M的操作符枚举 /// 信号M的操作符枚举
/// </summary> /// </summary>
@ -387,13 +434,14 @@ public class Analyzer
} }
/// <summary> /// <summary>
/// 设置逻辑分析仪的深度、预采样深度、有效通道 /// 设置逻辑分析仪的深度、预采样深度、有效通道、分频系数
/// </summary> /// </summary>
/// <param name="capture_length">深度</param> /// <param name="capture_length">深度</param>
/// <param name="pre_capture_length">预采样深度</param> /// <param name="pre_capture_length">预采样深度</param>
/// <param name="channel_div">有效通道(0-[1],1-[2],2-[4],3-[8],4-[16],5-[32])</param> /// <param name="channel_div">有效通道(0-[1],1-[2],2-[4],3-[8],4-[16],5-[32])</param>
/// <param name="clock_div">采样时钟分频系数</param>
/// <returns>操作结果成功返回true否则返回异常信息</returns> /// <returns>操作结果成功返回true否则返回异常信息</returns>
public async ValueTask<Result<bool>> SetCaptureParams(int capture_length, int pre_capture_length, AnalyzerChannelDiv channel_div) public async ValueTask<Result<bool>> SetCaptureParams(int capture_length, int pre_capture_length, AnalyzerChannelDiv channel_div, AnalyzerClockDiv clock_div)
{ {
if (capture_length == 0) capture_length = 1; if (capture_length == 0) capture_length = 1;
if (pre_capture_length == 0) pre_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")); 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; return true;
} }

View File

@ -16,7 +16,7 @@ public class HttpHdmiVideoStreamService : BackgroundService
{ {
private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private HttpListener? _httpListener; private HttpListener? _httpListener;
private readonly int _serverPort = 4322; private readonly int _serverPort = 6666;
private readonly ConcurrentDictionary<string, HdmiIn> _hdmiInDict = new(); private readonly ConcurrentDictionary<string, HdmiIn> _hdmiInDict = new();
private bool _isEnabled = true; private bool _isEnabled = true;

View File

@ -3948,9 +3948,10 @@ export class LogicAnalyzerClient {
* @param capture_length (optional) * @param capture_length (optional)
* @param pre_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 channel_div (optional) (0-[1],1-[2],2-[4],3-[8],4-[16],5-[32])
* @param clock_div (optional)
* @return * @return
*/ */
setCaptureParams(capture_length: number | undefined, pre_capture_length: number | undefined, channel_div: AnalyzerChannelDiv | undefined, cancelToken?: CancelToken): Promise<boolean> { setCaptureParams(capture_length: number | undefined, pre_capture_length: number | undefined, channel_div: AnalyzerChannelDiv | undefined, clock_div: AnalyzerClockDiv | undefined, cancelToken?: CancelToken): Promise<boolean> {
let url_ = this.baseUrl + "/api/LogicAnalyzer/SetCaptureParams?"; let url_ = this.baseUrl + "/api/LogicAnalyzer/SetCaptureParams?";
if (capture_length === null) if (capture_length === null)
throw new Error("The parameter 'capture_length' cannot be 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."); throw new Error("The parameter 'channel_div' cannot be null.");
else if (channel_div !== undefined) else if (channel_div !== undefined)
url_ += "channel_div=" + encodeURIComponent("" + channel_div) + "&"; 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(/[?&]$/, ""); url_ = url_.replace(/[?&]$/, "");
let options_: AxiosRequestConfig = { let options_: AxiosRequestConfig = {
@ -8233,6 +8238,18 @@ export enum AnalyzerChannelDiv {
XXXII = 5, 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 { export class CaptureConfig implements ICaptureConfig {
/** 全局触发模式 */ /** 全局触发模式 */
@ -8243,6 +8260,8 @@ export class CaptureConfig implements ICaptureConfig {
preCaptureLength!: number; preCaptureLength!: number;
/** 有效通道 */ /** 有效通道 */
channelDiv!: AnalyzerChannelDiv; channelDiv!: AnalyzerChannelDiv;
/** 时钟分频系数 */
clockDiv!: AnalyzerClockDiv;
/** 信号触发配置列表 */ /** 信号触发配置列表 */
signalConfigs!: SignalTriggerConfig[]; signalConfigs!: SignalTriggerConfig[];
@ -8264,6 +8283,7 @@ export class CaptureConfig implements ICaptureConfig {
this.captureLength = _data["captureLength"]; this.captureLength = _data["captureLength"];
this.preCaptureLength = _data["preCaptureLength"]; this.preCaptureLength = _data["preCaptureLength"];
this.channelDiv = _data["channelDiv"]; this.channelDiv = _data["channelDiv"];
this.clockDiv = _data["clockDiv"];
if (Array.isArray(_data["signalConfigs"])) { if (Array.isArray(_data["signalConfigs"])) {
this.signalConfigs = [] as any; this.signalConfigs = [] as any;
for (let item of _data["signalConfigs"]) for (let item of _data["signalConfigs"])
@ -8285,6 +8305,7 @@ export class CaptureConfig implements ICaptureConfig {
data["captureLength"] = this.captureLength; data["captureLength"] = this.captureLength;
data["preCaptureLength"] = this.preCaptureLength; data["preCaptureLength"] = this.preCaptureLength;
data["channelDiv"] = this.channelDiv; data["channelDiv"] = this.channelDiv;
data["clockDiv"] = this.clockDiv;
if (Array.isArray(this.signalConfigs)) { if (Array.isArray(this.signalConfigs)) {
data["signalConfigs"] = []; data["signalConfigs"] = [];
for (let item of this.signalConfigs) for (let item of this.signalConfigs)
@ -8304,6 +8325,8 @@ export interface ICaptureConfig {
preCaptureLength: number; preCaptureLength: number;
/** 有效通道 */ /** 有效通道 */
channelDiv: AnalyzerChannelDiv; channelDiv: AnalyzerChannelDiv;
/** 时钟分频系数 */
clockDiv: AnalyzerClockDiv;
/** 信号触发配置列表 */ /** 信号触发配置列表 */
signalConfigs: SignalTriggerConfig[]; signalConfigs: SignalTriggerConfig[];
} }

View File

@ -10,6 +10,7 @@ import {
SignalTriggerConfig, SignalTriggerConfig,
SignalValue, SignalValue,
AnalyzerChannelDiv, AnalyzerChannelDiv,
AnalyzerClockDiv,
} from "@/APIClient"; } from "@/APIClient";
import { AuthManager } from "@/utils/AuthManager"; import { AuthManager } from "@/utils/AuthManager";
import { useAlertStore } from "@/components/Alert"; import { useAlertStore } from "@/components/Alert";
@ -30,16 +31,8 @@ export type Channel = {
// 全局模式选项 // 全局模式选项
const globalModes = [ const globalModes = [
{ {value: GlobalCaptureMode.AND,label: "AND",description: "所有条件都满足时触发",},
value: GlobalCaptureMode.AND, {value: GlobalCaptureMode.OR,label: "OR",description: "任一条件满足时触发",},
label: "AND",
description: "所有条件都满足时触发",
},
{
value: GlobalCaptureMode.OR,
label: "OR",
description: "任一条件满足时触发",
},
{ value: GlobalCaptureMode.NAND, label: "NAND", description: "AND的非" }, { value: GlobalCaptureMode.NAND, label: "NAND", description: "AND的非" },
{ value: GlobalCaptureMode.NOR, label: "NOR", description: "OR的非" }, { value: GlobalCaptureMode.NOR, label: "NOR", description: "OR的非" },
]; ];
@ -76,12 +69,23 @@ const channelDivOptions = [
{ value: 32, label: "32通道", description: "启用32个通道 (CH0-CH31)" }, { 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_MIN = 1024; // 最小捕获深度 1024
const CAPTURE_LENGTH_MAX = 0x10000000 - 0x01000000; // 最大捕获深度 const CAPTURE_LENGTH_MAX = 0x10000000 - 0x01000000; // 最大捕获深度
// 预捕获深度限制常量 // 预捕获深度限制常量
const PRE_CAPTURE_LENGTH_MIN = 0; // 最小预捕获深度 0 const PRE_CAPTURE_LENGTH_MIN = 2; // 最小预捕获深度 2
// 默认颜色数组 // 默认颜色数组
const defaultColors = [ const defaultColors = [
@ -95,9 +99,8 @@ const defaultColors = [
"#8C33FF", "#8C33FF",
]; ];
// 添加逻辑分析仪频率常量 // 添加逻辑分析仪基础频率常量
const LOGIC_ANALYZER_FREQUENCY = 125_000_000; // 125MHz const BASE_LOGIC_ANALYZER_FREQUENCY = 120_000_000; // 120MHz基础频率
const SAMPLE_PERIOD_NS = 1_000_000_000 / LOGIC_ANALYZER_FREQUENCY; // 采样周期,单位:纳秒
const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState( const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
() => { () => {
@ -112,6 +115,7 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
const currentChannelDiv = ref<number>(8); // 默认启用8个通道 const currentChannelDiv = ref<number>(8); // 默认启用8个通道
const captureLength = ref<number>(CAPTURE_LENGTH_MIN); // 捕获深度,默认为最小值 const captureLength = ref<number>(CAPTURE_LENGTH_MIN); // 捕获深度,默认为最小值
const preCaptureLength = ref<number>(PRE_CAPTURE_LENGTH_MIN); // 预捕获深度默认0 const preCaptureLength = ref<number>(PRE_CAPTURE_LENGTH_MIN); // 预捕获深度默认0
const currentclockDiv = ref<AnalyzerClockDiv>(AnalyzerClockDiv.DIV1); // 默认时钟分频为1
const isApplying = ref(false); const isApplying = ref(false);
const isCapturing = ref(false); // 添加捕获状态标识 const isCapturing = ref(false); // 添加捕获状态标识
@ -152,6 +156,17 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
channels.filter((channel) => channel.enabled), 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 => { const getChannelDivEnum = (channelCount: number): AnalyzerChannelDiv => {
switch (channelCount) { switch (channelCount) {
@ -252,9 +267,16 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
alert?.info(`全局触发模式已设置为 ${modeOption?.label}`, 2000); 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 = () => { const resetConfiguration = () => {
currentGlobalMode.value = GlobalCaptureMode.AND; currentGlobalMode.value = GlobalCaptureMode.AND;
currentChannelDiv.value = 8; // 重置为默认的8通道 currentChannelDiv.value = 8; // 重置为默认的8通道
currentclockDiv.value = AnalyzerClockDiv.DIV1; // 重置为默认采样频率
setChannelDiv(8); // 重置为默认的8通道 setChannelDiv(8); // 重置为默认的8通道
signalConfigs.forEach((signal) => { signalConfigs.forEach((signal) => {
@ -285,7 +307,7 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
// 根据当前通道数量解析数据 // 根据当前通道数量解析数据
const channelCount = currentChannelDiv.value; const channelCount = currentChannelDiv.value;
const timeStepNs = SAMPLE_PERIOD_NS; const timeStepNs = currentSamplePeriodNs.value;
let sampleCount: number; let sampleCount: number;
let x: number[]; let x: number[];
@ -528,6 +550,7 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
channelDiv: getChannelDivEnum(currentChannelDiv.value), channelDiv: getChannelDivEnum(currentChannelDiv.value),
captureLength: captureLength.value, captureLength: captureLength.value,
preCaptureLength: preCaptureLength.value, preCaptureLength: preCaptureLength.value,
clockDiv: currentclockDiv.value,
signalConfigs: allSignals, signalConfigs: allSignals,
}); });
@ -666,13 +689,13 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
// 添加生成测试数据的方法 // 添加生成测试数据的方法
const generateTestData = () => { const generateTestData = () => {
const sampleRate = LOGIC_ANALYZER_FREQUENCY; // 使用实际的逻辑分析仪频率 const sampleRate = currentSampleFrequency.value; // 使用当前设置的采样频率
const duration = 0.001; // 1ms的数据 const duration = 0.001; // 1ms的数据
const points = Math.floor(sampleRate * duration); const points = Math.floor(sampleRate * duration);
const x = Array.from( const x = Array.from(
{ length: points }, { length: points },
(_, i) => (i * SAMPLE_PERIOD_NS) / 1000, // 时间轴,单位:微秒 (_, i) => (i * currentSamplePeriodNs.value) / 1000, // 时间轴,单位:微秒
); );
// Generate 8 channels with different digital patterns // Generate 8 channels with different digital patterns
@ -745,6 +768,7 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
currentChannelDiv, // 导出当前通道组状态 currentChannelDiv, // 导出当前通道组状态
captureLength, // 导出捕获深度 captureLength, // 导出捕获深度
preCaptureLength, // 导出预捕获深度 preCaptureLength, // 导出预捕获深度
currentclockDiv, // 导出当前采样频率状态
isApplying, isApplying,
isCapturing, // 导出捕获状态 isCapturing, // 导出捕获状态
isOperationInProgress, // 导出操作进行状态 isOperationInProgress, // 导出操作进行状态
@ -753,12 +777,15 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
enabledChannelCount, enabledChannelCount,
channelNames, channelNames,
enabledChannels, enabledChannels,
currentSampleFrequency, // 导出当前采样频率
currentSamplePeriodNs, // 导出当前采样周期
// 选项数据 // 选项数据
globalModes, globalModes,
operators, operators,
signalValues, signalValues,
channelDivOptions, // 导出通道组选项 channelDivOptions, // 导出通道组选项
ClockDivOptions, // 导出采样频率选项
// 捕获深度常量和验证 // 捕获深度常量和验证
CAPTURE_LENGTH_MIN, CAPTURE_LENGTH_MIN,
@ -772,6 +799,7 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
// 触发设置方法 // 触发设置方法
setChannelDiv, // 导出设置通道组方法 setChannelDiv, // 导出设置通道组方法
setGlobalMode, setGlobalMode,
setClockDiv, // 导出设置采样频率方法
resetConfiguration, resetConfiguration,
setLogicData, setLogicData,
startCapture, startCapture,

View File

@ -81,6 +81,44 @@
{{ currentChannelDivDescription }} {{ currentChannelDivDescription }}
</p> </p>
</div> </div>
<div class="flex flex-col gap-2">
<label class="block text-sm font-semibold antialiased text-slate-800">
采样频率
</label>
<div class="relative w-[200px]">
<button
tabindex="0"
type="button"
class="flex items-center gap-4 justify-between h-max outline-none focus:outline-none text-slate-600 bg-transparent ring-transparent border border-slate-200 transition-all duration-300 ease-in disabled:opacity-50 disabled:pointer-events-none select-none text-start text-sm rounded-md py-2 px-2.5 ring shadow-sm hover:border-slate-800 hover:ring-slate-800/10 focus:border-slate-800 focus:ring-slate-800/10 w-full"
@click="toggleClockDivDropdown"
:aria-expanded="showClockDivDropdown"
aria-haspopup="listbox"
role="combobox"
>
<span>{{ currentClockDivLabel }}</span>
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="currentColor" class="h-[1em] w-[1em] translate-x-0.5 stroke-[1.5]">
<path d="M17 8L12 3L7 8" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M17 16L12 21L7 16" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</button>
<input readonly style="display:none" :value="currentclockDiv" />
<!-- 下拉菜单 -->
<div v-if="showClockDivDropdown" class="absolute top-full left-0 right-0 mt-1 bg-white border border-slate-200 rounded-md shadow-lg z-50">
<div
v-for="option in ClockDivOptions"
:key="option.value"
@click="selectClockDiv(option.value)"
class="px-3 py-2 text-sm text-slate-700 hover:bg-slate-100 cursor-pointer"
:class="{ 'bg-slate-100': option.value === currentclockDiv }"
>
{{ option.label }}
</div>
</div>
</div>
<p class="flex items-center text-xs text-slate-400">
{{ currentClockDivDescription }}
</p>
</div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<label class="block text-sm font-semibold antialiased"> <label class="block text-sm font-semibold antialiased">
捕获深度 捕获深度
@ -277,6 +315,7 @@ import { useLogicAnalyzerState } from "./LogicAnalyzerManager";
const { const {
currentGlobalMode, currentGlobalMode,
currentChannelDiv, currentChannelDiv,
currentclockDiv,
captureLength, captureLength,
preCaptureLength, preCaptureLength,
isApplying, isApplying,
@ -287,6 +326,7 @@ const {
operators, operators,
signalValues, signalValues,
channelDivOptions, channelDivOptions,
ClockDivOptions,
CAPTURE_LENGTH_MIN, CAPTURE_LENGTH_MIN,
CAPTURE_LENGTH_MAX, CAPTURE_LENGTH_MAX,
PRE_CAPTURE_LENGTH_MIN, PRE_CAPTURE_LENGTH_MIN,
@ -296,12 +336,14 @@ const {
setPreCaptureLength, setPreCaptureLength,
setChannelDiv, setChannelDiv,
setGlobalMode, setGlobalMode,
setClockDiv,
resetConfiguration, resetConfiguration,
} = useRequiredInjection(useLogicAnalyzerState); } = useRequiredInjection(useLogicAnalyzerState);
// //
const showGlobalModeDropdown = ref(false); const showGlobalModeDropdown = ref(false);
const showChannelDivDropdown = ref(false); const showChannelDivDropdown = ref(false);
const showClockDivDropdown = ref(false);
// //
const handleCaptureLengthChange = () => { const handleCaptureLengthChange = () => {
@ -362,11 +404,24 @@ const currentChannelDivDescription = computed(() => {
return option ? option.description : ''; return option ? option.description : '';
}); });
//
const currentClockDivLabel = computed(() => {
const option = ClockDivOptions.find(opt => opt.value === currentclockDiv.value);
return option ? option.label : '';
});
//
const currentClockDivDescription = computed(() => {
const option = ClockDivOptions.find(opt => opt.value === currentclockDiv.value);
return option ? option.description : '';
});
// //
const toggleGlobalModeDropdown = () => { const toggleGlobalModeDropdown = () => {
showGlobalModeDropdown.value = !showGlobalModeDropdown.value; showGlobalModeDropdown.value = !showGlobalModeDropdown.value;
if (showGlobalModeDropdown.value) { if (showGlobalModeDropdown.value) {
showChannelDivDropdown.value = false; showChannelDivDropdown.value = false;
showClockDivDropdown.value = false;
} }
}; };
@ -380,6 +435,7 @@ const toggleChannelDivDropdown = () => {
showChannelDivDropdown.value = !showChannelDivDropdown.value; showChannelDivDropdown.value = !showChannelDivDropdown.value;
if (showChannelDivDropdown.value) { if (showChannelDivDropdown.value) {
showGlobalModeDropdown.value = false; showGlobalModeDropdown.value = false;
showClockDivDropdown.value = false;
} }
}; };
@ -388,12 +444,27 @@ const selectChannelDiv = (value: number) => {
showChannelDivDropdown.value = false; showChannelDivDropdown.value = false;
}; };
//
const toggleClockDivDropdown = () => {
showClockDivDropdown.value = !showClockDivDropdown.value;
if (showClockDivDropdown.value) {
showGlobalModeDropdown.value = false;
showChannelDivDropdown.value = false;
}
};
const selectClockDiv = (value: any) => {
setClockDiv(value);
showClockDivDropdown.value = false;
};
// //
const handleClickOutside = (event: MouseEvent) => { const handleClickOutside = (event: MouseEvent) => {
const target = event.target as HTMLElement; const target = event.target as HTMLElement;
if (!target.closest('.relative')) { if (!target.closest('.relative')) {
showGlobalModeDropdown.value = false; showGlobalModeDropdown.value = false;
showChannelDivDropdown.value = false; showChannelDivDropdown.value = false;
showClockDivDropdown.value = false;
} }
}; };