58按理说STC15W408AS应该可以做到吧
理论上是可以,但是要在这么短时钟内处理好捕获结果,代码就得很精简。 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捕获测周期计算频率。 我试试,谢谢了,定时器0最大可以测多少
Printy陈 发表于 2025-8-26 16:29
我试试,谢谢了,定时器0最大可以测多少
定时器测频最高可以FOSC/2,比如FOSC=32MHz,则测频最高16MHz。 梁工 发表于 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陈 发表于 2025-8-28 18:33
发现这两脚要连一起,否则干扰很大,想不通啊 哪怕模拟地和数字地连我都可以理解
...
你输入信号是悬空的吗? 你的信号地(或信号负极)接哪里?
你输入接口不是差分,也没有地回路,你的干扰会很大甚至不正常,所以你要将2脚接地才有回路。
先用信号发生器输出信号直接接单片机测试,测试稳定后再接外部输入信号测试。 梁工 发表于 2025-8-28 22:54
你输入信号是悬空的吗? 你的信号地(或信号负极)接哪里?
你输入接口不是差分,也没有地回路,你的干扰 ...
恩恩,应该是这个原因,非常感谢指导。{:baoquan:} ,这次测频用计数器最终解决了,下次改进打算换种单片机 实现精密测频,占空比,有推荐的型号吗 Printy陈 发表于 2025-8-29 08:03
恩恩,应该是这个原因,非常感谢指导。 ,这次测频用计数器最终解决了,下次改进打算换种单片机 实现精密 ...
STC8H、STC32G、AI8051U这些都可以。精度取决于时钟的精度,高精度方式推荐使用恒温可压控微调的有源晶振(通常是10MHz)+北斗模块驯服,作为MCU主时钟,输入增加外部高速分频计数,可以做到0~50MHz、0~2.4GHz两档测频,也可以测周期,精度能达到0.01ppm,用于定时年误差不超过0.3秒。 梁工 发表于 2025-8-29 09:56
STC8H、STC32G、AI8051U这些都可以。精度取决于时钟的精度,高精度方式推荐使用恒温可压控微调的有源晶振 ...
好的,谢谢 我去了解一下
页:
1
[2]