由于芯片行业局势动荡,目前公司好多产品开始考虑应用国产芯片替代,目前拿到中电的送样GD32F470VGT6进行驱动移植,在移植过程中都很顺利,唯独在定时器的编码模式出现一些小插曲,耽误了我两天时间。以下将针对调试过程和最终结果来详细介绍如何在GD32F4XX中正确应用定时器编码模式。
首先带大家看一下ST32F4XX的定时器编码模式驱动:
1)定时器通道引脚初始化
<div>void Coder_PinConfig(TIM_TypeDef* pTimPort)</div><div>{</div><div><span style="white-space:pre"> </span>GPIO_InitTypeDef GPIO_InitStructure;</div><div><span style="white-space:pre"> </span>if(pTimPort == TIM3)</div><div><span style="white-space:pre"> </span>{</div><div><span style="white-space:pre"> </span>RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);</div><div><span style="white-space:pre"> </span>RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD,ENABLE); //TIM4,GPIOC,GPIOD时钟使能</div><div><span style="white-space:pre"> </span>GPIO_PinAFConfig(GPIOC,GPIO_PinSource6, GPIO_AF_TIM3); //GPIOD6复用为TIM3_CH1,GPIOD7复用为TIM3_CH2</div><div> GPIO_PinAFConfig(GPIOC,GPIO_PinSource7, GPIO_AF_TIM3);</div><div><span style="white-space:pre"> </span></div><div><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;</div><div><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; <span style="white-space:pre"> </span></div><div><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;<span style="white-space:pre"> </span> //无上下拉</div><div><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;<span style="white-space:pre"> </span></div><div><span style="white-space:pre"> </span>GPIO_Init(GPIOC, &GPIO_InitStructure); </div><div>
</div><div><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //key:GPIO_Pin_9;</div><div><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;</div><div><span style="white-space:pre"> </span>GPIO_Init(GPIOC, &GPIO_InitStructure); </div><div><span style="white-space:pre"> </span>}</div><div><span style="white-space:pre"> </span>else if(pTimPort == TIM4)</div><div><span style="white-space:pre"> </span>{</div><div><span style="white-space:pre"> </span>RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);</div><div><span style="white-space:pre"> </span>RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD,ENABLE); //TIM4,GPIOC,GPIOD时钟使能</div><div><span style="white-space:pre"> </span>GPIO_PinAFConfig(GPIOD,GPIO_PinSource12,GPIO_AF_TIM4); //GPIOD12复用为TIM4_CH1,GPIOD13复用为TIM4_CH2</div><div><span style="white-space:pre"> </span>GPIO_PinAFConfig(GPIOD,GPIO_PinSource13,GPIO_AF_TIM4);</div><div> </div><div><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13; //key:GPIO_Pin_11;</div><div><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; <span style="white-space:pre"> </span> </div><div><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;<span style="white-space:pre"> </span> //无上下拉</div><div><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;<span style="white-space:pre"> </span> </div><div><span style="white-space:pre"> </span>GPIO_Init(GPIOD, &GPIO_InitStructure);</div><div><span style="white-space:pre"> </span></div><div><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //key:GPIO_Pin_8;</div><div><span style="white-space:pre"> </span>GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;</div><div><span style="white-space:pre"> </span>GPIO_Init(GPIOC, &GPIO_InitStructure); <span style="white-space:pre"> </span></div><div><span style="white-space:pre"> </span>} </div><div>}</div>
2)编码器初始化
void Drv_CoderConfig(TIM_TypeDef* pTimPort,uint16 CoderType)
{
TIM_ICInitTypeDef TIM_ICInitStructure;
if(pTimPort == TIM1)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
}
else if(pTimPort == TIM2)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
}
else if(pTimPort == TIM3)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
}
else if(pTimPort == TIM4)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
}
else if(pTimPort == TIM8)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
}
//配置编码器模式触发源和极性 Falling 反向 1 Rising 不反向 0
TIM_EncoderInterfaceConfig(pTimPort, TIM_EncoderMode_TI1, CoderType,
TIM_ICPolarity_Rising);
TIM_ICStructInit(&TIM_ICInitStructure); //配置滤波器
TIM_ICInitStructure.TIM_ICFilter = 6;
TIM_ICInit(pTimPort, &TIM_ICInitStructure);
TIM_SetCounter(pTimPort,DRV_CODER_CNT_MIDDLE);
TIM_Cmd(pTimPort, ENABLE);
}
根据ST提供的标准库函数找到相对应的GD提供的标准库函数:
1)void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity)函数分析
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] Configures the TIMx Encoder Interface.
* @param TIMx: where x can be 1, 2, 3, 4, 5, 8, 9 or 12 to select the TIM
* peripheral.
* @param TIM_EncoderMode: specifies the TIMx Encoder Mode.
* This parameter can be one of the following values:
* [url=home.php?mod=space&uid=2817080]@ARG[/url] TIM_EncoderMode_TI1: Counter counts on TI1FP1 edge depending on TI2FP2 level.
* @arg TIM_EncoderMode_TI2: Counter counts on TI2FP2 edge depending on TI1FP1 level.
* @arg TIM_EncoderMode_TI12: Counter counts on both TI1FP1 and TI2FP2 edges depending
* on the level of the other input.
* @param TIM_IC1Polarity: specifies the IC1 Polarity
* This parameter can be one of the following values:
* @arg TIM_ICPolarity_Falling: IC Falling edge.
* @arg TIM_ICPolarity_Rising: IC Rising edge.
* @param TIM_IC2Polarity: specifies the IC2 Polarity
* This parameter can be one of the following values:
* @arg TIM_ICPolarity_Falling: IC Falling edge.
* @arg TIM_ICPolarity_Rising: IC Rising edge.
* @retval None
*/
void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,
uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity)
{
uint16_t tmpsmcr = 0;
uint16_t tmpccmr1 = 0;
uint16_t tmpccer = 0;
/* Check the parameters */
assert_param(IS_TIM_LIST2_PERIPH(TIMx));
assert_param(IS_TIM_ENCODER_MODE(TIM_EncoderMode));
assert_param(IS_TIM_IC_POLARITY(TIM_IC1Polarity));
assert_param(IS_TIM_IC_POLARITY(TIM_IC2Polarity));
/* Get the TIMx SMCR register value */
tmpsmcr = TIMx->SMCR;
/* Get the TIMx CCMR1 register value */
tmpccmr1 = TIMx->CCMR1;
/* Get the TIMx CCER register value */
tmpccer = TIMx->CCER;
/* Set the encoder Mode */
tmpsmcr &= (uint16_t)~TIM_SMCR_SMS;
tmpsmcr |= TIM_EncoderMode;
/* Select the Capture Compare 1 and the Capture Compare 2 as input */
tmpccmr1 &= ((uint16_t)~TIM_CCMR1_CC1S) & ((uint16_t)~TIM_CCMR1_CC2S);
tmpccmr1 |= TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC2S_0;
/* Set the TI1 and the TI2 Polarities */
tmpccer &= ((uint16_t)~TIM_CCER_CC1P) & ((uint16_t)~TIM_CCER_CC2P);
tmpccer |= (uint16_t)(TIM_IC1Polarity | (uint16_t)(TIM_IC2Polarity << (uint16_t)4));
/* Write to TIMx SMCR */
TIMx->SMCR = tmpsmcr;
/* Write to TIMx CCMR1 */
TIMx->CCMR1 = tmpccmr1;
/* Write to TIMx CCER */
TIMx->CCER = tmpccer;
}
根据上面函数分析找出GD对应的库函数:
void timer_quadrature_decoder_mode_config(uint32_t timer_periph, uint32_t decomode, uint16_t ic0polarity, uint16_t ic1polarity)
\brief configure TIMER quadrature decoder mode
\param[in] timer_periph: TIMERx(x=0..4,7)
\param[in] decomode:
only one parameter can be selected which is shown as below:
\arg TIMER_ENCODER_MODE0: counter counts on CI0FE0 edge depending on CI1FE1 level
\arg TIMER_ENCODER_MODE1: counter counts on CI1FE1 edge depending on CI0FE0 level
\arg TIMER_ENCODER_MODE2: counter counts on both CI0FE0 and CI1FE1 edges depending on the level of the other input
\param[in] ic0polarity:
only one parameter can be selected which is shown as below:
\arg TIMER_IC_POLARITY_RISING: capture rising edge
\arg TIMER_IC_POLARITY_FALLING: capture falling edge
\param[in] ic1polarity:
only one parameter can be selected which is shown as below:
\arg TIMER_IC_POLARITY_RISING: capture rising edge
\arg TIMER_IC_POLARITY_FALLING: capture falling edge
\param[out] none
\retval none
*/
void timer_quadrature_decoder_mode_config(uint32_t timer_periph, uint32_t decomode,
uint16_t ic0polarity, uint16_t ic1polarity)
{
TIMER_SMCFG(timer_periph) &= (~(uint32_t)TIMER_SMCFG_SMC);
TIMER_SMCFG(timer_periph) |= (uint32_t)decomode;
TIMER_CHCTL0(timer_periph) &= (uint32_t)(((~(uint32_t)TIMER_CHCTL0_CH0MS)) & ((~(uint32_t)TIMER_CHCTL0_CH1MS)));
TIMER_CHCTL0(timer_periph) |= (uint32_t)(TIMER_IC_SELECTION_DIRECTTI | ((uint32_t)TIMER_IC_SELECTION_DIRECTTI << 8U));
TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH0P | TIMER_CHCTL2_CH0NP));
TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH1P | TIMER_CHCTL2_CH1NP));
TIMER_CHCTL2(timer_periph) |= ((uint32_t)ic0polarity | ((uint32_t)ic1polarity << 4U));
}
(这里给大家一个小提示,不要盲目查找,依据ST库函数的@brief 能够很快了解该函数的作用,以及定位到GD库函数,想要更准确了解定位函数,就需要去了解函数中所配置的寄存器含义,这就需要大家熟悉ST芯片datasheet和GD芯片datasheet)
接下来的几个函数代码我就不一一贴上去了,大家自行分析,我只说明对应关系
2)配置滤波器函数
在上面的驱动函数中ST应用如下操作
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 6;
TIM_ICInit(pTimPort, &TIM_ICInitStructure);
在GD应用中按如下操作
timer_icinitpara.icpolarity = TIMER_IC_POLARITY_RISING;
timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI;
timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1;
timer_icinitpara.icfilter = 0x06;
timer_input_capture_config(TimPort,TIMER_CH_0,&timer_icinitpara);
3)计数器值配置
ST应用:void TIM_SetCounter(TIM_TypeDef* TIMx, uint32_t Counter)
GD应用:void timer_counter_value_config(uint32_t timer_periph, uint32_t counter)
4)使能外设
ST应用:void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
GD应用:void timer_enable(uint32_t timer_periph)
替换完成库函数以后GD的定时器编码模式驱动如下所示:
1)引脚初始化
void Coder_PinConfig(uint32 TimPort)
{
if(TimPort == TIMER0)
{
}
else if(TimPort == TIMER1)
{
}
else if(TimPort == TIMER2)
{
}
else if(TimPort == TIMER3)
{
rcu_periph_clock_enable(RCU_GPIOD);
rcu_periph_clock_enable(RCU_TIMER3);
gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE,GPIO_PIN_12|GPIO_PIN_13);
gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_12|GPIO_PIN_13);
gpio_af_set(GPIOD, GPIO_AF_2, GPIO_PIN_12);
gpio_af_set(GPIOD, GPIO_AF_2, GPIO_PIN_13);
}
}
2)编码器初始化
void Drv_CoderConfig(uint32 TimPort,uint16 CoderType)
{
timer_ic_parameter_struct timer_icinitpara;
switch(TimPort)
{
case TIMER0:
rcu_periph_clock_enable(RCU_TIMER0);
timer_deinit(TIMER0);
break;
case TIMER1:
rcu_periph_clock_enable(RCU_TIMER1);
timer_deinit(TIMER1);
break;
case TIMER2:
rcu_periph_clock_enable(RCU_TIMER2);
timer_deinit(TIMER2);
break;
case TIMER3:
rcu_periph_clock_enable(RCU_TIMER3);
timer_deinit(TIMER3);
break;
case TIMER4:
rcu_periph_clock_enable(RCU_TIMER4);
timer_deinit(TIMER4);
break;
case TIMER5:
rcu_periph_clock_enable(RCU_TIMER5);
timer_deinit(TIMER5);
break;
case TIMER6:
rcu_periph_clock_enable(RCU_TIMER6);
timer_deinit(TIMER6);
break;
case TIMER7:
rcu_periph_clock_enable(RCU_TIMER7);
timer_deinit(TIMER7);
break;
case TIMER8:
rcu_periph_clock_enable(RCU_TIMER8);
timer_deinit(TIMER8);
break;
case TIMER9:
rcu_periph_clock_enable(RCU_TIMER9);
timer_deinit(TIMER9);
break;
case TIMER10:
rcu_periph_clock_enable(RCU_TIMER10);
timer_deinit(TIMER10);
break;
case TIMER11:
rcu_periph_clock_enable(RCU_TIMER11);
timer_deinit(TIMER11);
break;
case TIMER12:
rcu_periph_clock_enable(RCU_TIMER12);
timer_deinit(TIMER12);
break;
case TIMER13:
rcu_periph_clock_enable(RCU_TIMER13);
timer_deinit(TIMER13);
break;
default:
break;
}
/* TIMER1 CH0 input capture configuration */
timer_icinitpara.icpolarity = TIMER_IC_POLARITY_RISING;
timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI;
timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1;
timer_icinitpara.icfilter = 0x06;
timer_input_capture_config(TimPort,TIMER_CH_0,&timer_icinitpara);
//timer_input_capture_config(TimPort,TIMER_CH_1,&timer_icinitpara);
timer_quadrature_decoder_mode_config(TimPort, TIMER_ENCODER_MODE0,CoderType,TIMER_IC_POLARITY_RISING);
timer_counter_value_config(TimPort,DRV_CODER_CNT_MIDDLE);
/* TIMER1 counter enable */
timer_enable(TimPort);
}
遗憾的事替换完成以后并不能正常进行编码计数,和ST作对比以后也没有任何问题,经过两天的野战,根据以往应用定时器的经验发现并没有像以前应用一样增加定时器初始化处理,于是抱着试试的心态增加初始化处理后居然计数器动作了,很欣慰!!!以下是调试代码:
void Drv_CoderConfig(uint32 TimPort,uint16 CoderType)
{
timer_ic_parameter_struct timer_icinitpara;
timer_parameter_struct timer_initpara;
switch(TimPort)
{
case TIMER0:
rcu_periph_clock_enable(RCU_TIMER0);
timer_deinit(TIMER0);
break;
case TIMER1:
rcu_periph_clock_enable(RCU_TIMER1);
timer_deinit(TIMER1);
break;
case TIMER2:
rcu_periph_clock_enable(RCU_TIMER2);
timer_deinit(TIMER2);
break;
case TIMER3:
rcu_periph_clock_enable(RCU_TIMER3);
timer_deinit(TIMER3);
break;
case TIMER4:
rcu_periph_clock_enable(RCU_TIMER4);
timer_deinit(TIMER4);
break;
case TIMER5:
rcu_periph_clock_enable(RCU_TIMER5);
timer_deinit(TIMER5);
break;
case TIMER6:
rcu_periph_clock_enable(RCU_TIMER6);
timer_deinit(TIMER6);
break;
case TIMER7:
rcu_periph_clock_enable(RCU_TIMER7);
timer_deinit(TIMER7);
break;
case TIMER8:
rcu_periph_clock_enable(RCU_TIMER8);
timer_deinit(TIMER8);
break;
case TIMER9:
rcu_periph_clock_enable(RCU_TIMER9);
timer_deinit(TIMER9);
break;
case TIMER10:
rcu_periph_clock_enable(RCU_TIMER10);
timer_deinit(TIMER10);
break;
case TIMER11:
rcu_periph_clock_enable(RCU_TIMER11);
timer_deinit(TIMER11);
break;
case TIMER12:
rcu_periph_clock_enable(RCU_TIMER12);
timer_deinit(TIMER12);
break;
case TIMER13:
rcu_periph_clock_enable(RCU_TIMER13);
timer_deinit(TIMER13);
break;
default:
break;
}
timer_initpara.period = DRV_CODER_CNT_MIDDLE;
timer_initpara.prescaler = 0;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0;
timer_init(TIMER3, &timer_initpara);
/* TIMER1 CH0 input capture configuration */
timer_icinitpara.icpolarity = TIMER_IC_POLARITY_RISING;
timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI;
timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1;
timer_icinitpara.icfilter = 0x06;
timer_input_capture_config(TimPort,TIMER_CH_0,&timer_icinitpara);
//timer_input_capture_config(TimPort,TIMER_CH_1,&timer_icinitpara);
timer_quadrature_decoder_mode_config(TimPort, TIMER_ENCODER_MODE0,CoderType,TIMER_IC_POLARITY_RISING);
timer_counter_value_config(TimPort,DRV_CODER_CNT_MIDDLE);
/* TIMER1 counter enable */
timer_enable(TimPort);
}
最好在DEBUG模式下进行调整后,顺利实现驱动移植,完成需求匹配。
|