STM32单个定时器的多路捕获与多路输出
关于蓝桥杯嵌入式的第六届的模拟题,碰到了一个问题就是关于输入捕获与PWM输出的一些问题,经过一天半的总结终于实现。
单个定时器实现两个频率的捕获
这里实现的是 PA1与PA2的输入捕获,他们分别属于Timer2的通道2和通道3,这里实现了多路捕获
void Timer2_PwmIn(void){
TIM_ICInitTypeDef TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1| GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = 0xffff;
TIM_TimeBaseStructure.TIM_Prescaler = 35;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
**// TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);**
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);
TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);
TIM_Cmd(TIM2, ENABLE);
TIM_ITConfig(TIM2, TIM_IT_CC2|TIM_IT_CC3, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void TIM2_IRQHandler(void)
{
__IO uint16_t temp;
static __IO uint16_t IC1Value_temp,IC2Value_temp;
if(TIM_GetITStatus(TIM2, TIM_IT_CC2)!= RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
IC2Value = TIM_GetCapture2(TIM2);
temp = IC2Value - IC1Value_temp;
if(temp==0){
Frequency_1=0;
}
else{
Frequency_1 = 2000000/temp; //此处的2_000_000 与上面的72M/36相关
}
IC1Value_temp = IC2Value;
}
if(TIM_GetITStatus(TIM2, TIM_IT_CC3)!= RESET)
{
TIM_ClearITPendingBit(TIM2,TIM_IT_CC3);
IC3Value = TIM_GetCapture3(TIM2);
temp = IC3Value - IC2Value_temp;
if(temp==0){
Frequency_2=0;
}
else{
Frequency_2 = 2000000/temp;
}
IC2Value_temp = IC3Value;
}
}
注意事项
- 首先是ARR寄存器的设置一定要是0XFFFF。
- 关于分频系数要考虑采的频率的大小,参考下表所示。
PSC | 定时器计数频率 | 最高输出方波频率 | 最低输出方波频率 |
---|---|---|---|
0 | 72000000 | 72MHZ | 1098.65 |
1 | 36000000 | 36MHZ | 549.32 |
3 | 18000000 | 18MHZ | 274.66 |
5 | 12000000 | 12MHZ | 183.11 |
7 | 9000000 | 9MHZ | 137.33 |
11 | 6000000 | 6MHZ | 91.55 |
23 | 3000000 | 3MHZ | 45.78 |
35 | 2000000 | 2MHZ | 30.52 |
71 | 1000000 | 1MHZ | 15.26 |
719 | 100000 | 0.1MHZ | 1.53 |
- 每设置一个通道一定要初始化一次TIM_PWMIConfig(TIM2,&TIM_ICInitStructure);,上次就因为忘了初始化通道3,导致浪费了一天的时间!
- TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);这个要关掉,如果打开的话会导致通道的没办法测量相应的频率。
单个定时器实现测量占空比和频率
我这里是对PA6进行输入捕获。
Timer.c
#include "timer.h"
__IO uint16_t IC2Value = 0;
__IO uint16_t DutyCycle = 0;
__IO uint32_t Frequency = 0;
void Timer3_InputInit(void){
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = 65535;
TIM_TimeBaseStructure.TIM_Prescaler = 71;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
//如果说PA7这里改成TIM_Channel_2
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);
//如果说PA7这里改成TIM_TS_TI2FP2
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);
TIM_Cmd(TIM3, ENABLE);
//如果说PA7这里改成TIM_IT_CC2
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);
}
void TIM3_IRQHandler(void)
{
//如果说PA7这里改成TIM_IT_CC2
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
//如果说PA7这里改成TIM_GetCapture2
IC2Value = TIM_GetCapture1(TIM3);
if (IC2Value != 0)
{
//如果说PA7这里改成TIM_GetCapture1
DutyCycle = (TIM_GetCapture2(TIM3) * 100) / IC2Value;
Frequency = SystemCoreClock/72/IC2Value;
}
else
{
DutyCycle = 0;
Frequency = 0;
}
}
注意事项
- 这里对IC捕获的结构体初始化的是TIM_PWMIConfig前面的是TIM_ICInit!!!
- TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);这个要使能。
- 关于初始化的时候一定要清晰对于TimerX哪个系列。
单个定时器实现多路输出频率与占空比都可调
在这里我使用的是PA6与PA7,他们对应的通道分别是Timer3的通道1与通道2
void Timer3_PwmOut(u16 arr,u16 psc){
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
/* Enable the TIM2 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 0xffff;
TIM_TimeBaseStructure.TIM_Prescaler = 35;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel1 */
TIM3CH1_set(0); //这个函数的实现在后面
/* PWM1 Mode configuration: Channel2 */
TIM3CH2_set(0);
TIM_Cmd(TIM3, ENABLE); /* TIM3 enable counter */
//TIM_ITConfig(TIM3, TIM_IT_CC1 | TIM_IT_CC2, ENABLE);//通道1、通道2中断使能
}
uint16_t capture = 0;
_Bool pa6_state=0,pa7_state=0;
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1 );
capture = TIM_GetCapture1(TIM3);
if(pa6_state==0){
TIM_SetCompare1(TIM3,(u16)(capture + CCR1_Val*CCR1_ZKB));
pa6_state=1;
}
else if(pa6_state==1){
TIM_SetCompare1(TIM3,(u16)(capture + CCR1_Val*(1-CCR1_ZKB)));
pa6_state=0;
}
}
if (TIM_GetITStatus(TIM3,TIM_IT_CC2) != RESET)
{
TIM_ClearITPendingBit(TIM3,TIM_IT_CC2);
capture = TIM_GetCapture2(TIM3);
if(pa7_state==0){
TIM_SetCompare2(TIM3, (u16)(capture+(u16)CCR2_Val*CCR2_ZKB));
pa7_state=1;
}
else if(pa7_state==1){
TIM_SetCompare2(TIM3, (u16)(capture +(u16)CCR2_Val*(1-CCR2_ZKB)));
pa7_state=0;
}
}
}
void TIM3CH1_set(u8 status)
{
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
if(status)
{
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_ITConfig(TIM3, TIM_IT_CC1 , ENABLE);
}
else
{
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
TIM_ITConfig(TIM3, TIM_IT_CC1 , DISABLE);
}
TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);
}
void TIM3CH2_set(u8 status)
{
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
if(status)
{
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_ITConfig(TIM3, TIM_IT_CC2, ENABLE);
}
else
{
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
TIM_ITConfig(TIM3, TIM_IT_CC2, DISABLE);
}
TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);
}
注意事项
- ARR寄存器一定要设置为0xFFFF,很重要!
- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; 这个设置要注意。因为STM32的官方库给的模式是TIM_OCMode_Timing,但是用TIM_OCMode_Toggle这个模式最好的。
- pa6_state与pa7_state这两个变量千万别忘了哦!!
- 最重要的一点!!!就是GPIO的模式一定要设置为复用输出GPIO_Mode_AF_PP!!!STM32官方库给的是GPIO_Mode_Out_PP,最好改一下。
缺点
- 多路输出的很明显的一个缺点就是输出的频率与占空比不准确,但是我没有很好的解决这个问题。如果有其他其他好的办法,希望大哥们能够指出来。
补充:单个定时器实现多路输出频率固定占空比可调
其实前面也可以实现这个功能,但是会出现一个Bug,就是如果说通过按键,来回的去开关定时器的通道,然后就会出现占空比相反的问题,就比如说你想让它输出的是10%占空比,但是他给你输出的是90%的占空比。
针对于这个问题我想了一下,最后还是想到了一个解决的方法,那就是使用TC_MODE中的TIM_OCMode_Timing。那么它与TIM_OCMode_Toggle模式不同在于当TIM->CNT与TIM->CRR的相同的时候,如果选TIM_OCMode_Toggle,相应的引脚会相应的反转,而TIM_OCMode_Timing什么都不做。那么重头戏就来了,如果我选择TIM_OCMode_Timing就意味着我可以人工去控制什么时候输出高电平什么,什么时候输出低电平了。
PS:可能也有人会说,我选择TIM_OCMode_Toggle模式,但是我在中断里,先让它反转,然后在选择人工去控制它输出相应的引脚不可以吗?答案是不可以的。。。本人已经测试过,还是会出现那个现象。。具体原因我也不是很清楚,希望有大神能够解释一下。
__IO uint16_t CCR2_Val = 200; //2M/2000 = 1Khz
__IO uint16_t CCR3_Val = 200; //2M/2000 = 1KHz
__IO double ZKB_C2=0.1,ZKB_C3=0.1;
void Timer2_Init(void){
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
//注意当选择是TIM_OCMode_Timing,这里为GPIO_Mode_Out_PP。
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_TimeBaseStructure.TIM_Period = 65535;
TIM_TimeBaseStructure.TIM_Prescaler = 35;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* Output TIM_OCMode_Toggle Mode configuration: Channel2 */
Set_Tim2Ch2(1);
/* Output Compare Timing Mode configuration: Channel3 */
Set_Tim2Ch3(1);
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
GPIO_SetBits(GPIOA, GPIO_Pin_1|GPIO_Pin_2);
}
_Bool PA1_State=0,PA2_State=0;
void TIM2_IRQHandler(void)
{
uint16_t capture = 0;
if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
capture = TIM_GetCapture2(TIM2);
if(PA1_State==0){
PA1_On(); //这里我用的是一个宏定义GPIO_SetBits(GPIOA,GPIO_Pin_1) 下同
PA1_State=1;
TIM_SetCompare2(TIM2,capture + (u16)CCR2_Val*ZKB_C2);
}
else{
PA1_Off();
PA1_State=0;
TIM_SetCompare2(TIM2,capture + (u16)CCR2_Val*(1.0-ZKB_C2));
}
}
if(TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);
capture = TIM_GetCapture3(TIM2);
if(PA2_State==0){
PA2_On();
PA2_State=1;
TIM_SetCompare3(TIM2,(u16)(capture + (u16)(CCR3_Val*ZKB_C3)));
}
else{
PA2_Off();
PA2_State=0;
TIM_SetCompare3(TIM2,(u16)(capture + (u16)(CCR3_Val*(1.0-ZKB_C3))));
}
}
}
void Set_Tim2Ch2(u8 mode){
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
if(mode==1){
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_ITConfig(TIM2,TIM_IT_CC2,ENABLE);
}
else if(mode==0){
PA1_Off();
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
TIM_ITConfig(TIM2,TIM_IT_CC2,DISABLE);
}
TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM2,&TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Disable);
}
void Set_Tim2Ch3(u8 mode){
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
if(mode==1){
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_ITConfig(TIM2,TIM_IT_CC3,ENABLE);
}
else if(mode==0){
PA2_Off();
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
TIM_ITConfig(TIM2,TIM_IT_CC3,DISABLE);
}
TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM2,TIM_OCPreload_Disable);
}
这样操作之后,确实再也没有出现占空比输出相反的情况了。。。具体原因我也不是很清楚,等本人开学之后,请教老师之后再补充原理吧。。。。