ARM32开发--PWM通道输出

news2024/11/15 14:05:50

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

文章目录

前言

内容

需求

通用定时器多通道

开发流程

多通道配置

占空比更新

完整代码

高级定时器通道输出

开发流程

通道配置

Break配置

完整代码

总结


前言

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

内容

需求

点亮4个灯,采用pwm的方式。

定时器

通道

引脚

AF

LED序号

T3

CH0

PD12

AF2

LED5

CH1

PD13

AF2

LED6

CH2

PD14

AF2

LED7

CH3

PD15

AF2

LED8

实现LED5, LED6, LED7, LED8呼吸灯效果

通用定时器多通道

点亮T3定时器下的多个通道的灯。

开发流程

  1. 添加Timer依赖
  2. 初始化PWM相关GPIO
  3. 初始化PWM,包含多通道配置
  4. PWM占空比控制

多通道配置

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);
  /* 启用P极输出 */
  ocpara.outputstate  = (uint16_t)TIMER_CCX_ENABLE;
  /* 配置输出参数 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);
}

输出比较模式

  • TIMER_OC_MODE_PWM0: 高电平有效
  • TIMER_OC_MODE_PWM1:低电平有效

占空比更新

/**********************************************************
 * @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);
}

完整代码

#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);
}

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  = (uint16_t)TIMER_CCX_ENABLE;
  /* 配置输出参数 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);
}


// TIMER CH
#define LED5 TIMER3, TIMER_CH_0
#define LED6 TIMER3, TIMER_CH_1
#define LED7 TIMER3, TIMER_CH_2
#define LED8 TIMER3, TIMER_CH_3

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

// LED5 TM3CH0 PD12
// LED6 TM3CH1 PD13
// LED7 TM3CH2 PD14
// LED8 TM3CH3 PD15
static void Timer_config() {
  // 定时器

  // GPIO ----------------------------------------
  timer_gpio_config(RCU_GPIOD, GPIOD, GPIO_PIN_12, GPIO_AF_2);
  timer_gpio_config(RCU_GPIOD, GPIOD, GPIO_PIN_13, GPIO_AF_2);
  timer_gpio_config(RCU_GPIOD, GPIOD, GPIO_PIN_14, GPIO_AF_2);
  timer_gpio_config(RCU_GPIOD, GPIOD, GPIO_PIN_15, GPIO_AF_2);

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

  // TIMER channel-------------------------------
  timer_channel_config(LED5);
  timer_channel_config(LED6);
  timer_channel_config(LED7);
  timer_channel_config(LED8);

}

/**********************************************************
 * @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(LED5, duty);
    PWM_update(LED6, duty);
    PWM_update(LED7, duty);
    PWM_update(LED8, duty);

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

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

    delay_1ms(10);
  }
}

高级定时器通道输出

高级定时器只有TIMER0和TIMER7支持。由于扩展板上的高级定时器没有对应的LED,我们可以使用跳线的方式,将TIMER0CH0对应的PE8引脚,短接到PD8(LED1)上,通过观察LED1的亮灭,了解是否正确输出。

开发流程

  1. 添加Timer依赖
  2. 初始化PWM,包含多通道配置
  3. Break配置
  4. PWM占空比控制

通道配置

void timer0_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);
  // 禁用 OP极
//  ocpara.outputstate  = TIMER_CCX_ENABLE;
  // 启用 ON极
  ocpara.outputnstate = TIMER_CCXN_ENABLE;
  /* 配置输出参数 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);
}

#define LED1 TIMER0, TIMER_CH_0

timer0_channel_config(LED1);
  • 特别强调,这里的引脚分为P和N类型,不同引脚要配置不同的输出状态

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;
/* bread输入的启用*/
breakpara.breakstate     = TIMER_BREAK_ENABLE;
/* 配置TIMER7 break */
timer_break_config(TIMER0, &breakpara);
/* 启用TIMER7 break */
timer_break_enable(TIMER0);
  • breakstate:break状态开启
  • ouputostate:输出状态,自动开启
  • breakpolarity:输出极性,高电平

完整代码

#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);
}

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 timer0_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);
  // 禁用 OP极
//  ocpara.outputstate  = TIMER_CCX_ENABLE;
  // 启用用 OP极
  ocpara.outputnstate = TIMER_CCXN_ENABLE;
  /* 配置输出参数 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);
}

// TIMER CH
#define LED1 TIMER0, TIMER_CH_0

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

// LED1 TM0CH0_ON PE8
static void Timer_config() {
  // 定时器

  // GPIO ----------------------------------------
  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-------------------------------
  timer0_channel_config(LED1);

  // 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;
  /* bread输入的启用*/
  breakpara.breakstate     = TIMER_BREAK_ENABLE;
  /* 配置TIMER7 break */
  timer_break_config(TIMER0, &breakpara);
  /* 启用TIMER7 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(LED1, duty);
    if (duty >= 100) {
      dir = -1;
    } else if (duty <= 0) {
      dir = 1;
    }
    duty += dir;

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

    delay_1ms(10);
  }
}

总结

高级定时器只有TIMER0和TIMER7支持。由于扩展板上的高级定时器没有对应的LED,我们可以使用跳线的方式,将TIMER0CH0对应的PE8引脚,短接到PD8(LED1)上,通过观察LED1的亮灭,了解是否正确输出。

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

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

相关文章

专属部署简介

什么是专属部署 专属部署(也称为专用部署)是一种部署选择&#xff0c;它允许用户将数据和应用部署到自己的专用云基础架构中&#xff0c;而不是与其他租户共享基础架构。这种部署方式可以提供更高的安全性、控制力和性能优化&#xff0c;因为用户可以完全控制和管理自己的基础设…

IT人的拖延——让“优先队列”带你走出“频繁切换”的拖延

在快节奏的IT行业&#xff0c;我们经常会面临多任务并行的挑战&#xff0c;经常这个事情还没做&#xff0c;那个事情就找上门&#xff0c;然后放下手中的活&#xff0c;去干另一件事。我们的工作环境多半是开放的环境&#xff0c;频繁的任务切换不仅降低了工作效率&#xff0c;…

2024/06/11--代码随想录算法1/17|理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

理论基础 动态规划&#xff1a;当前状态由前面的状态推导而来 贪心&#xff1a;局部选最优 动态规划5步曲 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 509. 斐波那契数 力扣链接 动态规划5步曲 确定d…

【机器学习】让计算机变得更加智能

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 目录 机器学习&#xff1a;让计算机…

[linux] 上手新ubuntu机器的初始化工作(自用侵删)

文章目录 环境类Vimzshother 应用类Typora激活环境准备解包替换文件app.asar激活Typora VsCodeextension.vscode乱码 WattToolkitQQWPS输入法:FcitxDeepin-wine : Wechat 环境类 Vim 直接贴配置 vim-Plug: let mapleader "," let g:mapleader "," le…

据阿谱尔APO Research统计显示,2023年全球有机硅弹性体凝胶市场销售额约为2.1亿美元

根据阿谱尔 (APO Research&#xff09;的统计及预测&#xff0c;2023年全球有机硅弹性体凝胶市场销售额约为2.1亿美元&#xff0c;预计在2024-2030年预测期内将以超过4.17%的CAGR&#xff08;年复合增长率&#xff09;增长。 有机硅弹性体凝胶是一类具有独特性质和广泛应用领域…

labelme转YOLOv8、YOLOv5 标签格式 标注数据

前言 本文分析将labelme的标签&#xff0c;转为YOLOv8、YOLOv5的格式&#xff0c;实现模型训练。 首先了解YOLOv8和YOLOv5标签格式&#xff0c;然后了解labelme标签格式&#xff0c;最近实现数据格式转换。 1、YOLOv8和YOLOv5标签格式 YOLOv8 的标签格式与 YOLOv5 基本相同&…

拿到一个职称,竟然有这么多好处,你不知道吗?

很多从事建筑行业的小伙伴们都想评一个职称&#xff0c;但是很多人可能都是跟风&#xff0c;觉得同事评一个我也要评一个&#xff0c;那么拿到职称具体有哪些好处&#xff1f;甘建二给您分享一下&#xff1a; 1.专业认可&#xff1a; 俗话说&#xff0c;术业有专攻&#xff0c…

电磁兼容(EMC):整改案例(一)无时钟电路哪来的窄带干扰

目录 1. 异常现象 2. 原因分析 3. 整改方案 4. 总结 1. 异常现象 某家电用电器产品依据GB 4343.1-2018 家用电器、电动工具和类似器具的电磁兼容要求 第1部分&#xff1a;发射进行端子电压测试&#xff0c;测试结果如下图示。在频点40MHz裕量不足&#xff0c;仅有0.4dB。 2…

【因果推断python】28_面板数据和固定效应2

目录 固定效应 固定效应 为了方面后面更正式地讲述&#xff0c;让我们首先看一下我们拥有的数据。按照我们的例子&#xff0c;我们将尝试估计婚姻对收入的影响。我们的数据包含多年以来多个个体 (nr) 的这两个变量&#xff0c;married 和lwage。请注意&#xff0c;工资采用对数…

无线麦克风什么牌子的音质效果好?轻揭无线领夹麦克风哪个牌子好

​随着科技的不断发展&#xff0c;无线领夹麦克风已经成为现代演讲、演出和采访中不可或缺的工具。这种小巧便携的设备&#xff0c;能够让我们摆脱线缆的束缚&#xff0c;自由地在舞台上或讲台上移动&#xff0c;同时保持声音的清晰和稳定。在这篇文章中&#xff0c;我们将介绍…

【课程总结】Day6(下):机器学习项目实战–成人收入预测

机器学习项目实战&#xff1a;成人收入预测 项目目的 基于个人收入数据(包括教育程度、年龄、性别等)的数据集&#xff0c;通过机器学习算法&#xff0c;预测一个人的年收入是否超过5万美金。 数据集 地址&#xff1a;http://idatascience.cn/dataset-detail?table_id10036…

【UML用户指南】-14-对高级结构建模-实例

目录 1、实例的组成结构 1.1、类型 1.2、名称 1.3、操作 1.4、状态 1.5、其他特征 1.5.1、主动对象 1.5.2、链 1.5.3、静态属性 1.6、标准元素 实例是抽象的具体表现&#xff0c;可以对它施加一组操作&#xff0c;而且它可能有一组状态&#xff0c;来存储操作的结果。…

leetcode-04-[24]两两交换链表中的节点[19]删除链表的倒数第N个节点[160]相交链表[142]环形链表II

一、[24]两两交换链表中的节点 重点&#xff1a;暂存节点 class Solution {public ListNode swapPairs(ListNode head) {ListNode dummyHeadnew ListNode(-1);dummyHead.nexthead;ListNode predummyHead;//重点&#xff1a;存节点while(pre.next!null&&pre.next.next…

AI智能体的分级

技术的分级 人们往往通过对一个复杂的技术进行分级&#xff0c;明确性能、适用范围和价值&#xff0c;方便比较、选择和管理&#xff0c;提高使用效率&#xff0c;促进资源合理分配和技术改进和标准化。 比如&#xff0c;国际汽车工程师学会&#xff08;SAE&#xff09;定义了自…

CANOpen转PROFINET网关连接低压伺服系统

在现代工业自动化领域&#xff0c;随着技术的不断进步&#xff0c;各种总线通讯协议之间的转换和互操作性变得越来越重要。CANOpen和PROFINET作为两种广泛应用的通讯协议&#xff0c;各自具有独特的优势和应用场景。然而&#xff0c;在实际应用中&#xff0c;往往需要将CANOpen…

python使用wkhtmltopdf将html字符串保存pdf,解决出现方框的问题

出现的问题: 解决办法: <html> <head><meta charset="UTF-8"/> </head> <style> * {font-family: Arial,SimSun !important; } </style> </html>在html字符串前面加上上面代码,意思是设置字体编码和样式 html示例:…

vue2前置路由守卫中使用this.$store.state报错解决

1、问题描述&#xff1a;在前置路由守卫逻辑中&#xff0c;要更改vuex中的store的state状态&#xff0c;使用常规的this.$store.state报错 2、问题原因&#xff1a; 在vue2是vueRouter前置路由守卫中&#xff0c;this关键字并不会指向vue实例&#xff0c;因此不能使用this.$st…

如何优雅的实现Excel导入通用处理流程

目录 1.业务背景2.业务导入流程3.流程优化3.1 模板模式3.1.1 导入处理器接口ImportProcessor3.1.2 抽象父类 AbstractImportProcessor3.1.3 子类实现 ImportDemoProcessor 3.2 工厂模式3.2.1 标识子类的枚举ImportTypeEnum3.2.2 工厂类ProcessorHolder3.2.3 工厂类的调用 4. 特…

纹理贴图必须要输入顶点坐标或纹理坐标吗

最近知识星球的一位同学,面试时被问到:纹理贴图必须要输入顶点坐标或纹理坐标吗? 他一下子被这个问题问蒙了,虽然他知道正确答案是否定的,但是说不上来理由。 这个就引出了文本提到的全屏三角形,它不需要顶点缓冲区,而是利用顶点着色器直接生成所需的顶点坐标和纹理坐标…