草帽王子 发表于 2021-4-23 00:24:16

第九章:CH32V103应用教程——输入捕获

本帖最后由 草帽王子 于 2021-9-10 16:27 编辑

本章教程通过CH32V103开发板PA8引脚检测PWM脉宽和周期,并通过串口调试助手打印显示。


1、输入捕获简介及相关函数介绍

输入捕获模式是定时器的基本功能之一,其通常用于测量频率或者测量脉宽。

输入捕获可对输入信号的上升沿、下降沿或者双边沿进行捕获,其捕获原理为:当发生并捕获信号跳变沿之后,计数器(CNT)值将被锁存到捕获比较寄存器(CCR)中,将前后两次捕获到的CCR寄存器中的值相减,即可计算出频率或者脉宽。如果捕获脉宽时长超过捕获定时器的周期,会发生溢出,此时需要进行额外处理。

关于定时器输入捕获具体介绍,可参考CH32V103应用手册。输入捕获程序所需函数所在库函数在定时器中断教程中已介绍,本章不做过多介绍。


2、硬件设计

本章教程使用输入捕获中PWM输入模式,需要用到两个开发板,一个用于产生PWM输出,一个用于输入捕获。本章教程使用两个CH32V103开发板,一个直接下载CH32V103 EVT中PWM输出例程,一个直接下载本章教程中例程,然后将两个开发板PA8引脚连接起来。


3、软件设计

本章教程使用PWM输入模式进行输入捕获,其使用两个通道和两个捕获寄存器,具体实现方式为:当使用PWM输入模式时,PWM信号由输入通道TI1进入,配置滤波后的定时器输入1(TI1FP1)为触发信号并设置上升沿捕获。当上升沿的时候捕获比较通道IC1和IC2同时捕获,计数器CNT清零,到了下降沿的时候,IC2捕获,此时计数器CNT的值被锁存到捕获比较寄存器CCR2中,到了下一个上升沿的时候,IC1捕获,计数器CNT 的值被锁存到捕获比较寄存器CCR1中。其中CCR2+1测量的是脉宽, CCR1+1测量的是周期。这里要注意的是 CCR2 和 CCR1 的值在计算占空比和频率的时候都必须加1,因为计数器是从0开始计数的。

使用PWM输入模式实现输入捕获具体程序如下:

capture.h文件
#ifndef __CAPTURE_H
#define __CAPTURE_H

#include "ch32v10x_conf.h"

void Input_Capture_Init( u16 arr, u16 psc );
void TIM1_CC_IRQHandler(void);

#endifcapture.h文件主要是函数的声明。

capture.c文件
#include "capture.h"

void TIM1_CC_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));

/* ----------------   PWM信号 周期和占空比的计算--------------- */
// arr :自动重装载寄存器的值
// clk_cnt:计数器的时钟,等于 Fck_int / (psc+1) = 72M/(psc+1)
// PWM信号的周期 T = (arr +1)* (1/clk_cnt) = arr*(psc+1) / 72M
// 占空比P=CCR/(arr+1)
/*******************************************************************************
* Function Name: Input_Capture_Init
* Description    : Initializes TIM1 input capture.
* Input          : arr: the period value.
*                  psc: the prescaler value.
*                  ccp: the pulse value.
* Return         : None
*******************************************************************************/
void Input_Capture_Init( u16 arr, u16 psc )
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_ICInitTypeDef TIM_ICInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE );//使能GPIOA时钟和TIM1时钟

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init( GPIOA, &GPIO_InitStructure);
    GPIO_ResetBits( GPIOA, GPIO_Pin_8 );

    //定时器周期,实际就是设定自动重载寄存器 ARR 的值, ARR 为要装载到实际自动重载寄存器(即影子寄存器) 的值, 可设置范围为 0 至 65535。
    TIM_TimeBaseInitStructure.TIM_Period = arr;
    //定时器预分频器设置,时钟源经该预分频器才是定时器计数时钟CK_CNT,它设定 PSC 寄存器的值。
    //计算公式为: 计数器时钟频率 (fCK_CNT) 等于fCK_PSC / (PSC + 1),可实现 1 至 65536 分频。
    TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
    //时钟分频,设置定时器时钟 CK_INT 频率与死区发生器以及数字滤波器采样时钟频率分频比。可以选择 1、 2、 4 分频。
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置计数模式,向上计数模式
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter =0x00;      //设置重复计数器的值,0。重复计数器,只有 8 位,只存在于高级定时器。
    TIM_TimeBaseInit( TIM1, &TIM_TimeBaseInitStructure); //初始化

    // 当用作PWM输入捕获模式时,只需要设置触发信号的那一路即可(用于测量周期)
    // 另外一路(用于测量占空比)会由硬件自带设置,不需要再配置

    // TIM_Channel:捕获通道 ICx 选择,可选 TIM_Channel_1、 TIM_Channel_2、TIM_Channel_3 或 TIM_Channel_4 四个通道。
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
    //配置输入捕获预分频器值,如果需要捕获输入信号的每个有效边沿,则设置 1 分频 即可
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    //输入捕获滤波器设置,可选设置 0x0 至 0x0F。它设定 CHCTLRx 寄存器ICxF位的值。一般我们不使用滤波器,即设置为 0
    TIM_ICInitStructure.TIM_ICFilter = 0x0;
    //输入捕获边沿触发选择,可选上升沿触发、下降沿触发或边沿跳变触发。
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
    //输入通道选择,捕获通道 ICx 的信号可来自三个输入通道,分别为TIM_ICSelection_DirectTI、 TIM_ICSelection_IndirectTI 或 TIM_ICSelection_TRC
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;

    TIM_PWMIConfig( TIM1, &TIM_ICInitStructure );

    NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;       //TIM1捕获比较中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//设置抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;       //设置响应优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;          //使能通道
    NVIC_Init(&NVIC_InitStructure);

    TIM_ITConfig( TIM1, TIM_IT_CC1 | TIM_IT_CC2, ENABLE ); //使能TIM1捕获中断

    // 选择输入捕获的触发信号
    TIM_SelectInputTrigger(TIM1, TIM_TS_TI1FP1);

    // 选择从模式: 复位模式
    // PWM输入模式时,从模式必须工作在复位模式,当捕获开始时,计数器CNT会被复位
    TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset);
    TIM_SelectMasterSlaveMode(TIM1,TIM_MasterSlaveMode_Enable);
    TIM_Cmd( TIM1, ENABLE );//定时器使能
}

/*******************************************************************************
* Function Name: TIM1_CC_IRQHandler
* Description    : This function handles TIM1Capture Compare Interrupt exception.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void TIM1_CC_IRQHandler(void)
{

    if( TIM_GetITStatus( TIM1, TIM_IT_CC1 ) != RESET )   //若捕获比较1发生中断
    {
      printf( "cycle:%d\r\n", (TIM_GetCapture1(TIM1)+1) );      //打印得到的捕获比较1寄存器值,其值加1表示周期
      TIM_SetCounter( TIM1, 0 );
    }

    if( TIM_GetITStatus( TIM1, TIM_IT_CC2 ) != RESET )   //若捕获比较2发生中断
    {
      printf( "Pulsewidth:%d\r\n", (TIM_GetCapture2(TIM1)+1) ); //打印得到的捕获比较2寄存器值,其值加1表示脉宽
    }

    TIM_ClearITPendingBit( TIM1, TIM_IT_CC1 | TIM_IT_CC2 ); //清除TIM1捕获比较1和捕获比较2中断挂起位
}



capture.c文件主要是输入捕获相关函数配置,包括输入捕获相关函数初始化配置和定时器中断服务函数,其具体配置流程如下:

1、使能GPIOA时钟和TIM1时钟
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE );//使能GPIOA时钟和TIM1时钟
2、GPIO初始化配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init( GPIOA, &GPIO_InitStructure);
GPIO_ResetBits( GPIOA, GPIO_Pin_8 );
3、根据TIM_TimeBaseInitStruct中指定的参数初始化TIM1时基单元外围设备。
//定时器周期,实际就是设定自动重载寄存器 ARR 的值, ARR 为要装载到实际自动重载寄存器(即影子寄存器) 的值, 可设置范围为 0 至 65535。
    TIM_TimeBaseInitStructure.TIM_Period = arr;
    //定时器预分频器设置,时钟源经该预分频器才是定时器计数时钟CK_CNT,它设定 PSC 寄存器的值。
    //计算公式为: 计数器时钟频率 (fCK_CNT) 等于fCK_PSC / (PSC + 1),可实现 1 至 65536 分频。
    TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
    //时钟分频,设置定时器时钟 CK_INT 频率与死区发生器以及数字滤波器采样时钟频率分频比。可以选择 1、 2、 4 分频。
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置计数模式,向上计数模式
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter =0x00;      //设置重复计数器的值,0。重复计数器,只有 8 位,只存在于高级定时器。
    TIM_TimeBaseInit( TIM1, &TIM_TimeBaseInitStructure); //初始化
4、输入捕获结构体初始化,配置IC1捕获通道
// TIM_Channel:捕获通道 ICx 选择,可选 TIM_Channel_1、 TIM_Channel_2、TIM_Channel_3 或 TIM_Channel_4 四个通道。
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
    //配置输入捕获预分频器值,如果需要捕获输入信号的每个有效边沿,则设置 1 分频 即可
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    //输入捕获滤波器设置,可选设置 0x0 至 0x0F。它设定 CHCTLRx 寄存器ICxF位的值。一般我们不使用滤波器,即设置为 0
    TIM_ICInitStructure.TIM_ICFilter = 0x0;
    //输入捕获边沿触发选择,可选上升沿触发、下降沿触发或边沿跳变触发。
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
    //输入通道选择,捕获通道 ICx 的信号可来自三个输入通道,分别为TIM_ICSelection_DirectTI、 TIM_ICSelection_IndirectTI 或 TIM_ICSelection_TRC
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
5、初始化PWM输入模式
TIM_PWMIConfig( TIM1, &TIM_ICInitStructure );
6、配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;       //TIM1捕获比较中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;            //设置响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //使能通道
NVIC_Init(&NVIC_InitStructure);//NVIC初始化
7、使能捕获中断
TIM_ITConfig( TIM1, TIM_IT_CC1 | TIM_IT_CC2, ENABLE ); //使能TIM1捕获中断
8、选择输入捕获触发信号以及主从模式
TIM_SelectInputTrigger( TIM1, TIM_TS_TI1FP1 );                            //选择过滤定时器输入1作为触发源
TIM_SelectSlaveMode( TIM1, TIM_SlaveMode_Reset );                     //TIM1从机模式,所选触发信号(TRGI)的上升沿重新初始化。
TIM_SelectMasterSlaveMode( TIM1, TIM_MasterSlaveMode_Enable );//设置或重置TIM1主从模式,使能定时器与从机之间同步
9、使能定时器
TIM_Cmd( TIM1, ENABLE );
10、编写中断服务函数
void TIM1_CC_IRQHandler(void)
{
    if( TIM_GetITStatus( TIM1, TIM_IT_CC1 ) != RESET )   //若捕获比较1发生中断
    {
      printf( "cycle:%d\r\n", (TIM_GetCapture1(TIM1)+1) ); //打印得到的捕获比较1寄存器值,其值加1表示周期
    }
    if( TIM_GetITStatus( TIM1, TIM_IT_CC2 ) != RESET )   //若捕获比较2发生中断
    {
      printf( "Pulsewidth:%d\r\n", (TIM_GetCapture2(TIM1)+1) ); //打印得到的捕获比较2寄存器值,其值加1表示脉宽
    }
    TIM_ClearITPendingBit( TIM1, TIM_IT_CC1 | TIM_IT_CC2 ); //清除TIM1捕获比较1和捕获比较2中断挂起位
}
capture.c文件主要是进行输入捕获的相关配置以及中断服务函数功能的设置,通过以上配置,即可进行输入捕获并通过中断服务函数打印输出脉宽和周期。

main.c文件
int main(void)
{
    USART_Printf_Init(115200);
    printf("SystemClk:%d\r\n",SystemCoreClock);

    Input_Capture_Init( 1000-1, 72-1 );

    while(1);
}main.c文件主要是相关函数的初始化。


4、下载验证

将编译好的程序下载到开发板并复位,将两个开发板PA8引脚连接起来,打开串口调试助手,可看见在不停打印PWM脉宽和周期,具体如下:


输入捕获.rar 附件下载

链接:https://pan.baidu.com/s/1L1lF5kzIQfzLbMPJb2nJUg
提取码:ugio
复制这段内容后打开百度网盘手机App,操作更方便哦



页: [1]
查看完整版本: 第九章:CH32V103应用教程——输入捕获