PWM输入捕获代码讲解
大家好,我终于出现了,由于要开始准备考研的复习,再加上32的学习已经学到了摄像头,就鸽了几天。
好,话不多说,我们直接进入正题。
这个实验其实就是用于测量PWM信号,同一个定时器不能同时使用输入捕获和输出比较,因此这个实验中我们使用通用定时器来产生信号,高级定时器来捕获信号。
与之前的实验一样,在例程中的User文件夹下添加bsp_AdvanceTim.c和bsp_AdvanceTim.h文件。 这里注意一点,要把advance和general文件夹。这是因为使用不同的定时器时,对应的GPIO是不一样的。
我们先对PIO进行初始化:
static void ADVANCE_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(ADVANCE_TIM_CH1_PORT, &GPIO_InitStructure);
}
这里注意一点,要把GPIO设置为IN_FLOATING浮空输入,用来捕获方波。
再移植例程中的.h文件:
#ifndef __BSP_ADVANCETIME_H
#define __BSP_ADVANCETIME_H
#include "stm32f10x.h"
#define ADVANCE_TIM TIM1
#define ADVANCE_TIM_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADVANCE_TIM_CLK RCC_APB2Periph_TIM1
//输入捕获能捕获到的最小频率为72M/{(ARR+1)*(PSC+1)}
#define ADVANCE_TIM_PERIOD (1000-1)
#define ADVANCE_TIM_PSC (72-1)
//中断相关宏定义
#define ADVANCE_TIM_IRQ TIM1_CC_IRQn
#define ADVANCE_TIM_IRQHandler TIM1_CC_IRQHandler
//TIM1输入捕获通道一
#define ADVANCE_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define ADVANCE_TIM_CH1_PORT GPIOA
#define ADVANCE_TIM_CH1_PIN GPIO_Pin_8
#define ADVANCE_TIM_IC1PWM_CHANNEL TIM_Channel_1
void ADVANCE_TIM_Init(void);
#endif
在.c文件中配置完GPIO后,我们需要对其进行初始化:
static void ADVANCE_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = ADVANCE_TIM_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(ADVANCE_TIM_CH1_PORT, &GPIO_InitStructure);
}
这里有一个重点要特别注意:当我们在配置时,Period和PSC的值不能随意配置。我们假设生成的方波是100K,则周期为1/100=10us,如果计数周期<10us,那就无法对周期进行捕获,如果<1khz,或>10亅,那就无法捕获。一般的常用电机的驱动频率在10K~25K之间。
记下来,我们需要设置中断优先级以及输入捕获结构体初始化:
static void ADVANCE_TIM_Mode_Config(void)
{
ADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK,ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period=ADVANCE_TIM_PERIOD;
//自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新中断
TIM_TimeBaseStructure.TIM_Prescaler= ADVANCE_TIM_PSC;
//驱动CNT计数器的时钟
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
//时钟分频因子,配置死区时间要用
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
//计数模式,设置为向上计数
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
//重复计数器的值
TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);
//初始化定时器
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = ADVANCE_TIM_IC1PWM_CHANNEL;
//捕获通道IC1配置
//选择捕获通道
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
//设置捕获的边沿
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
//设置捕获通道的信号来自于哪个输入通道,由直连和非直连两种
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
//1分频,即捕获信号的每个有效边沿都捕获
TIM_ICInitStructure.TIM_ICFilter = 0x0;
//不滤波
TIM_PWMIConfig(ADVANCE_TIM, &TIM_ICInitStructure);
//初始化PWM输入模式
TIM_SelectInputTrigger(ADVANCE_TIM, TIM_TS_TI1FP1);
//选择输入捕获的触发信号
TIM_SelectSlaveMode(ADVANCE_TIM, TIM_SlaveMode_Reset);
TIM_SelectMasterSlaveMode(ADVANCE_TIM,TIM_MasterSlaveMode_Enable);
//选择从模式:复位模式
//PWM输入模式时,从模式必须工作在复位模式,当捕获开始时,计数器CNT会被复位
TIM_ITConfig(ADVANCE_TIM, TIM_IT_CC1, ENABLE);
//使能捕获中断,这个中断针对的是主捕获通道
TIM_ClearITPendingBit(ADVANCE_TIM, TIM_IT_CC1);
//清除中断标志位
TIM_Cmd(ADVANCE_TIM, ENABLE);
//使能高级控制定时器,计数器开始计数
中断优先级为:
static void ADVANCE_TIM_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
//设置中断组为0
NVIC_InitStructure.NVIC_IRQChannel = ADVANCE_TIM_IRQ;
//设置中断来源
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
//设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
//设置子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
最后,我们把.h里面写的三个函数,放进定时器的初始化函数:
void ADVANCE_TIM_Init(void)
{
ADVANCE_TIM_NVIC_Config();
ADVANCE_TIM_GPIO_Config();
ADVANCE_TIM_Mode_Config();
}
我们需要在it.c中编写中断服务函数:
void ADVANCE_TIM_IRQHandler(void)
{
TIM_ClearITPendingBit(ADVANCE_TIM, TIM_IT_CC1);
//清除中断标志位
IC1Value = TIM_GetCapture1(ADVANCE_TIM);
IC2Value = TIM_GetCapture2(ADVANCE_TIM);
//获取输入捕获值
if (IC1Value != 0)
//注意:捕获寄存器CCR1和CCR2的值在计算占空比和频率的时候必须+1
{
DutyCycle = (float)((IC2Value+1) * 100) / (IC1Value+1);
//占空比计算
Frequency = (72000000/(ADVANCE_TIM_PSC+1))/(float)(IC1Value+1);
//频率计算
printf("Õ¼¿Õ±È£º%0.2f%% ƵÂÊ£º%0.2fHz\n",DutyCycle,Frequency);
}
else
{
DutyCycle = 0;
Frequency = 0;
}
}
``如果是第一个上升沿中断,计数器会被复位,锁存到CCR1的值为0,CCR2的值也是0.无法计算频率与占空比; 当第二次上升沿到来的时候,CCR1和CCR2捕获到的值为有效的值,其中CCR1对应的是周期,CCR2对应的是占空比。
好的,到这里程序基本上就编写完成了,但是如果现在别写程序的时候就会出现:DutyCycle、Frequency未定义的情况出现,因此我们还需要加最后一步。就是在中断服务函数掐面加上定义:
```c
__IO uint16_t IC2Value = 0;
__IO uint16_t IC1Value = 0;
__IO float DutyCycle = 0;
__IO float Frequency = 0;
最后,我们整理一下main.c就行了:
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_GeneralTim.h"
#include "bsp_AdvanceTim.h"
#include "bsp_usart.h"
int main(void)
{
USART_Config();
GENERAL_TIM_Init();
ADVANCE_TIM_Init();
while(1)
{
}
}
好的,那么定时器的所有相关都讲完了,下次更新就是摄像头的章节了。要是对于定时器还有不懂的,可以在下方评论留言或者进入一下网址查看完整视频:https://www.bilibili.com/video/av28951854?from=search&seid=13956434192947447162