GD32 PWM输入捕获

news2024/9/21 10:56:41

前言


通过本次的学习,了解定时器的分类和配置,高级定时器,通用定时器等不同等级的定时器拥有的功能,学习定时器的输入捕获与输出比较功能(PWM)脉冲宽度调制,来控制电机等外设。


 定义

定时器的分类

在GD32这款单片机中不同的单片机拥有的功能数不同的,其中高级定时器的功能最多,通用定时其次,基本定时的功能最少。


高级定时器简介

高级定时器(TIMER0 和 TIMER7)是四通道定时器,支持输入捕获和输出比较。可以产生 PWM信号控制电机和电源管理。高级定时器含有一个 16 位无符号计数器。

高级定时器是可编程的,可以被用来计数,其外部事件可以驱动其他定时器。高级定时器包含了一个死区时间插入模块,非常适合电机控制。

定时器和定时器之间是相互独立,但是它们的计数器可以被同步在一起形成一个更大的定时器。

高级定时器的主要特征

 总通道数: 4;

 计数器宽度: 16位;

 时钟源可选:内部时钟,内部触发,外部输入,外部触发;

 多种计数模式:向上计数,向下计数和中央计数;

 正交编码器接口:被用来追踪运动和分辨旋转方向和位置;

 霍尔传感器接口:用来做三相电机控制;

 可编程的预分频器: 16位,运行时可以被改变;

 每个通道可配置:输入捕获模式,输出比较模式,可编程的PWM模式,单脉冲模式;

 可编程的死区时间;

 自动重装载功能;

 可编程的计数器重复功能;

 中止输入功能;

 中断输出和DMA请求:更新事件,触发事件,比较/捕获事件和中止事件;

 多个定时器的菊链使得一个定时器可以同时启动多个定时器;

 定时器的同步允许被选择的定时器在同一个时钟周期开始计数;

 定时器主/从模式控制器。


预分频的概念

预分频器可以将定时器的时钟(TIMER_CK)频率按 1 到 65536 之间的任意值分频,分频后的时钟 PSC_CLK 驱动计数器计数。分频系数受预分频寄存器 TIMERx_PSC 控制,这个控制寄存器带有缓冲器,它能够在运行时被改变。新的预分频器的参数在下一次更新事件到来时被采用。(简单来说就是GD32这款MCU的时钟频率默认是120MHz,经过一分频之后的值是60MHz,经过2分频后的时钟频率是30MHz),预分频器可以和重装计数器CAR和计数值CNT之间进行配合控制系统的运行周期和时间。


计数方式

高级定时器提供三种计数方式,第一中方式的向上计数,第二种方式是向下计数,第三种方式是中央对齐的方式进行计数,以下是本人对于三种计数方式的理解。

1:向上计数方式

CAR 表示的是重装计数器的值,当计数值重装计数器的值时系统产生一个中断,此时CNT计数值加 1同时计数重新设置为0开始下一轮的计数

2: 向下计数的方式

向下计数的方式和向上计数的方式原理上是一样的,一个的数值是向上加,一个的数值是向下减,当计数的值减到0的时候产生中断计数一次。

3:中央对齐的方式计数

计数的值首相向上开始计数,当计数到规定的数值的时候产生一个中断注意:在嵌入式系统中中断是一定存在的,无论是你想还是不想中断一定存在),达到中断触发条件中断被触发此时开始向另外的一个方向开始新的一轮计数。


定时器使用场景

定时器的使用场景主要有三种,第一种是基本的定时功能,当定时计数器达到某个设定的值之后去执行认为规定的系统相关操作,输出比较:主要是对PWM脉冲宽度调制的比较,输入捕获,主要是对PWM的波形进行捕获之后对一些外设或者时钟的周期进行相关的操作。

定时器的基本原理

定时器的本质:

定时器被本质上是一个电子计数器,当输入端输入三个周期的脉冲信号之后,CNT的计数值 + 3

假设一个脉冲的信号周期的时间是1s,那么三个时钟周期的时间就过去了三秒。

系统主频和定时时间的关系:

在GD32这款单片机系统的主频是120MHz的时钟主频那么一个周期的计数时间就是T = 1 / 120us,那么如果计数120次,对应过去的时间t就是1us。

定时器计时的上限:

CAR寄存器是用于设置CNT计时上限的,比如将Car设置成12000,CNT从0开始向上计数,当CNT=12000时、CNT将被系统清零.同时,系统可以产生一个定时中断。

这里将CNT的值设置为12000,系统时钟的主频是120MHz,这个时候一个计时的周期就是T = 1/ 120 ,然后定时的时间t = CNT * (1/120)us = 12000 * 1 / 120 = 100us

预分频器:

PSC是预分频器,可以灵活调整进入计数器的时钟频率,比如PSC设置为120后,那么实际进入CNT的信号周期也就变成了1us,然后再把CAR设置成100,此时定时中断的周期依然是100us,所以PSC可以从硬件层面调节定时周期的长短,非常灵活:

高级定时器的硬件结构:

这里定时器配置主要使用到的是CNT计数器,CAR重装载寄存器,预分频器PSC


定时器驱动配置

这里设置的是一个1ms中断一次的定时器延时函数,基于1ms中断一次,我们这里默认系统时钟的主频是120MHz的时钟主频,然后预分频器的值设置为120这个时候t定时一个的时间就是1us,那么我们设置重装载寄存器的值为1000,也就是计数值达到1000的时候溢出一次,产生一次定时器中断,那么1000 * 1us就是1ms,也就是我们这个定时器定时到1ms的时候会产生一次定时中断。

注:在定时器配置的同时,定时器预分频的值和CAR重装载寄存器的值要减去1,原因数和C语言的数组下标类似,数组的下表是从0开始的,我们配置定时器CAR的值如果我们希望是1000那么如果直接写1000的话实际的值是1001,所以需要将我们的数值减去1这样CAR的值才是1000。


代码编写

这里使用通用定时器4为例进行定时器驱动的编写,创建定时器初始化函数,这里的static静态函数表示这个函数的作用域只在本C语言文件中起作用,无法通过extern等关键字的声明被外部的C语言文件调用,函数的生命周期就是本C语言文件的生命周期。

定时器接口函数:

// 初始化定时器接口函数
void TIMER_Init(void)
{
	// 参数表示定时的周期单位是us
	TimerInit(1000);
}

 定时器初始化函数:

static void TimerInit(uint32_t periodUs)
{
	// 使能定时器时钟
	rcu_periph_clock_enable(RCU_TIMER4);
	// 复位定时器
	timer_deinit(TIMER4);
	// 初始化定时器结构体
	timer_parameter_struct timerInitPara;
	// 结构体变量初始化函数,定时器没有初始化赋值之前值是不确定的,通过这个初始化赋值
	timer_struct_para_init(&timerInitPara);
	// 设置重装载寄存器CAR的值
	timerInitPara.prescaler = 120 - 1;
	// 预分频器的值
	timerInitPara.period = periodUs - 1;
    // 使能定时器更新中断
	timer_interrupt_enable(TIMER4,TIMER_INT_UP);
	// 使能定时器中断和优先级,设置优先级为最高
	nvic_irq_enable(TIMER4_IRQn,0,0);
	// 使能定时器
	timer_enable(TIMER4);
}

定时器中断函数:

当定时的时间达到指定的时间时触发定时器中断,这个中断是自动触发的无需外部的调用,定时器中断函数的函数名在hal库gd32的启动文件中。

void TIMER4_IRQHandler(void)
{
		// 获取中断标志位
		if(timer_interrupt_flag_get(TIMER4,TIMER_INT_FLAG_UP) == SET)
		{
				// 清除中断标志位
				timer_interrupt_flag_clear(TIMER4,TIMER_INT_FLAG_UP);
				// LED1对应的gpio口反转
				ToggleLed(LED1);
		}
}

注:在中断服务函数中需要获取中断服务函数的中断标志位,如果中断函数中断标志位等于 “1”表示进入中断,这里RESET == 0, SET == 1,两者之间是一种等价的关系,因此 0 可以使用RESET进行代替,SET可以使用 1 来代替,注意获取中断标志位之后要手动清除中断标志位,如果:程序员不手动清除中断标志位的话会导致程序频繁的进入中断,系统会在这个位置卡死,然后执行LED灯的翻转工作,这里实际上是使用示波器查看LED灯对应引脚的PWM(脉冲宽度调制)波形是否是我们设定的中断时间。


完整代码

#include <stdint.h>
#include "gd32f30x.h"
#include "led_drv.h"


#if 0
// 初始化定时器
static void TimerInit(uint32_t periodUs)
{
	// 使能定时器时钟
	rcu_periph_clock_enable(RCU_TIMER0);
	// 复位定时器
	timer_deinit(TIMER0);
	// 初始化定时器结构体
	timer_parameter_struct timerInitPara;
	// 结构体变量初始化函数,定时器没有初始化赋值之前值是不确定的,通过这个初始化赋值
	timer_struct_para_init(&timerInitPara);
	// 设置重装载数值
	timerInitPara.prescaler = 120 - 1;
	timerInitPara.period = periodUs - 1;
    // 使能定时器更新中断
	timer_interrupt_enable(TIMER0,TIMER_INT_UP);
	// 使能定时器中断和优先级,设置优先级为最高
	nvic_irq_enable(TIMER0_UP_IRQn,0,0);
	// 使能定时器
	timer_enable(TIMER0);
}

void TIMER0_UP_IRQHandler(void)
{
		// 获取中断标志位
		if(timer_interrupt_flag_get(TIMER0,TIMER_INT_FLAG_UP) == SET)
		{
				// 清除中断标志位
				timer_interrupt_flag_clear(TIMER0,TIMER_INT_FLAG_UP);
				// LED1对应的gpio口反转
				ToggleLed(LED1);
		}
}
#else
static void TimerInit(uint32_t periodUs)
{
	// 使能定时器时钟
	rcu_periph_clock_enable(RCU_TIMER4);
	// 复位定时器
	timer_deinit(TIMER4);
	// 初始化定时器结构体
	timer_parameter_struct timerInitPara;
	// 结构体变量初始化函数,定时器没有初始化赋值之前值是不确定的,通过这个初始化赋值
	timer_struct_para_init(&timerInitPara);
	// 设置重装载寄存器CAR的值
	timerInitPara.prescaler = 120 - 1;
	// 预分频器的值
	timerInitPara.period = periodUs - 1;
    // 使能定时器更新中断
	timer_interrupt_enable(TIMER4,TIMER_INT_UP);
	// 使能定时器中断和优先级,设置优先级为最高
	nvic_irq_enable(TIMER4_IRQn,0,0);
	// 使能定时器
	timer_enable(TIMER4);
}

void TIMER4_IRQHandler(void)
{
		// 获取中断标志位
		if(timer_interrupt_flag_get(TIMER4,TIMER_INT_FLAG_UP) == SET)
		{
				// 清除中断标志位
				timer_interrupt_flag_clear(TIMER4,TIMER_INT_FLAG_UP);
				// LED1对应的gpio口反转
				ToggleLed(LED1);
		}
}

#endif

// 初始化定时器接口函数
void TIMER_Init(void)
{
	// 参数表示定时的周期单位是us
	TimerInit(1000);
}

头文件:

#ifndef _TIMING_DRV_H_
#define _TIMING_DRV_H_
#include <stdint.h>

void TIMER_Init(void);

#endif

 注:以上的程序是关于定时器配置的,为后续的PWM知识点做铺垫


PWM 定义

Def: PWM (脉冲宽度调制)通过调节PWM脉冲的宽度调节外设做工的功率,简单来说就是占空比,W脉宽(也就是在一个技术周期内高电平所占的比例)W所占的比例越大做工的功率就越大,占空比,也就是高电平的时间占用,W越大D越大,那么D越大输出的功率就越大。

不同等级定时器输入捕获和输出比较资源的概述


PWM波形的生成

相较于计时功能,除了CAR自动重装载寄存器,输出比较模式还要用到CHxCV寄存器。它是输入捕获和输出比较寄存器,是一个可以复用的寄存器。


CV 通常代表“Capture/Compare Value”,即捕获/比较值寄存器。这个寄存器用于存储一个预设的数值,该数值可以用来与定时器的计数器进行比较(比较模式),或者用于存储从定时器输入捕获到的时间点(捕获模式)。综上所述,TIMERx_CHxCV 可以理解为“定时器 x 的通道 x 捕获/比较值寄存器”。


注:输入捕获和输出比较寄存器主要是用于配置PWM中W也就是脉宽的值,或者是脉宽所占的比例而CAR寄存器则是用来配置 T 一个脉冲的时间周期。

PWM 波形的生成

计数值从0开始向上升的过程在达到输出捕获和输出比较寄存器的限定值之前一直处于高电平的状态,当超出输入捕获输出比较的限定值时转换为低电平的状态,一个高低电平的起伏变化时间就是一个周期。

因为:预分频器的值是120

T = (1/120)us 注意这里的120指的是时钟的主频,GD32的时钟主频是
120 默认值为120,具体可以参考GD32的时钟树结构

然后PSC = 120 这里的时钟主频是120 所以

一份周期T 就是 120 * 1/120 = 1us是T1经过预分频后得到的值

t = 500 * 1us = 500us

那么输出捕获和输出比较寄存器的值就是250
注:实际配置的时候需要将各个的值减去1,“因为:计时值从0开始的”

高级定时器的硬件结构

定时器配置


代码实现

以下代码的作用主要是生成一段PWM(脉冲宽度调制波形),

定时器初始化

void PWM_Init(void)
{
	// gpio 初始化
	rcu_periph_clock_enable(RCU_GPIOA);
	// 初始化gpio
	gpio_init
	(
		GPIOA,
		GPIO_MODE_AF_PP,
		GPIO_OSPEED_10MHZ,
		GPIO_PIN_8
	);
	// 使能定时器时钟
	rcu_periph_clock_enable(RCU_TIMER0);
	// 复位定时器
	timer_deinit(TIMER0);
	// 配置定时器
	timer_parameter_struct timerInitStruct;
	// 给定时器赋值默认的初始值
	timer_struct_para_init(&timerInitStruct);
	// 预分频器的值
	timerInitStruct.prescaler = 120 - 1;
	// 设置重装计数器的值
	timerInitStruct.period = 500 - 1;
    // 初始化定时
	timer_init(TIMER0,&timerInitStruct);
	
	// 配置定时器通道
	timer_oc_parameter_struct ocInitPara;
	// 结构体调用初始化接口函数
	timer_channel_output_struct_para_init(&ocInitPara);
	// 设置通道为输出的功能
	ocInitPara.outputstate = TIMER_CCX_ENABLE;
	// 设置通道的输出极性
	ocInitPara.ocpolarity = TIMER_OC_POLARITY_HIGH;
	timer_channel_output_config(TIMER0, TIMER_CH_0, &ocInitPara);
	
	/* 设置占空比设置chcv寄存器的数值;*/
	timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, 250 - 1); 
	/* 设置通道输出PWM模式;*/
	timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_PWM0);
	// 使能互补通道保护寄存器
	timer_primary_output_config(TIMER0, ENABLE);
	/* 使能定时器;*/ 
	timer_enable(TIMER0);	
}

以下是对以上库函数做出的解释:

rcu 时钟初始化,这里主要是对MCU的CPIOA这组引脚进行时钟的初始化

	rcu_periph_clock_enable(RCU_GPIOA);

初始化gpio,参数包含GPIO的引脚这里的引脚是GPIOA这个引脚,使用的模式是复用推挽输出

这里的推挽指的是高低电平都是有效的,力量大,由片上外设提供,具体参考芯片手册,第三个参数是时钟的频率有50MHz的这里选用10MHz即可,最后一个参数是GPIO的引脚也可以是原理图对应的外设引脚,通过控制这个外设来查看PWM输出结果。

	// 初始化gpio
	gpio_init
	(
		GPIOA,
		GPIO_MODE_AF_PP,
		GPIO_OSPEED_10MHZ,
		GPIO_PIN_8
	);

 以下代码的含义是:(初始化定时器)

第一行代码:使能定时器0的时钟

第二行代码:复位定时0的时钟,这里复位的作用是方便后面对定时器的配置,复位的结果是设置为默认的值。

第三行代码:定时器结构体初始化,和STM32的结构体初始化类似

第四行代码: 这段代码的作用是在定时器没有初始化之前给未初始化的定时器赋值默认的值防止出现定时器没有定义的情况

第四行代码:赋值预分频器的值

第五行代码:赋值CAR重装载寄存器的值

最后一行代码表示的含义是定时器初始化,后面的一个参数将结构体初始化后的地址传递进去和STM32的标准库是类似的。

	// 使能定时器时钟
	rcu_periph_clock_enable(RCU_TIMER0);
	// 复位定时器
	timer_deinit(TIMER0);
	// 配置定时器
	timer_parameter_struct timerInitStruct;
	// 给定时器赋值默认的初始值
	timer_struct_para_init(&timerInitStruct);
	// 预分频器的值
	timerInitStruct.prescaler = 120 - 1;
	// 设置重装计数器的值
	timerInitStruct.period = 500 - 1;
    // 初始化定时
	timer_init(TIMER0,&timerInitStruct);

初始化定时器通道

以下代码是定时器通道配置,这里我们使用到的定时器是高级定时器定时器0,高级定时器拥有所有的通用定时器和普通定时器拥有的功能(以上值题外话,在回顾知识点),这段代码从源码出手

我们转到对应函数的定义中进行查看

 对应的参数:

timer_channel_output_config外设通道输出配置
timer_channel_output_struct_para_init将TIMER通道输出参数结构体中所有参数初始化为默认值

 结构体含义:

	// 配置定时器通道
	timer_oc_parameter_struct ocInitPara;
	// 结构体调用初始化接口函数
	timer_channel_output_struct_para_init(&ocInitPara);
	// 设置通道为输出的功能
	ocInitPara.outputstate = TIMER_CCX_ENABLE;
	// 设置通道的输出极性,通道输出的极性是高
	ocInitPara.ocpolarity = TIMER_OC_POLARITY_HIGH;
	timer_channel_output_config(TIMER0, TIMER_CH_0, &ocInitPara);

 以下是上面库函数对应参数的含义

  • ocpara: 输出比较参数结构体,用于配置定时器的输出比较功能。
    • outputstate: 输出状态,可以是:
      • TIMER_CCX_ENABLE: 启用输出比较功能
      • TIMER_CCX_DISABLE: 禁用输出比较功能。
    • outputnstate: 输出互补状态,可以是:
      • TIMER_CCXN_ENABLE: 启用输出比较的互补输出。
      • TIMER_CCXN_DISABLE: 禁用输出比较的互补输出。
    • ocpolarity: 输出极性,可以是:
      • TIMER_OC_POLARITY_HIGH: 非互补输出在有效电平为高时激活。
      • TIMER_OC_POLARITY_LOW: 非互补输出在有效电平为低时激活。
    • ocnpolarity: 输出互补极性,可以是:
      • TIMER_OCN_POLARITY_HIGH: 互补输出在有效电平为高时激活。
      • TIMER_OCN_POLARITY_LOW: 互补输出在有效电平为低时激活。
    • ocidlestate: 在非活动状态下的输出状态,可以是:
      • TIMER_OC_IDLE_STATE_LOW: 在非活动状态下输出为低电平。
      • TIMER_OC_IDLE_STATE_HIGH: 在非活动状态下输出为高电平。
    • ocnidlestate: 在非活动状态下的互补输出状态,可以是:
      • TIMER_OCN_IDLE_STATE_LOW: 在非活动状态下互补输出为低电平。
      • TIMER_OCN_IDLE_STATE_HIGH: 在非活动状态下互补输出为高电平。

这些参数通常用于配置定时器输出比较通道的行为,包括何时启用或禁用输出、输出的极性以及在非活动状态下的默认输出电平。这些配置对于实现PWM(脉冲宽度调制)等功能非常重要。

初始化输入捕获与输出比较寄存器的值

参数说明

  • channel: 选择要配置的定时器输出比较通道。

    • TIMER_CH_0: 定时器通道0 (TIMERx 其中 x=0..4,7..13)
    • TIMER_CH_1: 定时器通道1 (TIMERx 其中 x=0..4,7,8,11)
    • TIMER_CH_2: 定时器通道2 (TIMERx 其中 x=0..4,7)
    • TIMER_CH_3: 定时器通道3 (TIMERx 其中 x=0..4,7)
  • ocmode: 设置输出比较通道的工作模式。

    • TIMER_OC_MODE_TIMING: 定时模式。在这种模式下,当计数器达到比较寄存器中的值时,输出状态改变。
    • TIMER_OC_MODE_ACTIVE: 激活模式。在这种模式下,当计数器小于或等于比较寄存器中的值时,输出被激活。
    • TIMER_OC_MODE_INACTIVE: 非激活模式。与激活模式相反,当计数器大于比较寄存器中的值时,输出被激活。
    • TIMER_OC_MODE_TOGGLE: 切换模式。每当计数器达到比较寄存器中的值时,输出状态就会切换。
    • TIMER_OC_MODE_LOW: 强制低模式。输出总是保持低电平。
    • TIMER_OC_MODE_HIGH: 强制高模式。输出总是保持高电平。
    • TIMER_OC_MODE_PWM0: PWM0 模式。输出作为 PWM 信号使用,当计数器值小于比较寄存器值时输出为低电平。
    • TIMER_OC_MODE_PWM1: PWM1 模式。输出作为 PWM 信号使用,当计数器值小于比较寄存器值时输出为高电平。

总结

这些参数用于配置定时器的输出比较通道,以控制输出信号的行为。通过选择不同的通道和工作模式,您可以实现各种功能,如定时、PWM 生成等。这些配置对于许多应用来说都非常关键,比如电机控制、LED 亮度调节等。

	/* 设置占空比设置chcv寄存器的数值;*/
	timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, 250 - 1); 
	/* 设置通道输出PWM模式;*/
	timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_PWM0);
	// 使能互补通道保护寄存器
	timer_primary_output_config(TIMER0, ENABLE);
	/* 使能定时器;*/ 
	timer_enable(TIMER0);	

以上代码的含义可以直接鼠标右键转到源码进行查看


PWM 输出比较测试函数

注:该函数的功能是实现LED灯的翻转对应到mcu上就是PA8号引脚LED1主要的功能是实现呼吸灯。

void PwmDrvTest(void)
{
	for(uint32_t i = 0; i < 500; i = i + 10)
	{
		// 改变脉宽占空比
		timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, i); 
		DelayNms(50);
	}
	for(uint32_t i = 500; i > 0; i = i - 10)
	{
		// 改变脉宽占空比
		timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, i); 
		DelayNms(50);
	}
}

PWM 完整代码

#include "gd32f30x.h"                   // Device header
#include <stdint.h>
#include "delay.h"

void PWM_Init(void)
{
	// gpio 初始化
	rcu_periph_clock_enable(RCU_GPIOA);
	// 初始化gpio
	gpio_init
	(
		GPIOA,
		GPIO_MODE_AF_PP,
		GPIO_OSPEED_10MHZ,
		GPIO_PIN_8
	);
	
	// 使能定时器时钟
	rcu_periph_clock_enable(RCU_TIMER0);
	// 复位定时器
	timer_deinit(TIMER0);
	// 配置定时器
	timer_parameter_struct timerInitStruct;
	// 给定时器赋值默认的初始值
	timer_struct_para_init(&timerInitStruct);
	// 预分频器的值
	timerInitStruct.prescaler = 120 - 1;
	// 设置重装计数器的值
	timerInitStruct.period = 500 - 1;
    // 初始化定时
	timer_init(TIMER0,&timerInitStruct);
	
	// 配置定时器通道
	timer_oc_parameter_struct ocInitPara;
	// 结构体调用初始化接口函数
	timer_channel_output_struct_para_init(&ocInitPara);
	// 设置通道为输出的功能
	ocInitPara.outputstate = TIMER_CCX_ENABLE;
	// 设置通道的输出极性
	ocInitPara.ocpolarity = TIMER_OC_POLARITY_HIGH;
	timer_channel_output_config(TIMER0, TIMER_CH_0, &ocInitPara);
	
	/* 设置占空比设置chcv寄存器的数值;*/
	timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, 250 - 1); 
	/* 设置通道输出PWM模式;*/
	timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_PWM0);
	// 使能互补通道保护寄存器
	timer_primary_output_config(TIMER0, ENABLE);
	/* 使能定时器;*/ 
	timer_enable(TIMER0);	
}

void PwmDrvTest(void)
{
	for(uint32_t i = 0; i < 500; i = i + 10)
	{
		// 改变脉宽占空比
		timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, i); 
		DelayNms(50);
	}
	for(uint32_t i = 500; i > 0; i = i - 10)
	{
		// 改变脉宽占空比
		timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, i); 
		DelayNms(50);
	}
}

头文件代码:

#ifndef  _PWM_DRV_H_
#define  _PWM_DRV_H_
#include <stdint.h>

void PWM_Init(void);

void PwmDrvTest(void);
#endif


主函数调用

结语

通过本次的学习掌握定时器的基本配置方法,了解预分频器以及定时周期,定时时间的计算,PWM输出比较的实现,基于PWM输出比较的配置,本次文章的编写参考GD32数据手册,以及郭天祥ARM32课程进行编写,仅供学习参考

......

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1953240.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pytorch学习笔记——在GPU上进行训练

文章目录 1. 环境准备2. 导入必要库3. 加载数据集4. 定义简单的神经网络模型5. 检查和设置GPU设备6. 定义损失函数和优化器7. 训练模型8. 全部代码展示及运行结果 1. 环境准备 首先&#xff0c;确保PyTorch已经安装&#xff0c;且CUDA&#xff08;NVIDIA的并行计算平台和编程模…

go-kratos 学习笔记(6) 数据库gorm使用

数据库是项目的核心&#xff0c;数据库的链接数据是data层的操作&#xff0c;选择了比较简单好用的gorm作为数据库的工具&#xff1b;之前是PHP开发&#xff0c;各种框架都是orm的操作&#xff1b;gorm还是很相似的&#xff0c;使用起来比较顺手 go-kratos官网的实例是ent&…

鸿蒙UI系统组件10——菜单(Menu)

果你也对鸿蒙开发感兴趣&#xff0c;加入“Harmony自习室”吧&#xff01;扫描下面名片&#xff0c;关注公众号。 Menu是菜单接口&#xff0c;一般用于鼠标右键弹窗、点击弹窗等。 1、创建默认样式的菜单 菜单需要调用bindMenu接口来实现。bindMenu响应绑定组件的点击事件&am…

ModuleNotFoundError: No module named ‘py3langid‘ 以及如何将包安在虚拟环境下

前提&#xff1a;已经安装过改包&#xff08;pip install py3langid&#xff09;&#xff0c;但仍报错 原因&#xff1a;安装在其他目录下了 解决办法&#xff1a; 1、再次在终端输入pip install py3langid 显示安装位置 Requirement already satisfied: py3langid in c:\…

css大屏设置中间元素四周渐变透明效果

css大屏设置中间元素四周渐变透明效果 四周透明效果&#xff1a; // 设置蒙版上下左右渐变显示mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 10%, rgba(0, 0, 0, 1) 90%, rgba(0, 0, 0, 0) 100%),linear-gradient(to bottom, rgba(0, 0, 0…

性能测试的指标及流程

性能测试指标 相应时间 并发数 吞吐量&#xff1a; 点击数 错误率 资源使用率 所有的东西是存在磁盘里的&#xff0c;在代码运行的时候会将磁盘的东西读取到内存里&#xff0c;磁盘IO和网络都是衡量速度&#xff0c;在任务管理器可查看资源使用率 题&#xff1a; 答案&#xf…

创建vue3项目,以及使用示例

1.在根目录下cmd&#xff1a;vue create myobj&#xff08;没有切换淘宝镜像记得切换&#xff0c;这样创建项目运行快&#xff09; 2. 3.(按空格键选中&#xff0c;选好回撤就到下一步了) 4. 5. 6. 7. 8. 9. 10. 11. 12. 13.然后输入执行以下两步就已经运行项目了 以…

Java算法之递归算法-如何计算阶乘的值

上一篇学了递归之后&#xff0c;练习一下递归算法。 题目&#xff1a;使用递归算法计算阶乘的值&#xff0c;也就是5&#xff01;5*4*3*2*1&#xff0c;直接使用循环是非常简单的&#xff0c;这边练习一下递归算法。 先写一下两个条件 基线条件&#xff1a;等于1的时候返回1…

windows下实现mongodb备份还原

添加环境变量 把mongodb安装目录下的bin路径添加到环境变量的path路径: 备份库 打开CMD&#xff0c;执行以下命令&#xff1a; mongodump -u test -p test -d test -o D://backup_mongodb//20220706 –gzip 参数说明&#xff1a; -u 用户名 -p 密码 -d 需要备份的库名称…

GraphHopper路劲规划导航(Android源码调试运行)

本文主要记录在运行graphhopper安卓版路径规划导航源码的步骤和遇到的问题。 成功运行了程序&#xff0c;但是路劲规划一直不成功&#xff0c;问题一开始是服务地址&#xff0c;后来又是key的问题&#xff0c;在这个项目中涉及到了graphhopper、mapbox、mapilion的key&#xff…

map、foreach、filter这些方法你还不知道什么时候该用哪个吗?那就看过来

forEach&#xff1a;‌主要用于遍历数组并对每个元素执行某种操作&#xff0c;‌通常用于改变当前数组里的值。‌它不会返回新数组&#xff0c;‌而是直接在原数组上进行操作。‌forEach方法不支持return、‌break、‌continue等语句&#xff0c;‌因为这些语句在forEach中不会…

多线程实例-线程池

线程池&#xff0c;就是把线程提前从系统中申请好&#xff0c;放到一个地方&#xff0c;后面需要使用线程的时候&#xff0c;直接从这个地方取&#xff0c;而不是从系统重新申请&#xff0c;线程用完之后也回到刚才的地方。 线程池的优点&#xff1a;降低线程创建和销毁的开销…

MICA:面向复杂嵌入式系统的混合关键性部署框架

背景 在嵌入式场景中&#xff0c;虽然 Linux 已经得到了广泛应用&#xff0c;但并不能覆盖所有需求&#xff0c;例如高实时、高可靠、高安全的场合。这些场合往往是实时操作系统的用武之地。有些应用场景既需要 Linux 的管理能力、丰富的生态&#xff0c;又需要实时操作系统的高…

戴尔vostro15-3568硬盘升级+系统重装

硬盘升级 原2.5机械硬盘换成了SATA2.5的固态硬盘 按F2进入bios后看到的电池信息如下&#xff1a; 需要重新换一个电池 系统重装 步骤如下 1.U盘需要格式化成 NTFS 类型的&#xff0c;并且从官网下载后介质 2.BV1z3411K7AD b站这个视频前三步可以参考设置

八、桥接模式

文章目录 1 基本介绍2 案例2.1 OperatingSystem 抽象类2.2 LinuxOS 类2.3 WindowsOS 类2.4 FileOperation 类2.5 FileAppender 类2.6 FileReplicator 类2.7 Client 类2.8 Client 类运行结果2.9 总结 3 各角色之间的关系3.1 角色3.1.1 Implementor ( 实现者 )3.1.2 ConcreteImpl…

微信答题小程序产品研发-UI界面设计

高保真原型虽然已经很接近产品形态了&#xff0c;但毕竟还不能够直接交付给开发。这时就需要UI设计师依据之前的原型设计&#xff0c;进一步细化和实现界面的视觉元素&#xff0c;包括整体视觉风格、颜色、字体、图标、按钮以及交互细节优化等。 UI设计不仅关系到用户的直观感…

1.c#(winform)编程环境安装

目录 安装vs创建应用帮助查看器安装与使用&#xff08; msdn&#xff09; 安装vs 安装什么版本看个人心情&#xff0c;或者公司开发需求需要 而本栏全程使用vs2022进行开发c#&#xff0c;着重讲解winform桌面应用开发 使用***.net framework***开发 那先去官网安装企业版的vs…

这一文,关于Java泛型的点点滴滴 一

作为一个 Java 程序员&#xff0c;用到泛型最多的&#xff0c;我估计应该就是这一行代码&#xff1a; List<String> list new ArrayList<>();这也是所有 Java 程序员的泛型之路开始的地方啊。 不过本文讲泛型&#xff0c;先不从这里开始讲&#xff0c;而是再往前…

CVPR 2024最佳论文分享:Mip-Splatting: 无混叠3D高斯溅射

本推文详细介绍了CVPR 2024最佳论文提名《Mip-Splatting: Alias-free 3D Gaussian Splatting》。该论文的第一作者为 Zehao Yu&#xff08;图宾根大学在读博士&#xff0c;导师&#xff1a;Andreas Geiger &#xff09;。论文提出了一种名为Mip-Splatting的方法&#xff0c;用于…