外观
十三、输入捕获实验
输入捕获基础知识
输入捕获原理
通道输入捕获功能允许通道测量一个波形时序、频率、周期等, 输入级包括一个数字滤波器, 一个通道极性选择,边沿检测和一个通道预分频器。如果在输入引脚上出现被选择的边沿,定时器输入捕获寄存器会捕获计数器当前的值,同时捕获标志位置1,如果有使能捕获中断,则产生通道中断。
GD32E230输入捕获介绍
GD32E230除基础定时器外,都有捕获比较通道,可自由选择配置。
捕获频率方法
下图是一个方波信号,一个信号从上升沿开始,到另一个上升沿结束,此时将输入捕获设置为上升沿捕获,当上升沿到来时,触发中断,记录当前时间,直到下一个上升沿到来时再次触发,记录两次时间的差值,即是整个信号的时间。最后通过定时器自身频率/时间=信号周期。
输入捕获引脚原理图
这里仅需要知道输入捕获测频引脚连接至PA6,电路说明请前往硬件设计阅读:
输入捕获
项目介绍
通过本次实验,实现1KHz方波频率捕获测量,并将测量到的频率打印在串口上。
配置流程
一般使用定时器输入捕获功能,都需要有以下几个步骤:
- 配置通道引脚GPIO&时钟
- 配置定时器
- 配置定时器输入通道
- 配置定时器中断
- 使能定时器
- 编写中断服务函数
配置通道引脚GPIO&时钟
从上方原理介绍已经了解到,输入捕获通道引脚位于PA6,通过查找《GD32E230数据手册》可以看到PA6对应定时器2通道0;
这里选择PA6的复用功能AF1模式进行映射。
使用GPIO引脚功能以及定时器功能,需要打开其时钟,然后对GPIO引脚进行配置,使其映射到AF1模式下,同时捕获到指定跳变沿需要立刻响应,需要打开捕获中断功能:
C
//使能时钟
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_TIMER2);
//设置引脚模式
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
//设置输出状态
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_6);
//设置为复用功能
gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_6);
//定时器中断使能
nvic_irq_enable(TIMER2_IRQn, 0U);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
配置定时器
输入捕获依赖定时器,所以需要对定时器进行配置。
在配置定时器前,需要在函数最前方定义定时器参数结构体:
C
//定时器参数结构体
timer_parameter_struct timer_initpara;
1
2
2
在配置参数前,先对定时器以及定时器参数结构体进行初始化,确保其在初始状态。
C
//复位定时器
timer_deinit(TIMER2);
//定时器参数初始化
timer_struct_para_init(&timer_initpara);
1
2
3
4
5
2
3
4
5
配置定时器参数,注意参数设置:
要求捕获频率为1KHz方波,尽量保证自身定时器频率要大于被采样频率的10~100倍,否则加大计算难度。
相关频率计算参数在前两章已经介绍详细,这里就不在赘述了。
C
timer_initpara.prescaler = 710; //预分频器参数
timer_initpara.alignedmode = TIMER_COUNTER_EDGE; //边沿对齐
timer_initpara.counterdirection = TIMER_COUNTER_UP; //向上计数
timer_initpara.period = 65535; //周期
timer_initpara.clockdivision = TIMER_CKDIV_DIV1; //时钟分频
timer_init(TIMER2, &timer_initpara); //参数初始化
1
2
3
4
5
6
2
3
4
5
6
自此,定时器参数设置完成。
配置输入通道
要使用定时器的输入捕获功能,需要对通道输入参数进行配置。
- icpolarity: 通道输入极性,指明捕获的跳边沿是上升沿还是下降沿
- icselection: 通道输入模式选择,分为直接输入、间接输入、ITS输入,这个需要结合图像进行解答;一般情况选择直接输入即可。
- icprescaler:输入捕获预分频值
- icfilter: 输入捕获滤波,简单了解就是当滤波器连续采样到N次个有效电平时,认为一次有效的输入电平。
在配置输入捕获前,先在函数最前方对输入参数结构体进行定义:
同时,在使用前先进行初始化,确保其在初始状态:
C
//定时器通道输入参数结构体
timer_ic_parameter_struct timer_icinitpara;
1
2
2
C
//定时器通道输入参数初始化
timer_channel_input_struct_para_init(&timer_icinitpara);
timer_icinitpara.icpolarity = TIMER_IC_POLARITY_RISING; //通道输入上升沿
timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI; //通道输入直接模式
timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1; //通道输入捕获1分频
timer_icinitpara.icfilter = 0x0; //通道输入捕获不滤波
timer_input_capture_config(TIMER2,TIMER_CH_0,&timer_icinitpara);
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
在输入通道配置完成后,需要使能自动重装载其,这样才可以使定时器在完成一次计数后自动开启下一次计数;
C
//使能自动重装载值
timer_auto_reload_shadow_enable(TIMER2);
1
2
2
配置定时器中断
在上升沿到来时,需要快速做出响应,所以需要使能定时器输入中断,并在使能前清除对应中断标志位,确保其在初始状态;
C
//清除中断标志位
timer_interrupt_flag_clear(TIMER2,TIMER_INT_FLAG_CH0);
//使能定时器通道中断
timer_interrupt_enable(TIMER2,TIMER_INT_CH0);
1
2
3
4
5
2
3
4
5
使能定时器
最后,打开总开关,使能定时器;
C
//定时器中断使能
timer_enable(TIMER2);
1
2
2
编写中断服务函数
使能中断、使能定时器后,如果中断事件到来,就会跳转到中断服务函数中执行。需要编写对应的中断服务函数,来实现指定的功能;
中断服务函数名是固定的,在startup_gd32e23x.s启动文件中有定义。
中断服务函数并不只为某一个特殊的中断事件服务,所以,在进入中断服务函数后,需要先进行判断,检测对应中断标志位是否有被置位,确定是指定中断事件产生后再进行相应的处理。
处理完成后续将相应的中断标志位进行清除,以待下一次进入。
在第二次捕获时,如果捕获值小于第一次的,则意味着一个周期已经过了,需要使用总周期减去第一次+第二次才是正确的。
注意不可使用局部变量,否则第一次捕获值会在第二次捕获到来是清空。
C
static uint16_t readvalue1 = 0, readvalue2 = 0;
static __IO uint16_t ccnumber = 0;
static __IO uint32_t count = 0;
static __IO float freq=0;
void TIMER2_IRQHandler(void)
{
if(SET == timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_CH0))
{
if(0 == ccnumber){
// 读第一次通道0捕获值
readvalue1 = timer_channel_capture_value_register_read(TIMER2, TIMER_CH_0);
ccnumber = 1;
}else if(1 == ccnumber){
// 读第2次通道0捕获值
readvalue2 = timer_channel_capture_value_register_read(TIMER2, TIMER_CH_0);
// 如果第二次捕获值大于第一次
if(readvalue2 > readvalue1){
count = (readvalue2 - readvalue1);
}else{
count = ((0xFFFFU - readvalue1) + readvalue2);
}
//计算频率
freq = (float)1000000U / count;
printf("freq:%.1f\r\n",freq);
readvalue1=0;
count=0;
freq=0;
readvalue2=0;
ccnumber = 0;
}
timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_CH0);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
烧录代码到单片机中,连接前方的输入探头到信号发生器,或结合上节课的输出PWM实验,测量一个1Khz的方波,会发现串口助手打印频率为1Khz。
资料下载
工程代码可前往gitee下载资料包,简易数字示波器资料包