N32定时器测量频率

N32定时器测量频率

最近要测量一个频率为40KHz~400KHz的方波,刚开始用输入捕获,使用输入捕获测量方波,首先遇到上升沿(这个自己设定)进入一次中断,记录下定时器的计数值,再次遇到上升沿进入中断,记录定时器的计数值,根据两次的差值就可以算出频率,这种方法直接测出频率是最直接的,理论上也是最简单的。但是,400KHz频率的方波会让单片机频繁进中断,导致单片机根本无法工作!

所以我换了个方法,测周期,然后反推频率。使用方波当单片机定时器的外部时钟源,这个定时器只用来计数,不做其他任何事,然后再用一个定时器以固定的时间去读外部时钟的计数值,比如我10ms读一次计数值,读到计数值为1000,那么这个方波的频率为1000*100=100KHz。一个方波只有一个上升沿和下降沿!

这样测量能够有效减少单片机进入中断的次数,但测量精度没有输入捕获测量的高,不过也够用了。

我使用的是N32G003,最高主频48M,只有一个高级定时器TIM1支持外部时钟源,外部时钟源模式有两种,一种是使用TI2,在这里也就是TIM1的通道2,还有一种是使用ETR引脚。这两个有什么区别?目前我没发现。我这里使用的是ETR引脚做外部时钟源输入。

void water_sensor_init(void)
{
    GPIO_InitType gpio_init_struct;
    TIM_TimeBaseInitType tim_base_struct;

    RCC_APB_Peripheral_Clock_Enable(RCC_APB_PERIPH_TIM1 | RCC_APB_PERIPH_AFIO);
    GPIO_Structure_Initialize(&gpio_init_struct);
    gpio_init_struct.GPIO_Alternate = GPIO_AF5_TIM1;
    gpio_init_struct.GPIO_Current = GPIO_LOW_DREIVE;
    gpio_init_struct.GPIO_Mode = GPIO_MODE_INPUT;
    gpio_init_struct.GPIO_Pull = GPIO_NO_PULL;
    gpio_init_struct.GPIO_Slew_Rate = GPIO_SLEW_RATE_SLOW;
    gpio_init_struct.Pin = WATER_SENSOR_PIN;
    GPIO_Peripheral_Initialize(WATER_SENSOR_PORT, &gpio_init_struct);

    TIM_External_Clock_Mode2_Set(WATER_SENSOR_TIM, TIM_EXT_TRG_PSC_OFF, TIM_EXT_TRIG_POLARITY_INVERTED, 0);
    TIM_Base_Struct_Initialize(&tim_base_struct);

    tim_base_struct.Prescaler = 0;
    tim_base_struct.Period = 65535;
    tim_base_struct.CntMode = TIM_CNT_MODE_UP;
    tim_base_struct.ClkDiv = TIM_CLK_DIV1;
    // 这里的预分频计数器其实就是计数器,CK_PSC经过分频后为CK_CNT
    TIM_Base_Reload_Mode_Set(WATER_SENSOR_TIM, TIM_PSC_RELOAD_MODE_IMMEDIATE); // 当由软件设置时,该位可以生成更新事件。 而此时计数器会重新初始化,预分频计数器会被清零,计数器在中央对齐或向上计数模式下会被清零,但在向下计数模式下取 TIMx_AR寄存器的值。 该位由硬件自动清零。
    TIM_Auto_Reload_Preload_Enable(WATER_SENSOR_TIM);                          // 使能自动重装载
    TIM_On(WATER_SENSOR_TIM);
}

以上代码中最主要的函数就是设置外部时钟源函数TIM_External_Clock_Mode2_Set(WATER_SENSOR_TIM, TIM_EXT_TRG_PSC_OFF, TIM_EXT_TRIG_POLARITY_INVERTED, 0);​然后还有配置下定时器计数的周期和预分频,这里不分频,计数周期直接取最大的。

/**
 * @brief 获取水传感器的频率值
 *
 * 该函数通过读取定时器的计数值并进行换算,获取水传感器的频率值。
 * 需要定时调用(建议每10ms调用一次)以保证测量精度。
 *
 * @param 无参数
 * @return 计算得到的频率值(单位:Hz)
 *         返回值为原始计数值乘以时间基准系数后的结果
 */
uint32_t water_sensor_get_frequency(void)
{
    uint16_t count = 0;
    uint32_t frequency = 0;

    /* 获取定时器当前计数值(反映脉冲信号周期) */
    count = TIM_Base_Count_Get(WATER_SENSOR_TIM);

    /* 清除当前计数值,为下一次测量做准备 */
    TIM_Base_Count_Set(WATER_SENSOR_TIM, 0);

    /* 将计数值转换为实际频率值:
     * MS_TO_SECOND_FACTOR 为时间基准系数(10ms对应100Hz基准)
     * 计算公式:频率(Hz) = 计数值 * (1000ms / 测量周期ms) */
    frequency = count * MS_TO_SECOND_FACTOR;

    return frequency;
}

然后在中断里置标志位后调用这个函数或者直接在中断里调用计算频率。

posted @ 2025-05-29 19:30  CBYHZ  阅读(21)  评论(0)    收藏  举报