乘风飞扬 发表于 2025-8-26 15:23:37

Printy陈 发表于 2025-8-26 15:06
58按理说STC15W408AS应该可以做到吧

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

梁工 发表于 2025-8-26 16:10:03

Printy陈 发表于 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捕获测周期计算频率。

Printy陈 发表于 2025-8-26 16:29:46

我试试,谢谢了,定时器0最大可以测多少

梁工 发表于 2025-8-26 16:53:54

Printy陈 发表于 2025-8-26 16:29
我试试,谢谢了,定时器0最大可以测多少

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

Printy陈 发表于 6 天前

梁工 发表于 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;
u8 buffer_index;
u32 filtered_freq;

u16 gate_time_ms;
char disp_buf;

//------------------------------------------------------------------
// 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 = '0' + (temp % 10);
      temp /= 10;
    }

    // 前面补空格
    for (i = digits; i < 8; i++)
    {
      buf = ' ';
    }
    buf = '\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' + (temp / 10); // 整数位
      buf = '.';
      buf = '0' + (temp % 10); // 一位小数
      buf = 'M';
      buf = 'H';
      buf = 'z';
      buf = ' ';
      buf = ' ';
    }
    else if (freq_hz >= 100000)
    {
      // 100kHz-999kHz:显示为XXX.X kHz
      u32 temp = freq_hz / 100; // 转换为0.1kHz单位

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

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

      buf = '0' + (temp / 10); // 整数位
      buf = '.';
      buf = '0' + (temp % 10); // 一位小数
      buf = 'k';
      buf = 'H';
      buf = 'z';
      buf = ' ';
      buf = ' ';
    }
    else
    {
      // 1Hz以下:显示为XXX Hz
      if (freq_hz == 0)
      {
            buf = '0';
            buf = ' ';
            buf = 'H';
            buf = '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 = '0' + (temp % 10);
                temp /= 10;
            }

            // 添加单位
            buf = ' ';
            buf = 'H';
            buf = 'z';

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

    buf = '\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 = 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;

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

Printy陈 发表于 6 天前



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

梁工 发表于 6 天前

Printy陈 发表于 2025-8-28 18:33
发现这两脚要连一起,否则干扰很大,想不通啊 哪怕模拟地和数字地连我都可以理解
...

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

Printy陈 发表于 5 天前

梁工 发表于 2025-8-28 22:54
你输入信号是悬空的吗? 你的信号地(或信号负极)接哪里?
你输入接口不是差分,也没有地回路,你的干扰 ...

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

梁工 发表于 5 天前

Printy陈 发表于 2025-8-29 08:03
恩恩,应该是这个原因,非常感谢指导。 ,这次测频用计数器最终解决了,下次改进打算换种单片机 实现精密 ...
STC8H、STC32G、AI8051U这些都可以。精度取决于时钟的精度,高精度方式推荐使用恒温可压控微调的有源晶振(通常是10MHz)+北斗模块驯服,作为MCU主时钟,输入增加外部高速分频计数,可以做到0~50MHz、0~2.4GHz两档测频,也可以测周期,精度能达到0.01ppm,用于定时年误差不超过0.3秒。

Printy陈 发表于 5 天前

梁工 发表于 2025-8-29 09:56
STC8H、STC32G、AI8051U这些都可以。精度取决于时钟的精度,高精度方式推荐使用恒温可压控微调的有源晶振 ...

好的,谢谢 我去了解一下
页: 1 [2]
查看完整版本: 15W408AS PCA捕获测频率,只能600Hz-180KHz,指导