ARM32开发--PWM高级定时器

目录

文章目录

前言

目标

学习内容

需求

高级定时器通道互补输出

开发流程

通道配置

打开互补保护电路

完整代码

练习题

总结


前言

在嵌入式软件开发中,PWM(脉冲宽度调制)技术被广泛应用于控制各种电子设备的亮度、速度等参数。理解PWM开发流程、定时器与通道的关系,掌握多通道配置策略和互补PWM配置策略,以及掌握定时器查询方式和代码抽取优化策略,对于提高嵌入式软件开发的效率和质量至关重要。

本次学习的目标是点亮2个LED灯,采用互补PWM的方式来控制互补LED效果。通过学习高级定时器通道互补输出,我们将逐步了解如何初始化PWM、配置通道的P极和N极、控制PWM占空比,以实现目标效果。


目标

  1. 加强掌握PWM开发流程
  2. 理解定时器与通道的关系
  3. 掌握多通道配置策略
  4. 掌握互补PWM配置策略
  5. 掌握定时器查询方式
  6. 掌握代码抽取优化策略
  7. 掌握PWM调试方式

学习内容

需求

点亮2个灯,采用互补pwm的方式

|-----|-----|-----|-----|----|-------|
| 定时器 | 通道 | 引脚 | AF | 极性 | LED序号 |
| T0 | ch0 | PE8 | AF1 | ON | LED1 |
| T0 | ch0 | PE9 | AF1 | OP | LED2 |

将PE8短接至PD8,PE9短接至PD9,实现互补LED效果。

高级定时器通道互补输出

开发流程

  1. 添加Timer依赖
  2. 初始化PWM
  3. 配置通道的P极和N极
  4. PWM占空比控制

通道配置

vb 复制代码
`void timer_channel_config(uint32_t timer_periph, uint16_t channel) {
  /* TIMER 通道输出配置 */
  timer_oc_parameter_struct ocpara;
  /* initialize TIMER channel output parameter struct */
  timer_channel_output_struct_para_init(&ocpara);
  ocpara.outputstate  = TIMER_CCX_ENABLE;        // OP Enable
  ocpara.outputnstate = TIMER_CCXN_ENABLE;       // ON Enable
  ocpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;   // OP Polarity
  ocpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;  // ON Polarity
  /* 配置输出参数 configure TIMER channel output function */
  timer_channel_output_config(timer_periph, channel, &ocpara);
  /* 配置通道输出输出比较模式 configure TIMER channel output compare mode */
  timer_channel_output_mode_config(timer_periph, channel, TIMER_OC_MODE_PWM0);
}`
  • ocnpolarity:N极性电平
  • ocpolarity:P极性电平

打开互补保护电路

// break 只针对高级定时器TIMER0 & TIMER7,打开互补保护电路
/* TIMER通道互补保护电路 */
timer_break_parameter_struct breakpara;
/* 初始化TIMER break参数结构体 */
timer_break_struct_para_init(&breakpara);
/* break输入的极性 HIGH */
breakpara.breakpolarity   = TIMER_BREAK_POLARITY_HIGH;
/* 输出自动的启用 */
breakpara.outputautostate = TIMER_OUTAUTO_ENABLE;
/* break输入的启用*/
breakpara.breakstate      = TIMER_BREAK_ENABLE;
/* 配置TIMER0 break */
timer_break_config(TIMER0, &breakpara);
/* 启用TIMER0 break */
timer_break_enable(TIMER0);

完整代码

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

void USART0_on_recv(uint8_t* data, uint32_t len) {
  printf("g_rx_buffer: %s g_rx_cnt:%d \n", data, len);
}

static void GPIO_config() {
  rcu_periph_clock_enable(RCU_GPIOC);
  gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_6);
  gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);

  gpio_bit_reset(GPIOC, GPIO_PIN_6);
  
//  rcu_periph_clock_enable(RCU_GPIOD);
//  gpio_mode_set(GPIOD, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_8 | GPIO_PIN_9);
}

void timer_gpio_config(uint32_t gpio_rcu, uint32_t gpio_port, uint32_t gpio_pin, uint32_t gpio_af) {
  rcu_periph_clock_enable(gpio_rcu);
  /* 设置gpio模式 */
  gpio_mode_set(gpio_port, GPIO_MODE_AF, GPIO_PUPD_NONE, gpio_pin);
  gpio_output_options_set(gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, gpio_pin);
  gpio_af_set(gpio_port, gpio_af, gpio_pin);
}

void timer_init_config(rcu_periph_enum rcu_periph, uint32_t timer_periph,
                       uint16_t t_prescaler, uint32_t t_period) {

  rcu_periph_clock_enable(rcu_periph);
  timer_deinit(timer_periph);
  /*初始化参数 */
  timer_parameter_struct initpara;
  /* initialize TIMER init parameter struct */
  timer_struct_para_init(&initpara);
  /* 根据需要配置值 分频系数 (可以实现更低的timer频率) */
  initpara.prescaler 	= t_prescaler - 1;
  /* 1个周期的计数(period Max: 65535) Freq > 3662  */
  initpara.period		= t_period - 1;
  /* initialize TIMER counter */
  timer_init(timer_periph, &initpara);
  /* enable a TIMER */
  timer_enable(timer_periph);

}

void timer_channel_config(uint32_t timer_periph, uint16_t channel) {
  /* TIMER 通道输出配置 */
  timer_oc_parameter_struct ocpara;
  /* initialize TIMER channel output parameter struct */
  timer_channel_output_struct_para_init(&ocpara);
  ocpara.outputstate  = TIMER_CCX_ENABLE;        // OP Enable
  ocpara.outputnstate = TIMER_CCXN_ENABLE;       // ON Enable
  ocpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;   // OP Polarity
  ocpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;  // ON Polarity
  /* 配置输出参数 configure TIMER channel output function */
  timer_channel_output_config(timer_periph, channel, &ocpara);
  /* 配置通道输出输出比较模式 configure TIMER channel output compare mode */
  timer_channel_output_mode_config(timer_periph, channel, TIMER_OC_MODE_PWM0);
}


// PWM
#define	PRESCALER		1
#define	FREQ			  10000
#define PERIOD			(SystemCoreClock / FREQ)

// LED1 TM0CH1 PE9  OP
// LED2 TM0CH0 PE8  ON
static void Timer_config() {
  // 定时器

  // GPIO ----------------------------------------
  timer_gpio_config(RCU_GPIOE, GPIOE, GPIO_PIN_9, GPIO_AF_1);
  timer_gpio_config(RCU_GPIOE, GPIOE, GPIO_PIN_8, GPIO_AF_1);

  // TIMER----------------------------------------
  /* 升级频率*/
  rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
  timer_init_config(RCU_TIMER0, TIMER0, PRESCALER, PERIOD); // 与通道无关

  // TIMER channel-------------------------------
  timer_channel_config(TIMER0, TIMER_CH_0);
  
  // Break --------------------------------------------------
  // break 只针对高级定时器TIMER0 & TIMER7,打开互补保护电路
  /* TIMER通道互补保护电路 */
  timer_break_parameter_struct breakpara;
  /* 初始化TIMER break参数结构体 */
  timer_break_struct_para_init(&breakpara);
  /* break输入的极性 HIGH */
  breakpara.breakpolarity   = TIMER_BREAK_POLARITY_HIGH;
  /* 输出自动的启用 */
  breakpara.outputautostate = TIMER_OUTAUTO_ENABLE;
  /* break输入的启用*/
  breakpara.breakstate      = TIMER_BREAK_ENABLE;
  
  /* 配置TIMER0 break */
  timer_break_config(TIMER0, &breakpara);
  /* 启用TIMER0 break */
  timer_break_enable(TIMER0);


}

/**********************************************************
 * @brief 更新pwm占空比
 * @param timer_periph 定时器
 * @param channel 通道
 * @param duty  占空比[0, 100]
 * @return 
 **********************************************************/
void PWM_update(uint32_t timer_periph, uint16_t channel, float duty) { // 0-100

  if(duty > 100) duty = 100;
  else if(duty < 0) duty = 0;

//	pulse / PERIOD == duty / 100
  uint32_t pulse = PERIOD * duty / 100.0f - 1;

  // 计数值 65535
  timer_channel_output_pulse_value_config(timer_periph, channel, pulse);
}

int main(void)
{
  systick_config();
  USART0_init();

  // 拉低总开关
//  GPIO_config();

  Timer_config();
  printf("Init Complete!\n");

  float duty = 0;
  int8_t dir = 1;
  while(1) {
    PWM_update(TIMER0, TIMER_CH_0, duty);

    if (duty >= 100) {
      dir = -1;
    } else if (duty <= 0) {
      dir = 1;
    }
    duty += dir;

    printf("duty: %.2f \n", duty);

    delay_1ms(10);
  }
}

练习题


总结

通过本次学习,我们深入了解了PWM技术在嵌入式软件开发中的应用。从添加Timer依赖开始,我们逐步完成了初始化PWM、配置通道的P极和N极、设定PWM占空比等步骤,最终实现了点亮2个LED灯的效果,并采用互补PWM的方式来控制LED的亮度。

在实现过程中,我们学习了定时器与通道的关系,掌握了多通道配置策略和互补PWM配置策略,同时了解了定时器查询方式和代码抽取优化策略,为日后更高效地进行PWM开发打下了坚实的基础。通过不断学习和实践,我们将能够提升自己在嵌入式软件开发领域的技能水平,开发出更加优秀和稳定的产品和应用。

相关推荐
JaneZJW6 分钟前
Proteus仿真——《51单片机AD和DA转换器的设计》
单片机·嵌入式硬件·51单片机·proteus
NEWEVA__zzera222 小时前
利用光耦来隔离485芯片与串口引脚,实现自动收发485电路
单片机·嵌入式硬件
m0_748240543 小时前
STM32第十一课:STM32-基于标准库的42步进电机的简单IO控制(附电机教程,看到即赚到)
stm32·单片机·嵌入式硬件
温柔的男孩像海洋丶3 小时前
vscode的keil assistant 中搜索不到全局变量
ide·vscode·单片机
沐欣工作室_lvyiyi3 小时前
基于单片机的多功能智能小车(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·单片机毕业设计
鸿喵小仙女4 小时前
C# WPF读写STM32/GD32单片机Flash数据
stm32·单片机·c#·wpf
lucy153027510794 小时前
MCU 功耗基准测试
科技·单片机·嵌入式硬件·智能家居·信号处理·工控主板
m0_748240915 小时前
OpenMV与STM32通信全面指南
stm32·单片机·嵌入式硬件
Cchengzu7 小时前
阿里巴巴2017实习生笔试题(二)
stm32·单片机·嵌入式硬件
重生之我是数学王子11 小时前
单片机 STM32入门
stm32·单片机·嵌入式硬件