目录
原理实现
代码
总结
设计方案:
利用AT32定时器主从模式输出两路相位差的方波,后通过定时器输入捕获上升沿的时间差,最后通过进行转换。
注:tmr5freq是同频方波的频率,tmr6cut是两方波的时间差。
代码:
定时器主从模式输出方波代码:
/**
*TMR pwm 移相
*主定时器:TMR2
*从定时器:TMR3
*phase:移相值
*div:定时器预分频系数
*pre:定时器重载值
*duty:通道数据寄存器值
*pwm周期:(pre/(240/div))us
*频率:1/(pre/(240/div))hz
*pwm占空比:duty/pre
*移相幅度:phase/pre
*/
void pwm_phase_shift(u16 phase,u16 div,u32 pre,u32 duty)
{
gpio_init_type gpio_init_struct = {0};
tmr_output_config_type tmr_output_struct;
tmr_reset(TMR2);
tmr_reset(TMR3);
/* 初始化时钟 */
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); //开启gpioa时钟
crm_periph_clock_enable(CRM_TMR2_PERIPH_CLOCK, TRUE); //开启tmr2时钟
crm_periph_clock_enable(CRM_TMR3_PERIPH_CLOCK, TRUE); //开启tmr3时钟
gpio_default_para_init(&gpio_init_struct);
gpio_init_struct.gpio_pins = GPIO_PINS_0|GPIO_PINS_6 ; //PA0/6
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; //推挽输出
gpio_init_struct.gpio_pull = GPIO_PULL_NONE; //无上下拉
gpio_init_struct.gpio_mode = GPIO_MODE_MUX; //复用
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init(GPIOA, &gpio_init_struct);
tmr_base_init(TMR2, pre, div); // 240M/(0+1)/(23999+1)=10khz
tmr_cnt_dir_set(TMR2, TMR_COUNT_UP); //向上计数方式
tmr_base_init(TMR3, pre, div); // 240M/(0+1)/(23999+1)=10khz
tmr_cnt_dir_set(TMR3, TMR_COUNT_UP); //向上计数方式
/* 通道输出模式配置 */
tmr_output_default_para_init(&tmr_output_struct);
tmr_output_struct.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_B; //PWM B模式
tmr_output_struct.oc_output_state = TRUE; //使能输出
tmr_output_struct.oc_polarity = TMR_OUTPUT_ACTIVE_LOW; //极性低
/* TMR2通道1 */
tmr_output_channel_config(TMR2, TMR_SELECT_CHANNEL_1, &tmr_output_struct); //TMR2-CH1 (PA0)
tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_1, duty); //设置TMR2的通道1的数据寄存器的值(占空比)
/* TMR3通道1*/
tmr_output_channel_config(TMR3, TMR_SELECT_CHANNEL_1, &tmr_output_struct); //TMR3-CH1 (PA6)
tmr_channel_value_set(TMR3, TMR_SELECT_CHANNEL_1, duty); //设置TMR3的通道1的数据寄存器的值(占空比)
/* TMR2通道2 */
tmr_output_struct.oc_output_state = FALSE; //可输出也可不输出,因为是使用的中间信号.
tmr_output_channel_config(TMR2, TMR_SELECT_CHANNEL_2, &tmr_output_struct); //TMR2-CH2
tmr_channel_value_set(TMR2, TMR_SELECT_CHANNEL_2, phase); //设置TMR2的通道2的数据寄存器的值(占空比)
/* timer2 select enable signal to sub timer */
tmr_primary_mode_select(TMR2, TMR_PRIMARY_SEL_C2ORAW); //设置TMR2为主模式,通道2中间信号作为次定时器的输入
tmr_sub_sync_mode_set(TMR2, TRUE); //主次定时器高度同步
/* sub mode selection: tmr3 */
tmr_sub_mode_select(TMR3, TMR_SUB_TRIGGER_MODE); //设置TMR3为次定时器,设定为触发模式
tmr_trigger_input_select(TMR3, TMR_SUB_INPUT_SEL_IS1); //设置主次定时器内部关联
/* 使能TMR2*/
tmr_counter_enable(TMR2, TRUE); //使能主定时器
}
定时器输入捕获代码:
/**
*TMR5 输入捕获初始化函数
*预分频1,重载值0xffff
*定时器计数时钟240M,
*周期:(65536/240)us=273us,
*引脚:PA1 PA2
*
*/
void input_tmr3_init(void)
{
gpio_init_type gpio_init_struct;
tmr_input_config_type tmr_input_config_struct;
crm_periph_clock_enable(CRM_TMR5_PERIPH_CLOCK, TRUE); //开启tmr3时钟
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); //开启gpioa时钟
gpio_default_para_init(&gpio_init_struct);
gpio_init_struct.gpio_pins = GPIO_PINS_1|GPIO_PINS_2; //PA1 PA2
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; //推挽(输入模式,此参数无效)
gpio_init_struct.gpio_pull = GPIO_PULL_NONE; //无上下拉
gpio_init_struct.gpio_mode = GPIO_MODE_INPUT; //输入模式
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init(GPIOA, &gpio_init_struct);
tmr_base_init(TMR5, 0xffff, 0); //设置TMR3的预分频为1=0+1;重载值480=479+1;(参数要减去1)
tmr_cnt_dir_set(TMR5, TMR_COUNT_UP); //设置为向上计数方式
/* configure tmr3 channel1 to get clock signal */
tmr_input_config_struct.input_channel_select = TMR_SELECT_CHANNEL_2; //通道2,
tmr_input_config_struct.input_mapped_select = TMR_CC_CHANNEL_MAPPED_DIRECT; //直接映射
tmr_input_config_struct.input_polarity_select = TMR_INPUT_RISING_EDGE; //上升沿
tmr_input_config_struct.input_filter_value=0; //不滤波
tmr_input_channel_init(TMR5, &tmr_input_config_struct, TMR_CHANNEL_INPUT_DIV_1); //输入1分频
/* configure tmr3 channel1 to get clock signal */
tmr_input_config_struct.input_channel_select = TMR_SELECT_CHANNEL_3; //通道3
tmr_input_config_struct.input_mapped_select = TMR_CC_CHANNEL_MAPPED_DIRECT; //直接映射
tmr_input_config_struct.input_polarity_select = TMR_INPUT_RISING_EDGE; //上升沿
tmr_input_config_struct.input_filter_value=0; //不滤波
tmr_input_channel_init(TMR5, &tmr_input_config_struct, TMR_CHANNEL_INPUT_DIV_1); //输入1分频
tmr_interrupt_enable(TMR5, TMR_C2_INT, TRUE); //使能TMR3通道2中断
tmr_interrupt_enable(TMR5, TMR_C3_INT, TRUE); //使能TMR3通道3中断
nvic_irq_enable(TMR5_GLOBAL_IRQn, 0, 0); //使能tmr3中断
/*使能TMR3 */
tmr_counter_enable(TMR5, TRUE); //使能定时器
}
/**
* TMR3中断服务函数
*
*
*/
uint32_t tmr5freq = 0;//输入信号的频率
uint16_t capturenumber = 0;//捕获到高电平标志位
uint16_t ic3readvalue1 = 0, ic3readvalue2 = 0;
uint32_t capture = 0;
uint32_t Phase_difference;//相位差
void TMR5_GLOBAL_IRQHandler(void)
{
if(tmr_flag_get(TMR5, TMR_C2_FLAG) != RESET) //通道2中断
{
tmr6cut=0;
tmr_flag_clear(TMR5, TMR_C2_FLAG); //清除通道2中断
if(capturenumber == 0) //第一次捕获到高电平
{
/* get the Input Capture value */
ic3readvalue1 = tmr_channel_value_get(TMR5, TMR_SELECT_CHANNEL_2); //把通道2的数值保存起来
capturenumber = 1;
}
else if(capturenumber == 1) //第二次捕获到高电平
{
/* get the Input Capture value */
ic3readvalue2 = tmr_channel_value_get(TMR5, TMR_SELECT_CHANNEL_2); //把通道2的数值保存起来
/* capture computation */
if(ic3readvalue2 > ic3readvalue1) //判定是否有超过0xffff,这里就是限制了输入的最低频率。
{
capture = (ic3readvalue2 - ic3readvalue1); //两个高电平之间的计数差,也就是1个周期的时间
}
else
{
capture = ((0xFFFF - ic3readvalue1) + ic3readvalue2); //两个高电平之间的计数差,也就是1个周期的时间
}
/* frequency computation */
tmr5freq = (uint32_t) system_core_clock / capture; //计算出输入的频率。
capturenumber = 0;
}
}
if(tmr_flag_get(TMR5, TMR_C3_FLAG) != RESET) //通道1中断
{
tmr_flag_clear(TMR5, TMR_C3_FLAG); //清除通道1中断
Phase_difference=360*(tmr5freq*tmr6cut*5)/1000000;
display_dec_int(Phase_difference);
display_scan();//刷新函数
}
}
定时器读取两方波上升沿时间差:
/***
*TMR6 溢出中断初始化
*预分频 480 ,重载值
*定时器计数时钟为240M
*溢出时间=240M/240*5=5US;
*/
void m_tmr_init(void)
{
crm_periph_clock_enable(CRM_TMR6_PERIPH_CLOCK,TRUE);//开启TMR6时钟
tmr_base_init(TMR6,5-1,239); //设置TMR6的预分频480=479+1;重载值2=1+1;参数要减去1;
tmr_cnt_dir_set(TMR6,TMR_COUNT_UP);//设置为向上计数方式
nvic_irq_enable(TMR6_GLOBAL_IRQn,0,1);//使能TMR6中断
tmr_interrupt_enable(TMR6,TMR_OVF_INT,TRUE);//使能TMR6溢出中断
/* enable tmr1 */
tmr_counter_enable(TMR6, TRUE);
}
/*
*TMR中断服务函数
*溢出中断
*/
uint16_t tmr6cut;
void TMR6_GLOBAL_IRQHandler(void)
{
if(tmr_flag_get(TMR6,TMR_OVF_FLAG)!=RESET)//溢出中断
{
tmr6cut++;
if(tmr6cut>=65536)
tmr6cut=0;
}
tmr_flag_clear(TMR6,TMR_OVF_FLAG);//清除标志位
}
总结:
以上代码存在一些问题,比如1khz以下的方波采集不准确,后续找出问题会进行改正,同时欢迎各位批评指正。