32单片机基础:输入捕获测频率

接线图如下图所示:

我们复制之前写过的代码6-3 PWM驱动LED呼吸灯

在PWM模块中,执行的逻辑是,初始化TIM2的通道1,产生一个PWM波形,输出引脚是PA0,通过SetCompare1的函数,可以调节CCR1寄存器的值,从而控制PWM的占空比。目前PWM的频率是在初始化里面写好了的,是固定的,运行时调节不太方便,所以我们在最后加一个函数,用来快捷调节PWM的频率。

我们知道PWM频率=更新频率=72M/(PSC+1)/(ARR+1)所以PSC和ARR都可以调节频率,但是占空比等于CRR/(ARR+1),所以通过ARR调节频率,还同时会影响到占空比,而通过PSC调节频率,不会影响占空比,显然比较方便,所以我们计划是,固定ARR为100-1.通过调节PSC来改变PWM频率,另外,ARR为100-1,CCR的数值直接就是占空比

void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);

这个函数就是单独写入PSC的函数

我们写一个函数单独改变PSC的值。

cpp 复制代码
void PWM_SetPrescaler(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM2,Prescaler, TIM_PSCReloadMode_Immediate);
}

在主函数写入这些代码,就可以输出频率为1Khz,占空比为50%的波形了

cpp 复制代码
    PWM_SetPrescaler(720-1);    //Freq=72M/(PSC+1)/(ARR+1)
	PWM_SetCompare1(50);         //Duty=CCR/(ARR+1)

建立输入捕获的.c,.h文件

首先对IC(输入捕获)初始化,怎么初始化呢?目前我们需要配置电路连接成下图所示的这个样子

第一步:RCC开启时钟,把GPIO和TIM的时钟打开

第二步:GPIO初始化,把GPIO配置成输入模式,一般选择上拉输入和浮空输入

第三步:配置时基单元,让CNT计数器在内部时钟的驱动下自增运行。

第四步:配置输入捕获单元,包括滤波器,直连通道还是交叉通道,分频器这些参数。用一个结构体就可以统一配置了。

第五步:选择从模式的触发源,触发源选择为TI1FP1,这里调用一个库函数,给一个参数就OK了。

第六步:选择触发之后执行的操作,执行Reset操作,这里也是调用一个库函数就OK了。

最后,当这些电路都配置好了,调用TIM_Cmd函数,开启定时器。

这样所有的电路就能配合起来,按照我们之前的要求工作了。当我们需要读取最新一个周期的频率时,直接读取CCR寄存器,然后按照fc/N,计算一下就行了。

我们了解一下需要的库函数:

void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);

这个是用结构体配置输入捕获单元的函数,另外注意,输入捕获和输出比较都有四个通道,ICInit是共用一个函数的,所以在结构体里会额外有一个参数,可以选择具体配置哪个通道。因为可能有交叉通道的配置,所以函数合在一起比较方便。

void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);

这个函数与上一个函数类似。都是用于初始化输入捕获单元的,上一个函数只是单一的配置一个通道,而这个函数,可以快速配置两个通道,把外设电路结构配置成PWMI模式

void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);

可以给输入捕获结构体赋一个初始值。

void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);

选择输入触发源TRGI,对应下图所示:调用这个函数,就能选择从模式触发源了。

void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);

选择输出触发源TRGO,对应下图所示,选择主模式的触发源

void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);

选择从模式

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的分频器

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);

分别读取四个通道的CCR,这四个函数和上面的SetCompare1,2,3,4是对应的,读写的都是CCR寄存器,输出比较模式下,CCR是只写的,要用SetCompare写入,输入捕获模式下,CCR是只读的,要用GetCapture读出,

下面是输入捕获频率的代码:

main.c

cpp 复制代码
#include "stm32f10x.h"                  // Device header              
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"

int main()
{
	
	OLED_Init();
	PWM_Init();
	IC_Init();
	
	OLED_ShowString(1,1,"Freq:00000Hz");
	
    PWM_SetPrescaler(720-1);    //Freq=72M/(PSC+1)/(ARR+1)
	PWM_SetCompare1(50);         //Duty=CCR/(ARR+1)
	while(1)
	{
      OLED_ShowNum(1,6,IC_GetFreq(),5);
	}
}

PWM.c

cpp 复制代码
#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	//时基单元的代码写过,我们找之前定时中断的代码复制一下
	 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //打开时钟、
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	//重映射
	
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable ,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;     //GPIO_Pin_15
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	
    TIM_InternalClockConfig(TIM2);//选择内部时钟
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//初始化时基单元
	TIM_TimeBaseInitStructure.TIM_ClockDivision= TIM_CKD_DIV1 ;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up ;
	TIM_TimeBaseInitStructure.TIM_Period=100-1;//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler=720-1;//PSC
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter=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=0;//CCR   等下再算
	TIM_OC1Init(TIM2,&TIM_OCInitStructure);
	//通道已经初始化完成,在TIM2的OC1通道上就可以输出PWM波形了,这个波形借用GPIO口输出
	 
	 //初始化GPIO
	
	TIM_Cmd(TIM2,ENABLE);//启动定时器
}

void PWM_SetCompare1(uint16_t Compare)
{
	 TIM_SetCompare1(TIM2,Compare);
}

void PWM_SetPrescaler(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM2,Prescaler, TIM_PSCReloadMode_Immediate);
}

PWM.h

cpp 复制代码
#ifndef __OLED_H
#define __OLED_H

void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);
void PWM_SetPrescaler(uint16_t Prescaler);
#endif

IC.c

cpp 复制代码
#include "stm32f10x.h"                  // Device header

void IC_Init(void)
{
	//时基单元的代码写过,我们找之前定时中断的代码复制一下
	 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //打开时钟、
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;     //GPIO_Pin_15
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	
    TIM_InternalClockConfig(TIM3);//选择内部时钟
	
	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;//PSC
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
	
	//初始化输入捕获单元
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;
	TIM_ICInitStructure.TIM_ICFilter=0xF;
	TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
	TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;
	TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;
	TIM_ICInit(TIM3,&TIM_ICInitStructure);
	
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
	TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
	
	TIM_Cmd(TIM3,ENABLE);
}

uint32_t IC_GetFreq(void)
{
	return 1000000/ (TIM_GetCapture1(TIM3)+1);
}

IC.h

cpp 复制代码
#ifndef __IC_H
#define __IC_H

void IC_Init(void);
uint32_t IC_GetFreq(void);
#endif

下面,我们讲解一下代码的操作步骤和逻辑。

开启时钟,配置GPIO,配置时基单元,TIM2输出PWM,TIM3输入捕获。GPIO的时钟。看一下引脚定义表,比如我选择TIM3的通道1,就是PA6,

配置好了之后,初始化输入捕获单元。在tim.h找相应的函数,

接下来找函数把主从模式配置好。

配置TRGI的触发源为TI1FP1.

开启时钟,

之后再写一个函数读取CCR进行计算。

相关推荐
第二层皮-合肥5 分钟前
硬件设计-时钟振荡器
嵌入式硬件
零壹&硬件7 小时前
D类音频应用EMI管理
单片机·嵌入式硬件·硬件架构·音视频·硬件工程·智能硬件
7yewh9 小时前
嵌入式硬件杂谈(七)IGBT MOS管 三极管应用场景与区别
驱动开发·嵌入式硬件·mcu·物联网·硬件架构·硬件工程·pcb工艺
raysync88810 小时前
半导体企业内外网数据摆渡技术实现方案
单片机·嵌入式硬件
m0_7482359511 小时前
LWIP(stm32+lwip+freertos)
stm32·单片机·嵌入式硬件
小鱼做毕设11 小时前
单片机实物成品-008 智能浇花系统
单片机·嵌入式硬件
程序员JerrySUN12 小时前
BitBake 执行流程深度解析:从理论到实践
linux·开发语言·嵌入式硬件·算法·架构
RFID舜识物联网12 小时前
RFID智能文件柜:高效安全的档案管理新方案
大数据·网络·人工智能·嵌入式硬件·物联网
1101 110113 小时前
STM32-笔记12-实现SysTick模拟多线程流水灯
笔记·stm32·嵌入式硬件
美式小田13 小时前
Cadence学习笔记 12 PCB初始化设置
笔记·嵌入式硬件·学习·cadence