STM32(六):定时器PWM呼吸灯 (标准库函数)

前言

上一篇文章已经介绍了如何用STM32单片机中的TIMER定时器来控制LED灯的交替闪烁,实现了点灯的第五种方式。这篇文章我们来介绍一下如何用STM32单片机中的定时器的PWM波来实现LED的“呼吸”。

一、实验原理

关于定时器这边就不多加赘述,详细请看上一篇文章,链接放在下面了

STM32(五):TIMER定时器 (标准库函数)

这边我们介绍一下PWM

1.PWM的介绍

PWM的全称是脉冲宽度调制(Pulse-width modulation),是指在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效的地获得所需要的模拟参量,常应用于电机控速等领域。在STM32微控制器的应用中,PWM技术也非常重要,因为它能够提供精确的信号控制,从而达到对设备行为的精确管理。

  • 频率:一秒钟内从高电平时间在到低电平时间,再从低电平跳到高电平的瞬间次数,也就是一秒钟内有多少个PWM的周期,f = 1/Ts。
  • 周期:一秒钟内从高电平时间在到低电平时间,Ts= 1/f。
  • PWM信号产生:用于控制电机速度、灯光亮度等。
  • 占空比:一个周期内高电平时间和总时间的比值,Ton/Ts。
  • 分辨率:占空比变化步距。

2.PWM参数计算公式

PWM主要参数计算公式如下所示:

  • PWM频率 Freq=CK_PSC/[(PSC+1) (ARR+1)], CK_PSC=72M
  • PWM占空比=CCR/(ARR+1)
  • PWM分辨率=1/(ARR+1)

3.PWM工作原理

以下是PWM输入模式时序工作原理图,以下对其符号进行简单的解释说明

  1. T1:PWM信号的波形,显示了信号的高电平和低电平状态。
  2. TIMx_CNT :定时器的计数器(Counter),它的值会在定时器时钟的脉冲下从0开始增加,直到达到自动重载寄存器(ARR)的值后归零,循环开始新的周期。
  3. TIMx_CCR1TIMx_CCR2 :捕捉/比较寄存器(Capture/Compare Register),用于设置PWM的高电平宽度。它们的值决定了在计数器达到这些值时输出比较匹配事件,影响PWM输出信号的占空比。
  • TIMx_CCR1 设置为 0004,代表在计数器计数到4时,第一个通道的PWM输出从高电平跳变到低电平。
  • TIMx_CCR2 设置为 0002,代表在计数器计数到2时,第二个通道的PWM输出从高电平跳变到低电平。

 接下来简单介绍一下PWM信号的一个典型的边缘对齐模式,

  • ARR = 8:设置了定时器的计数上限,决定了PWM周期的长度,计数器会从0计数到8,然后重置为0,开始新的周期。
  • CCRx = 4:决定了PWM信号从高电平变为低电平的切换点,当计数器计数到4时,输出信号从高电平切换到低电平。
  • 红线(有效电平):PWM输出从高电平切换到低电平的时刻,即在计数器值等于CCR值时,输出发生变化。
  • 蓝线(无效电平):PWM输出在一个完整的周期结束时仍然是低电平。
  • CCxIF:捕捉/比较中断标志,当定时器的计数器值与CCR寄存器的预设值相匹配时,该标志被设置。

  • OCxREF:输出比较引用信号,通常与PWM的实际输出相对应,反映了PWM信号的状态更改。

二、实验步骤

1.引脚设置

这边在h文件的宏定义中定义即可

static void GENERAL_TIMx_GPIO_Config(void) 
{GPIO_InitTypeDef GPIO_InitStructure;/* 使能定时器始终:设置TIM3CLK 为 72MHZ */GENERAL_TIM_APBxClock_FUN (GENERAL_TIM_CLK, ENABLE);/* 使能定时器通道引脚GPIO时钟 */GENERAL_TIM_GPIO_APBxClock_FUN(GENERAL_TIM_GPIO_CLK1|GENERAL_TIM_GPIO_CLK1, ENABLE); /* 配置定时器通道3输出引脚模式:复用推挽输出模式 */GPIO_InitStructure.GPIO_Pin =  GENERAL_TIM_PIN1;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GENERAL_TIM_PORT1, &GPIO_InitStructure);
}

2.时钟中断

时钟中断函数void NVIC_Config_PWM(void),这边是对中断来源以及优先级的配置,前面在Systick中有所介绍,可以看一下之前的连接:STM32(四):Systick (标准库函数)-CSDN博客

static void NVIC_Config_PWM(void)
{NVIC_InitTypeDef NVIC_InitStructure;/* 选择中断优先级配置组为2个抢占式优先级和2个子优先级,可以参考misc.h文件了解相关设置 */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);/* 配置TIM3_IRQ中断为中断源 */NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIMx_IRQn;/* 设置抢占式优先级为0 */NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;/* 设置子优先级为3 */NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;/* 使能外部中断通道 */NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);
}

但到这里我们的中断配置还没结束!!重点!!易踩坑!!

我们还需要在stm32f10x_it.c的文件里面加上新的中断服务函数 GENERAL_TIMx_IRQHandler (),记得加上头文件,例如:

#include "bsp/GeneralTIM/bsp_GeneralTIM.h"
extern uint8_t indexWave[];void GENERAL_TIMx_IRQHandler(void)
{	static uint8_t pwm_index = 0;			/* 用于PWM查表 */static uint8_t period_cnt = 0;		/* 用于计算周期数 *//* 定时器更新中断 */if (TIM_GetITStatus(GENERAL_TIMx, TIM_IT_Update) != RESET)	{			period_cnt++;/* 若输出的周期数大于20,输出下一种脉冲宽的PWM波 */if(period_cnt >= 20)										{/* 根据PWM表修改定时器的比较寄存器值 */GENERAL_TIMx->GENERAL_TIM_CCRx = indexWave[pwm_index];	/* 标志PWM表的下一个元素 */pwm_index++;												/* 若PWM脉冲表已经输出完成一遍,重置PWM查表标志 */if( pwm_index >=  40)								{pwm_index=0;								}/* 重置周期计数标志 */period_cnt=0;												}/* 必须要清除中断标志位 */TIM_ClearITPendingBit (GENERAL_TIMx, TIM_IT_Update);	}
}

3.定时器配置

定时周期和预分频需要根据实际需要进行调整,而这边TIM_Pulse=0是因为我们给了一个脉冲配置的数组indexWave[],若不用,调整这个大小会改变“呼吸”的速率。

static void GENERAL_TIMx_Configuration(void)
{TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;TIM_OCInitTypeDef  TIM_OCInitStructure;/* 当定时器从0计数到255,即为256次,为一个定时周期 */TIM_TimeBaseStructure.TIM_Period = 255; /* 设置预分频:不预分频,即为72MHz,输出脉冲频率:72MHz/(1999+1)/(255+1) */TIM_TimeBaseStructure.TIM_Prescaler = 1999;/* 设置时钟分频系数:不分频(这里用不到) */TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;/* 向上计数模式 */TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(GENERAL_TIMx, &TIM_TimeBaseStructure);/* 模式配置:PWM模式1 */TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;/* 输出状态设置:使能输出 */TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	/* 设置跳变值,当计数器计数到这个值时,电平发生跳变 */TIM_OCInitStructure.TIM_Pulse = 0;/* 当定时器计数值小于CCR1_Val时为低电平 */TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;/* 初始化定时器通道1输出PWM */GENERAL_TIM_OCxInit(GENERAL_TIMx, &TIM_OCInitStructure);/* 定时器比较输出通道1预装载配置:使能预装载 */GENERAL_TIM_OCxPreloadConfig(GENERAL_TIMx, TIM_OCPreload_Enable);  /* 使能定时器重载寄存器ARR */TIM_ARRPreloadConfig(GENERAL_TIMx, ENABLE);/* 使能定时器 */TIM_Cmd(GENERAL_TIMx, ENABLE);  /* 配置NVIC */NVIC_Config_PWM();/* 定时器更新中断 */TIM_ITConfig(GENERAL_TIMx, TIM_IT_Update, ENABLE);}

4.PWM信号初始化


void GENERAL_TIMx_PWM_Init(void)
{GENERAL_TIMx_GPIO_Config();GENERAL_TIMx_Configuration();	
}

三、实操代码

程序分为3个文件:bsp_GeneralTIM.c、bsp_GeneralTIM.h、main.c

1.bsp_GeneralTIM.c

#include "bsp/GeneralTIM/bsp_GeneralTIM.h" 
uint8_t indexWave[] = {1,1,2,2,3,4,6,8,10,14,19,25,33,44,59,80,107,143,191,255,255,191,143,107,80,59,44,33,25,19,14,10,8,6,4,3,2,2,1,1};/*** 函数功能: 配置TIMx复用输出PWM时用到的I/O*/
static void GENERAL_TIMx_GPIO_Config(void) 
{GPIO_InitTypeDef GPIO_InitStructure;/* 使能定时器始终:设置TIM3CLK 为 72MHZ */GENERAL_TIM_APBxClock_FUN (GENERAL_TIM_CLK, ENABLE);/* 使能定时器通道引脚GPIO时钟 */GENERAL_TIM_GPIO_APBxClock_FUN(GENERAL_TIM_GPIO_CLK1|GENERAL_TIM_GPIO_CLK1, ENABLE); /* 配置定时器通道3输出引脚模式:复用推挽输出模式 */GPIO_InitStructure.GPIO_Pin =  GENERAL_TIM_PIN1;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GENERAL_TIM_PORT1, &GPIO_InitStructure);}/*** 函数功能: 配置嵌套向量中断控制器NVIC*/
static void NVIC_Config_PWM(void)
{NVIC_InitTypeDef NVIC_InitStructure;/* 选择中断优先级配置组为2个抢占式优先级和2个子优先级,可以参考misc.h文件了解相关设置 */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);/* 配置TIM3_IRQ中断为中断源 */NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIMx_IRQn;/* 设置抢占式优先级为0 */NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;/* 设置子优先级为3 */NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;/* 使能外部中断通道 */NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);
}/*** 函数功能: 配置TIMx输出的PWM信号的模式,如周期、极性、占空比*/static void GENERAL_TIMx_Configuration(void)
{TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;TIM_OCInitTypeDef  TIM_OCInitStructure;/* 定时器基本参数始终 */		 /* 当定时器从0计数到255,即为256次,为一个定时周期 */TIM_TimeBaseStructure.TIM_Period = 255; /* 设置预分频:不预分频,即为72MHz,输出脉冲频率:72MHz/(1999+1)/(255+1) */TIM_TimeBaseStructure.TIM_Prescaler = 1999;/* 设置时钟分频系数:不分频(这里用不到) */TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;/* 向上计数模式 */TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(GENERAL_TIMx, &TIM_TimeBaseStructure);/* 定时器输出通道1模式配置 *//* 模式配置:PWM模式1 */TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;/* 输出状态设置:使能输出 */TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	/* 设置跳变值,当计数器计数到这个值时,电平发生跳变 */TIM_OCInitStructure.TIM_Pulse = 0;/* 当定时器计数值小于CCR1_Val时为低电平 */TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;/* 初始化定时器通道1输出PWM */GENERAL_TIM_OCxInit(GENERAL_TIMx, &TIM_OCInitStructure);/* 定时器比较输出通道1预装载配置:使能预装载 */GENERAL_TIM_OCxPreloadConfig(GENERAL_TIMx, TIM_OCPreload_Enable);  /* 使能定时器重载寄存器ARR */TIM_ARRPreloadConfig(GENERAL_TIMx, ENABLE);/* 使能定时器 */TIM_Cmd(GENERAL_TIMx, ENABLE);  /* 配置NVIC */NVIC_Config_PWM();/* 定时器更新中断 */TIM_ITConfig(GENERAL_TIMx, TIM_IT_Update, ENABLE);}/*** 函数功能: TIMx输出PWM信号初始化* 说    明:只要调用这个函数TIMx的通道就会有PWM信号输出*/
void GENERAL_TIMx_PWM_Init(void)
{GENERAL_TIMx_GPIO_Config();GENERAL_TIMx_Configuration();	
}

2.bsp_GeneralTIM.h

#ifndef __GENERAL_TIM_H__
#define __GENERAL_TIM_H__/* 包含头文件 ----------------------------------------------------------------*/
#include <stm32f10x.h>/* 类型定义 ------------------------------------------------------------------*/
/* 宏定义 --------------------------------------------------------------------*/#define GENERAL_TIMx                        TIM3
#define GENERAL_TIM_APBxClock_FUN           RCC_APB1PeriphClockCmd
#define GENERAL_TIM_CLK                     RCC_APB1Periph_TIM3
#define GENERAL_TIM_GPIO_APBxClock_FUN      RCC_APB2PeriphClockCmd
#define GENERAL_TIM_GPIO_CLK1               RCC_APB2Periph_GPIOA
#define GENERAL_TIM_PORT1                   GPIOA
#define GENERAL_TIM_PIN1                    GPIO_Pin_11
#define GENERAL_TIM_OCxInit                 TIM_OC3Init
#define GENERAL_TIM_OCxPreloadConfig        TIM_OC3PreloadConfig
#define GENERAL_TIM_CCRx                    CCR3
#define GENERAL_TIMx_IRQn                   TIM3_IRQn              //中断
#define GENERAL_TIMx_IRQHandler             TIM3_IRQHandler/* 扩展变量 ------------------------------------------------------------------*/
/* 函数声明 ------------------------------------------------------------------*/void GENERAL_TIMx_PWM_Init(void);#endif	/* __GENERAL_TIM_H__ */

3.main.c


/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f10x.h"
#include "bsp/led/bsp_led.h"
#include "bsp/key/bsp_key.h"
#include "bsp/delay/delay.h"
#include "bsp/systick/bsp_SysTick.h"
#include "bsp/GeneralTIM/bsp_GeneralTIM.h" 
/* 函数体 --------------------------------------------------------------------*//*** 函数功能: 主函数.*/
int main(void)
{/* 初始化定时器PWM输出 */GENERAL_TIMx_PWM_Init();  while (1){//空}
}

四、实验效果

呼吸灯

结束语

本文以STM32VET6为例讲解了用STM32单片机中用定时器的PWM波来实现LED的“呼吸”,并指出其中的易坑点。希望对大家有所帮助!如果还有什么问题,欢迎评论区留言,谢谢!

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

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

相关文章

综合能力 | 误差 | 学习笔记

误差指真值与观测值的差值。 误差分为系统误差&#xff08;消除方法&#xff1a;观测方法、仪器校正、修正等&#xff09;、偶然误差&#xff08;符合正态分布规律&#xff0c;进行计算纠正&#xff09;、粗差&#xff08;一般舍弃&#xff09;。 中误差&#xff08;均方差或标…

Gartner发布降低企业软件供应链安全风险指南:全球软件供应链相关法规、指南以及企业需要开展的三个方面工作

软件供应链攻击呈三位数增长&#xff0c;但很少有企业机构采取措施对这些复杂攻击的风险进行评估。安全和风险管理领导者可参考本文&#xff0c;采用三种实践来检测和预防攻击&#xff0c;保护企业机构的安全。 主要发现 虽然软件供应链攻击频繁发生&#xff0c;但其安全评估尚…

linux基础命令第三篇

雷迪斯and the乡亲们 欢迎你们来到 奇幻的编程世界 17.wc命令 作用&#xff1a; 统计行数、单词数、字符分数 格式&#xff1a; wc 选项 文件 选项&#xff1a; -l&#xff1a; 统计行数 -w&#xff1a; 统计单词 -c &#xff1a;统计字符 例子&#xff1a; 162&…

Wallace树乘法器及Verilog实现

一、Wallace树乘法器 Wallace树乘法器就是将多个部分积进行分组&#xff0c;每三个一组&#xff0c;最后如果剩下的部分积个数不够三个的不做处理&#xff0c;然后将各组的部分积进行相加得到和以及进位信息&#xff0c;直到最终只剩下两行部分积&#xff0c;相加后得到最终结…

3月笔记本电脑行业线上市场销售数据分析

笔记本电脑市场在过去几年中经历了起伏&#xff0c;但总体上呈现出稳定增长的态势。特别是随着远程办公、在线学习等需求的增加&#xff0c;以及消费者对于便携性、高性能等方面的追求&#xff0c;笔记本电脑市场得到了进一步的发展。 据鲸参谋数据统计&#xff0c;线上平台&a…

简单的神经网络

一、softmax的基本概念 我们之前学过sigmoid、relu、tanh等等激活函数&#xff0c;今天我们来看一下softmax。 先简单回顾一些其他激活函数&#xff1a; Sigmoid激活函数&#xff1a;Sigmoid函数&#xff08;也称为Logistic函数&#xff09;是一种常见的激活函数&#xff0c…

四十九坊股权设计,白酒新零售分红制度,新零售策划机构

肆拾玖坊商业模式 | 白酒新零售体系 | 新零售系统开发 坐标&#xff1a;厦门&#xff0c;我是易创客肖琳 深耕社交新零售行业10年&#xff0c;主要提供新零售系统工具及顶层商业模式设计、全案策划运营陪跑等。 不花钱开3000多家门店&#xff0c;只靠49个男人用一套方法卖白酒…

2010年认证杯SPSSPRO杯数学建模D题(第一阶段)服务网点的分布全过程文档及程序

2010年认证杯SPSSPRO杯数学建模 D题 服务网点的分布 原题再现&#xff1a; 服务网点、通讯基站的设置&#xff0c;都存在如何设置较少的站点&#xff0c;获得较大效益的问题。通讯基站的覆盖范围一般是圆形的&#xff0c;而消防、快餐、快递服务则受到道路情况和到达时间的限…

[图解]实现领域驱动设计译文暴露的问题01

0 00:00:00,430 --> 00:00:03,470 今天呢&#xff0c;我们来说一个主题 1 00:00:03,810 --> 00:00:04,041 2 00:00:04,041 --> 00:00:05,430 我们来谈一谈 3 00:00:05,960 --> 00:00:07,710 实现领域驱动设计 4 00:00:09,120 --> 00:00:11,070 这本书的中译本…

Android使用Chaquo来运行Python的librosa的相关代码【有详细案例教程】

在某些情况下&#xff0c;我们可能需要在android上运行python的代码&#xff0c;那么常见的解释器有很多&#xff0c;目前比较成熟的就是chaquo&#xff0c;它适配的第三方机器学习的库很多&#xff0c;下面是它的简单使用教程 1.环境的搭建 1.1 在Android studio中新建安卓工…

社交媒体数据恢复:飞书

飞书数据恢复过程包括以下几个步骤&#xff1a; 确认数据丢失&#xff1a;首先要确认数据是否真的丢失&#xff0c;有时候可能只是被隐藏或者误操作删除了。 检查回收站&#xff1a;飞书中删除的文件会默认保存在回收站中&#xff0c;用户可以通过进入回收站找到被删除的文件&…

springboot整合redis多数据源(附带RedisUtil)

单数据源RedisUtil(静态) 单数据源RedisUtil,我这里implements ApplicationContextAware在setApplicationContext注入redisTemplate,工具类可以直接类RedisUtil.StringOps.get()使用 package com.vehicle.manager.core.util;import com.alibaba.fastjson.JSON; import lombok.e…

如何向Linux内核提交开源补丁?

2021年&#xff0c;我曾经在openEuler社区上看到一项改进Linux内核工具的需求&#xff0c;因此参与过Linux内核社区的开源贡献。贡献开源社区的流程都可以在内核社区文档中找到&#xff0c;但是&#xff0c;单独学习需要一个较长的过程&#xff0c;新手难以入门&#xff0c;因此…

AI 数据观 | TapData Cloud + MongoDB Atlas:大模型与 RAG 技术有机结合,落地实时工单处理智能化解决方案

本篇为「AI 数据观」系列文章第二弹&#xff0c;在这里&#xff0c;我们将进一步探讨 AI 行业的数据价值。以 RAG 的智能工单应用场景为例&#xff0c;共同探索如何使用 Tapdata Cloud MongoDB Atlas 实现具备实时更新能力的向量数据库&#xff0c;为企业工单处理的智能化和自…

[C/C++] -- 大数的加减法

大数加减法的问题主要产生于计算机基本数据类型的表示范围限制。通常情况下&#xff0c;计算机采用有限位数的数据类型&#xff08;如int、long&#xff09;来表示整数&#xff0c;这些数据类型的表示范围有限&#xff0c;无法表示超出范围的大整数。 例如超过了long类型的表示…

【JavaScript】内置对象 - 数组对象 ⑤ ( 数组转字符串 | toString 方法 | join 方法 )

文章目录 一、数组转字符串1、数组转字符串 ( 逗号分割 ) - toString()2、数组转字符串 ( 自定义分割符 ) - join() Array 数组对象参考文档 : https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array 一、数组转字符串 1、数组转字符串 ( 逗…

指针(脑图梳理)

今天让我们来梳理一下指针都有哪些概念吧 这个脑图是整理的一些指针相关知识的概念&#xff0c;希望对大家有帮助

Web前端开发 小实训(三) 商品秒杀小练习

学生能够在本次实训中完成商品秒杀页面的基本逻辑 任务要求 能够实现某一个商品的秒杀&#xff0c;在倒计时结束后不再进行秒杀。 操作步骤 1、打开预设好的页面 <html><head><meta charset"utf-8"><title>秒杀</title><link …

python中如何把list变成字符串

python中如何把list变成字符串&#xff1f;方法如下&#xff1a; python中list可以直接转字符串&#xff0c;例如&#xff1a; data ["hello", "world"] print(data1:,str(data)) 得到结果&#xff1a; (data1:, "[hello, world]") 这里将整个…

视频号小店究竟有什么秘密,值得商家疯狂入驻,商家必看!

大家好&#xff0c;我是电商花花。 我们都知道视频号和抖音本身都是一个短视频平台&#xff0c;但是随着直播电商的发展&#xff0c;背后的流量推动逐步显露出强大的红利市场和变现机会。 视频号小店流量大和赚钱之外&#xff0c;还非常适合普通人创业。 这也使得越来越多的…