梁山派入门指南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_PWM1timer_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_PWM1pwm1_gpio_config();		//初始化gpio和定时器1#endif	/* USING_PWM1 *//* 如果开启了PWM2信号功能,就初始化PWM2信号的相关内容,包括gpio和定时器配置 */#if USING_PWM2pwm2_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_PWM1timer_channel_output_pulse_value_config(BSP_PWM1_TIMER,BSP_PWM1_CHANNEL,value*5); // 配置定时器通道输出脉冲值#endif /* USING_PWM1 *//* 开启了PWM2信号的产生 */#if USING_PWM2timer_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_PWM1pwm1_gpio_config();		//初始化gpio和定时器1#endif	/* USING_PWM1 *//* 如果开启了PWM2信号功能,就初始化PWM2信号的相关内容,包括gpio和定时器配置 */#if USING_PWM2pwm2_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_PWM1timer_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_PWM2timer_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.rhkb.cn/news/2901.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

excel仅复制可见单元格,仅复制筛选后内容

背景 我们经常需要将内容分给不同的人&#xff0c;做完后需要合并 遇到情况如下 那是因为直接选择了整列&#xff0c;当然不可以了。 下面提供几种方法&#xff0c;应该都可以 直接选中要复制区域然后复制&#xff0c;不要选中最上面的列alt;选中可见单元格正常复制&#xff…

JVM类加载器(附面试题)

什么是类加载器 类加载器&#xff08;ClassLoader&#xff09; 是 Java 虚拟机&#xff08;JVM&#xff09;中的一个组件&#xff0c;用于在运行时将字节码文件加载到内存中&#xff0c;并将其转换为 JVM 可以执行的二进制数据结构。 字节码文件通常是以.class为扩展名的文件…

FFmpeg硬件解码

使用FFmpeg进行硬件解码时&#xff0c;通常需要结合FFmpeg的API和硬件加速API&#xff08;如CUDA、VAAPI、DXVA2等&#xff09;。以下是一个简单的C代码示例&#xff0c;展示如何使用FFmpeg进行硬件解码。这个示例使用了CUDA作为硬件加速的后端。 1. 安装FFmpeg和CUDA 确保你…

Linux 进程前篇(冯诺依曼体系结构和操作系统)

目录 一.冯诺依曼体系结构 1.概念 2.硬件层面的数据流 3.总结加补充 二.操作系统 (Operating System) 1.概念 2.设计OS的目的 3.定位 4.操作系统的管理 5.计算机体系的层状结构 在我们认识进程之前&#xff0c;我们先了解什么是冯诺依曼体系结构 一.冯诺依曼体系结构…

Redis 3.2.1在Win10系统上的安装教程

诸神缄默不语-个人CSDN博文目录 这个文件可以跟我要&#xff0c;也可以从官网下载&#xff1a;https://github.com/MicrosoftArchive/redis/releases 这个是微软以前维护的Windows版Redis安装包&#xff0c;如果想要比较新的版本可以从别人维护的项目里下&#xff08;https://…

Unity-Mirror网络框架-从入门到精通之RigidbodyPhysics示例

文章目录 前言示例一、球体的基础配置二、三个球体的设置差异三、示例意图LatencySimulation前言 在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。本系列文章将为读者提供对Mirror网络框架的深入了解,涵盖从基础到高级的多个主题。Mirror是一个用于Unity的开…

JAVA实现五子棋小游戏(附源码)

文章目录 一、设计来源捡金币闯关小游戏讲解1.1 主界面1.2 黑棋胜利界面1.3 白棋胜利界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载更多优质源码分享 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/145161039 JA…

深度学习图像算法中的网络架构:Backbone、Neck 和 Head 详解

深度学习已经成为图像识别领域的核心技术&#xff0c;特别是在目标检测、图像分割等任务中&#xff0c;深度神经网络的应用取得了显著进展。在这些任务的网络架构中&#xff0c;通常可以分为三个主要部分&#xff1a;Backbone、Neck 和 Head。这些部分在整个网络中扮演着至关重…

Jmeter如何进行多服务器远程测试

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 JMeter是Apache软件基金会的开源项目&#xff0c;主要来做功能和性能测试&#xff0c;用Java编写。 我们一般都会用JMeter在本地进行测试&#xff0c;但是受到单…

摄像头模块在狩猎相机中的应用

摄像头模块是狩猎相机的核心组件&#xff0c;在狩猎相机中发挥着关键作用&#xff0c;以下是其主要应用&#xff1a; 图像与视频拍摄 高清成像&#xff1a;高像素的摄像头模块可确保狩猎相机拍摄出清晰的图像和视频&#xff0c;能够捕捉到动物的毛发纹理、行为细节及周围环境的…

[操作系统] 深入理解操作系统的概念及定位

概念 任何计算机系统都包含⼀个基本的程序集合&#xff0c;称为操作系统(OS)。 其核心功能如图片所示&#xff0c;包括&#xff1a; 内核 (Kernel)&#xff1a; 内核是操作系统的核心部分&#xff0c;被认为是狭义上的操作系统&#xff0c;直接与硬件打交道。负责进程管理、内…

Jmeter 简单使用、生成测试报告(一)

一、下载Jmter 去官网下载&#xff0c;我下载的是apache-jmeter-5.6.3.zip&#xff0c;解压后就能用。 二、安装java环境 JMeter是基于Java开发的&#xff0c;运行JMeter需要Java环境。 1.下载JDK、安装Jdk 2.配置java环境变量 3.验证安装是否成功&#xff08;java -versio…

postgresql分区表相关问题处理

1.使用pg_cron按日创建分区表&#xff0c;会出现所在数据库对应用户权限不足的问题。 原因是pg_cron运行在postgres数据库中&#xff0c;是用superuser进行执行的&#xff0c;对应的分区表的owner为postgres&#xff0c;所以需要单独授权对表的所有操作权限。不知道直接改变ow…

Agent一键安装,快速上手Zabbix监控!

目录 一、Linux操作系统部署Agent环境配置1、防火墙配置2、永久关闭selinux yum方式安装1、配置zabbix仓库2、安装agent3、配置 Zabbix-Agent 指向 Zabbix-Server4、启动agent服务 二进制包安装1、下载二进制包2、创建用户和目录及更改属主&#xff08;组&#xff09;3、解压二…

内存与缓存:保姆级图文详解

文章目录 前言1、计算机存储设备1.1、硬盘、内存、缓存1.2、金字塔结构1.3、数据流通过程 2、数据结构内存效率3、数据结构缓存效率 前言 亲爱的家人们&#xff0c;创作很不容易&#xff0c;若对您有帮助的话&#xff0c;请点赞收藏加关注哦&#xff0c;您的关注是我持续创作的…

【大数据】机器学习-----模型的评估方法

一、评估方法 留出法&#xff08;Holdout Method&#xff09;&#xff1a; 将数据集划分为训练集和测试集两部分&#xff0c;通常按照一定比例&#xff08;如 70% 训练集&#xff0c;30% 测试集&#xff09;。训练集用于训练模型&#xff0c;测试集用于评估模型性能。优点&…

人工智能-机器学习之多分类分析(项目实战二-鸢尾花的多分类分析)

Softmax回归听名字&#xff0c;依然好像是做回归任务的算法&#xff0c;但其实它是去做多分类任务的算法。 篮球比赛胜负是二分类&#xff0c;足球比赛胜平负就是多分类 识别手写数字0和1是二分类&#xff0c;识别手写数字0-9就是多分类 Softmax回归算法是一种用于多分类问题…

[系统安全] 六十一.恶意软件分析 (12)LLM赋能Lark工具提取XLM代码的抽象语法树(初探)

您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列。因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全、逆向分析和恶意代码检测,“系统安全”系列文章会更加聚焦,更加系…

一文掌握Docker

目录 1.快速入门 1.1.部署MySQL 1.2.命令解读 2.Docker基础 2.1.常见命令 2.1.1.命令介绍 2.1.2.演示 2.1.3.命令别名 2.2.数据卷 2.2.1.什么是数据卷 2.2.2.数据卷命令 2.2.3.挂载本地目录或文件 2.3.镜像 2.3.1.镜像结构 2.3.2.Dockerfile构建镜像 2.3.3.构建…

【网络编程】基础知识

目录 网络发展史 局域网和广域网 局域网&#xff08;LAN&#xff09; 广域网&#xff08;Wan&#xff09; 光猫 路由器 网线 设备通信的要素 IP地址 基本概念 地址划分 特殊地址&#xff08;后续编程使用&#xff09; IP地址转换 字节序 网络模型 网络的体系结…