【STM8】PWM 捕获实战:占空比和频率(TIM2)

1. 前言

基于《【众拳STM8 40 讲】PWM 输入捕获原理与实验(频率)》我们已经了解了利用 STM8 库函数 如何捕获和计算 PWM 的频率,但是确没有介绍占空比的捕获方法。

基于《【思修STM8 39 讲】PWM 输入捕获之占空比测量(寄存器版)》我们了解了 PWM 捕获的详细流程步骤,但是其并非利用 STM8库函数 实现,同时也没有利用中断实现采集。

基于《【STM8】PWM 捕获实战:占空比和频率(TIM1)》我们了解了利用PWM输入模式和复位触发模式采集 PWM,但是并非所有定时器都具有复位触发模式

 

下面我们介绍利用 STM8库函数 和 中断 的方式来实现利用 TIM2 捕获 PWM 占空比和频率。

 

2. 实现代码

PWM 采集规定是:CH1 上升捕获,CH2 下降沿捕获

不同于 TIM1 的复位触发模式,TIM2 不具备复位触发模式,所以编码方式上还是有些不同

// tim2.c

u16 GV_Tim2PwmiFreq = 0;       //【系统使用】定时器捕获的 PWM 频率
double GV_Tim2PwmiDuty = 0.0;  //【系统使用】定时器捕获的 PWM 正占空比

/**
 * TIM2 PWM 输入初始化
 */
void TIM2_PWMI_Init()
{
    // 重置初始化
    TIM2_DeInit();

    // 初始化 TIME1 时基单元
    // 16预分频,向上计数,每 1s/1M = 1us 计数器值加 1(累加 1000 次需要 1 毫秒)
    // 参数说明:预分配值,自动重装载值(计数器值到 65536 后重新装载)
    TIM2_TimeBaseInit(TIM2_PRESCALER_16, 65536-1);

    // PWM的通道1配置
    //
    // 参数说明:
    //  1. TIM2 通道1
    //  2. 极性:上升沿捕获
    //  3. 输入脚:TIM2_ICSELECTION_DIRECTTI 表示直接将 T1 信号经过 TI1FP1 发送给 IC1 模块进行处理
    //  4. 预分频:意思是控制在多少个输入周期做一次捕获;【经过实际测试,复位触发模式下该值无效,都是按照1分频执行】
    //           1M 系统时钟,捕获 20Hz PWM 一个周期需要计数 50000  次(1M/20=50000),TIM2 最大计数有65536,可以捕获
    //           1M 系统时钟,捕获 15Hz PWM 一个周期需要计数 66666  次(1M/15=66666),TIM2 最大计数有65536,无法捕获
    //  5. 滤波频率:经历几个周期相同的跳变则波形稳定。如果频率捕获偶发值不对,那么可以启用 0x02(4个事件)
    TIM2_PWMIConfig(TIM2_CHANNEL_1, TIM2_ICPOLARITY_RISING, TIM2_ICSELECTION_DIRECTTI, TIM2_ICPSC_DIV1, 0x00);
    
    // PWM的通道2配置
    //【经过实际测试,通道2 可以不用配置,也可以配置,但是如果配置那么参数必须要填写正确:TIM2_ICPOLARITY_FALLING,TIM2_ICSELECTION_INDIRECTTI】
    //【至于为什么不用配置系统仍旧能正常运行,猜想可能是 STM8库函数 内部自动做了一些处理】
    // 
    // 参数说明:
    //  1. TIM2 通道2
    //  2. 极性:下降沿捕获
    //  3. TIM2_ICSELECTION_INDIRECTTI 表示直接将 T1 信号经过 TI1FP2 发送给 IC2 模块进行处理
    //  4. 预分频:意思是控制在多少个输入周期做一次捕获;【经过实际测试,复位触发模式下该值无效,都是按照1分频执行】
    //  5. 滤波频率:经历几个周期相同的跳变则波形稳定。如果频率捕获偶发值不对,那么可以启用 0x02(4个事件)
    //TIM2_PWMIConfig(TIM2_CHANNEL_2, TIM2_ICPOLARITY_FALLING, TIM2_ICSELECTION_INDIRECTTI, TIM2_ICPSC_DIV1, 0x00);

    // 输入捕获/比较输出使能
    //【经过实际测试,以下使能代码可以注释掉,但是为了安心还是加上去】
    TIM2_CCxCmd(TIM2_CHANNEL_1, ENABLE);
    TIM2_CCxCmd(TIM2_CHANNEL_2, ENABLE);
}

/**
 * 开始 PWM 捕获
 */
void TIM2_PWMI_Start()
{
    // 清空俘获中断标志
    TIM2_ClearITPendingBit(TIM2_IT_CC1);
    TIM2_ClearFlag(TIM2_FLAG_CC1);
    
    // 启用中断函数:俘获比较通道CH1中断
    TIM2_ITConfig(TIM2_IT_CC1, ENABLE);

    // 开定时器
    //【经过实际测试,定时器就算未开启,上面的 TIM2_IT_CC1 中断仍正常进入,但是获取的计数器值不确定】
    TIM2_Cmd(ENABLE);
}


/**
 * 终止 PWM 捕获
 * 利用 TIM2_Cmd(DISABLE) 关闭输出,注意其他函数可能受影响,例如:TIM2_PWMI_Init
 */
void TIM2_PWMI_Stop()
{
    // 关闭中断函数:俘获比较通道CH1中断
    TIM2_ITConfig(TIM2_IT_CC1, DISABLE);

    // 关闭定时器
    TIM2_Cmd(DISABLE);
}


/**
 * 获取 TIM2 CH1 捕获的 PWM 频率
 *
 * @return integer  频率
 */
u16 TIM2_PWMI_GetFreq()
{
    return GV_Tim2PwmiFreq;
}


/**
 * 获取 TIM2 CH1 捕获的 PWM 正占空比
 *
 * @return double  占空比
 */
double TIM2_PWMI_GetDuty()
{
    return GV_Tim2PwmiDuty;
}
// stm8s_it.c

INTERRUPT_HANDLER(TIM2_CAP_COM_IRQHandler, 12)
{
    // 初始化定时器输入捕获值
    // 分别对应一个PWM周期的第一个上升沿,第一个下降沿,第二个上升沿
    static u16 icValueA, icValueB, icValueC;

    // 获取定时器输入捕获值
    if( icValueA == 0 ){
        icValueA = TIM2_GetCapture1();
    }else{
        icValueB = TIM2_GetCapture2();
        icValueC = TIM2_GetCapture1();
        
        // 处理定时器捕获值溢出情况
        if( icValueC < icValueA ) icValueC = icValueC-icValueA+65535;
        else icValueC = icValueC - icValueA;
 
        if( icValueB < icValueA ) icValueB = icValueB-icValueA+65535;
        else icValueB = icValueB - icValueA;

        // 计算频率和周期
        // 这里的 1000000 是基于初始化函数中的系统时钟设置(16预分频 = 1000000)
        // 这里的 100.0 小数点表示将表达式 icValueB*100.0/icValueC 的过程和结果以浮点数来进行
        GV_Tim2PwmiFreq = 1000000/icValueC;
        GV_Tim2PwmiDuty = icValueB*100.0/icValueC;

        // 重置定时器输入捕获值
        icValueA = icValueB = icValueC = 0;
    }

    // 清除中断标志位
    TIM2_ClearITPendingBit(TIM2_IT_CC1);    
    TIM2_ClearFlag(TIM2_FLAG_CC1);
}

注意:建议使用 TIM2 就启用一个功能:输入捕获或者输出比较

   主要考虑到不同通道输出/捕获时都同时设置 TIM2_TimeBaseInit 函数,会相互干扰,为了减少程序复杂度所以建议使用 TIM2 就启用一个功能

 

 

 

 

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
下面是一个简单的示例代码,可以在 STM32F405 上实现 PWM 波的频率占空比捕获: ```c #include "stm32f4xx.h" #define PWM_FREQ 1000 // PWM 频率为 1 kHz #define PWM_PERIOD ((SystemCoreClock / PWM_FREQ) - 1) // 计算 PWM 周期 uint32_t pwm_duty_cycle = 0; void TIM2_IRQHandler(void) { if (TIM2->SR & TIM_SR_CC1IF) { // 如果是捕获比较器 1 中断 static uint32_t last_capture = 0; uint32_t current_capture = TIM2->CCR1; uint32_t capture_interval = current_capture - last_capture; uint32_t pwm_period = TIM2->CCR2 + 1; pwm_duty_cycle = (capture_interval * 100) / pwm_period; last_capture = current_capture; TIM2->SR &= ~TIM_SR_CC1IF; // 清除中断标志 } } int main(void) { // 初始化时钟和 GPIO RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; GPIOA->MODER |= GPIO_MODER_MODER5_1; // 将 PA5 配置为复用模式 GPIOA->AFR[0] |= GPIO_AFRL_AFRL5_2; // 将 PA5 配置为 TIM2_CH1 复用功能 // 初始化定时器 2 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; TIM2->PSC = 0; // 定时器分频系数为 1 TIM2->ARR = PWM_PERIOD; // 定时器自动重载寄存器 TIM2->CCMR1 = TIM_CCMR1_CC1S_0; // 将捕获比较器 1 配置为输入 TIM2->CCER = TIM_CCER_CC1E; // 使能捕获比较器 1 TIM2->DIER = TIM_DIER_CC1IE; // 使能捕获比较器 1 中断 TIM2->CR1 = TIM_CR1_CEN; // 启动定时器 while (1) { // 设置 PWM 占空比 TIM2->CCR1 = (pwm_duty_cycle * (PWM_PERIOD + 1)) / 100; } } ``` 该示例代码使用定时器 2 实现了 PWM 波的占空比捕获功能。在定时器 2 的捕获比较器 1 中断处理函数中,通过计算捕获比较器 1 的两个连续捕获值之间的时间差和 PWM 周期,可以计算出当前的 PWM 占空比
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值