接线图如下图所示:
我们复制之前写过的代码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进行计算。