首页 > 其他分享 >梁山派入门指南4——定时器使用详解,包括定时器中断、PWM产生、输入捕获测量频率

梁山派入门指南4——定时器使用详解,包括定时器中断、PWM产生、输入捕获测量频率

时间:2024-11-03 16:16:49浏览次数:3  
标签:定时器 BSP TIMER 详解 timer PWM PWM1 define

梁山派入门指南4——定时器使用详解,包括定时器中断、PWM产生、输入捕获测量频率

1. 定时器概览

GD32F470ZGT6 一共有 14 个定时器,可以分为五种类型,高级定时器 0/7、通用定时器(L0)1-4、通用定时器(L1)8/11、通用定时器(L2)9/10/12/13 和基本定时器 5/6。不同类型的定时器所拥有的功能数量不同,一般高级定时器的功能最多,通用定时器次之,基本定时器功能最少。

【注】:用户手册350页
在这里插入图片描述

关于定时器其他内容,请参照以下内容:

下面直接介绍GD32上的定时器开发流程:

2.基本定时器

2.1 基本定时器介绍

在这里插入图片描述
通过查阅GD32的用户手册,可以看到基本定时器的功能比较简单,通常采用定时器+中断的方式来周期性的执行一些操作,下面我将介绍具体的开发流程:

2.2 梁山派上的基本定时器开发

2.2.1. 了解梁山派上的基本定时器资源(实际上我们以及在上面了解过了)

【注】:数据手册低65页
在这里插入图片描述
【注】:数据手册低66页在这里插入图片描述

  • 可以看到梁山派上的基本定时器分别为定时器5和定时器6,常用来完成一些周期性执行的任务,比如定期触发DAC转化。

接着我们看他们具体挂载到哪个总线上:

【注】:数据手册第10页
在这里插入图片描述

  • 可以看到定时器5和定时器6都是挂载到APB1总线上。

有了以上这些信息,就足够了,下面我们开始具体的配置:

2.2.2. 配置定时器

通过查阅用户手册,了解基本定时器的配置流程如下:

在这里插入图片描述
有了这个之后,我们就可以根据手册进行一步一步的配置,或者自己查阅固件库手册,自行配置,不管哪种方式,下面是配置好的样子:


/* 定时器的时钟 */
#define BSP_TIMER5_RCU  		RCU_TIMER5

/* 定时器定义 */
#define BSP_TIMER5				TIMER5

/* 定时器中断 */
#define BSP_TIMER5_IQR			TIMER5_DAC_IRQn

/* 定时器中断服务程序 */
#define BSP_TIMER5_IRQHander 	TIMER5_DAC_IRQHandler

/* 配置定时器的预分配系数 */
#define BSP_TIMER5_PRESCALER	(24000 - 1)	

/* 配置定时器的自动重装载值 */
#define BSP_TIMER5_PERIOD	(10000 - 1)

/* 中断触发频率 = 240M /( (pre + 1) * ( per + 1 ) ) */

/************************************************
函数名称 : basic_timer5_config
功    能 : 基本定时器5初始化
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void basic_timer_config(void)
{
	/* 定义定时器结构体,适用于基本定时器 */
	timer_parameter_struct timer_initpara;
	
	/* 开始时钟 */
	rcu_periph_clock_enable(BSP_TIMER5_RCU);
	
	/* 设置定时器时钟为240Mhz,由于定时器5挂载在APB1总线上,
	时钟在system_gd32f4xx.c中被初始化为SYSCLK/4,所以需要4倍频回240MHz */
	rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
	
	/* 复位时钟 */
	timer_deinit(BSP_TIMER5);
	
	/* 初始化定时器结构体成员 */
	timer_initpara.prescaler = BSP_TIMER5_PRESCALER;	//预分配系数
	timer_initpara.alignedmode = TIMER_COUNTER_EDGE;	//边缘对齐
	timer_initpara.counterdirection = TIMER_COUNTER_UP;	//向上计数
	timer_initpara.period = BSP_TIMER5_PERIOD - 1;		//自动重装载值
	timer_initpara.clockdivision = TIMER_CKDIV_DIV1;	//输入捕获时使用,数字滤波器使用的采样频率之间的分配比例
	timer_initpara.repetitioncounter = 0;				//重复计数器,重复X+1次才触发一次中断,高级定时器才有此功能
	timer_init(BSP_TIMER5,&timer_initpara);				//初始化定时器结构体成员
	
	/* 使能定时器中断优先级 */
	nvic_irq_enable(BSP_TIMER5_IQR,2,0);
	
	/* 使能定时器中断 */
	timer_interrupt_enable(BSP_TIMER5,TIMER_INT_UP);
	
	/* 使能定时器 */
	timer_enable(BSP_TIMER5);
	
}

2.2.3. 编写定时器中断服务程序

完成了定时器的配置之后,下一步就是编写中断服务函数:

/************************************************
函数名称 : BSP_TIMER5_IRQHander
功    能 : 基本定时器5中断服务函数
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void BSP_TIMER5_IRQHander(void)
{
	/* 查询中断标志位 */
	if(timer_interrupt_flag_get(BSP_TIMER5,TIMER_INT_FLAG_UP) == SET)
	{
		/* 清除中断标志位 */
		timer_interrupt_flag_clear(BSP_TIMER5,TIMER_INT_FLAG_UP);
		
		/* 执行相应操作 */
		printf("BaseTimerInterrupt\r\n");
		
		gpio_bit_toggle(PORT_LED1,PIN_LED1);
	}
}

2.2.4. 举一反三,兼容定时器5和6

完成了以上三步之后,就可以正常使用基本定时器中断实现周期执行了,当然我们也可以举一反三,完成定时器6的配置和中断服务程序的编写,并整合成bsp文件(bsp文件我放在2.3 小节)。

2.3 bsp_base_timer文件(兼容定时器5和定时器6)

  1. bsp_base_timer.c
#include "bsp_base_timer.h"

/************************************************
函数名称 : basic_timer_config
功    能 : 基本定时器初始化
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void basic_timer_config(void)
{
	/* 如果使用了定时器5,则初始化定时器5 */
	#if USING_TIMER5
	
	/* 初始化定时器5 */
	basic_timer5_config();
	
	#endif	/* USING_TIMER5 */
	
	/* 如果使用了定时器6,则初始化定时器6 */
	#if USING_TIMER6
	
	/* 初始化定时器5 */
	basic_timer6_config();
	
	#endif	/* USING_TIMER6 */
}

#if USING_TIMER5
/************************************************
函数名称 : basic_timer5_config
功    能 : 基本定时器5初始化
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
static void basic_timer5_config(void)
{
	/* 定义定时器结构体,适用于基本定时器 */
	timer_parameter_struct timer_initpara;
	
	/* 开始时钟 */
	rcu_periph_clock_enable(BSP_TIMER5_RCU);
	
	/* 设置定时器时钟为240Mhz,由于定时器5挂载在APB1总线上,
	时钟在system_gd32f4xx.c中被初始化为SYSCLK/4,所以需要4倍频回240MHz */
	rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
	
	/* 复位时钟 */
	timer_deinit(BSP_TIMER5);
	
	/* 初始化定时器结构体成员 */
	timer_initpara.prescaler = BSP_TIMER5_PRESCALER;	//预分配系数
	timer_initpara.alignedmode = TIMER_COUNTER_EDGE;	//边缘对齐
	timer_initpara.counterdirection = TIMER_COUNTER_UP;	//向上计数
	timer_initpara.period = BSP_TIMER5_PERIOD - 1;		//自动重装载值
	timer_initpara.clockdivision = TIMER_CKDIV_DIV1;	//输入捕获时使用,数字滤波器使用的采样频率之间的分配比例
	timer_initpara.repetitioncounter = 0;				//重复计数器,重复X+1次才触发一次中断,高级定时器才有此功能
	timer_init(BSP_TIMER5,&timer_initpara);				//初始化定时器结构体成员
	
	/* 使能定时器中断优先级 */
	nvic_irq_enable(BSP_TIMER5_IQR,2,0);
	
	/* 使能定时器中断 */
	timer_interrupt_enable(BSP_TIMER5,TIMER_INT_UP);
	
	/* 使能定时器 */
	timer_enable(BSP_TIMER5);
	
}

/************************************************
函数名称 : BSP_TIMER5_IRQHander
功    能 : 基本定时器5中断服务函数
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void BSP_TIMER5_IRQHander(void)
{
	/* 查询中断标志位 */
	if(timer_interrupt_flag_get(BSP_TIMER5,TIMER_INT_FLAG_UP) == SET)
	{
		/* 清除中断标志位 */
		timer_interrupt_flag_clear(BSP_TIMER5,TIMER_INT_FLAG_UP);
		
		/* 执行相应操作 */
		printf("BaseTimerInterrupt\r\n");
		
		gpio_bit_toggle(PORT_LED1,PIN_LED1);
	}
}

#endif	/* USING_TIMER5 */

#if USING_TIMER6
/************************************************
函数名称 : basic_timer5_config
功    能 : 基本定时器5初始化
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
static void basic_timer6_config(void)
{
	/* 定义定时器结构体,适用于基本定时器 */
	timer_parameter_struct timer_initpara;
	
	/* 开始时钟 */
	rcu_periph_clock_enable(BSP_TIMER6_RCU);
	
	/* 设置定时器时钟为240Mhz, */
	rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
	
	/* 复位时钟 */
	timer_deinit(BSP_TIMER6);
	
	/* 初始化定时器结构体成员 */
	timer_initpara.prescaler = BSP_TIMER6_PRESCALER;	//预分配系数
	timer_initpara.alignedmode = TIMER_COUNTER_EDGE;	//边缘对齐
	timer_initpara.counterdirection = TIMER_COUNTER_UP;	//向上计数
	timer_initpara.period = BSP_TIMER6_PERIOD - 1;		//自动重装载值
	timer_initpara.clockdivision = TIMER_CKDIV_DIV1;	//输入捕获时使用,数字滤波器使用的采样频率之间的分配比例
	timer_initpara.repetitioncounter = 0;				//重复计数器,重复X+1次才触发一次中断,高级定时器才有此功能
	timer_init(BSP_TIMER6,&timer_initpara);				//初始化定时器结构体成员
	
	/* 使能定时器中断优先级 */
	nvic_irq_enable(BSP_TIMER6_IQR,2,0);
	
	/* 使能定时器中断 */
	timer_interrupt_enable(BSP_TIMER6,TIMER_INT_UP);
	
	/* 使能定时器 */
	timer_enable(BSP_TIMER6);
	
}

/************************************************
函数名称 : BSP_TIMER6_IRQHander
功    能 : 基本定时器6中断服务函数
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void BSP_TIMER6_IRQHander(void)
{
	/* 查询中断标志位 */
	if(timer_interrupt_flag_get(BSP_TIMER6,TIMER_INT_FLAG_UP) == SET)
	{
		/* 清除中断标志位 */
		timer_interrupt_flag_clear(BSP_TIMER6,TIMER_INT_FLAG_UP);
		
		/* 执行相应操作 */
		printf("BaseTimerInterrupt\r\n");
		
		gpio_bit_toggle(PORT_LED2,PIN_LED2);
	}
}

#endif	/* USING_TIMER6 */


【注】:这个代码时我在梁山派(GD32F470ZGT6)上写的,还没有对其他芯片做兼容,但是大部分应该都没问题。

  1. bsp_base_timer.h
#ifndef _BSP_BASE_TIMER_H
#define _BSP_BASE_TIMER_H

#include "gd32f4xx.h"
#include "systick.h"

#include "bsp_uart.h"
#include "bsp_led.h"

/* 配置需要使用那个定时器,可以同时选择多个使用,如果选择多个,
会自己完成初始化,只需要修改对应的中断服务函数和相关的宏即可 */

/* 需要使用的定时器,基本定时器只有5和6,所有这里只有两个 */
#define USING_TIMER5		1
#define USING_TIMER6		1

#if		USING_TIMER5
/* 定时器的时钟 */
#define BSP_TIMER5_RCU  		RCU_TIMER5

/* 定时器定义 */
#define BSP_TIMER5				TIMER5

/* 定时器中断 */
#define BSP_TIMER5_IQR			TIMER5_DAC_IRQn

/* 定时器中断服务程序 */
#define BSP_TIMER5_IRQHander 	TIMER5_DAC_IRQHandler

/* 配置定时器的预分配系数 */
#define BSP_TIMER5_PRESCALER	(24000 - 1)	

/* 配置定时器的自动重装载值 */
#define BSP_TIMER5_PERIOD	(10000 - 1)

/* 中断触发频率 = 240M /( (pre + 1) * ( per + 1 ) ) */

#endif 	/* USING_TIMER5 */

#if		USING_TIMER6
/* 定时器的时钟 */
#define BSP_TIMER6_RCU  		RCU_TIMER6

/* 定时器定义 */
#define BSP_TIMER6				TIMER6

/* 定时器中断 */
#define BSP_TIMER6_IQR			TIMER6_IRQn

/* 定时器中断服务程序 */
#define BSP_TIMER6_IRQHander 	TIMER6_IRQHandler

/* 配置定时器的预分配系数 */
#define BSP_TIMER6_PRESCALER	(24000 - 1)	

/* 配置定时器的自动重装载值 */
#define BSP_TIMER6_PERIOD	(10000 - 1)

/* 中断触发频率 = 240M /( (pre + 1) * ( per + 1 ) ) */

#endif 	/* USING_TIMER6 */

/* 基本定时器的初始化 */
void basic_timer_config(void);

/* 基本定时器5初始化 */
static void basic_timer5_config(void);
/* 基本定时器6初始化 */
static void basic_timer6_config(void);

#endif /* _BSP_BASE_TIMER_H */
 
 

【注】:这个代码时我在梁山派(GD32F470ZGT6)上写的,还没有对其他芯片做兼容,但是大部分应该都没问题。

2.4 基本定时器中断使用示例

在上面的bsp文件基础上,我们就可以周期的执行任务了,前提是在main函数中调用basic_timer_config(),下面是一个使用的示例:

  • main.c
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "string.h"

#include "bsp_led.h"
#include "bsp_key.h"
#include "bsp_uart.h"
#include "bsp_base_timer.h"

/*!
    \brief    main function
    \param[in]  none
    \param[out] none
    \retval     none
*/
int main(void)
{
	/* 配置中断优先级分组,4位抢占优先级,0位子优先级 */
	nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
	
	/* 配置sysTick的时钟和中断周期*/
    systick_config();
	
	/* 串口初始化 */
	uart_gpio_config(115200);
	
	/* led初始化 */
	led_gpio_config(Led_Name_Led1);
	led_gpio_config(Led_Name_Led2);
	
	/* 基本定时器初始化,具体内容查询bsp_base_timer.h文件 */
	basic_timer_config();  // 定时器初始化
	
    while(1) 
	{
	
    }
}

  • 中断服务程序
/************************************************
函数名称 : BSP_TIMER5_IRQHander
功    能 : 基本定时器5中断服务函数
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void BSP_TIMER5_IRQHander(void)
{
	/* 查询中断标志位 */
	if(timer_interrupt_flag_get(BSP_TIMER5,TIMER_INT_FLAG_UP) == SET)
	{
		/* 清除中断标志位 */
		timer_interrupt_flag_clear(BSP_TIMER5,TIMER_INT_FLAG_UP);
		
		/* 执行相应操作 */
		printf("BaseTimerInterrupt\r\n");
		
		gpio_bit_toggle(PORT_LED1,PIN_LED1);
	}
}

/************************************************
函数名称 : BSP_TIMER6_IRQHander
功    能 : 基本定时器6中断服务函数
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void BSP_TIMER6_IRQHander(void)
{
	/* 查询中断标志位 */
	if(timer_interrupt_flag_get(BSP_TIMER6,TIMER_INT_FLAG_UP) == SET)
	{
		/* 清除中断标志位 */
		timer_interrupt_flag_clear(BSP_TIMER6,TIMER_INT_FLAG_UP);
		
		/* 执行相应操作 */
		printf("BaseTimerInterrupt\r\n");
		
		gpio_bit_toggle(PORT_LED2,PIN_LED2);
	}
}

最后的示例结果就是,每隔1s中led1和led2改变一次状态,并在串口打印 “ BaseTimerInterrupt ” 。

3. 通用定时器

3.1 通用定时器介绍

  • 通用定时器L2
    在这里插入图片描述
  • 通用定时器L1
    在这里插入图片描述
  • 通用定时器L0
    在这里插入图片描述

通过查阅GD32的用户手册,可以看到通用定时器的数量比较多,分为了L2、L1、L0三种,其中L0的功能更强大,也更复杂,L2的功能最简单,也更简单,最典型的功能有输入捕获检测频率和PWM信号产生,下面将在梁山派上完成这两个功能。

3.2 产生PWM信号(通过定时器1和定时2)

3.2.1. 了解梁山派上的定时器1和定时器2

【注】:数据手册第10页
在这里插入图片描述
可以看到定时器1和定时器2都是挂载在APB1总线上的。

3.2.2. 配置定时器

通过查阅用户手册,了解基本定时器的配置流程如下:

在这里插入图片描述
在这个过程中,我们需要将GPIO引脚复用成定时器1/2通道0(代码中使用的是通道0)的PWM输出引脚,下面是具体的复用关系:

【注】:数据手册第52页 ~ 60页,这里只是展示了一部分
在这里插入图片描述
有了以上信息,我们就可以按照用户指南编写初始化代码,下面是编写好了的样子:

//PWM1信号的相关配置:可选PA0、PA5、PA15选择复用通道1,使用定时器1通道0
/* PWM1输出引脚的时钟*/
#define BSP_PWM1_RCU			RCU_GPIOA
/* PWM1输出引脚的端口*/
#define BSP_PWM1_PORT			GPIOA
/* PWM1输出引脚 */
#define BSP_PWM1_PIN			GPIO_PIN_5
/* GPIO的复用通道 */
#define BSP_PWM1_AF				GPIO_AF_1
//需要修改PWM1输出的引脚,只需要修改时钟,端口和引脚即可,这里默认PA5。

/* TIMER1_CH0配置输出PWM1信号 */
#define BSP_PWM1_TIMER_RCU	RCU_TIMER1
#define BSP_PWM1_TIMER		TIMER1
#define BSP_PWM1_CHANNEL  	TIMER_CH_0    

/* 配置定时器的预分配系数 */
#define BSP_TIMER1_PRESCALER	(240 - 1)	

/* 配置定时器的自动重装载值 */
#define BSP_TIMER1_PERIOD	(10000 - 1)
/* PWM1信号的频率 = 240M /( (pre + 1) * ( per + 1 ) ),这里就是100Hz */

/* 配置定时器的默认脉冲值,占空比 = 脉冲值 / 自动重装载值,
后续修改占空比也是通过修改这个实现的,注意脉冲值不能大于自动重装载值 */
#define BSP_TIMER1_PULSE	(5000-1)


/************************************************
函数名称 : pwm1_gpio_config
功    能 : 初始化PWM1信号的产生,使用定时器1的通道0,可选择输出到PA0、PA5、PA15,具体配置见bsp_general_timer.h
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void pwm1_gpio_config(void)
{
//GPIO配置	
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_PWM1_RCU);
	
	/* GPIO模式配置: 复用模式,无上下拉 */
	gpio_mode_set(BSP_PWM1_PORT,GPIO_MODE_AF,GPIO_PUPD_NONE,BSP_PWM1_PIN);
	
	/* GPIO输出配置:推挽输出,超级高速 */
	gpio_output_options_set(BSP_PWM1_PORT,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,BSP_PWM1_PIN);
	
	/* 配置GPIO为定时器的通道 */
	gpio_af_set(BSP_PWM1_PORT,BSP_PWM1_AF,BSP_PWM1_PIN);
	
//定时器配置
	/* 定义定时器结构体 */
	timer_parameter_struct timer_initpara;
	
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_PWM1_TIMER_RCU);
	
	/* 设置定时器时钟为240Mhz,由于定时器1挂载在APB1总线上,
	时钟在system_gd32f4xx.c中被初始化为SYSCLK/4,所以需要4倍频回240MHz */
	rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
	
	/* 复位定时器 */
	timer_deinit(BSP_PWM1_TIMER);
	
	/* 初始化定时器结构体成员 */
	timer_initpara.prescaler = BSP_TIMER1_PRESCALER;						//预分配系数
	timer_initpara.alignedmode = TIMER_COUNTER_EDGE;	//边缘对齐
	timer_initpara.counterdirection = TIMER_COUNTER_UP;	//向上计数
	timer_initpara.period = BSP_TIMER1_PERIOD;					//自动重装载值
	timer_initpara.clockdivision = TIMER_CKDIV_DIV1;	//输入捕获时使用,数字滤波器使用的采样频率之间的分配比例
	timer_initpara.repetitioncounter = 0;				//重复计数器,重复X+1次才触发一次中断,高级定时器才有此功能
	timer_init(BSP_PWM1_TIMER,&timer_initpara);			//初始化定时器结构体成员
	
	/* 使能定时器 */
	timer_enable(BSP_PWM1_TIMER);

//定时器输出配置
	/* 定义定时器比较输出结构体 */
	timer_oc_parameter_struct timer_ocintpara;			
	
	/* 初始化定时器比较输出结构体成员 */
	timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH;	//有效电平为高电平
	timer_ocintpara.outputstate = TIMER_CCX_ENABLE;			//通道的输出状态:使能PWM1输出到端口
	/* 此结构体的其他功能只有高级定时器才会使用,所以不需要配置 */
	
	/* 初始化定时器输出结构体 */
	timer_channel_output_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,&timer_ocintpara);
	
	/* 配置占空比,占空比 = 5000 / 10000,其中5000是我们给的,10000是定时器的自动重装载值  */
	timer_channel_output_pulse_value_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,BSP_TIMER1_PULSE);
	
	/* 配置定时器的输出比较模式,这里是PWM10模式 */
	timer_channel_output_mode_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,TIMER_OC_MODE_PWM0);
	
	/* 配置输出比较影子寄存器的功能,这里是禁止输出比较寄存器 */
	timer_channel_output_shadow_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,TIMER_OC_SHADOW_DISABLE);
	
	/* 只有高级定时器使用 */
	//	timer_primary_output_config(TIMER0,ENABLE);
	
	/* 自动重装载影子寄存器使能 */
	timer_auto_reload_shadow_enable(BSP_PWM1_TIMER);
	
	/* 使能定时器 */
	timer_enable(BSP_PWM1_TIMER);
}

完成以上步骤之后,默认会在PA5引脚上输出一个频率为100Hz,占空比为50%的PWM信号。

3.2.3 修改占空比

由于PWM信号的产生配置中,我们不需要使能中断,因此也不需要编写中断服务程序,但是我们可能需要改变PWM信号的占空比,下面是一个修改占空比的函数:

/************************************************
函数名称 : pwm_duty_change
功    能 : 改变PWM信号的占空比,可外部调用
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void pwm_duty_change(void)
{
	static uint8_t direct = 0; // 方向
	static uint16_t value = 0; // 脉冲值
	
	if(direct == 0)					 // 逐渐变亮 
	{
		value += 500;					 // 占空比增加
		if(value > 10000)		 
		   direct = 1;         // 改变方向
	}else // 逐渐变暗
	{ 
		value -= 500;          // 占空比减小
		if(value <= 0)
			direct = 0;
	}
	#if USING_PWM1
	timer_channel_output_pulse_value_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,value); // 配置定时器通道输出脉冲值
	printf("Duty =  %d\r\n",value/100);
	delay_1ms(50);                                                                // 延时50ms
}

3.2.4 举一反三,兼容定时器1和2

完成了以上几步,完成了PWM信号的产生和占空比的修改,当然我们可以举一反三,兼容定时器1和定时2产生两组PWM信号,并将其封装成bsp文件(对应的bsp文件在3.3小节展示)

3.2.5 bsp_general_timer文件(兼容定时器1和2,两组PWM信号产生)

  1. bsp_general_timer.c
#include "bsp_general_timer.h"

/************************************************
函数名称 : pwm_gpio_config
功    能 : 初始化PWM信号的产生,可能会用到定时器1和2
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void pwm_gpio_config(void)
{
	/* 如果开启了PWM1信号功能,就初始化PWM1信号的相关内容,包括gpio和定时器配置 */
	#if USING_PWM1
	pwm1_gpio_config();		//初始化gpio和定时器1
	#endif	/* USING_PWM1 */
	
	/* 如果开启了PWM2信号功能,就初始化PWM2信号的相关内容,包括gpio和定时器配置 */
	#if USING_PWM2
	pwm2_gpio_config();		//初始化gpio和定时器1
	#endif	/* USING_PWM2 */
	
}
/* 开启了PWM1信号的产生 */
#if USING_PWM1
/************************************************
函数名称 : pwm1_gpio_config
功    能 : 初始化PWM1信号的产生,使用定时器1的通道0,可选择输出到PA0、PA5、PA15,具体配置见bsp_general_timer.h
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
static void pwm1_gpio_config(void)
{
//GPIO配置	
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_PWM1_RCU);
	
	/* GPIO模式配置: 复用模式,无上下拉 */
	gpio_mode_set(BSP_PWM1_PORT,GPIO_MODE_AF,GPIO_PUPD_NONE,BSP_PWM1_PIN);
	
	/* GPIO输出配置:推挽输出,超级高速 */
	gpio_output_options_set(BSP_PWM1_PORT,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,BSP_PWM1_PIN);
	
	/* 配置GPIO为定时器的通道 */
	gpio_af_set(BSP_PWM1_PORT,BSP_PWM1_AF,BSP_PWM1_PIN);
	
//定时器配置
	/* 定义定时器结构体 */
	timer_parameter_struct timer_initpara;
	
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_PWM1_TIMER_RCU);
	
	/* 设置定时器时钟为240Mhz,由于定时器1挂载在APB1总线上,
	时钟在system_gd32f4xx.c中被初始化为SYSCLK/4,所以需要4倍频回240MHz */
	rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
	
	/* 复位定时器 */
	timer_deinit(BSP_PWM1_TIMER);
	
	/* 初始化定时器结构体成员 */
	timer_initpara.prescaler = BSP_TIMER1_PRESCALER;						//预分配系数
	timer_initpara.alignedmode = TIMER_COUNTER_EDGE;	//边缘对齐
	timer_initpara.counterdirection = TIMER_COUNTER_UP;	//向上计数
	timer_initpara.period = BSP_TIMER1_PERIOD;					//自动重装载值
	timer_initpara.clockdivision = TIMER_CKDIV_DIV1;	//输入捕获时使用,数字滤波器使用的采样频率之间的分配比例
	timer_initpara.repetitioncounter = 0;				//重复计数器,重复X+1次才触发一次中断,高级定时器才有此功能
	timer_init(BSP_PWM1_TIMER,&timer_initpara);			//初始化定时器结构体成员
	
	/* 使能定时器 */
	timer_enable(BSP_PWM1_TIMER);

//定时器输出配置
	/* 定义定时器比较输出结构体 */
	timer_oc_parameter_struct timer_ocintpara;			
	
	/* 初始化定时器比较输出结构体成员 */
	timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH;	//有效电平为高电平
	timer_ocintpara.outputstate = TIMER_CCX_ENABLE;			//通道的输出状态:使能PWM1输出到端口
	/* 此结构体的其他功能只有高级定时器才会使用,所以不需要配置 */
	
	/* 初始化定时器输出结构体 */
	timer_channel_output_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,&timer_ocintpara);
	
	/* 配置占空比,占空比 = 5000 / 10000,其中5000是我们给的,10000是定时器的自动重装载值  */
	timer_channel_output_pulse_value_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,BSP_TIMER1_PULSE);
	
	/* 配置定时器的输出比较模式,这里是PWM10模式 */
	timer_channel_output_mode_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,TIMER_OC_MODE_PWM0);
	
	/* 配置输出比较影子寄存器的功能,这里是禁止输出比较寄存器 */
	timer_channel_output_shadow_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,TIMER_OC_SHADOW_DISABLE);
	
	/* 只有高级定时器使用 */
	//	timer_primary_output_config(TIMER0,ENABLE);
	
	/* 自动重装载影子寄存器使能 */
	timer_auto_reload_shadow_enable(BSP_PWM1_TIMER);
	
	/* 使能定时器 */
	timer_enable(BSP_PWM1_TIMER);
}
#endif	/* USING_PWM1 */

/* 开启了PWM2信号的产生 */
#if USING_PWM2
/************************************************
函数名称 : pwm2_gpio_config
功    能 : 初始化PWM1信号的产生,使用定时器2的通道0,可选择输出到PA6、PB4、PC6,具体配置见bsp_general_timer.h
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
static void pwm2_gpio_config(void)
{
//GPIO配置	
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_PWM2_RCU);
	
	/* GPIO模式配置: 复用模式,无上下拉 */
	gpio_mode_set(BSP_PWM2_PORT,GPIO_MODE_AF,GPIO_PUPD_NONE,BSP_PWM2_PIN);
	
	/* GPIO输出配置:推挽输出,超级高速 */
	gpio_output_options_set(BSP_PWM2_PORT,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,BSP_PWM2_PIN);
	
	/* 配置GPIO为定时器的通道 */
	gpio_af_set(BSP_PWM2_PORT,BSP_PWM2_AF,BSP_PWM2_PIN);
	
//定时器配置
	/* 定义定时器结构体 */
	timer_parameter_struct timer_initpara;
	
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_PWM2_TIMER_RCU);
	
	/* 设置定时器时钟为240Mhz,由于定时器2挂载在APB1总线上,
	时钟在system_gd32f4xx.c中被初始化为SYSCLK/4,所以需要4倍频回240MHz */
	rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
	
	/* 复位定时器 */
	timer_deinit(BSP_PWM2_TIMER);
	
	/* 初始化定时器结构体成员 */
	timer_initpara.prescaler = BSP_TIMER2_PRESCALER;						//预分配系数
	timer_initpara.alignedmode = TIMER_COUNTER_EDGE;	//边缘对齐
	timer_initpara.counterdirection = TIMER_COUNTER_UP;	//向上计数
	timer_initpara.period = BSP_TIMER2_PERIOD;					//自动重装载值
	timer_initpara.clockdivision = TIMER_CKDIV_DIV1;	//输入捕获时使用,数字滤波器使用的采样频率之间的分配比例
	timer_initpara.repetitioncounter = 0;				//重复计数器,重复X+2次才触发一次中断,高级定时器才有此功能
	timer_init(BSP_PWM2_TIMER,&timer_initpara);			//初始化定时器结构体成员
	
	/* 使能定时器 */
	timer_enable(BSP_PWM2_TIMER);

//定时器输出配置
	/* 定义定时器比较输出结构体 */
	timer_oc_parameter_struct timer_ocintpara;			
	
	/* 初始化定时器比较输出结构体成员 */
	timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH;	//有效电平为高电平
	timer_ocintpara.outputstate = TIMER_CCX_ENABLE;			//通道的输出状态:使能PWM2输出到端口
	/* 此结构体的其他功能只有高级定时器才会使用,所以不需要配置 */
	
	/* 初始化定时器输出结构体 */
	timer_channel_output_config(BSP_PWM2_TIMER,BSP_PWM2_CHANNEL,&timer_ocintpara);
	
	/* 配置占空比,占空比 = 500 / 1000,其中500是我们给的,1000是定时器的自动重装载值  */
	timer_channel_output_pulse_value_config(BSP_PWM2_TIMER,BSP_PWM2_CHANNEL,BSP_TIMER2_PULSE);
	
	/* 配置定时器的输出比较模式,这里是PWM2模式 */
	timer_channel_output_mode_config(BSP_PWM2_TIMER,BSP_PWM2_CHANNEL,TIMER_OC_MODE_PWM0);
	
	/* 配置输出比较影子寄存器的功能,这里是禁止输出比较寄存器 */
	timer_channel_output_shadow_config(BSP_PWM2_TIMER,BSP_PWM2_CHANNEL,TIMER_OC_SHADOW_DISABLE);
	
	/* 只有高级定时器使用 */
	//	timer_primary_output_config(TIMER0,ENABLE);
	
	/* 自动重装载影子寄存器使能 */
	timer_auto_reload_shadow_enable(BSP_PWM2_TIMER);
	
	/* 使能定时器 */
	timer_enable(BSP_PWM2_TIMER);
}
#endif	/* USING_PWM2 */

/************************************************
函数名称 : pwm_duty_change
功    能 : 改变PWM信号的占空比,可外部调用
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void pwm_duty_change(void)
{
	static uint8_t direct = 0; // 方向
	static uint16_t value = 0; // 脉冲值
	
	if(direct == 0)					 // 逐渐变亮 
	{
		value += 50;					 // 值越大 越亮 
		if(value > 1000)		 
		   direct = 1;         // 改变方向
	}else // 逐渐变暗
	{ 
		value -= 50;          // 值越小 越暗
		if(value <= 0)
			direct = 0;
	}
	/* 开启了PWM1信号的产生 */
	#if USING_PWM1
	timer_channel_output_pulse_value_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,value*5); // 配置定时器通道输出脉冲值
	#endif /* USING_PWM1 */
	
	/* 开启了PWM2信号的产生 */
	#if USING_PWM2
	timer_channel_output_pulse_value_config(BSP_PWM2_TIMER,BSP_PWM2_CHANNEL,value); // 配置定时器通道输出脉冲值
	#endif /* USING_PWM1 */
	
	printf("Duty =  %d\r\n",value/100);
	delay_1ms(50);                                                                // 延时50ms
}


【注】:这个代码时我在梁山派(GD32F470ZGT6)上写的,还没有对其他芯片做兼容,但是大部分应该都没问题。

  1. bsp_general_timer.h
#ifndef _BSP_GENERAL_TIMER_H
#define _BSP_GENERAL_TIMER_H

#include "gd32f4xx.h"
#include "systick.h"

#include "bsp_uart.h"
#include "bsp_led.h"

/* 这里用来控制PWM信号的产生与关闭,以及具体那个PWM信号产生,可以多个选项同时选择 */
#define USING_PWM1		1
#define USING_PWM2		1

#if 	USING_PWM1
//PWM1信号的相关配置:可选PA0、PA5、PA15选择复用通道1,使用定时器1通道0
/* PWM1输出引脚的时钟*/
#define BSP_PWM1_RCU			RCU_GPIOA
/* PWM1输出引脚的端口*/
#define BSP_PWM1_PORT			GPIOA
/* PWM1输出引脚 */
#define BSP_PWM1_PIN			GPIO_PIN_5
/* GPIO的复用通道 */
#define BSP_PWM1_AF				GPIO_AF_1
//需要修改PWM1输出的引脚,只需要修改时钟,端口和引脚即可,这里默认PA5。

/* TIMER1_CH0配置输出PWM1信号 */
#define BSP_PWM1_TIMER_RCU	RCU_TIMER1
#define BSP_PWM1_TIMER		TIMER1
#define BSP_PWM1_CHANNEL  	TIMER_CH_0    

/* 配置定时器的预分配系数 */
#define BSP_TIMER1_PRESCALER	(240 - 1)	

/* 配置定时器的自动重装载值 */
#define BSP_TIMER1_PERIOD	(10000 - 1)
/* PWM1信号的频率 = 240M /( (pre + 1) * ( per + 1 ) ),这里就是100Hz */

/* 配置定时器的默认脉冲值,占空比 = 脉冲值 / 自动重装载值,
后续修改占空比也是通过修改这个实现的,注意脉冲值不能大于自动重装载值 */
#define BSP_TIMER1_PULSE	(5000-1)
#endif	/* USING_PWM1 */

#if 	USING_PWM2

//PWM2信号的相关配置:可选PA6、PB4、PC6选择复用通道2,使用定时器2通道0

/* PWM2输出引脚的时钟*/
#define BSP_PWM2_RCU			RCU_GPIOA
/* PWM2输出引脚的端口*/
#define BSP_PWM2_PORT			GPIOA
/* PWM2输出引脚*/
#define BSP_PWM2_PIN			GPIO_PIN_6
/* GPIO的复用通道 */
#define BSP_PWM2_AF				GPIO_AF_2
//需要修改PWM2输出的引脚,只需要修改时钟,端口和引脚即可,这里默认PA6。

/* TIMER2_CH0配置输出PWM2信号 */
#define BSP_PWM2_TIMER_RCU	RCU_TIMER2
#define BSP_PWM2_TIMER		TIMER2
#define BSP_PWM2_CHANNEL  	TIMER_CH_0    

/* 配置定时器的预分配系数 */
#define BSP_TIMER2_PRESCALER	(240 - 1)	

/* 配置定时器的自动重装载值 */
#define BSP_TIMER2_PERIOD	(1000 - 1)
/* PWM1信号的频率 = 240M /( (pre + 1) * ( per + 1 ) ),这里就是1000Hz */

/* 配置定时器的默认脉冲值,占空比 = 脉冲值 / 自动重装载值,
后续修改占空比也是通过修改这个实现的,注意脉冲值不能大于自动重装载值 */
#define BSP_TIMER2_PULSE	(500-1)

#endif	/* USING_PWM2 */


/* 初始化PWM信号的产生,可能会用到定时器1和2 */
void pwm_gpio_config(void);

/* 改变PWM信号的占空比,可外部调用 */
void pwm_duty_change(void); 

/* 初始化PWM1信号的产生,使用定时器1的通道0,可选择输出到PA0、PA5、PA15 */
static void pwm1_gpio_config(void);

/* 初始化PWM1信号的产生,使用定时器2的通道0,可选择输出到PA6、PB4、PC6 */
static void pwm2_gpio_config(void);
    
#endif /* _BSP_GENERAL_TIMER_H */
 
 

【注】:这个代码时我在梁山派(GD32F470ZGT6)上写的,还没有对其他芯片做兼容,但是大部分应该都没问题。

3.2.6 两路PWM信号产生使用示例

在上面的bsp文件基础上,我们就可以产生PWM信号量了,前提是需要在mian函数中调用pwm_gpio_config() 和pwm_duty_change(),下面是一个简单的使用示例:

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "string.h"

#include "bsp_led.h"
#include "bsp_key.h"
#include "bsp_uart.h"
#include "bsp_base_timer.h"
#include "bsp_general_timer.h"
/*!
    \brief    main function
    \param[in]  none
    \param[out] none
    \retval     none
*/
int main(void)
{
	/* 配置中断优先级分组,4位抢占优先级,0位子优先级 */
	nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
	
	/* 配置sysTick的时钟和中断周期*/
    systick_config();
	
	/* 串口初始化 */
	uart_gpio_config(115200);
	
	/* led初始化 */
	led_gpio_config(Led_Name_Led1);
	led_gpio_config(Led_Name_Led2);
	
	/* 基本定时器初始化,具体内容查询bsp_base_timer.h文件 */
	basic_timer_config();  // 定时器初始化
	pwm_gpio_config();
    while(1) 
	{
		pwm_duty_change();
    }
}

运行结果如下:
在这里插入图片描述
以上就是通用定时器产生PWM信号部分的内容,下面我们介绍输入捕获测量频率。

3.3 输入捕获测量频率

【注】:数据手册第10页
在这里插入图片描述
可以看到定时器1和定时器2都是挂载在APB1总线上的。

3.2.2. 配置定时器

通过查阅用户手册,发现没有关于输入捕获的配置介绍,我们自己学习一下,总结出下面的配置流程:

  • 配置通道引脚的GPIO
  • 配置定时器
  • 配置输入捕获结构体
  • 配置定时器的输入通道,使能定时器
  • 使能定时器中断

在这个过程中,我们需要将GPIO引脚复用成定时器3通道0(代码中使用的是通道0)作为输入捕获引脚,下面是具体的复用关系:

【注】:数据手册第52页 ~ 60页,这里只是展示了一部分
在这里插入图片描述

有了以上信息,我们就可以参考例程编写初始化代码,下面是编写好了的样子:


//输入捕获的相关配置:可选PB6和PD12选择复用通道2,使用定时器3通道0
/* 输入捕获引脚的时钟 */
#define		BSP_INPUT_CAPTURE1_RCU		RCU_GPIOB
/* 输入捕获引脚的端口 */
#define		BSP_INPUT_CAPTURE1_PORT		GPIOB
/* 输入捕获引脚的引脚 */
#define		BSP_INPUT_CAPTURE1_PIN		GPIO_PIN_6
/* 输入捕获引脚的复用通道 */
#define		BSP_INPUT_CAPTURE1_AF		GPIO_AF_2
//需要修改输入捕获的引脚引脚,只需要修改时钟,端口和引脚即可,这里默认PB6。

/* TIMER3_CH0配置输入捕获功能 */
#define BSP_INPUT_CAPTURE1_TIMER_RCU	RCU_TIMER3
#define BSP_INPUT_CAPTURE1_TIMER		TIMER3
#define BSP_INPUT_CAPTURE1_CHANNEL  	TIMER_CH_0    

/* 配置定时器的预分配系数 */
#define BSP_TIMER3_PRESCALER	(240 - 1)	//这里分频完是1MHz,1us定时器加1	

/* 配置定时器的自动重装载值,定时器每溢出1次,表示时间过了65.536ms */
#define BSP_TIMER3_PERIOD	(65536 - 1)		

//输入捕获的相关的中断配置

/* 中断标志位:通道0中断 */
#define	BSP_INPUT_CAPTURE1_TIMER_INT_FLAG		TIMER_INT_CH0

/* 中断源:定时器3中断 */
#define BSP_INPUT_CAPTURE1_TIMER_INT_IRQn		TIMER3_IRQn

/* 中断服务函数 */
#define BSP_INPUT_CAPTURE1_TIMER_INT_IRQHandler	TIMER3_IRQHandler

/************************************************
函数名称 : input_capture_gpio_config
功    能 : 初始化输入捕获1,会使用到定时器3
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void input_capture_gpio_config(void)
{
//GPIO配置	
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_INPUT_CAPTURE1_RCU);
	
	/* GPIO模式配置: 复用模式,无上下拉 */
	gpio_mode_set(BSP_INPUT_CAPTURE1_PORT,GPIO_MODE_AF,GPIO_PUPD_NONE,BSP_INPUT_CAPTURE1_PIN);
	
	/* 配置GPIO为定时器的通道 */
	gpio_af_set(BSP_INPUT_CAPTURE1_PORT,BSP_INPUT_CAPTURE1_AF,BSP_INPUT_CAPTURE1_PIN);
	
//定时器配置
	/* 定义定时器结构体 */
	timer_parameter_struct timer_initpara;
	
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_INPUT_CAPTURE1_TIMER_RCU);
	
	/* 设置定时器时钟为240Mhz,由于定时器3挂载在APB1总线上,
	时钟在system_gd32f4xx.c中被初始化为SYSCLK/4,所以需要4倍频回240MHz */
	rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
	
	/* 复位定时器 */
	timer_deinit(BSP_INPUT_CAPTURE1_TIMER);
	
	/* 初始化定时器结构体成员 */
	timer_initpara.prescaler = BSP_TIMER3_PRESCALER;		//预分配系数
	timer_initpara.alignedmode = TIMER_COUNTER_EDGE;		//边缘对齐
	timer_initpara.counterdirection = TIMER_COUNTER_UP;		//向上计数
	timer_initpara.period = BSP_TIMER3_PERIOD;				//自动重装载值
	timer_initpara.clockdivision = TIMER_CKDIV_DIV1;		//输入捕获时使用,数字滤波器使用的采样频率之间的分配比例
	timer_initpara.repetitioncounter = 0;					//重复计数器,重复X+2次才触发一次中断,高级定时器才有此功能
	timer_init(BSP_INPUT_CAPTURE1_TIMER,&timer_initpara);	//初始化定时器结构体成员
	
//定时器输入捕获
	/* 定义定时器输入捕获配置结构体 */
	timer_ic_parameter_struct	timer_icinitpara;
	
	/* 初始化定时器输入捕获配置结构体成员 */
	timer_icinitpara.icpolarity = TIMER_IC_POLARITY_RISING;		//捕获上升沿
	timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI;	//直接输入捕获触发模式
	timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1;			//不分频
	timer_icinitpara.icfilter = 0x00;							//不滤波
	
	/* 初始化定时器输入捕获结构体成员 */
	timer_input_capture_config(BSP_INPUT_CAPTURE1_TIMER,BSP_INPUT_CAPTURE1_CHANNEL,&timer_icinitpara);

//使能定时器
	/* 开启自动重装载 */
	timer_auto_reload_shadow_enable(BSP_INPUT_CAPTURE1_TIMER);
	
	/* 清除定时器使用通道的中断标志位 */
	timer_interrupt_flag_clear(BSP_INPUT_CAPTURE1_TIMER,TIMER_INT_FLAG_CH0);
	timer_interrupt_flag_clear(BSP_INPUT_CAPTURE1_TIMER,TIMER_INT_FLAG_UP);
	
	/* 定时器通道0的中断使能 */
	timer_interrupt_enable(BSP_INPUT_CAPTURE1_TIMER,TIMER_INT_CH0);
	
	/* 定时器更新中断使能 */
	timer_interrupt_enable(BSP_INPUT_CAPTURE1_TIMER,TIMER_INT_UP);
	
	/* 使能定时器 */
	timer_enable(BSP_INPUT_CAPTURE1_TIMER);
	
	/* 配置中断优先级 */
	nvic_irq_enable(BSP_INPUT_CAPTURE1_TIMER_INT_IRQn,0,0);
}

紧接着,编写中断服务函数。

3.2.3 中断服务函数

/************************************************
函数名称 : BSP_INPUT_CAPTURE1_TIMER_INT_IRQHandler
功    能 : 定时器3的中断回调函数
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void BSP_INPUT_CAPTURE1_TIMER_INT_IRQHandler(void)
{
	static uint8_t edgeFlag = 0;	//标志位为0,表示第一个边沿,为1表示第二个边沿
	static uint32_t count = 0;		//定时器溢出的次数
	uint32_t readVal = 0;			//定时器的计数值
	
	/* 中断标志为更新中断 */
	if(timer_interrupt_flag_get(BSP_INPUT_CAPTURE1_TIMER,TIMER_INT_UP) == SET)
	{
		/* 清除中断标志位 */
		timer_interrupt_flag_clear(BSP_INPUT_CAPTURE1_TIMER,TIMER_INT_UP);
		
		if(edgeFlag == 1)
		{
			count++;
		}
	}
	/* 捕获到上升沿 */
	if(timer_interrupt_flag_get(BSP_INPUT_CAPTURE1_TIMER,BSP_INPUT_CAPTURE1_TIMER_INT_FLAG) == SET)
	{
		/* 清除中断标志位 */
		timer_interrupt_flag_clear(BSP_INPUT_CAPTURE1_TIMER,BSP_INPUT_CAPTURE1_TIMER_INT_FLAG);
		
		if(edgeFlag == 0)
		{
			/* 第一个上升沿,清零定时器的计数值 */
			timer_counter_value_config(BSP_INPUT_CAPTURE1_TIMER,0);
			edgeFlag = 1;	
		}
		if(edgeFlag == 1)
		{
			/* 第二个上升沿,获取定时器的计数值 */
			readVal = timer_channel_capture_value_register_read(BSP_INPUT_CAPTURE1_TIMER,BSP_INPUT_CAPTURE1_CHANNEL);
			
			/* 计算频率,这里1000,000是定时器1240分频之后的频率*/
			fre1 = 1000000 / ((65535 * count) + readVal);
			
			edgeFlag = 0;	
			count = 0;
		}
		
	}
}

还有获取测量频率的API函数:

static float fre1 = 0;

/************************************************
函数名称 : getFre1
功    能 : 获取输入捕获测量的频率
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
float getFre1(void)
{
	return fre1;
}
#endif /* USING_INPUT_CAPTURE1 */

以上就是输入捕获测量频率的具体实现,下面是对应的bsp文件(我把这里的输入捕获和3.2.5中的PWM输出,放在放在了一起)

3.2.4 bsp_base_timer文件(1,2,3,带有输入捕获和两路PWM输出)

  1. bsp_base_timer.c
#include "bsp_general_timer.h"

static float fre1 = 0;
/***********************************PWM信号输出**********************************************/
#if USING_PWM1 || USING_PWM2
/************************************************
函数名称 : pwm_gpio_config
功    能 : 初始化PWM信号的产生,可能会用到定时器1和2
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void pwm_gpio_config(void)
{
	/* 如果开启了PWM1信号功能,就初始化PWM1信号的相关内容,包括gpio和定时器配置 */
	#if USING_PWM1
	pwm1_gpio_config();		//初始化gpio和定时器1
	#endif	/* USING_PWM1 */
	
	/* 如果开启了PWM2信号功能,就初始化PWM2信号的相关内容,包括gpio和定时器配置 */
	#if USING_PWM2
	pwm2_gpio_config();		//初始化gpio和定时器1
	#endif	/* USING_PWM2 */
	
}
#endif /* USING_PWM1 || USING_PWM2 */

#if USING_PWM1
/************************************************
函数名称 : pwm1_gpio_config
功    能 : 初始化PWM1信号的产生,使用定时器1的通道0,可选择输出到PA0、PA5、PA15,具体配置见bsp_general_timer.h
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
static void pwm1_gpio_config(void)
{
//GPIO配置	
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_PWM1_RCU);
	
	/* GPIO模式配置: 复用模式,无上下拉 */
	gpio_mode_set(BSP_PWM1_PORT,GPIO_MODE_AF,GPIO_PUPD_NONE,BSP_PWM1_PIN);
	
	/* GPIO输出配置:推挽输出,超级高速 */
	gpio_output_options_set(BSP_PWM1_PORT,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,BSP_PWM1_PIN);
	
	/* 配置GPIO为定时器的通道 */
	gpio_af_set(BSP_PWM1_PORT,BSP_PWM1_AF,BSP_PWM1_PIN);
	
//定时器配置
	/* 定义定时器结构体 */
	timer_parameter_struct timer_initpara;
	
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_PWM1_TIMER_RCU);
	
	/* 设置定时器时钟为240Mhz,由于定时器1挂载在APB1总线上,
	时钟在system_gd32f4xx.c中被初始化为SYSCLK/4,所以需要4倍频回240MHz */
	rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
	
	/* 复位定时器 */
	timer_deinit(BSP_PWM1_TIMER);
	
	/* 初始化定时器结构体成员 */
	timer_initpara.prescaler = BSP_TIMER1_PRESCALER;						//预分配系数
	timer_initpara.alignedmode = TIMER_COUNTER_EDGE;	//边缘对齐
	timer_initpara.counterdirection = TIMER_COUNTER_UP;	//向上计数
	timer_initpara.period = BSP_TIMER1_PERIOD;					//自动重装载值
	timer_initpara.clockdivision = TIMER_CKDIV_DIV1;	//输入捕获时使用,数字滤波器使用的采样频率之间的分配比例
	timer_initpara.repetitioncounter = 0;				//重复计数器,重复X+1次才触发一次中断,高级定时器才有此功能
	timer_init(BSP_PWM1_TIMER,&timer_initpara);			//初始化定时器结构体成员
	
	/* 使能定时器 */
	timer_enable(BSP_PWM1_TIMER);

//定时器输出配置
	/* 定义定时器比较输出结构体 */
	timer_oc_parameter_struct timer_ocintpara;			
	
	/* 初始化定时器比较输出结构体成员 */
	timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH;	//有效电平为高电平
	timer_ocintpara.outputstate = TIMER_CCX_ENABLE;			//通道的输出状态:使能PWM1输出到端口
	/* 此结构体的其他功能只有高级定时器才会使用,所以不需要配置 */
	
	/* 初始化定时器输出结构体 */
	timer_channel_output_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,&timer_ocintpara);
	
	/* 配置占空比,占空比 = 5000 / 10000,其中5000是我们给的,10000是定时器的自动重装载值  */
	timer_channel_output_pulse_value_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,BSP_TIMER1_PULSE);
	
	/* 配置定时器的输出比较模式,这里是PWM10模式 */
	timer_channel_output_mode_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,TIMER_OC_MODE_PWM0);
	
	/* 配置输出比较影子寄存器的功能,这里是禁止输出比较寄存器 */
	timer_channel_output_shadow_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,TIMER_OC_SHADOW_DISABLE);
	
	/* 只有高级定时器使用 */
	//	timer_primary_output_config(TIMER0,ENABLE);
	
	/* 自动重装载影子寄存器使能 */
	timer_auto_reload_shadow_enable(BSP_PWM1_TIMER);
	
	/* 使能定时器 */
	timer_enable(BSP_PWM1_TIMER);
}
#endif	/* USING_PWM1 */

#if USING_PWM2
/************************************************
函数名称 : pwm2_gpio_config
功    能 : 初始化PWM1信号的产生,使用定时器2的通道0,可选择输出到PA6、PB4、PC6,具体配置见bsp_general_timer.h
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
static void pwm2_gpio_config(void)
{
//GPIO配置	
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_PWM2_RCU);
	
	/* GPIO模式配置: 复用模式,无上下拉 */
	gpio_mode_set(BSP_PWM2_PORT,GPIO_MODE_AF,GPIO_PUPD_NONE,BSP_PWM2_PIN);
	
	/* GPIO输出配置:推挽输出,超级高速 */
	gpio_output_options_set(BSP_PWM2_PORT,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,BSP_PWM2_PIN);
	
	/* 配置GPIO为定时器的通道 */
	gpio_af_set(BSP_PWM2_PORT,BSP_PWM2_AF,BSP_PWM2_PIN);
	
//定时器配置
	/* 定义定时器结构体 */
	timer_parameter_struct timer_initpara;
	
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_PWM2_TIMER_RCU);
	
	/* 设置定时器时钟为240Mhz,由于定时器2挂载在APB1总线上,
	时钟在system_gd32f4xx.c中被初始化为SYSCLK/4,所以需要4倍频回240MHz */
	rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
	
	/* 复位定时器 */
	timer_deinit(BSP_PWM2_TIMER);
	
	/* 初始化定时器结构体成员 */
	timer_initpara.prescaler = BSP_TIMER2_PRESCALER;						//预分配系数
	timer_initpara.alignedmode = TIMER_COUNTER_EDGE;	//边缘对齐
	timer_initpara.counterdirection = TIMER_COUNTER_UP;	//向上计数
	timer_initpara.period = BSP_TIMER2_PERIOD;					//自动重装载值
	timer_initpara.clockdivision = TIMER_CKDIV_DIV1;	//输入捕获时使用,数字滤波器使用的采样频率之间的分配比例
	timer_initpara.repetitioncounter = 0;				//重复计数器,重复X+2次才触发一次中断,高级定时器才有此功能
	timer_init(BSP_PWM2_TIMER,&timer_initpara);			//初始化定时器结构体成员
	
	/* 使能定时器 */
	timer_enable(BSP_PWM2_TIMER);

//定时器输出配置
	/* 定义定时器比较输出结构体 */
	timer_oc_parameter_struct timer_ocintpara;			
	
	/* 初始化定时器比较输出结构体成员 */
	timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH;	//有效电平为高电平
	timer_ocintpara.outputstate = TIMER_CCX_ENABLE;			//通道的输出状态:使能PWM2输出到端口
	/* 此结构体的其他功能只有高级定时器才会使用,所以不需要配置 */
	
	/* 初始化定时器输出结构体 */
	timer_channel_output_config(BSP_PWM2_TIMER,BSP_PWM2_CHANNEL,&timer_ocintpara);
	
	/* 配置占空比,占空比 = 500 / 1000,其中500是我们给的,1000是定时器的自动重装载值  */
	timer_channel_output_pulse_value_config(BSP_PWM2_TIMER,BSP_PWM2_CHANNEL,BSP_TIMER2_PULSE);
	
	/* 配置定时器的输出比较模式,这里是PWM2模式 */
	timer_channel_output_mode_config(BSP_PWM2_TIMER,BSP_PWM2_CHANNEL,TIMER_OC_MODE_PWM0);
	
	/* 配置输出比较影子寄存器的功能,这里是禁止输出比较寄存器 */
	timer_channel_output_shadow_config(BSP_PWM2_TIMER,BSP_PWM2_CHANNEL,TIMER_OC_SHADOW_DISABLE);
	
	/* 只有高级定时器使用 */
	//	timer_primary_output_config(TIMER0,ENABLE);
	
	/* 自动重装载影子寄存器使能 */
	timer_auto_reload_shadow_enable(BSP_PWM2_TIMER);
	
	/* 使能定时器 */
	timer_enable(BSP_PWM2_TIMER);
}
#endif	/* USING_PWM2 */

#if USING_PWM1 || USING_PWM2

/************************************************
函数名称 : pwm_duty_change
功    能 : 改变PWM信号的占空比,可外部调用
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void pwm_duty_change(void)
{
	static uint8_t direct = 0; // 方向
	static uint16_t value = 0; // 脉冲值
	
	if(direct == 0)					 // 逐渐变亮 
	{
		value += 500;					 // 值越大 越亮 
		if(value > 10000)		 
		   direct = 1;         // 改变方向
	}else // 逐渐变暗
	{ 
		value -= 500;          // 值越小 越暗
		if(value <= 0)
			direct = 0;
	}
	/* 开启了PWM1信号的产生 */
	#if USING_PWM1
	timer_channel_output_pulse_value_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,value); // 配置定时器通道输出脉冲值
	printf("Duty =  %d\r\n",value/100);
	#endif /* USING_PWM1 */
	
	/* 开启了PWM2信号的产生 */
	#if USING_PWM2
	timer_channel_output_pulse_value_config(BSP_PWM2_TIMER,BSP_PWM2_CHANNEL,value/10); // 配置定时器通道输出脉冲值
	printf("Duty =  %d\r\n",value/10);
	#endif /* USING_PWM1 */
	
	
	                                                                // 延时50ms
}
#endif /* USING_PWM1 || USING_PWM2 */


/***********************************输入捕获1**********************************************/
#if USING_INPUT_CAPTURE1
/************************************************
函数名称 : input_capture_gpio_config
功    能 : 初始化输入捕获1,会使用到定时器3
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void input_capture_gpio_config(void)
{
//GPIO配置	
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_INPUT_CAPTURE1_RCU);
	
	/* GPIO模式配置: 复用模式,无上下拉 */
	gpio_mode_set(BSP_INPUT_CAPTURE1_PORT,GPIO_MODE_AF,GPIO_PUPD_NONE,BSP_INPUT_CAPTURE1_PIN);
	
	/* 配置GPIO为定时器的通道 */
	gpio_af_set(BSP_INPUT_CAPTURE1_PORT,BSP_INPUT_CAPTURE1_AF,BSP_INPUT_CAPTURE1_PIN);
	
//定时器配置
	/* 定义定时器结构体 */
	timer_parameter_struct timer_initpara;
	
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_INPUT_CAPTURE1_TIMER_RCU);
	
	/* 设置定时器时钟为240Mhz,由于定时器3挂载在APB1总线上,
	时钟在system_gd32f4xx.c中被初始化为SYSCLK/4,所以需要4倍频回240MHz */
	rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
	
	/* 复位定时器 */
	timer_deinit(BSP_INPUT_CAPTURE1_TIMER);
	
	/* 初始化定时器结构体成员 */
	timer_initpara.prescaler = BSP_TIMER3_PRESCALER;		//预分配系数
	timer_initpara.alignedmode = TIMER_COUNTER_EDGE;		//边缘对齐
	timer_initpara.counterdirection = TIMER_COUNTER_UP;		//向上计数
	timer_initpara.period = BSP_TIMER3_PERIOD;				//自动重装载值
	timer_initpara.clockdivision = TIMER_CKDIV_DIV1;		//输入捕获时使用,数字滤波器使用的采样频率之间的分配比例
	timer_initpara.repetitioncounter = 0;					//重复计数器,重复X+2次才触发一次中断,高级定时器才有此功能
	timer_init(BSP_INPUT_CAPTURE1_TIMER,&timer_initpara);	//初始化定时器结构体成员
	
//定时器输入捕获
	/* 定义定时器输入捕获配置结构体 */
	timer_ic_parameter_struct	timer_icinitpara;
	
	/* 初始化定时器输入捕获配置结构体成员 */
	timer_icinitpara.icpolarity = TIMER_IC_POLARITY_RISING;		//捕获上升沿
	timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI;	//直接输入捕获触发模式
	timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1;			//不分频
	timer_icinitpara.icfilter = 0x00;							//不滤波
	
	/* 初始化定时器输入捕获结构体成员 */
	timer_input_capture_config(BSP_INPUT_CAPTURE1_TIMER,BSP_INPUT_CAPTURE1_CHANNEL,&timer_icinitpara);

//使能定时器
	/* 开启自动重装载 */
	timer_auto_reload_shadow_enable(BSP_INPUT_CAPTURE1_TIMER);
	
	/* 清除定时器使用通道的中断标志位 */
	timer_interrupt_flag_clear(BSP_INPUT_CAPTURE1_TIMER,TIMER_INT_FLAG_CH0);
	timer_interrupt_flag_clear(BSP_INPUT_CAPTURE1_TIMER,TIMER_INT_FLAG_UP);
	
	/* 定时器通道0的中断使能 */
	timer_interrupt_enable(BSP_INPUT_CAPTURE1_TIMER,TIMER_INT_CH0);
	
	/* 定时器更新中断使能 */
	timer_interrupt_enable(BSP_INPUT_CAPTURE1_TIMER,TIMER_INT_UP);
	
	/* 使能定时器 */
	timer_enable(BSP_INPUT_CAPTURE1_TIMER);
	
	/* 配置中断优先级 */
	nvic_irq_enable(BSP_INPUT_CAPTURE1_TIMER_INT_IRQn,0,0);
}

/************************************************
函数名称 : BSP_INPUT_CAPTURE1_TIMER_INT_IRQHandler
功    能 : 定时器3的中断回调函数
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void BSP_INPUT_CAPTURE1_TIMER_INT_IRQHandler(void)
{
	static uint8_t edgeFlag = 0;	//标志位为0,表示第一个边沿,为1表示第二个边沿
	static uint32_t count = 0;		//定时器溢出的次数
	uint32_t readVal = 0;			//定时器的计数值
	
	/* 中断标志为更新中断 */
	if(timer_interrupt_flag_get(BSP_INPUT_CAPTURE1_TIMER,TIMER_INT_UP) == SET)
	{
		/* 清除中断标志位 */
		timer_interrupt_flag_clear(BSP_INPUT_CAPTURE1_TIMER,TIMER_INT_UP);
		
		if(edgeFlag == 1)
		{
			count++;
		}
	}
	/* 捕获到上升沿 */
	if(timer_interrupt_flag_get(BSP_INPUT_CAPTURE1_TIMER,BSP_INPUT_CAPTURE1_TIMER_INT_FLAG) == SET)
	{
		/* 清除中断标志位 */
		timer_interrupt_flag_clear(BSP_INPUT_CAPTURE1_TIMER,BSP_INPUT_CAPTURE1_TIMER_INT_FLAG);
		
		if(edgeFlag == 0)
		{
			/* 第一个上升沿,清零定时器的计数值 */
			timer_counter_value_config(BSP_INPUT_CAPTURE1_TIMER,0);
			edgeFlag = 1;	
		}
		if(edgeFlag == 1)
		{
			/* 第二个上升沿,获取定时器的计数值 */
			readVal = timer_channel_capture_value_register_read(BSP_INPUT_CAPTURE1_TIMER,BSP_INPUT_CAPTURE1_CHANNEL);
			
			/* 计算频率,这里1000,000是定时器1240分频之后的频率*/
			fre1 = 1000000 / ((65535 * count) + readVal);
			
			edgeFlag = 0;	
			count = 0;
		}
		
	}
}

/************************************************
函数名称 : getFre1
功    能 : 获取输入捕获测量的频率
参    数 : 无
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
float getFre1(void)
{
	return fre1;
}
#endif /* USING_INPUT_CAPTURE1 */


【注】:这个代码时我在梁山派(GD32F470ZGT6)上写的,还没有对其他芯片做兼容,但是大部分应该都没问题。

  1. bsp_base_timer.h
#ifndef _BSP_GENERAL_TIMER_H
#define _BSP_GENERAL_TIMER_H

#include "gd32f4xx.h"
#include "systick.h"
#include "bsp_uart.h"
#include "bsp_led.h"

/* 这里用来控制PWM信号的产生与关闭,以及具体那个PWM信号产生,可以多个选项同时选择 */
#define USING_PWM1		1
#define USING_PWM2		0

/* 这里用来控制是否使用定时器3的通道0测量频率,默认使用的IO口为PB6 */
#define USING_INPUT_CAPTURE1	1


/***********************************PWM1信号输出**********************************************/
#if USING_PWM1
//PWM1信号的相关配置:可选PA0、PA5、PA15选择复用通道1,使用定时器1通道0
/* PWM1输出引脚的时钟*/
#define BSP_PWM1_RCU			RCU_GPIOA
/* PWM1输出引脚的端口*/
#define BSP_PWM1_PORT			GPIOA
/* PWM1输出引脚 */
#define BSP_PWM1_PIN			GPIO_PIN_5
/* GPIO的复用通道 */
#define BSP_PWM1_AF				GPIO_AF_1
//需要修改PWM1输出的引脚,只需要修改时钟,端口和引脚即可,这里默认PA5。

/* TIMER1_CH0配置输出PWM1信号 */
#define BSP_PWM1_TIMER_RCU	RCU_TIMER1
#define BSP_PWM1_TIMER		TIMER1
#define BSP_PWM1_CHANNEL  	TIMER_CH_0    

/* 配置定时器的预分配系数 */
#define BSP_TIMER1_PRESCALER	(240 - 1)	

/* 配置定时器的自动重装载值 */
#define BSP_TIMER1_PERIOD	(10000 - 1)
/* PWM1信号的频率 = 240M /( (pre + 1) * ( per + 1 ) ),这里就是100Hz */

/* 配置定时器的默认脉冲值,占空比 = 脉冲值 / 自动重装载值,
后续修改占空比也是通过修改这个实现的,注意脉冲值不能大于自动重装载值 */
#define BSP_TIMER1_PULSE	(5000-1)
#endif	/* USING_PWM1 */

/***********************************PWM2信号输出**********************************************/
#if USING_PWM2

//PWM2信号的相关配置:可选PA6、PB4、PC6选择复用通道2,使用定时器2通道0

/* PWM2输出引脚的时钟*/
#define BSP_PWM2_RCU			RCU_GPIOA
/* PWM2输出引脚的端口*/
#define BSP_PWM2_PORT			GPIOA
/* PWM2输出引脚*/
#define BSP_PWM2_PIN			GPIO_PIN_6
/* GPIO的复用通道 */
#define BSP_PWM2_AF				GPIO_AF_2
//需要修改PWM2输出的引脚,只需要修改时钟,端口和引脚即可,这里默认PA6。

/* TIMER2_CH0配置输出PWM2信号 */
#define BSP_PWM2_TIMER_RCU	RCU_TIMER2
#define BSP_PWM2_TIMER		TIMER2
#define BSP_PWM2_CHANNEL  	TIMER_CH_0    

/* 配置定时器的预分配系数 */
#define BSP_TIMER2_PRESCALER	(240 - 1)	

/* 配置定时器的自动重装载值 */
#define BSP_TIMER2_PERIOD	(1000 - 1)
/* PWM1信号的频率 = 240M /( (pre + 1) * ( per + 1 ) ),这里就是1000Hz */

/* 配置定时器的默认脉冲值,占空比 = 脉冲值 / 自动重装载值,
后续修改占空比也是通过修改这个实现的,注意脉冲值不能大于自动重装载值 */
#define BSP_TIMER2_PULSE	(500-1)

#endif	/* USING_PWM2 */

/***********************************输入捕获1**********************************************/
#if USING_INPUT_CAPTURE1

//输入捕获的相关配置:可选PB6和PD12选择复用通道2,使用定时器3通道0
/* 输入捕获引脚的时钟 */
#define		BSP_INPUT_CAPTURE1_RCU		RCU_GPIOB
/* 输入捕获引脚的端口 */
#define		BSP_INPUT_CAPTURE1_PORT		GPIOB
/* 输入捕获引脚的引脚 */
#define		BSP_INPUT_CAPTURE1_PIN		GPIO_PIN_6
/* 输入捕获引脚的复用通道 */
#define		BSP_INPUT_CAPTURE1_AF		GPIO_AF_2
//需要修改输入捕获的引脚引脚,只需要修改时钟,端口和引脚即可,这里默认PB6。

/* TIMER3_CH0配置输入捕获功能 */
#define BSP_INPUT_CAPTURE1_TIMER_RCU	RCU_TIMER3
#define BSP_INPUT_CAPTURE1_TIMER		TIMER3
#define BSP_INPUT_CAPTURE1_CHANNEL  	TIMER_CH_0    

/* 配置定时器的预分配系数 */
#define BSP_TIMER3_PRESCALER	(240 - 1)	//这里分频完是1MHz,1us定时器加1	

/* 配置定时器的自动重装载值,定时器每溢出1次,表示时间过了65.536ms */
#define BSP_TIMER3_PERIOD	(65536 - 1)		

//输入捕获的相关的中断配置

/* 中断标志位:通道0中断 */
#define	BSP_INPUT_CAPTURE1_TIMER_INT_FLAG		TIMER_INT_CH0

/* 中断源:定时器3中断 */
#define BSP_INPUT_CAPTURE1_TIMER_INT_IRQn		TIMER3_IRQn

/* 中断服务函数 */
#define BSP_INPUT_CAPTURE1_TIMER_INT_IRQHandler	TIMER3_IRQHandler

#endif	/* USING_INPUT_CAPTURE1 */


/***********************************PWM信号输出**********************************************/

/* 初始化PWM信号的产生,可能会用到定时器1和2 */
void pwm_gpio_config(void);

/* 改变PWM信号的占空比,可外部调用 */
void pwm_duty_change(void); 

/* 初始化PWM1信号的产生,使用定时器1的通道0,可选择输出到PA0、PA5、PA15 */
static void pwm1_gpio_config(void);

/* 初始化PWM1信号的产生,使用定时器2的通道0,可选择输出到PA6、PB4、PC6 */
static void pwm2_gpio_config(void);

/***********************************输入捕获**********************************************/
/* 初始化输入捕获测量频率功能,使用定时器3的通道0,可选择配置到PB6和PD12*/
void input_capture_gpio_config(void);

/* 获取频率接口函数 */
float getFre1(void);

#endif /* _BSP_GENERAL_TIMER_H */
 
 

【注】:这个代码时我在梁山派(GD32F470ZGT6)上写的,还没有对其他芯片做兼容,但是大部分应该都没问题。

3.2.5 输入捕获的使用示例

下面是一个简单的示例,基于3.2.4中的bsp_base_timer文件:

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "string.h"

#include "bsp_led.h"
#include "bsp_key.h"
#include "bsp_uart.h"
#include "bsp_base_timer.h"
#include "bsp_general_timer.h"
/*!
    \brief    main function
    \param[in]  none
    \param[out] none
    \retval     none
*/
int main(void)
{
	/* 配置中断优先级分组,4位抢占优先级,0位子优先级 */
	nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
	
	/* 配置sysTick的时钟和中断周期*/
    systick_config();
	
	/* 串口初始化 */
	uart_gpio_config(115200);

	/* 通用定时器产生PWM信号初始化,具体内容查询bsp_general_timer.h文件 */
	pwm_gpio_config();
	
	/* 通用定时器输入捕获功能初始化,具体内容查询bsp_general_timer.h文件 */
	input_capture_gpio_config();
    while(1) 
	{	
		delay_1ms(500);
		printf("Fre1 = %.2f Hz\r\n",getFre1());
    }
}

【注】:需要用杜邦线把PB6和PA5连接在一起:

运行结果如下:
在这里插入图片描述

4. 高级定时器

关于高级定时器,我这里先不做介绍了,不然篇幅太长了,后面有时间再补一下,这里先挖个坑。

以上就是本期的所有内容,创造不易,点个关注再走呗。

在这里插入图片描述

标签:定时器,BSP,TIMER,详解,timer,PWM,PWM1,define
From: https://blog.csdn.net/weixin_67907028/article/details/143455131

相关文章

  • Python自动化操作Word文档详解
        在日常办公和数据处理中,我们经常需要处理Word文档。手动操作Word文档可能会非常繁琐和耗时,而使用Python可以实现自动化操作,提高工作效率。本文将详细介绍如何使用Python自动化操作Word文档,包括读取、写入、修改和格式化等操作。一、安装所需的库    要使用......
  • Nuxt.js 应用中的 nitro:init 事件钩子详解
    title:Nuxt.js应用中的nitro:init事件钩子详解date:2024/11/3updated:2024/11/3author:cmdragonexcerpt:nitro:init是Nuxt3中的一个生命周期钩子,在Nitro初始化完成后被调用。这个钩子允许开发者注册Nitro钩子,并直接与Nitro进行交互。这种灵活性使得开发者......
  • 现代谱分析方法——ARMA过程详解
    现代谱分析方法——ARMA(AutoregressiveMovingAverageprocess)过程详解目录简介ARMA过程的基本概念ARMA的定义AR与MA的区别ARMA过程的数学模型自回归模型(AR模型)移动平均模型(MA模型)ARMA模型的参数估计最小二乘法最大似然估计ARMA模型的性质平稳性白噪声ARMA模型的......
  • MT1411-MT1420 码题集 (c 语言详解)
    目录        MT1411·顺时针旋转数组        MT1412·合并        MT1413·并集        MT1414·数组的交集        MT1415·大小相同        MT1416·最长子数组        MT1417·连续子序列        ......
  • 通过Jmeter压测存储过程详解
    通过Jmeter压测存储过程详解在软件开发和数据库管理中,性能测试是确保系统稳定性和可靠性的重要环节。对于存储过程(StoredProcedure)这种数据库中的关键组件,进行压力测试(PressureTesting)尤为关键。ApacheJMeter作为一款开源的性能测试工具,因其强大的功能和易用性,被广泛用......
  • python-14-函数详解(定义、参数、返回值、高级函数、偏函数、装饰器)
    python函数详解(定义、参数、返回值、高级函数、偏函数、装饰器)一.说明这是python中的基础系列中的关于函数部分,来开始我们今天日拱一卒!对python函数部分进行详细整理和学习。二.定义在Python中,函数是通过def关键字来定义函数;函数定义的结构如下deffunction_name......
  • 二进制补码及与原码的互相转换方法详解
    在数字计算机系统中,数据的表示和处理是至关重要的一环。二进制作为计算机内部的基本编码方式,其表示形式直接决定了计算机处理数据的效率和准确性。在二进制表示中,原码和补码是两种重要的编码方式,尤其在处理有符号整数时显得尤为重要。本文将深入探讨二进制补码的概念、作用以及其......
  • Air780E如何发送SMS?一文详解!
    ​今天一起来学习使用合宙低功耗4G模组Air780E发送SMS短消息:一、SMS简介SMS(短消息服务,ShortMessageService)功能主要用于在蜂窝网络中传输短消息。在4G网络中,短信可以在数据传输的同时进行,不会因数据业务占用网络资源而被延迟或阻塞。在Air780E模块中,当收到新短信后,MAIN_RI变......
  • 洛谷(DFS)-P2089 烤鸡详解
    提前说一下,做完才发现要先输出方案数而后才打印方案数,所以代码不能直接搬动提交,你可以建立个字符串存储一下.先上代码,看不懂的再看下面的详解:#include<iostream>#include<bits/stdc++.h>usingnamespacestd;constintN=20;intn;intac[N];//方案存储intres......
  • 《漫威复仇者联盟》游戏辅助工具修改器风灵月影版操作教程详解
    《漫威复仇者联盟》是一款基于漫威超级英雄的第三人称动作冒险游戏,玩家可以操控各种超级英雄完成任务和挑战。风灵月影版的修改器为这款游戏提供了多种便捷功能,帮助玩家更轻松地探索游戏世界,提升游戏体验。以下是《漫威复仇者联盟》游戏辅助工具修改器风灵月影版的操作教程详解......