ARM32开发--PWM通道输出

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

目录

文章目录

前言

内容

需求

通用定时器多通道

开发流程

多通道配置

占空比更新

完整代码

高级定时器通道输出

开发流程

通道配置

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.mfbz.cn/a/699614.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Python】Flask问答系统Demo项目

学习视频 我是跟着知了传课学的Flask&#xff0c;起初了解Flask还是GPT告诉我的&#xff0c;现在可以说用Flask做后端是真的方便&#xff01; https://www.bilibili.com/video/BV17r4y1y7jJ 项目结构与下载 FlaskOA&#xff08;项目文件夹&#xff09; │ app.py │ conf…

性能测试------LoadRunner 详解

性能测试------LoadRunner的使用 一、什么是LoadRunner LoadRunner是一款由Micro Focus&#xff08;以前是Hewlett-Packard或HP公司&#xff09;开发的性能测试工具。它用于测试和分析系统在负载下的行为和性能。具体来说&#xff0c;LoadRunner可以模拟数千名用户同时访问应…

r语言数据分析案例26-美元兑换欧元汇率分析与研究

一、研究背景&#xff1a; 汇率是国际贸易和金融中最重要的价格之一&#xff0c;它直接影响着各国的经济利益和国际竞争力。美元兑换欧元汇率是全球最重要的汇率之一&#xff0c;它的波动对全球经济和金融市场都有着深远的影响。因此&#xff0c;对美元兑换欧元汇率的分析和研…

树莓派4B_OpenCv学习笔记5:读取窗口鼠标状态坐标_TrackBar滑动条控件的使用

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Opencv 版本是4.5.1&#xff1a; 今日学习:读取窗口鼠标状态坐标_TrackBar滑动条控件的使…

华为机考入门python3--(35)牛客35-蛇形矩阵

分类&#xff1a;蛇形矩阵 知识点&#xff1a; 取出每行中非零的数字 row [str(num) for num in matrix[i] if num ! 0] 题目来自【牛客】 def generate_snake_matrix(n):# 初始化一个NN的矩阵matrix [[0] * n for _ in range(n)] start 1# i为行&#xff0c;&#xf…

国内著名的四个“大模型”

关于您提到的国内四大模型&#xff0c;这里为您详细介绍&#xff1a; 文心大模型&#xff1a;文心大模型是百度自主研发的产业级知识增强大模型。它以创新性的知识增强技术为核心&#xff0c;从单模态大模型发展到跨模态&#xff0c;从通用基础大模型到跨领域、跨行业&#xff…

Flutter打包网络问题解决办法

问题情况":app:compileReleaseJavaWithJavac" 报错的最主要问题其实在下一句 Failed to find Build Tools revision 30.0.3,请查看自己的Android sdk版本,比如我的就是’34.0.0’版本. 解决办法: 在app/build.gradle中的android下添加,即可 buildToolsVersion 3…

理解查准率P、查全率R及Fβ度量怎么得来的

如果得到的是一组样本在两个算法上的一次预测结果&#xff0c;其中每个样本都被赋予了一个为正样本的概率&#xff08;例如&#xff0c;通过逻辑回归或朴素贝叶斯分类器得到的概率估计&#xff09;&#xff0c;那么可以通过改变不同的阈值点来利用这些预测结果画出PR曲线。 如果…

SpringBoot基础配置文件

在SpringBoot当中, 所有的配置文件都将会被配置在application.preperties当中 一: 配置服务器端口 只需要在相应的properties当中输入, 修改我们的端口号即可 server.port80 二: 修改banner #设置 将我们的Banner不再进行显示, 或者是也可以让其在控制台当中显示, 或者是日…

02 DHCP原理与配置

目录 2.1 DHCP工作原理 1. 了解DHCP服务 2. 使用DHCP的好处 3. DHCP的分配方式 4. DHCP的租约过程 1. 客户机请求IP地址 2. 服务器响应 3. 客户机选择IP地址 4. 服务器确定租约 5. 重新登录 6. 更新租约 2.2 使用DHCP动态配置主机地址 2.2.1 配置DHCP服务器 1. 安装DHCP服务器…

ADS加不上路由的若干可能性与问题查找方法

文章目录 一.查询本机ip地址二. PIN ip地址 一.查询本机ip地址 用鼠标点击开始——运行&#xff08;或者快捷键winR&#xff09;、弹出【运行】对话框、在窗口中输入cmd&#xff0c;如下图所示&#xff1a; 输入ipconfig/all查找局域网当中的本机ip地址 在浏览器中打开百度…

Android Studio gradle下载失败

Android Studio下载Gradle插件总是出现网络超时问题。 解决办法&#xff1a; 替换为国内版本的镜像。 推荐使用国内腾讯的镜像&#xff1a; Index of /gradle/ 例如&#xff1a;gradle-8.0镜像&#xff1a; https://mirrors.cloud.tencent.com/gradle/gradle-8.0-bin.zip…

文案策划日常大公开:掌握这些技巧,让你工作效率翻倍

本周的工作和生活安排得满满当当&#xff0c;但这些看似繁重的任务&#xff0c;其实都是我利用碎片化时间高效完成的。 这里和大家分享一下&#xff0c;我是如何在这忙碌的一周中&#xff0c;既完成本职工作又享受生活的。 本周工作日常汇总&#xff1a; 1. 品牌小红薯笔记&…

观察 jvm 运行时数据区内存大小(native memory tracking)

jvm 运行时数据区 jvm 运行时数据区包括且不限于以下几个部分: 堆(heap): 用于存储对象实例和数组。堆内存的分配和释放由垃圾回收器进行管理。方法区(method area): 用于存储类的信息、静态变量、常量等。jdk 8 后方法区位于 metaspace。虚拟机栈(vm stack): 用于存储方法的…

石化加工vr中毒窒息事故培训系统开发降低了培训成本和风险。

化工行业是工伤预防工作的重点领域之一&#xff0c;工伤预防及安全生产整治工作任务艰巨、责任重大。为进一步强化红线意识、底线思维&#xff0c;深圳VR公司研发的化工VR事故警示教育系统&#xff0c;以其独特的沉浸式体验&#xff0c;为员工的安全教育开辟了新的道路。目前化…

【SWH】陆地生态系统蒸散模拟理论、蒸散估算、站点及区域尺度模拟

蒸散&#xff08;又称蒸散发&#xff09;估算是开展水资源管理、洪水预报、海绵城市成效评估、农业节水灌溉、水源涵养评估、生态需水评估等应用实践过程的关键环节&#xff0c;因此准确计算和模拟地表蒸散显得尤为重要。与此同时&#xff0c;蒸散也是陆地表层最重要的水循环过…

如何使用ai人工智能作诗?7个软件帮你快速作诗

如何使用ai人工智能作诗&#xff1f;7个软件帮你快速作诗 使用AI人工智能作诗是一种创新的写作方式&#xff0c;以下是一些可以帮助您快速作诗的AI人工智能软件&#xff1a; 1.AI创作云&#xff1a; 这是一个AI诗歌助手应用程序&#xff0c;可以根据您提供的主题或关键词生成…

Qt:QDialogButtonBox的使用

QDialogButtonBox是Qt自带的按钮箱&#xff0c;通过枚举QDialogButtonBox::ButtonRole可以添加Qt定义按钮&#xff0c;或者通过方法QDialogButtonBox::addButton添加自定义的按钮。 // 自定义按钮。 button_box_ new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonB…

冰蝎4.1webshell实验

1、基础环境 生成payload 2、default_xor_base64 HTTP 命令执行 chunked数据回包&#xff0c;48 3、default_xor_base64 HTTPS 命令执行 终端 文件管理 4、 default_aes https 虚拟终端 5、 default_aes http 虚拟终端

C#中的Web抓取:避免被阻挡

C# 是一种广泛应用于企业级项目和应用程序的多功能编程语言。它源自 C 系列语言&#xff0c;具有高效和强大的特点&#xff0c;使其成为任何开发人员工具包中不可或缺的一部分。 由于其广泛的应用&#xff0c;C# 提供了大量的工具&#xff0c;使开发人员能够解决复杂的解决方案…