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

1. 前言

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

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

 

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

 

2. 实现代码

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

以下代码利用了 TIM1 的 PWM输入模式+复位触发模式 进行 PWM 采集

// tim1.c

/**
 * TIM1 PWM 输入初始化
 */
void TIM1_PWMI_Init()
{
    // 重置初始化
    TIM1_DeInit();

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

    // PWM的通道1配置
    // PWM的通道2无需配置,因为 TIM1_PWMIConfig(...) 内部已经进行了配置
    //
    // 参数说明:
    //  1. TIM1 通道1
    //  2. 极性:上升沿捕获
    //  3. 输入脚:TIM1_ICSELECTION_DIRECTTI 表示直接将 T1 信号经过 TI1FP1 发送给 IC1 模块进行处理
    //  4. 预分频:意思是控制在多少个输入周期做一次捕获;【经过实际测试,复位触发模式下该值无效,都是按照1分频执行】
    //           1M 系统时钟,捕获 20Hz PWM 一个周期需要计数 50000  次(1M/20=50000),TIM1 最大计数有65536,可以捕获
    //           1M 系统时钟,捕获 15Hz PWM 一个周期需要计数 66666  次(1M/15=66666),TIM1 最大计数有65536,无法捕获
    //  5. 滤波频率:经历几个周期相同的跳变则波形稳定,此处不滤波。如果频率捕获偶发值不对,那么可以启用 0x02(4个事件)
    TIM1_PWMIConfig(TIM1_CHANNEL_1, TIM1_ICPOLARITY_RISING, TIM1_ICSELECTION_DIRECTTI, TIM1_ICPSC_DIV1, 0x02);
    
    // 选择输入触发源
    // 设置 TI1FP1 的信号为触发输入源
    // 芯片根据触发源的触发信息自动设置 触发复位模式 的一些动作(包括:复位计数器值等)
    TIM1_SelectInputTrigger(TIM1_TS_TI1FP1);  // TIM1_SMCR |= 0x50;

    // 从模式选择
    // 从模式选择为触发复位模式
    TIM1_SelectSlaveMode(TIM1_SLAVEMODE_RESET);  // TIM1_SMCR |= 0x04;

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

/**
 * 开始 PWM 捕获
 */
void TIM1_PWMI_Start()
{
    // 清空俘获中断标志
    TIM1_ClearITPendingBit(TIM1_IT_CC1);
    TIM1_ClearFlag(TIM1_FLAG_CC1);
    
    // 启用中断函数:俘获比较通道CH1中断
    TIM1_ITConfig(TIM1_IT_CC1, ENABLE);

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

/**
 * 终止 PWM 捕获
 */
void TIM1_PWMI_Stop()
{
    // 关闭中断函数:俘获比较通道CH1中断
    TIM1_ITConfig(TIM1_IT_CC1, DISABLE);

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


/**
 * 获取 TIM1 CH1 捕获的 PWM 频率
 *
 * @return integer  频率
 */
u16 TIM1_PWMI_GetFreq()
{
    return GV_Tim1PwmiFreq;
}


/**
 * 获取 TIM1 CH1 捕获的 PWM 正占空比
 *
 * @param short number  PCB上的编号
 */
double TIM1_PWMI_GetDuty()
{
    return GV_Tim1PwmiDuty;
}
// stm8s_it.c

INTERRUPT_HANDLER(TIM1_CAP_COM_IRQHandler, 12)
{
    // 初始化
    u16 icValueB, icValueC;

    // 中断:PWM 捕获
    //【经过实际测试】复位触发模式的第一个上升沿不会触发中断,第一个下降沿和第二个上升沿会触发中断
    //        实际编码过程中,我们只需要关注第二个上升沿中断就可以了,其他动作STM8库函数会自动处理。
    if( TIM1_GetITStatus(TIM1_IT_CC1) != RESET ){
        // B 点:第一个下降沿的计数器值(TIM1 CH2)
        // C 点:第二个上升沿的计数器值(TIM1 CH1)
        icValueB = TIM1_GetCapture2();
        icValueC = TIM1_GetCapture1();
    
        // 计算频率和周期
        // 这里的 1000000 是基于初始化函数中的系统时钟设置(16预分频 = 1000000)
        // 这里的 100.0 小数点表示将表达式 icValueB*100.0/icValueC 的过程和结果以浮点数来进行
        GV_Tim1PwmiFreq = 1000000/icValueC;
        GV_Tim1PwmiDuty = icValueB*100.0/icValueC;
    
        // 清除中断标志位
        TIM1_ClearITPendingBit(TIM1_IT_CC1);    
        TIM1_ClearFlag(TIM1_FLAG_CC1);
    }
}

上述代码中用到的PWM采集原理请参考《【思修STM8 39 讲】PWM 输入捕获之占空比测量(寄存器版)》

 

备注:仅 TIM1-CH1 和 CH2 两个通道支持复位触发模式;CH3 和 CH4 不支持复位触发模式,也不支持 TIM1_PWMIConfig(...) 进行配置,需要直接利用 TIM1_ICInit(...) 函数进行配置,PWM 的频率和占空比采集代码和上述也有不同,具体参见《【STM8】PWM 捕获实战:占空比和频率(TIM2)》。所以一个项目内如果需要需要同时开启 TIM1-CH1 和 CH3 输入捕获,那么就不能用 复位触发模式 了,否则两种方案的采集代码会冲突(因为复位触发模式会将计数器值设置为0,那么就会冲突到CH3的采集)。

 

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

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

 

 

 

 

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用STM32F103微控制器进行输入捕获测量频率占空比和波长的示例代码: ```c #include "stm32f10x.h" #define TIMER_PRESCALER 72 #define PULSE_PERIOD 2000 // 2ms uint32_t pulse_start_time = 0; uint32_t pulse_end_time = 0; float frequency = 0; float duty_cycle = 0; float wavelength = 0; void TIM2_IRQHandler(void) { if(TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) { if(pulse_start_time == 0) { pulse_start_time = TIM_GetCapture1(TIM2); } else { pulse_end_time = TIM_GetCapture1(TIM2); frequency = (float)TIMER_PRESCALER / (pulse_end_time - pulse_start_time); duty_cycle = (float)(pulse_end_time - pulse_start_time) / PULSE_PERIOD * 100; wavelength = frequency == 0 ? 0 : 1 / frequency; pulse_start_time = 0; pulse_end_time = 0; } TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); } } void TIM2_Configuration(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Period = PULSE_PERIOD - 1; TIM_TimeBaseStructure.TIM_Prescaler = TIMER_PRESCALER - 1; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_OCStructInit(&TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = PULSE_PERIOD / 2 - 1; TIM_OC1Init(TIM2, &TIM_OCInitStructure); TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE); TIM_Cmd(TIM2, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } int main(void) { TIM2_Configuration(); while(1) { // Your code here } } ``` 在这个示例代码中,我们将TIM2定时器配置为PWM输出模式,并启用输入捕获捕获PWM脉冲的上升沿和下降沿时间。当捕获到下降沿时间时,我们可以计算出频率占空比和波长,并将它们存储在相应的变量中。请注意,示例代码中的PWM周期为2ms,可以根据实际需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值