找回密码
 立即注册
楼主: Printy陈

15W408AS PCA捕获测频率,只能600Hz-180KHz,指导

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:506
  • 最近打卡:2025-09-03 10:26:55
已绑定手机

45

主题

2197

回帖

7722

积分

论坛元老

积分
7722
发表于 2025-8-26 15:23:37 | 显示全部楼层
Prin*** 发表于 2025-8-26 15:06
58按理说STC15W408AS应该可以做到吧

理论上是可以,但是要在这么短时钟内处理好捕获结果,代码就得很精简。
回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:169
  • 最近打卡:2025-09-03 22:50:50

76

主题

6683

回帖

1万

积分

超级版主

积分
14009
发表于 2025-8-26 16:10:03 | 显示全部楼层
Prin*** 发表于 2025-8-26 13:36
之前我用是P3.4 脚接外部脉冲的,就是看网上贴子说PCA可以测频率更准,就改成了P3.5 CCP0_2 引脚 PCA。哎。 ...

将P3.4、P3.5连在一起,Timer0通过P3.4-T0对外计频率,当频率较小时,启动P3.5-CCP0-2捕获测周期计算频率。
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 最近打卡:2025-08-29 08:03:13
已绑定手机

2

主题

17

回帖

56

积分

注册会员

积分
56
发表于 2025-8-26 16:29:46 | 显示全部楼层
我试试,谢谢了,定时器0最大可以测多少

点评

定时器测频最高可以FOSC/2,比如FOSC=32MHz,则测频最高16MHz。  详情 回复 发表于 2025-8-26 16:53
回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:169
  • 最近打卡:2025-09-03 22:50:50

76

主题

6683

回帖

1万

积分

超级版主

积分
14009
发表于 2025-8-26 16:53:54 | 显示全部楼层
Prin*** 发表于 2025-8-26 16:29
我试试,谢谢了,定时器0最大可以测多少

定时器测频最高可以FOSC/2,比如FOSC=32MHz,则测频最高16MHz。
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 最近打卡:2025-08-29 08:03:13
已绑定手机

2

主题

17

回帖

56

积分

注册会员

积分
56
发表于 7 天前 | 显示全部楼层
梁*** 发表于 2025-8-26 16:10
将P3.4、P3.5连在一起,Timer0通过P3.4-T0对外计频率,当频率较小时,启动P3.5-CCP0-2捕获测周期计算频率 ...

我改用T0计数,自己测试的时候1Hz-2Mhz 都还勉强过的去,拿给别人测试回来发现,数据跳动很大,也很不稳定,又全乱了,低频更是离谱,麻烦帮我分析一下原因。P34 P35短接的


#include <STC15.H>
#include <intrins.h>

//------------------------------------------------------------------
// 类型定义
//------------------------------------------------------------------
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

//------------------------------------------------------------------
// LCD引脚定义
//------------------------------------------------------------------
sbit LCD_RS = P3 ^ 2;
sbit LCD_RW = P3 ^ 3;
sbit LCD_EN = P3 ^ 7;
#define LCD_DATA P1

//------------------------------------------------------------------
// 全局变量定义
//------------------------------------------------------------------
#define RNG_HIGH 0
#define RNG_MID 1
#define RNG_LOW 2

volatile u8 range;
volatile u8 range_changed;
volatile u16 gate_ticks;
volatile u16 gate_counter;
volatile u16 t0_overflow_count;
volatile u32 raw_count;
volatile bit data_ready;
volatile bit measure_done;

#define FILTER_SIZE 3
u32 freq_buffer[FILTER_SIZE];
u8 buffer_index;
u32 filtered_freq;

u16 gate_time_ms;
char disp_buf[9];

//------------------------------------------------------------------
// LCD驱动函数
//------------------------------------------------------------------
void delay_ms(u16 n)
{
    u16 i, j;
    for (i = n; i > 0; i--)
        for (j = 1140; j > 0; j--)
            ;
}

void lcd_busy_check()
{
    delay_ms(2);
}

void lcd_write_cmd(u8 cmd)
{
    lcd_busy_check();
    LCD_RS = 0;
    LCD_RW = 0;
    LCD_DATA = cmd;
    LCD_EN = 1;
    _nop_();
    _nop_();
    LCD_EN = 0;
}

void lcd_write_data(u8 dat)
{
    lcd_busy_check();
    LCD_RS = 1;
    LCD_RW = 0;
    LCD_DATA = dat;
    LCD_EN = 1;
    _nop_();
    _nop_();
    LCD_EN = 0;
}

void lcd_set_cursor(u8 pos)
{
    lcd_write_cmd(0x80 | pos);
}

void lcd_init()
{
    delay_ms(15);
    lcd_write_cmd(0x38);
    delay_ms(5);
    lcd_write_cmd(0x38);
    delay_ms(100);
    lcd_write_cmd(0x38);
    delay_ms(100);
    lcd_write_cmd(0x38);
    lcd_write_cmd(0x0C);
    lcd_write_cmd(0x06);
    lcd_write_cmd(0x01);
    delay_ms(2);
}

void lcd_show_string(u8 pos, u8 *str, u8 len)
{
    u8 i;
    lcd_set_cursor(pos);
    for (i = 0; i < len; i++)
    {
        lcd_write_data(str);
    }
}

//------------------------------------------------------------------
// 数字转字符串函数(显示原始数据)
//------------------------------------------------------------------
/*void num_to_str(u32 num, u8 *buf)
{
    u8 i;
    u8 digits = 0;
    u32 temp = num;

    // 计算位数
    do
    {
        digits++;
        temp /= 10;
    } while (temp > 0);

    // 数字转字符
    temp = num;
    for (i = 0; i < digits; i++)
    {
        buf[digits - 1 - i] = '0' + (temp % 10);
        temp /= 10;
    }

    // 前面补空格
    for (i = digits; i < 8; i++)
    {
        buf = ' ';
    }
    buf[8] = '\0';
}*/
void freq_to_str(u32 freq_hz, u8 *buf)
{
    u8 i;

    if (freq_hz >= 1000000)
    {
        // 1MHz及以上:显示为X.X MHz
        u32 temp = freq_hz / 100000; // 转换为0.1MHz单位

        buf[0] = '0' + (temp / 10); // 整数位
        buf[1] = '.';
        buf[2] = '0' + (temp % 10); // 一位小数
        buf[3] = 'M';
        buf[4] = 'H';
        buf[5] = 'z';
        buf[6] = ' ';
        buf[7] = ' ';
    }
    else if (freq_hz >= 100000)
    {
        // 100kHz-999kHz:显示为XXX.X kHz
        u32 temp = freq_hz / 100; // 转换为0.1kHz单位

        buf[0] = '0' + (temp / 1000) % 10; // 百位
        buf[1] = '0' + (temp / 100) % 10;  // 十位
        buf[2] = '0' + (temp / 10) % 10;   // 个位
        buf[3] = '.';
        buf[4] = '0' + (temp % 10); // 一位小数
        buf[5] = 'k';
        buf[6] = 'H';
        buf[7] = 'z';
    }
    else if (freq_hz >= 10000)
    {
        // 10kHz-99.9kHz:显示为XX.X kHz
        u32 temp = freq_hz / 100; // 转换为0.1kHz单位

        buf[0] = '0' + (temp / 100) % 10; // 十位
        buf[1] = '0' + (temp / 10) % 10;  // 个位
        buf[2] = '.';
        buf[3] = '0' + (temp % 10); // 一位小数
        buf[4] = 'k';
        buf[5] = 'H';
        buf[6] = 'z';
        buf[7] = ' ';
    }
    else if (freq_hz >= 1000)
    {
        // 1kHz-9.9kHz:显示为X.X kHz
        u32 temp = freq_hz / 100; // 转换为0.1kHz单位

        buf[0] = '0' + (temp / 10); // 整数位
        buf[1] = '.';
        buf[2] = '0' + (temp % 10); // 一位小数
        buf[3] = 'k';
        buf[4] = 'H';
        buf[5] = 'z';
        buf[6] = ' ';
        buf[7] = ' ';
    }
    else
    {
        // 1Hz以下:显示为XXX Hz
        if (freq_hz == 0)
        {
            buf[0] = '0';
            buf[1] = ' ';
            buf[2] = 'H';
            buf[3] = 'z';
            for (i = 4; i < 8; i++)
                buf = ' ';
        }
        else
        {
            // 计算位数
            u8 digits = 0;
            u32 temp = freq_hz;
            do
            {
                digits++;
                temp /= 10;
            } while (temp > 0);

            // 数字转字符
            temp = freq_hz;
            for (i = 0; i < digits; i++)
            {
                buf[digits - 1 - i] = '0' + (temp % 10);
                temp /= 10;
            }

            // 添加单位
            buf[digits] = ' ';
            buf[digits + 1] = 'H';
            buf[digits + 2] = 'z';

            // 后面补空格
            for (i = digits + 3; i < 8; i++)
            {
                buf = ' ';
            }
        }
    }

    buf[8] = '\0';
}
//------------------------------------------------------------------
// 指数平滑滤波
// Y(n) = α × X(n) + (1-α) × Y(n-1)
// Y(n):当前滤波后的输出值
// X(n):当前输入的新值
// Y(n-1):上一次滤波后的输出值
// α:平滑系数(0 < α < 1)a值越小,平滑效果越强,响应越慢,显示越慢越稳定
// 推荐α值:
// 低频信号:α = 0.2(强平滑)
// 中频信号:α = 0.3(中等平滑)
// 高频信号:α = 0.4(弱平滑)
//------------------------------------------------------------------

u32 median_filter(u32 new_value)
{
    static u32 filtered = 0;

    if (filtered == 0)
    {
        filtered = new_value;
    }
    else
    {
        // 平滑系数可调:数值越小越平滑,但响应越慢
        filtered = (filtered * 7 + new_value * 3) / 10;
    }

    return filtered;
}

//------------------------------------------------------------------
// 频率计算和量程自动切换
//------------------------------------------------------------------

void process_measurement_data()
{
    u32 current_freq;
    u8 new_range;
    u8 i;

    if (!data_ready)
        return;

    // 计算频率 (Hz) = 计数值 / (闸门时间/1000)
    current_freq = raw_count * 1000UL / gate_time_ms;
    filtered_freq = median_filter(current_freq);

    // 量程自动切换
    new_range = range;
    if (range == RNG_HIGH)
    {
        if (filtered_freq < 8000)
            new_range = RNG_MID;
    }
    else if (range == RNG_MID)
    {
        if (filtered_freq > 9500)
            new_range = RNG_HIGH;
        else if (filtered_freq < 400)
            new_range = RNG_LOW;
    }
    else if (range == RNG_LOW)
    {
        if (filtered_freq > 450)
            new_range = RNG_MID;
    }

    if (new_range != range)
    {
        range = new_range;
        range_changed = 1;

        if (range == RNG_HIGH)
        {
            gate_ticks = 2; // 20ms
            gate_time_ms = 20;
        }
        else if (range == RNG_MID)
        {
            gate_ticks = 20; // 200ms
            gate_time_ms = 200;
        }
        else if (range == RNG_LOW)
        {
            gate_ticks = 200; // 2000ms
            gate_time_ms = 2000;
        }

        gate_counter = gate_ticks;

        for (i = 0; i < FILTER_SIZE; i++)
        {
            freq_buffer = 0;
        }
        buffer_index = 0;
    }

    data_ready = 0;
    measure_done = 1;
}

//------------------------------------------------------------------
// IO口初始化
//------------------------------------------------------------------
void io_init()
{

    // P3.4 P3.5 设置为高阻输入 P3.2 P3.3 P.7 设置为推挽输出
    P3M1 = 0x30;
    P3M0 = 0x8c;

    // LCD数据端口设置为准双向口
    P1M1 = 0x00;
    P1M0 = 0x00;
}

//------------------------------------------------------------------
// 定时器初始化(T0 1分频,T2 12分频)
//------------------------------------------------------------------
void timer_init()
{
    // 初始化变量
    range = RNG_MID;
    range_changed = 0;
    gate_ticks = 20; // 初始200ms
    gate_counter = 20;
    gate_time_ms = 200;
    t0_overflow_count = 0;
    raw_count = 0;
    data_ready = 0;
    measure_done = 0;
    buffer_index = 0;
    filtered_freq = 0;

    // 清空滤波缓冲区
    for (buffer_index = 0; buffer_index < FILTER_SIZE; buffer_index++)
    {
        freq_buffer[buffer_index] = 0;
    }
    buffer_index = 0;

    // T0 配置:计数器模式,1T模式(1分频)
    TMOD &= 0xF0;     // 清除T0相关设置
    TMOD |= 0x00;     // 设置为模式0(16位自动重装载)
    TMOD |= (1 << 2); // 设置为外部计数模式
    AUXR &= 0x7F;     // 清除AUXR最高位
    AUXR |= 0x80;     // T0 1T模式(1分频)
    TH0 = 0;
    TL0 = 0;

    AUXR &= 0xE3; // 0xE3 = 1110 0011//设置T2 为12T分频,用作定时器
    T2L = 0x00;   // 设置定时初始值10ms
    T2H = 0xDC;   // 设置定时初始值10ms

    TR0 = 1; // 启动T0
    ET0 = 1; // 使能T0中断

    AUXR |= 0x10; // 启动T2
    IE2 |= 0x04;  // 使能T2中断

    EA = 1; // 使能全局中断
}

//------------------------------------------------------------------
// 中断服务函数
//------------------------------------------------------------------
void timer0_isr() interrupt 1
{
    t0_overflow_count++;
}

void timer2_isr() interrupt 12
{
    u32 total_count;

    if (--gate_counter == 0)
    {
        gate_counter = gate_ticks;

        TR0 = 0;
        ET0 = 0;
        total_count = (u32)t0_overflow_count * 65536UL + (TH0 << 8) + TL0;

        TH0 = 0;
        TL0 = 0;
        t0_overflow_count = 0;
        TR0 = 1;
        ET0 = 1;

        raw_count = total_count;
        data_ready = 1;
    }
}

//------------------------------------------------------------------
// 主函数
//------------------------------------------------------------------
void main()
{
    u8 freq_str[8];

    io_init();
    lcd_init();
    timer_init();

    // 显示固定标题
    lcd_show_string(0x80, "Waiting.", 8);

    while (1)
    {
        process_measurement_data();

        if (measure_done && !range_changed)
        {
            // 显示原始频率数值
            freq_to_str(filtered_freq, freq_str);
            lcd_show_string(0x80, freq_str, 8);

            measure_done = 0;
        }

        if (range_changed)
        {
            range_changed = 0;
            measure_done = 0;
        }
    }
}

回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 最近打卡:2025-08-29 08:03:13
已绑定手机

2

主题

17

回帖

56

积分

注册会员

积分
56
发表于 7 天前 | 显示全部楼层
截图202508281832029258.jpg

发现这两脚要连一起,否则干扰很大,想不通啊 哪怕模拟地和数字地连我都可以理解

点评

你输入信号是悬空的吗? 你的信号地(或信号负极)接哪里? 你输入接口不是差分,也没有地回路,你的干扰会很大甚至不正常,所以你要将2脚接地才有回路。 先用信号发生器输出信号直接接单片机测试,测试稳定后再接外  详情 回复 发表于 7 天前
回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:169
  • 最近打卡:2025-09-03 22:50:50

76

主题

6683

回帖

1万

积分

超级版主

积分
14009
发表于 7 天前 | 显示全部楼层
Prin*** 发表于 2025-8-28 18:33
发现这两脚要连一起,否则干扰很大,想不通啊 哪怕模拟地和数字地连我都可以理解
...

你输入信号是悬空的吗? 你的信号地(或信号负极)接哪里?
你输入接口不是差分,也没有地回路,你的干扰会很大甚至不正常,所以你要将2脚接地才有回路。
先用信号发生器输出信号直接接单片机测试,测试稳定后再接外部输入信号测试。
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 最近打卡:2025-08-29 08:03:13
已绑定手机

2

主题

17

回帖

56

积分

注册会员

积分
56
发表于 6 天前 | 显示全部楼层
梁*** 发表于 2025-8-28 22:54
你输入信号是悬空的吗? 你的信号地(或信号负极)接哪里?
你输入接口不是差分,也没有地回路,你的干扰 ...

恩恩,应该是这个原因,非常感谢指导。 ,这次测频用计数器最终解决了,下次改进打算换种单片机 实现精密测频,占空比,有推荐的型号吗

点评

STC8H、STC32G、AI8051U这些都可以。  详情 回复 发表于 6 天前
回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:169
  • 最近打卡:2025-09-03 22:50:50

76

主题

6683

回帖

1万

积分

超级版主

积分
14009
发表于 6 天前 | 显示全部楼层
Prin*** 发表于 2025-8-29 08:03
恩恩,应该是这个原因,非常感谢指导。 ,这次测频用计数器最终解决了,下次改进打算换种单片机 实现精密 ...

STC8H、STC32G、AI8051U这些都可以。精度取决于时钟的精度,高精度方式推荐使用恒温可压控微调的有源晶振(通常是10MHz)+北斗模块驯服,作为MCU主时钟,输入增加外部高速分频计数,可以做到0~50MHz、0~2.4GHz两档测频,也可以测周期,精度能达到0.01ppm,用于定时年误差不超过0.3秒。
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:4
  • 最近打卡:2025-08-29 08:03:13
已绑定手机

2

主题

17

回帖

56

积分

注册会员

积分
56
发表于 6 天前 | 显示全部楼层
梁*** 发表于 2025-8-29 09:56
STC8H、STC32G、AI8051U这些都可以。精度取决于时钟的精度,高精度方式推荐使用恒温可压控微调的有源晶振 ...

好的,谢谢 我去了解一下
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-9-4 01:06 , Processed in 0.126636 second(s), 105 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表