目录
1.输入捕获简介
- IC(Input Capture)输入捕获
- 输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
- 每个高级定时器和通用定时器都拥有4个输入捕获通道
- 可配置为PWMI模式,同时测量频率和占空比
- 可配合主从触发模式,实现硬件全自动测量
- 输出比较,引脚是输出端口,根据CNT和CCR的大小关系来执行输出动作
- 输入捕获,引脚是输入端口,接收到输入信号执行CNT锁存到CCR的动作
2.频率测量
- 测频法:定时器中断 并记录捕获次数 1s内出现了多少个重复的周期,那频率就是多少Hz
- 测周法:捕获中断,并记录定时器次数 两个上升沿内 以标准频率fc计次 fc=72M/(psc+1) 得到N(就是读取CCR的值)(本节输入捕获测频率 使用的方法是测周法)
- 测频法适合测量高频信号 测周法适合测量低频信号 测频法在闸门时间内最好要多出现一些上升沿 计次数量多一些 这样有助于减小误差 侧周法就要求信号频率低一些 低频信号周期比较长 计次就会比较多 有助于减小误差 对于测频法和测周法的一个共同点是:N越大,误差就越小
- 由于两种方法的误差都与N的正负1误差有关 所以当两种方法计次的N相同时 两种方法的误差也就相同 消去两种方法公式中的N 将测频法和测周法的N提出来 令两个方法N相等 将fx解出来 可得第三个测频率公式 其中T是测频法的闸门时间 fc是测频法的标准频率 当待测信号频率小于中界频率时 测周法误差更小 选择测周法更合适 当待测信号频率大于中界频率时 测频法误差更小 选择测频法更合适
3.通用定时器中的输入捕获简介
- 输入捕获就是这左边一部分电路 那右边就是我们之前讲过的输出比较部分 四个输入捕获和输出比较通道 共用四个CCR寄存器 另外它们的CH1到CH4 4个通道的引脚也是共用的 所以对于同一个定时器输入捕获和输出比较 只能使用其中一个不能同时使用
- 引脚进来,有一个三输入的异或门 这个异或门的输入接在了通道1、2、3端口 异或门的执行逻辑是 当3个输入引脚的任何一个有电平翻转时 输出引脚就产生一次电平翻转 3个引脚电平都相同为0 3个引脚中有高有低为1
- 输入信号来到了输入滤波器和边沿检测器(极性选择) 输入滤波器可以对信号进行滤波 避免一些高频的毛刺信号误触发 边沿检测器就是和外部中断一样 可以选择高电平触发或者低电平触发 当出现指定的电平时 边沿检测电路就会触发后续电路执行动作
-
这里设计了两套滤波和边沿检测电路 第一套电路经过滤波和极性选择得到TI1FP1 输入给通道1的后续电路 第二套电路 经过另一个滤波和极性选择 得到TI1FP2 输入给通道2的后续电路 同理 下面TI2信号进来 也经过两套滤波和极性选择得到TI2FP1输入通道1和TI2FP2输入通道2 其中TI2FP1输给上面 TI2FP2输入给下面 在这里两个信号进来可以选择各走各的 也可以选择进行一个交叉 可以进行交叉连接 例如CH1引脚输入给通道2 CH2引脚输入给通道1 进行交叉连接的目的是两个:1.第一个目的可以灵活切换后续捕获电路的输入 比如你一会儿想以CH1作为输入 一会儿想以CH2作为输入 这样就可以通过这个数据选择器灵活的进行选择 2.第二个目的 也是它交叉的主要目的 就是可以把一个引脚的输入同时映射到两个捕获单元 这也是PWMI模式的经典结构
-
输入信号进行滤波和极性选择后就来到了预分频器 每个通道各有一个预分频器 可以选择对前面的信号进行分频 分频之后的触发信号 就可以触发捕获电路进行工作了 每来一个触发信号 CNT的值就会向CCR转运一次 转运的同时会发生一个捕获事件 这个事件会在状态寄存器置标志位 同时也可以产生中断 如果需要在捕获的瞬间 处理一些事情的话 就可以开启这个捕获中断 比如可以配置上升沿触发捕获 每来一个上升沿 CNT转运到CCR一次 又因为CNT计数器是由内部的标准时钟驱动的 所以CNT的数值可以用来记录两个上升沿之间的时间间隔(周期) 再取倒数就是测周法测量的频率了 在每次捕获后将CNT清零(可以用主从触发模式来自动完成CNT清零) 这样下次上升沿再捕获的时候 取出的CNT才是两个上升沿的时间间隔
4.输入捕获通道
- 滤波器的工作原理就是:以采样频率对输入信号进行采样 当连续N个值都为高电平 输出才为高电平 当连续N个值都为低电平 输出才为低电平 如果信号出现高频抖动 导致连续采样N个值不全都一样 那输出就不会变化 这样就可以达到滤波的效果 采样频率越低 采样个数N越大 滤波效果就越好
- CC1S位可以对数据选择器进行选择 CCMR寄存器的ICPS位可以配置分频器 CCMR寄存器的CC1E位控制输出使能或失能
5.主从触发模式
-
主从触发模式 即主模式、从模式和触发源选择三个功能的简称 主模式可以将定时器内部的信号映射到TRGO引脚 用于触发其他外设的操作 从模式可以接收其他外设或自身外设的一些信号 用于触发自己的一些操作(定时器的运行) 触发源选择 即选择从模式的触发信号源功能 也可以认为它是从模式的一部分
6.输入捕获基本结构
- 这个结构我们只使用了一个通道 所以它目前只能测量频率 在右上角这里是时基单元 我们把时基单元配置好 启动定时器 那这个CNT就会在预分频之后的这个时钟驱动下不断自增 这个CNT就是我们测周法用来计数计时的东西 经过预分频之后 这个位置的时钟频率就是驱动CNT的标准频率fc
-
分频器选择不分频 当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基本结构
- PWMI模式使用两个通道同时捕获一个引脚 可以同时测量周期和占空比 相比前面输入捕获 下面多了一个TI1FP2的通道
-
首先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 接线图
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 接线图
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上捕获到的频率和占空比值随之改变
11.附加
工程实现思路:
- 第一步,RCC开启时钟,把GPIO和TIM的时钟打开
- 第二步,GPIO初始化,把GPIO配置成输入模式(一般选择上拉输入或浮空输入模式)
- 第三步,配置时基单元,让CNT计数器在内部时钟的驱动下自增运行,和之前代码一样
- 第四步,配置输入捕获单元,包括滤波器、极性、直连通道还是交叉通道、分频器这些参数,用一个结构体就可以统一进行配置了
- 第五步,选择从模式的触发源,触发源选择为TI1FP1,这里调用一个库函数给一个参数就行了
- 第六步,选择触发之后执行的操作,执行Reset操作,这里调用一个库函数就行了