因此程序中对A0引脚可以这样配置,注意一定要配置引脚的复用功能:
GPIO_InitTypeDef GPIO_InitStructure; /*GPIO 结构体*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能PORTA时钟
/*输入信号的GPIO初始化*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIOA0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; /*复用功能*/
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; /*下拉*/
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA0
GPIO_PinAFConfig(GPIOA,GPIO_PinSource0,GPIO_AF_TIM5); //PA0复用位定时器5
4.1.2 时基初始化
使用定时器,时基初始化是必不可少的,就是要设置一些计数的频率与溢出值(自动重装载值):
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; /*时基 结构体*/
/*时基初始化*/
TIM_TimeBaseStructure.TIM_Period=arr; /* 自动重装载值 */
TIM_TimeBaseStructure.TIM_Prescaler=psc; /* 定时器分频 */
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure);
4.1.3 输入通道初始化
上篇配置的是输出通道,本篇需要将定时器的通道1设置为输入捕获模式:
TIM_ICInitTypeDef TIM5_ICInitStructure; /*输入通道 结构体*/
/*输入通道初始化,初始化TIM5输入捕获参数*/
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 选择输入端 IC1映射到TI1上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; /* 上升沿捕获 */
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM5_ICInitStructure.TIM_ICFilter = 0x00; //IC1F=0000 配置输入滤波器 不滤波
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE); /* 允许更新(溢出)中断 ,允许CC1IE捕获中断 */
TIM_Cmd(TIM5,ENABLE ); //使能定时器5
关于配置CCMR1、CCER寄存器
CCMR1:
CCER:
TIM_ICInit函数对应于输入通道的初始化,其实就是操作CCMR1、CCER寄存器:
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct)
{
if (TIM_ICInitStruct-》TIM_Channel == TIM_Channel_1)
{ /* TI1 配置 */
TI1_Config(TIMx, TIM_ICInitStruct-》TIM_ICPolarity,
TIM_ICInitStruct-》TIM_ICSelection,
TIM_ICInitStruct-》TIM_ICFilter);
/* 设置中断捕获预分频值 */
TIM_SetIC1Prescaler(TIMx, TIM_ICInitStruct-》TIM_ICPrescaler);
}
else if (TIM_ICInitStruct-》TIM_Channel == TIM_Channel_2)
{
/*省略。。.*/
}
}
static void TI1_Config(TIM_TypeDef* TIMx, uint16_t TIM_ICPolarity, uint16_t TIM_ICSelection,uint16_t TIM_ICFilter)
{
uint16_t tmpccmr1 = 0, tmpccer = 0;
/* 关闭通道1: 复位CC1E位 */
TIMx-》CCER &= (uint16_t)~TIM_CCER_CC1E;
tmpccmr1 = TIMx-》CCMR1;
tmpccer = TIMx-》CCER;
/* 通过设置CC1S选择为输入模式, 并配置滤波器 */
tmpccmr1 &= ((uint16_t)~TIM_CCMR1_CC1S) & ((uint16_t)~TIM_CCMR1_IC1F);
tmpccmr1 |= (uint16_t)(TIM_ICSelection | (uint16_t)(TIM_ICFilter 《《 (uint16_t)4));
/* 选择CC1P极性并设置CC1E位 */
tmpccer &= (uint16_t)~(TIM_CCER_CC1P | TIM_CCER_CC1NP);
tmpccer |= (uint16_t)(TIM_ICPolarity | (uint16_t)TIM_CCER_CC1E);
/* 写数据到 TIMx 的CCMR1 和 CCER 寄存器 */
TIMx-》CCMR1 = tmpccmr1;
TIMx-》CCER = tmpccer;
}
void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC)
{
TIMx-》CCMR1 &= (uint16_t)~TIM_CCMR1_IC1PSC; /* 复位IC1PSC位 */
TIMx-》CCMR1 |= TIM_ICPSC; /* 设置IC1PSC值 */
}
关于配置DIER寄存器
TIM_ITConfig函数对于中断的开启,其实就是操作DIER寄存器:
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)
{
if (NewState != DISABLE)
{ /* 使能中断 */
TIMx-》DIER |= TIM_IT;
}
else
{ /* 失能中断 */
TIMx-》DIER &= (uint16_t)~TIM_IT;
}
}
4.1.4 定时器中断初始化
定时器中断的使能设置已在上面的定时器配置中设置,这里只是进行常规的配置定时器中断的优先级:
/*定时器中断配置*/
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器
4.2 定时器中断服务函数
此处用到了两个全局变量,用于辅助实现高电平捕获。其中:
TIM5CH1_CAPTURE_VAL用来记录捕获到下降沿的时候 TIM5_CNT的值。
TIM5CH1_CAPTURE_STA用来记录捕获状态,我们把它当成一个寄存器那样来使用 。其各位描述下:
u8 TIM5CH1_CAPTURE_STA=0; //输入捕获状态(当中一个自制的寄存器使用,初始为0)
u32 TIM5CH1_CAPTURE_VAL; //输入捕获值(TIM2/TIM5是32位)
/**
*
@Brief 定时器5中断服务程序
*/
void TIM5_IRQHandler(void)
{
if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获(1000 0000)
{
/*定时器溢出中断*/
if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
{
if(TIM5CH1_CAPTURE_STA&0X40)/* 之前标记了开始信号(0100 0000) */
{
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F) /* 高电平太长了,计数溢出了 (0011 1111) */
{
TIM5CH1_CAPTURE_STA|=0X80; /* (强制)标记成功捕获了一次 (1000 0000) */
TIM5CH1_CAPTURE_VAL=0XFFFFFFFF; /* 因为溢出次数N不能再加了,就将当前的捕获值设置为32位的最大值,等效Nmax+1*/
}
else /* 正常情况是不会溢出,最终得出正确的高电平时间 */
{
TIM5CH1_CAPTURE_STA++; /* 累计定时器溢出次数N */
}
}
else
{
/* 还没有捕获到信号时,定时器溢出后什么也不做,自己清零继续计数即可 */
}
}
/*捕获1发生捕获事件*/
if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)
{
/*捕获到一个下降沿(结束信号)*/
if(TIM5CH1_CAPTURE_STA&0X40) /* 之前标记了开始信号(0100 0000) */
{
TIM5CH1_CAPTURE_STA|=0X80; /* 标记成功捕获到一次高电平脉宽 (1000 0000) */
TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5); /* 获取当前的捕获值 */
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); /* CC1P=0 重新设置为上升沿捕获,用于下次捕捉信号 */
}
/*还未开始,第一次捕获 上升沿(起始信号) */
else
{
TIM5CH1_CAPTURE_STA=0; /* 清空 捕获状态寄存器 */
TIM5CH1_CAPTURE_VAL=0; /* 清空 捕获值 */
TIM5CH1_CAPTURE_STA|=0X40; /* 标记捕获到了上升沿 (0100 0000) */
TIM_Cmd(TIM5,DISABLE ); /* 关闭定时器5 */
TIM_SetCounter(TIM5,0); /* 清空CNT,重新从0开始计数 */
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); /* CC1P=1 设置为下降沿捕获 */
TIM_Cmd(TIM5,ENABLE ); /* 使能定时器5 */
}
}
}
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
}
再来对比一下这张图:
初始化时设置为上升沿触发,触发后(起始信号),清空CNT,重新从0开始计数,并设置为下降沿捕获
在之后的过程中可能会有多次定时器计数溢出,即TIM5CH1_CAPTURE_STA++(使用低6位),也即N的值
最后捕捉到下降沿(结束信号),TIM5CH1_CAPTURE_VAL获取当前CNT的值,也即CCRx2的值
再看主函数中:
while(1)
{
/* 成功捕获到了一次高电平 (1000 0000) */
if(TIM5CH1_CAPTURE_STA&0X80)
{
temp=TIM5CH1_CAPTURE_STA&0X3F; /* 获取溢出的次数N (0011 1111) */
temp*=0XFFFFFFFF; /* 溢出时间总和 = N*溢出计数值 */
temp+=TIM5CH1_CAPTURE_VAL; /* 总的高电平时间 = 溢出时间总和 + 下降沿时的计数值*/
printf(“HIGH:%lld usrn”,temp); //打印总的高点平时间
TIM5CH1_CAPTURE_STA=0; //开启下一次捕获
}
}
当检查TIM5CH1_CAPTURE_STA为捕获到1次高电平后,打印高电平的持续时间:
总的高电平时间 =N(TIM5CH1_CAPTURE_STA的低6位) * ARR(溢出计数值)+ CCRx2(下降沿时的计数值)
原作者: xx
PCB 码农爱学习