当前位置: 首页 > article >正文

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

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

  • 1. 定时器概览
  • 2.基本定时器
    • 2.1 基本定时器介绍
    • 2.2 梁山派上的基本定时器开发
      • 2.2.1. 了解梁山派上的基本定时器资源(实际上我们以及在上面了解过了)
      • 2.2.2. 配置定时器
      • 2.2.3. 编写定时器中断服务程序
      • 2.2.4. 举一反三,兼容定时器5和6
    • 2.3 bsp_base_timer文件(兼容定时器5和定时器6)
    • 2.4 基本定时器中断使用示例
  • 3. 通用定时器
    • 3.1 通用定时器介绍
    • 3.2 产生PWM信号(通过定时器1和定时2)
      • 3.2.1. 了解梁山派上的定时器1和定时器2
      • 3.2.2. 配置定时器
      • 3.2.3 修改占空比
      • 3.2.4 举一反三,兼容定时器1和2
      • 3.2.5 bsp_general_timer文件(兼容定时器1和2,两组PWM信号产生)
      • 3.2.6 两路PWM信号产生使用示例
    • 3.3 输入捕获测量频率
      • 3.2.2. 配置定时器
      • 3.2.3 中断服务函数
      • 3.2.4 bsp_base_timer文件(1,2,3,带有输入捕获和两路PWM输出)
      • 3.2.5 输入捕获的使用示例
  • 4. 高级定时器

1. 定时器概览

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

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

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

  • 夜深人静学32系列14——基本定时器
  • 夜深人静学32系列15——通用定时器
  • STM32通用定时器产生PWM信号
  • STM32定时器输入捕获测量高电平时间

下面直接介绍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. 高级定时器

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

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

在这里插入图片描述


http://www.kler.cn/a/377716.html

相关文章:

  • Spring的常用注解之@Component——day1
  • 精准选型跨境客服,网页服务更高效
  • 「Mac畅玩鸿蒙与硬件18」鸿蒙UI组件篇8 - 高级动画效果与缓动控制
  • 【设计模式系列】总览
  • LSTM:解决梯度消失与长期依赖问题
  • Redis高级篇之缓存一致性详细教程
  • Java面试经典 150 题.P27. 移除元素(002)
  • 服务器上清理Docker容器运行日志的正确方法
  • tauri开发中如果取消了默认的菜单项,复制黏贴撤销等功能也就没有了,解决办法
  • 系统性能测试笔记-JMeter性能测试
  • 力扣(leetcode)题目总结——字符串篇
  • Zookeeper 和 Eureka 做注册中心有什么区别?
  • 基于JavaWeb+MySQL实现口算题卡
  • 服务器的免密登录和文件传输
  • 区块链-C++挖矿软件XMRIG源码分析
  • centos7.X zabbix监控参数以及邮件报警和钉钉报警
  • TCP Analysis Flags 之 TCP Keep-Alive
  • 春秋云境CVE-2022-21661,sqlmap+json一把梭哈
  • 微信小程序scroll-view吸顶css样式化表格的表头及iOS上下滑动表头的颜色覆盖、z-index应用及性能分析
  • Unknown at rule @tailwindscss(unknownAtRules)
  • 使用 web (vue 和DRF))实现 模拟一个IDE 功能思路
  • NIO 核心知识总结
  • RGA DEMO 上部
  • [BJDCTF 2020]babystack-好久不见9
  • Web API中的requestAnimationFrame
  • 基于深度学习的自主飞行器导航