使用FPGA实现频率检测(频率鉴别)

使用FPGA实现频率检测(频率鉴别)

        使用FPGA实现频率检测通常由两种方式,周期测量法和频率测量法。周期测量法是测量信号一个周期内基准时钟的个数,频率测量法是测量固定时间内有多少个信号周期。

        虽然频率测量法测量高频信号时精度更高,但其需要一个闸门时间,响应速度不够快,另外我想是实现的是可以区分1khz、10khz、高电平和低电平的功能,可以说是一个鉴频器,而不是一个频率计,所以根据我的需求说说我的思路和实现方式。

        我的基本思路是利用采样时钟对输入信号的周期和一个周期内的高电平进行计数,将测量值和不同频率的阈值范围进行比较,从而鉴别该输入信号的频率。这个过程不得不涉及到两个问题,一是信号频率识别范围,一是信号占空比的范围。信号传输过程中尤其是远距离传输必然会发生一些失真,所以需要对频率和占空比设计一个合理值,防止误识别。根据需求,我将频率识别范围设置为±10%,占空比设置为±5%,因为我设计的是频率区分,所以范围设计的稍宽,好处就是对不同的环境、温度漂移等容忍性更高。

        在实际使用代码时,遇到一些问题,比如当信号出现高频干扰,代码有时会将1khz误识别为10khz,又进行了一些优化,现记录一下目前的逻辑处理流程。

1、信号预处理。

        我使用了20mhz的外部晶振,系统时钟也选用了该时钟频率。一般情况下高频干扰是ns级别,几ns到几百ns,我将干扰持续时间设置为2us,足够消除高频干扰。具体做法是在每个时钟上升沿,将当前的输入电平与内部已锁存的信号进行比对。如果输入电平发生变化,启动滤波计数器。如果输入电平在 40个时钟周期(2µs) 内发生抖动,计数器清零,变化被忽略。代码如下:

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            glitch_filter_cnt <= 0;
            signal_filtered   <= 1'b0;
            signal_in_d1      <= 1'b0;
        end else begin
            signal_in_d1 <= signal_in; // 同步打拍
            if (signal_in_d1 == signal_filtered) begin
                // 输入与当前输出一致,复位计数器
                glitch_filter_cnt <= 0;
            end else begin
                // 输入电平发生变化,开始计时
                if (glitch_filter_cnt < GLITCH_FILTER_CYCLES) begin
                    glitch_filter_cnt <= glitch_filter_cnt + 1;
                end else begin
                    // 当电平连续稳定维持超过2µs,允许翻转
                    signal_filtered   <= signal_in_d1; 
                    glitch_filter_cnt <= 0;
                end
            end
        end
    end

2、测量计数。

        对经过滤波的信号进行处理,实时检测信号的上升沿和下降沿,通过两个相邻上升沿完成周期测量,通过上升沿和紧邻的下降沿完成脉宽测量,如果输入信号一直没变化,则设置超时时间,让计数器一直累加,达到超时时间后判断是高电平还是低电平。

边沿检测代码:

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) 
            edge_detect_reg <= 2'b00;
        else 
            edge_detect_reg <= {edge_detect_reg[0], signal_filtered};
    end

    // 01 -> 上升沿, 10 -> 下降沿
    assign is_rising_edge  = (edge_detect_reg == 2'b01);
    assign is_falling_edge = (edge_detect_reg == 2'b10);

计数器逻辑:

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            period_cnt     <= 0;
            high_width_cnt <= 0;
            watchdog_cnt   <= 0;
            dc_level_cnt   <= 0;
        end else begin
            // 看门狗计数器
            // 只要有边沿会清零看门狗,否则一直累加直到超时时间
            if (is_rising_edge || is_falling_edge || is_watchdog_timeout) begin
                watchdog_cnt <= 0;
            end else begin
                watchdog_cnt <= watchdog_cnt + 1;
            end

            // 周期计数器
            // 上升沿清零,重新开始测量
            if (is_rising_edge) begin
                period_cnt <= 0;
            end else if (period_cnt < COUNTER_MAX) begin
                period_cnt <= period_cnt + 1;
            end

            // 高电平脉宽计数器
            // 上升沿清零,仅在高电平期间累加
            if (is_rising_edge) begin
                high_width_cnt <= 0;
            end else if (signal_filtered) begin
                if (high_width_cnt < COUNTER_MAX) begin
                    high_width_cnt <= high_width_cnt + 1;
                end
            end

            // 直流电平统计 (DC Level Statistic)
            // 用于超时后判断高电平还是低电平
            if (is_watchdog_timeout || is_rising_edge || is_falling_edge) begin
                dc_level_cnt <= 0;
            end else if (signal_filtered) begin
                dc_level_cnt <= dc_level_cnt + 1;
            end
        end
    end

    assign is_watchdog_timeout = (watchdog_cnt == WATCHDOG_LIMIT_CYCLES);

3、状态输出。

        最终的状态我定义了4种状态,分别用00表示低电平,11表示高电平,01表示10khz,10表示1khz,高低电平属于故障状态,应具有较高优先级,也就是上文提到的看门狗超时时间,我定义的是1.5ms,也就是比1khz的周期再多半个。然后在再次检测到上升沿时判断频率信号的情况,如果周期在0.9ms~1.1ms 之间,且脉宽在0.45ms~0.55ms之间就输出10,如果周期在 90µs~110µs 之间,且脉宽在45µs~55µs之间就输出01,否则输出11,如果既没有发生超时,也不是上升沿时刻,那么输出信号保持上一状态不变。

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            freq_status_out <= STATUS_DC_LOW;
        end else begin
            if (is_watchdog_timeout) begin
                // 高优先级
                if (dc_level_cnt < DC_DETECT_THRESHOLD)
                    freq_status_out <= STATUS_DC_LOW;  // 低电平
                else
                    freq_status_out <= STATUS_UNKNOWN; // 高电平
            end 
            else if (is_rising_edge) begin
                // 低优先级,检测1khz
                if ((period_cnt > PERIOD_MIN_1K) && (period_cnt < PERIOD_MAX_1K) && 
                    (high_width_cnt > WIDTH_MIN_1K) && (high_width_cnt < WIDTH_MAX_1K)) begin
                    freq_status_out <= STATUS_1K;
                end 
                // 检测10khz
                else if ((period_cnt > PERIOD_MIN_10K) && (period_cnt < PERIOD_MAX_10K) && 
                         (high_width_cnt > WIDTH_MIN_10K) && (high_width_cnt < WIDTH_MAX_10K)) begin
                    freq_status_out <= STATUS_10K;
                end 
                // 其他情况输出11
                else begin
                    freq_status_out <= STATUS_UNKNOWN; 
                end
            end
            // 如果没有超时,也没有检测到上升沿,锁存
        end
    end

        上面是我优化后的代码的核心内容,但这份代码还存在一些问题,就是仅仅测量一次就输出结果,没有经过滤波处理,后续应该加上几级滤波处理再输出更好一些。另外,目前的代码只能检测两种频率信号,后续可以增加多频检测功能,比如1kHz,2kHz,5kHz,10kHz,修改相对来说简单,因为逻辑和基本框架已经搭建好了,但如果检测更高频率的信号,比如1mhz,需要注意高频干扰的2us不合适,会把1mhz信号当作干扰滤掉,需要更小滤波深度,比如200ns。

附上一张流程图:

链接:我的代码文件https://download.ZEEKLOG.net/download/xinzhong123456/92555674

Read more

(长期有效)接入第三方 OpenAI 兼容模型到 GitHub Copilot

目前 GitHub Copilot 仅支持接入国外的几家模型提供商,无法直接调用 OpenAI 兼容的自定义 API 进行扩展。参考相关解决方案,我总结了一下Copilot中接入OpenAI 兼容 API 的方法。 实现方法主要分为两种: 方案一:修改 Copilot Chat 源代码 在模型选择器中新增自定义提供商选项。 方案二:API 兼容适配 将 OpenAI 兼容的自定义 API 虚拟化封装为与 Ollama 兼容的 API(运行期间占用 Ollama 端口),从而利用 Copilot 模型选择器中原生的 Ollama 选项。 方法一(目前存在问题) 具体做法可参考修改Copilot chat插件增加自定义模型提供商 这里只说一下这个方法存在的问题: 1. 官方开源的Copilot chat插件版本通常滞后于最新版,可能存在未来兼容性问题 2.

Matlab Copilot_AI工具箱: 对接DeepSeek/Kimi/GPT/千问/文心一言等多款AI大模型,一站式提升编程效率

Matlab Copilot_AI工具箱: 对接DeepSeek/Kimi/GPT/千问/文心一言等多款AI大模型,一站式提升编程效率

🔥 为什么需要这款工具? * Matlab 2025虽自带Copilot功能,但受地区、许可证的限制,多数用户无法使用; * 在Matlab和ChatGPT、DeepSeek等AI模型之间来回切换操作繁琐,无法实现“所见即所得”的编程体验,且代码报错后的调试繁琐。 这款Matlab Copilot_AI工具箱作为Matlab与多款AI模型的对接载体,支持DeepSeek V3.2(基础/思考版)、Kimi K2、百度文心一言、阿里云通义千问、ChatGPT(百度千帆版)等模型,还支持4种自定义模型配置(可对接百度千帆平台近百种大模型); 工具直接在Matlab内(不限于2025a)运行,无需切换其他软件,支持“一键生成、运行、调试、修复bug、导出”全流程编程辅助,使用成本可控(单模型月均几元即可满足基础使用),且工具箱一次授权终身免费更新。 多款AI模型可选择,还支持四种自定义模型组合。 更新记录 1. 20260123更新至v4.0,更新:

从 99.8% 到 14.9%:Paperzz 降重 / 降 AIGC 实测,破解知网最新检测的实用指南

从 99.8% 到 14.9%:Paperzz 降重 / 降 AIGC 实测,破解知网最新检测的实用指南

Paperzz-AI官网免费论文查重复率AIGC检测/开题报告/文献综述/论文初稿paperzz - 降重/降AIGChttps://www.paperzz.cc/weight 当知网、维普再次升级 AIGC 检测机制,不少同学的论文初稿被打出 99.8% 的 AIGC 疑似度时,那种 “一夜回到解放前” 的焦虑,想必很多人都深有体会。传统的同义词替换、语序调整早已失效,单纯降重又容易让文本变得口语化、散文化。Paperzz 的 “降重 / 降 AIGC” 功能,正是在这样的背景下,成为了不少人应对学术检测的 “救命稻草”。本文将结合平台界面,为你深度拆解 Paperzz 如何通过 AI 技术与专业服务,帮你安全、高效地通过最新一轮学术检测。 一、检测升级:知网 AIGC

ChatGPT免费版与微软Copilot深度对比:技术选型与新手避坑指南

作为一名开发者,最近在项目里想集成一个AI助手,面对市面上眼花缭乱的选择,尤其是免费的ChatGPT和微软力推的Copilot,到底该选哪个?这确实是个让人纠结的问题。我花了一些时间,从技术实现、实际调用到性能表现,做了一次比较深入的对比和测试,希望能给同样有选择困难的朋友们一些参考。 1. 市场定位与典型场景:它们各自擅长什么? 简单来说,你可以把ChatGPT免费版看作一个“通用型对话专家”,而微软Copilot更像一个“深度集成在微软生态里的专业副驾驶”。 * ChatGPT免费版:它的核心优势在于强大的通用对话和文本生成能力。无论是头脑风暴、撰写邮件、学习新概念,还是进行开放式的创意讨论,它都能提供质量不错的回应。对于开发者而言,它非常适合用于: * 学习新技术:解释复杂的编程概念或算法。 * 代码解释与重构:将一段代码丢给它,让它解释逻辑或提出优化建议。 * 生成示例代码:根据自然语言描述,快速生成某个功能的代码片段原型。 * 微软Copilot:它的设计初衷就是提升开发和生产效率,与Visual Studio Code、GitHub、Micros