STM32标准库——(8)TIM输入捕获

1.输入捕获简介

  • IC(Input Capture)输入捕获
  • 输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
  • 每个高级定时器和通用定时器都拥有4个输入捕获通道
  • 可配置为PWMI模式,同时测量频率和占空比
  • 可配合主从触发模式,实现硬件全自动测量

  • 输出比较,引脚是输出端口,根据CNT和CCR的大小关系来执行输出动作
  • 输入捕获,引脚是输入端口,接收到输入信号执行CNT锁存到CCR的动作

2.频率测量

STM32标准库——(8)TIM输入捕获_第1张图片

  1. 测频法:定时器中断 并记录捕获次数 1s内出现了多少个重复的周期,那频率就是多少Hz
  2. 测周法:捕获中断,并记录定时器次数 两个上升沿内标准频率fc计次 fc=72M/(psc+1) 得到N(就是读取CCR的值)(本节输入捕获测频率 使用的方法是测周法)
  3. 测频法适合测量高频信号 测周法适合测量低频信号 测频法在闸门时间内最好要多出现一些上升沿 计次数量多一些 这样有助于减小误差 侧周法就要求信号频率低一些 低频信号周期比较长 计次就会比较多 有助于减小误差 对于测频法和测周法的一个共同点是:N越大,误差就越小
  4. 由于两种方法的误差都与N的正负1误差有关 所以当两种方法计次的N相同时 两种方法的误差也就相同 消去两种方法公式中的N 将测频法和测周法的N提出来 令两个方法N相等 将fx解出来 可得第三个测频率公式 其中T是测频法的闸门时间 fc是测频法的标准频率 当待测信号频率小于中界频率时 测周法误差更小 选择测周法更合适 当待测信号频率大于中界频率时 测频法误差更小 选择测频法更合适

3.通用定时器中的输入捕获简介

STM32标准库——(8)TIM输入捕获_第2张图片

  1. 输入捕获就是这左边一部分电路 那右边就是我们之前讲过的输出比较部分 四个输入捕获和输出比较通道 共用四个CCR寄存器 另外它们的CH1到CH4 4个通道的引脚也是共用的 所以对于同一个定时器输入捕获和输出比较 只能使用其中一个不能同时使用
  2. 引脚进来,有一个三输入的异或门 这个异或门的输入接在了通道1、2、3端口 异或门的执行逻辑是 当3个输入引脚的任何一个有电平翻转时 输出引脚就产生一次电平翻转 3个引脚电平都相同为0 3个引脚中有高有低为1
  3. 输入信号来到了输入滤波器和边沿检测器(极性选择) 输入滤波器可以对信号进行滤波 避免一些高频的毛刺信号误触发 边沿检测器就是和外部中断一样 可以选择高电平触发或者低电平触发 当出现指定的电平时 边沿检测电路就会触发后续电路执行动作
  4. 这里设计了两套滤波和边沿检测电路 第一套电路经过滤波和极性选择得到TI1FP1 输入给通道1的后续电路 第二套电路 经过另一个滤波和极性选择 得到TI1FP2 输入给通道2的后续电路 同理 下面TI2信号进来 也经过两套滤波和极性选择得到TI2FP1输入通道1和TI2FP2输入通道2 其中TI2FP1输给上面 TI2FP2输入给下面 在这里两个信号进来可以选择各走各的 也可以选择进行一个交叉 可以进行交叉连接 例如CH1引脚输入给通道2 CH2引脚输入给通道1 进行交叉连接的目的是两个:1.第一个目的可以灵活切换后续捕获电路的输入 比如你一会儿想以CH1作为输入 一会儿想以CH2作为输入 这样就可以通过这个数据选择器灵活的进行选择 2.第二个目的 也是它交叉的主要目的 就是可以把一个引脚的输入同时映射到两个捕获单元 这也是PWMI模式的经典结构

  5. 输入信号进行滤波和极性选择后就来到了预分频器 每个通道各有一个预分频器 可以选择对前面的信号进行分频 分频之后的触发信号 就可以触发捕获电路进行工作了 每来一个触发信号 CNT的值就会向CCR转运一次 转运的同时会发生一个捕获事件 这个事件会在状态寄存器置标志位 同时也可以产生中断 如果需要在捕获的瞬间 处理一些事情的话 就可以开启这个捕获中断 比如可以配置上升沿触发捕获 每来一个上升沿 CNT转运到CCR一次 又因为CNT计数器是由内部的标准时钟驱动的 所以CNT的数值可以用来记录两个上升沿之间的时间间隔(周期) 再取倒数就是测周法测量的频率了 在每次捕获后将CNT清零(可以用主从触发模式来自动完成CNT清零) 这样下次上升沿再捕获的时候 取出的CNT才是两个上升沿的时间间隔

4.输入捕获通道

STM32标准库——(8)TIM输入捕获_第3张图片

  1. 滤波器的工作原理就是:以采样频率对输入信号进行采样 当连续N个值都为高电平 输出才为高电平 当连续N个值都为低电平 输出才为低电平 如果信号出现高频抖动 导致连续采样N个值不全都一样 那输出就不会变化 这样就可以达到滤波的效果 采样频率越低 采样个数N越大 滤波效果就越好
  2. CC1S位可以对数据选择器进行选择 CCMR寄存器的ICPS位可以配置分频器  CCMR寄存器的CC1E位控制输出使能或失能

5.主从触发模式

STM32标准库——(8)TIM输入捕获_第4张图片

  • 主从触发模式 即主模式、从模式和触发源选择三个功能的简称 主模式可以将定时器内部的信号映射到TRGO引脚 用于触发其他外设的操作 从模式可以接收其他外设或自身外设的一些信号 用于触发自己的一些操作(定时器的运行) 触发源选择 即选择从模式的触发信号源功能 也可以认为它是从模式的一部分

6.输入捕获基本结构

STM32标准库——(8)TIM输入捕获_第5张图片

  1. 这个结构我们只使用了一个通道 所以它目前只能测量频率 在右上角这里是时基单元 我们把时基单元配置好 启动定时器 那这个CNT就会在预分频之后的这个时钟驱动下不断自增 这个CNT就是我们测周法用来计数计时的东西 经过预分频之后 这个位置的时钟频率就是驱动CNT的标准频率fc
  2. 分频器选择不分频 当TI1FP1出现上升沿之后 CNT的当前记录值转运到CCR1里 同时触发源选择选中TI1FP1为触发信号 从模式选择复位操作 这样TI1FP1的上升沿也会通过上面这一路去触发CNT清零 当然这里会有个先后顺序 肯定是得先转运CNT的值到CCR里再触发从模式给CNT清零 或者是非阻塞的同时转移:CNT的值转移到CCR 同时0转移到CNT里面去 总之是先捕获 再清零) 

  • CNT的计数值是有上限的 由于ARR最大为65535 故CNT最大也只能计65535个数 如果信号频率太低 CNT的计数值可能会溢出 
  • 从模式的触发源选择中有TI1FP1和TI2FP2 但是没有TI3和TI4的信号 所以如果要使用从模式自动清零CNT 就必须使用通道1或通道2作为输入 对于通道3和通道4 就只能开启捕获中断 在中断中手动清0了(这样程序会处于频繁中断的状态 比较占用软件资源)

7.PWMI基本结构

STM32标准库——(8)TIM输入捕获_第6张图片

  1.  PWMI模式使用两个通道同时捕获一个引脚 可以同时测量周期和占空比 相比前面输入捕获 下面多了一个TI1FP2的通道
  2. 首先TI1FP1配置上升沿触发 触发捕获和清零CNT 正常的捕获周期 再来一个TI1FP2 配置为下降沿触发 通过交叉通道去触发通道2的捕获单元(最开始上升沿CCR1捕获同时清零CNT 之后CNT一直加加 然后在下降沿时刻触发CCR2捕获 这时CCR2的值就是CNT从上升沿到下降沿的计数值也就是高电平期间的计数值 CCR2捕获并不触发CNT清零 所以CNT继续加加 直到下一次上升沿 CCR1捕获周期并CNT清零 这样执行之后CCR1就是一整个周期的计数值 CCR2就是高电平期间的计数值CCR2/CCR1就是占空比 以上就是PWMI模式使用两个通道来捕获频率和占空比的思路 另外也可以两个通道同时捕获第一个引脚的输入 这样通道二的前面这一部分就没有用到行 当然也可以配置两个通道同时捕获第二个引脚的输入 这样我们就是使用TI2FP1和TI2FP2这两个引脚了 这两个输入可以灵活切换

8.相关API

8.1 TIM_PrescalerConfig

void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
功能:
	单独写入PSC
参数:
   TIMx:其中x为1 ~ 17,表示选择TIM外设
   Prescaler:写入的PSC的值 
   TIM_PSCReloadMode:指定的重装模式  可以是:TIM_PSCReloadMode_Update:在更新事件中重装预分频器、TIM_PSCReloadMode_Immediate:预分频器立即重装(立刻生效 可能会在值改变时产生切断波形的现象 若在更新事件生效 会有一个缓冲器 延迟参数的写入时间 保证每个周期完整)
返回值:
	无      

8.2 TIM_ICInit

void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
功能:
	根据TIM_ICInitStruct中指定的参数初始化TIM外设
参数:
   TIMx:其中x除6、7外可选1 ~ 17,用于选择TIM外设
   TIM_ICInitStruct:指向TIM_ICInitTypeDef结构体的指针,该结构体包含指定TIM外设的配置信息
返回值:
	无  

//注意:输入捕获和输出比较都有四个通道 OCInit有4个 每个通道占一个函数 而IcIni中4个通道共用一个函数 在结构体中有一个参数 可以用来选择具体配置哪个通道  
//与第三个函数类似 但只能配置一个通道 用于第一个代码工程

 8.3 TIM_PWMIConfig

void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
功能:
	根据TIM_ICInitStruct中指定的参数配置TIM外设以测量外部PWM信号
参数:
   TIMx:其中x可以是1、2、3、4、5、8、9、12或15,选择TIM外设
   TIM_ICInitStruct:指向TIM_ICInitTypeDef结构体的指针,该结构体包含指定TIM外设的配置信息
返回值:
	无  

//与第二个函数类似 但这里可以快速配置两个通道 用于第二个代码工程

8.4 TIM_SelectInputTrigger

void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
功能:
    选择输入触发源TRGI
参数:
   TIMx:其中x可以是1、2、3、4、5、8、9、12或15,选择TIM外设
   TIM_InputTriggerSource:触发器输入源
    可以是以下几种:TIM_TS_ITR0:内部触发器0
                    TIM_TS_ITR1:内部触发器1
                    TIM_TS_ITR2:内部触发器2
                    TIM_TS_ITR3:内部触发
                    TIM_TS_TI1F_ED: TI1边缘检测器
                    TIM_TS_TI1FP1:滤波定时器输入1
                    TIM_TS_TI2FP2:滤波定时器输入2
                    TIM_TS_ETRF:外部触发输入
返回值:
	无

//对应主从触发模式图的触发源选择部分

8.5 TIM_SelectOutputTrigger

void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);
功能:
    选择输出触发源TRGO
参数:
   TIMx:其中x可以是1、2、3、4、5、8、9、12或15,选择TIM外设
   TIM_InputTriggerSource:输入触发器源
    该参数可以是以下值之一:
    所有TIMx
        TIM_TRGOSource_Reset:在TIM_EGR寄存器的UG位被用作触发输出(TRGO)
        TIM_TRGOSource_Enable:计数器启用CEN被用作触发输出(TRGO)
        TIM_TRGOSource_Update:选择更新事件作为触发输出(TRGO)
    除TIM6和TIM7外的所有TIMx
        TIM_TRGOSource_OC1:当要设置CC1IF标志时,一旦捕获或比较匹配发生(TRGO),触发器输出发送一个正脉冲
        TIM_TRGOSource_OC1Ref: OC1REF信号被用作触发输出(TRGO)
        TIM_TRGOSource_OC2Ref: OC2REF信号被用作触发输出(TRGO)
        TIM_TRGOSource_OC3Ref: OC3REF信号被用作触发输出(TRGO)
        TIM_TRGOSource_OC4Ref: OC4REF信号被用作触发输出(TRGO)
返回值:
	无

//对应主从触发输出图的主模式输出触发源选择

8.6 void TIM_SelectSlaveMode

void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);
功能:
    选择TIMx从模式
参数:
   TIMx:其中x可以是1、2、3、4、5、8、9、12或15,选择TIM外设
   TIM_SlaveMode:定时器从模式
    该参数可以是以下值之一:
    TIM_SlaveMode_Reset:所选触发信号(TRGI)的上升沿重新初始化计数器并触发寄存器的更新
    TIM_SlaveMode_Gated:当触发信号(TRGI)高时使能计数器
    TIM_SlaveMode_Trigger:计数器从触发TRGI的上升沿开始
    TIM_SlaveMode_External1:所选触发器(TRGI)的上升沿时钟计数器
返回值:
	无

//对应主从触发模式图的从模式选择部分

8.7 TIM_SetIC1Prescaler

void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
功能:
    分别单独配置通道1、2、3、4的分频器
参数:
   TIMx:通道1:其中x除6、7外可选1 ~ 17,用于选择TIM外设
        通道2:其中x可以是1、2、3、4、5、8、9、12或15来选择TIM外设
        通道3:其中x可以是1、2、3、4、5或8来选择TIM外设
        通道4:其中x可以是1、2、3、4、5或8来选择TIM外设
   TIM_ICPSC:指定输入寄存预分频器的新值
返回值:
	无

8.8 uint16_t TIM_GetCapture1

uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);
功能:
    分别读取4个通道的CCR值
参数:
   TIMx:通道1:其中x除6、7外可选1 ~ 17,用于选择TIM外设
        通道2:其中x可以是1、2、3、4、5、8、9、12或15来选择TIM外设
        通道3:其中x可以是1、2、3、4、5或8来选择TIM外设
        通道4:其中x可以是1、2、3、4、5或8来选择TIM外设
返回值:
	无

8.8和8.7中的函数是相互对应的 读写的都是CCR寄存器 输出比较模式下 CCR是只写的 要用SetComper写入 输入捕获模式下 CCR是只读的 要用GetCapture读出

9.输入捕获模式测量频率

9.2 接线图

STM32标准库——(8)TIM输入捕获_第7张图片

9.3 相关代码 

PWM.c
#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//使能TIM2的RCC时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//将GPIO口配置复用推挽输出 前面ppt中有图:普通开漏/推挽输出 引脚控制权来自输出数据寄存器 如果让定时器控制引脚 需要使用复用开漏/输出模式 在这里输出数据寄存器会被断开 输出控制权转移给片上外设 在这里片上外设是TIM2的CH1通道 PWM波形才能通过引脚输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	TIM_InternalClockConfig(TIM2);//时钟选择为RCC内部时钟
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//指定时钟分频 这里的选择的是1分频
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//配置计数模式 这里选择上拉计数模式
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;//周期配置 是ARR自动重装值 固定ARR+1为100 则CCR的值就是占空比的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;//配置预分频器PCS的值 通过PSC来调节频率
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//这里是重复计数器 是高级定时器才需要用到 而我们这里是通用定时器 随意直接给其配置0
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);//给下面的结构体赋初始值
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
//	TIM_OCInitStructure.TIM_Pulse = 50;//CCR 这里配置占空比为50% 频率为1KHz 分辨率为1%的PWM波形 通过计算可得CCR、PCS、ARR的值
	TIM_OCInitStructure.TIM_Pulse = 0;//占空比配置0 在下面封装一个占空比变化函数
	TIM_OC1Init(TIM2,&TIM_OCInitStructure);//输出比较初始化 需要初始化哪个通道就选哪个 这里选的是PA0口 对应第一个输出比较通道
	
	TIM_Cmd(TIM2,ENABLE);
}

void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2,Compare);//设置CCR1的值
}

void PWM_SetPrescal(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Immediate);//设置PSC的值
}
PWM.h
#ifndef __PWM_H
#define __PWM_H

void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);
void PWM_SetPrescal(uint16_t Prescaler);

#endif
IC.c
#include "stm32f10x.h"                  // Device header

void IC_Init(void)
{
    /*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
    /*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//将PA6引脚初始化为上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
    /*配置时钟源*/
	TIM_InternalClockConfig(TIM3);//选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
    /*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;//ARR的值 设置最大是防止计数溢出
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//标准频率fc=72M/72=1M
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
	
    /*PWMI模式初始化*/
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//配置通道参数 这里选择通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;//用来配置输入捕获的滤波器 若信号有毛刺或噪声 可以增大滤波器参数 可以有效避免干扰
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//对应图里的边沿选择、极性选择部分
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//这里配置的是分频器 不分频就是每次触发都有效 二分频就是每隔1秒有效1次 本次代码需要每次触发都有效 所以选择不分频
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//配置的是图中黄色的数据选择器 可以选择直连或者交叉 本次选择直连即可
	TIM_ICInit(TIM3,&TIM_ICInitStructure);
	
    /*选择触发源及从模式*/
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);//触发源选择TI1FP1
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);//从模式选择复位 即TI1产生上升沿时,会触发CNT归零
	
    /*TIM使能*/
	TIM_Cmd(TIM3,ENABLE);//使能TIM3,定时器开始运行
}

/**
  * 函    数:获取输入捕获的频率
  * 参    数:无
  * 返 回 值:捕获得到的频率
  */
uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);//测周法得到频率fx = fc / N(N就是CCR 直接调用读取CCR值函数即可),这里不执行+1的操作也可
}
 IC.h
#ifndef __IC_H
#define __IC_H

void IC_Init(void);
uint32_t IC_GetFreq(void);

#endif
main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"

uint8_t i;

int main(void)
{
	OLED_Init();
	PWM_Init();
	IC_Init();//输入捕获初始化
	
	OLED_ShowString(1, 1, "Freq:00000Hz");//1行1列显示字符串Freq:00000Hz
	
	PWM_SetPrescal(720 - 1);//PWM频率Freq = 72M / (PSC + 1) / 100 pwm.c中设置ARR+1为100 这里通过PSC改变调节频率 这里频率为1000
	PWM_SetCompare1(50);//PWM占空比Duty = CCR / 100 设置占空比为50%
	
	while (1)
	{
		OLED_ShowNum(1, 6, IC_GetFreq(), 5);//不断刷新显示输入捕获测得的频率
	}
}

现象:PWM模块将待测信号输出的PA0口 PA0通过导线输出到PA6 PA6是TIM3的通道1 通道1通过输入捕获模块 测量得到频率

10.PWMI模式测频率占空比

10.1 接线图

STM32标准库——(8)TIM输入捕获_第8张图片

10.2 相关代码

IC.C
#include "stm32f10x.h"                  // Device header

void IC_Init(void)
{
    /*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
    /*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//将PA6引脚初始化为上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;//这里TIM2要用来输出PWM 所以输入捕获的定时器需要换一个 暂时换到TIM3 由引脚定义表可以看到TIM3的通道1对应PA6 通道2对应PA7 通道3对应PB0 通道4对应PB1 这里选择TIM3的通道1 所以选择PA6引脚 这里需要配置上拉输入模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
    /*配置时钟源*/
	TIM_InternalClockConfig(TIM3);//选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
    /*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;//ARR的值 设置最大是防止计数溢出
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//标准频率fc=72M/72=1M
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
	
    /*PWMI模式初始化*/
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//配置通道参数 这里选择通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;//用来配置输入捕获的滤波器 若信号有毛刺或噪声 可以增大滤波器参数 可以有效避免干扰
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//对应图里的边沿选择、极性选择部分
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//这里配置的是分频器 不分频就是每次触发都有效 二分频就是每隔1秒有效1次 本次代码需要每次触发都有效 所以选择不分频
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//配置的是图中黄色的数据选择器 可以选择直连或者交叉 本次选择直连即可
	TIM_PWMIConfig(TIM3,&TIM_ICInitStructure);//配置通道二参数 结构体中的参数都与通道一相反
	
    /*选择触发源及从模式*/
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);//触发源选择TI1FP1
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);//从模式选择复位 即TI1产生上升沿时,会触发CNT归零
	
    /*TIM使能*/
	TIM_Cmd(TIM3,ENABLE);//使能TIM3,定时器开始运行
}

/**
  * 函    数:获取输入捕获的频率
  * 参    数:无
  * 返 回 值:捕获得到的频率
  */
uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);//测周法得到频率fx = fc / N(N就是CCR 直接调用读取CCR值函数即可),这里不执行+1的操作也可
}

/**
  * 函    数:获取输入捕获的占空比
  * 参    数:无
  * 返 回 值:捕获得到的占空比
  */
uint32_t IC_GetDuty(void)
{
	return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);	//占空比Duty = CCR2 / CCR1 * 100,这里不执行+1的操作也可
}
IC.h
#ifndef __IC_H
#define __IC_H

void IC_Init(void);
uint32_t IC_GetFreq(void);
uint32_t IC_GetDuty(void);

#endif
main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"

uint8_t i;

int main(void)
{
	OLED_Init();
	PWM_Init();
	IC_Init();//输入捕获初始化
	
	OLED_ShowString(1, 1, "Freq:00000Hz");//1行1列显示字符串Freq:00000Hz
	OLED_ShowString(2, 1, "Duty:00%");//2行1列显示字符串Duty:00%
	
	PWM_SetPrescal(720 - 1);//PWM频率Freq = 72M / (PSC + 1) / 100 pwm.c中设置ARR+1为100 这里通过PSC改变调节频率 这里频率为1000
	PWM_SetCompare1(50);//PWM占空比Duty = CCR / 100 设置占空比为50%
	
	while (1)
	{
		OLED_ShowNum(1, 6, IC_GetFreq(), 5);//不断刷新显示输入捕获测得的频率
		OLED_ShowNum(2, 6, IC_GetDuty(), 2);	//不断刷新显示输入捕获测得的占空比
	}
}

 现象:通过调节main.c中的PWM频率和占空比 可以让OLED上捕获到的频率和占空比值随之改变

12.附加

工程实现思路:

  1. 第一步,RCC开启时钟,把GPIO和TIM的时钟打开
  2. 第二步,GPIO初始化,把GPIO配置成输入模式(一般选择上拉输入或浮空输入模式)
  3. 第三步,配置时基单元,让CNT计数器在内部时钟的驱动下自增运行,和之前代码一样
  4. 第四步,配置输入捕获单元,包括滤波器、极性、直连通道还是交叉通道、分频器这些参数,用一个结构体就可以统一进行配置了
  5. 第五步,选择从模式的触发源,触发源选择为TI1FP1,这里调用一个库函数给一个参数就行了
  6. 第六步,选择触发之后执行的操作,执行Reset操作,这里调用一个库函数就行了

你可能感兴趣的:(STM32,stm32,单片机)