STM32基础教程——PWM驱动LED呼吸灯

目录

前言

技术实现 

原理图

接线图

代码实现 

 内容要点

PWM基本结构

开启外设时钟

配置GPIO端口

 

配置时基单元

 初始化输出比较单元

输出PWM波形

输出比较通道重映射 


前言

PWM(Pulse Width Modulation):一种通过调节脉冲信号的占空比(高电平持续时间与整个周期的比值)来控制输出功率或模拟信号的技术。它广泛应用于电机控制、LED亮度调节、音频信号生成以及电源管理等领域。通过改变占空比,PWM可以在保持频率不变的情况下精确地控制设备的能量输入或输出,同时具有高效、易于实现和低功耗的优点。

PWM参数 

频率 = 1 / TS           

占空比 = TON / TS         

分辨率 = 占空比变化步距

技术实现 

原理图

接线图

代码实现 

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"						//延时函数
#include "OLED.h"
#include "PWM.h"uint8_t duty;							//控制PWM占空比int main(void)
{/*OLED初始化*/OLED_Init();PWM_Init();while(1){/*输出PWM波形(此处使用一个变量即可,多一个变量将会多占用存储空间)*/for(duty=0;duty<=100;duty++){PWM_SetComparel(duty);Delay_ms(10);}for(duty=0;duty<=100;duty++){PWM_SetComparel(100-duty);Delay_ms(10);}}
}

PWM.h 

#ifndef __PWM_H__
#define __PWM_H__#include "stm32f10x.h"                  // Device headervoid PWM_Init(void);
void PWM_SetComparel(uint16_t Compare);#endif

 PWM.c

#include "PWM.h"/*** @brief	PWM初始化函数* @param  None* @retval None* @note   输出频率为1kHz,占空比为50%,分辨率为1%的PWM波形
**/
void PWM_Init(void)
{/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);						//开启TIM2时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);					//开启GPIOA时钟
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);						//开启AFIO时钟
//	
//	/*
//		引脚重映射
//	*/
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);					//将TIM2的CH1从PA0重映射到PA15
//	/*
//		解除引脚复用
//	*/
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);					//PA15引脚作为JTD1功能使用,此处解除AFIO的JTAG复用/*配置端口*/GPIO_InitTypeDef GPIO_InitStruct;								 		//定义结构体GPIO_InitStruct.GPIO_Pin 	= GPIO_Pin_0;				 				//选择PA0    //重映射到GPIO_Pin_15GPIO_InitStruct.GPIO_Mode 	= GPIO_Mode_AF_PP;							//复用推挽输出,将引脚的控制权交给片上外设GPIO_InitStruct.GPIO_Speed 	= GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);/*选择TIM时钟*/TIM_InternalClockConfig(TIM2);/*配置时基单元*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;							//定义一个TIM_TimeBaseInitTypeDef类型的结构体用于初始化时基单元TIM_TimeBaseInitStruct.TIM_ClockDivision     = TIM_CKD_DIV1;			//设置时钟分频模式为不分TIM_TimeBaseInitStruct.TIM_CounterMode       = TIM_CounterMode_Up;		//计数方式设置为向上计数模式/*输出的PWM波形的频率为1kHz,分辨率为1%,占空比为50%PWM频率:	Freq = CK_PSC / (PSC + 1) / (ARR + 1)PWM占空比:	Duty = CCR / (ARR + 1)PWM分辨率:	Reso = 1 / (ARR + 1)*/TIM_TimeBaseInitStruct.TIM_Period            = 100-1;					//ARR,自动加载重装寄存器,要写入自动重装值TIM_TimeBaseInitStruct.TIM_Prescaler         = 720-1;					//PSC,预分频器的分频值TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;						//重复计数,只有高级定时器才会使用,通用定时器用不到TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);							//初始化时基单元/*初始化输出比较单元*/TIM_OCInitTypeDef TIM_OCInitStruct;										//定义一个TIM_OCInitTypeDef类型的结构体用以输出比较单元初始化TIM_OCStructInit(&TIM_OCInitStruct);									//给每个结构体成员赋初始值,防止因未使用到的结构体成员未初始化而导致程序出现错误TIM_OCInitStruct.TIM_OCMode 		= TIM_OCMode_PWM1;					//选择PWM模式1TIM_OCInitStruct.TIM_OCPolarity 	= TIM_OCPolarity_High;				//OC1有效电平为高电平TIM_OCInitStruct.TIM_OutputState 	= TIM_OutputState_Enable;			//输出比较使能/*输出PWM的波形占空比PWM占空比:	Duty = CCR / (ARR + 1)*/TIM_OCInitStruct.TIM_Pulse 			= 0;								//CCR,捕获/比较器,本工程中有封装的函数用于改变占空比,此处先设置为0TIM_OC1Init(TIM2,&TIM_OCInitStruct);TIM_Cmd(TIM2,ENABLE);													//启动TIM2
}/*** @brief	指定CCR寄存器的值改变PWM Duty* @param  None* @retval None* @note   None
**/
void PWM_SetComparel(uint16_t Compare)
{TIM_SetCompare1(TIM2,Compare);
}

 Delay.h

#ifndef __DELAY_H
#define __DELAY_Hvoid Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);#endif

Delay.c 

#include "stm32f10x.h"/*** @brief  微秒级延时* @param  xus 延时时长,范围:0~233015* @retval 无*/
void Delay_us(uint32_t xus)
{SysTick->LOAD = 72 * xus;				//设置定时器重装值SysTick->VAL = 0x00;					//清空当前计数值SysTick->CTRL = 0x00000005;				//设置时钟源为HCLK,启动定时器while(!(SysTick->CTRL & 0x00010000));	//等待计数到0SysTick->CTRL = 0x00000004;				//关闭定时器
}/*** @brief  毫秒级延时* @param  xms 延时时长,范围:0~4294967295* @retval 无*/
void Delay_ms(uint32_t xms)
{while(xms--){Delay_us(1000);}
}/*** @brief  秒级延时* @param  xs 延时时长,范围:0~4294967295* @retval 无*/
void Delay_s(uint32_t xs)
{while(xs--){Delay_ms(1000);}
} 

 内容要点

PWM基本结构

开启外设时钟

/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);						//开启TIM2时钟

本实验使用TIM2输出调制PWM波来驱动LED呼吸灯,TIM2属于APB1外设,调用RCC_APB1PeriphClockCmd()函数来开启APB1外设时钟。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);					//开启GPIOA时钟

根据STM32引脚的定义,PA0的默认复用功能为TIM2的输出比较通道之一TIM2_CH1,GPIOA隶属于APB2外设,调用RCC_APB2PeriphClockCmd()函数开启APB2外设时钟。

配置GPIO端口

/*配置端口*/GPIO_InitTypeDef GPIO_InitStruct;								 		//定义结构体GPIO_InitStruct.GPIO_Pin 	= GPIO_Pin_0;				 				//选择PA0    //重映射到GPIO_Pin_15GPIO_InitStruct.GPIO_Mode 	= GPIO_Mode_AF_PP;							//复用推挽输出,将引脚的控制权交给片上外设GPIO_InitStruct.GPIO_Speed 	= GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);

GPIO初始化中,初始化结构体GPIO_InitStruct结构体成员,将GPIO引脚设置为PA0 Pin,将GPIO模式设置为复用推挽输出,因为GPIO的主功能为PA0 IO口,这里使用其默认复用功能,故应将GPIO模式设置为复用推挽,使用普通的推挽输出无法输出PWM波形。

配置时基单元

/*选择TIM时钟*/TIM_InternalClockConfig(TIM2);

选择TIM时钟为内部时钟

/*配置时基单元*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;							//定义一个TIM_TimeBaseInitTypeDef类型的结构体用于初始化时基单元TIM_TimeBaseInitStruct.TIM_ClockDivision     = TIM_CKD_DIV1;			//设置时钟分频模式为不分TIM_TimeBaseInitStruct.TIM_CounterMode       = TIM_CounterMode_Up;		//计数方式设置为向上计数模式/*输出的PWM波形的频率为1kHz,分辨率为1%PWM频率:	Freq = CK_PSC / (PSC + 1) / (ARR + 1)PWM占空比:	Duty = CCR / (ARR + 1)PWM分辨率:	Reso = 1 / (ARR + 1)*/TIM_TimeBaseInitStruct.TIM_Period            = 100-1;					//ARR,自动加载重装寄存器,要写入自动重装值TIM_TimeBaseInitStruct.TIM_Prescaler         = 720-1;					//PSC,预分频器的分频值TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;						//重复计数,只有高级定时器才会使用,通用定时器用不到TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);							//初始化时基单元

实验中驱动LED呼吸灯 ,输出的PWM波形为占空比(Duty)从0->100,然后Duty从100->0.按照输出PWM频率为1kHz,分辨率为1%计算出ARR(自动重装寄存器)和PSC(预分频器)的值。

PWM频率:        Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM占空比:    Duty = CCR / (ARR + 1)
PWM分辨率:    Reso = 1 / (ARR + 1) 

 初始化输出比较单元

/*初始化输出比较单元*/TIM_OCInitTypeDef TIM_OCInitStruct;										//定义一个TIM_OCInitTypeDef类型的结构体用以输出比较单元初始化TIM_OCStructInit(&TIM_OCInitStruct);									//给每个结构体成员赋初始值,防止因未使用到的结构体成员未初始化而导致程序出现错误TIM_OCInitStruct.TIM_OCMode 		= TIM_OCMode_PWM1;					//选择PWM模式1TIM_OCInitStruct.TIM_OCPolarity 	= TIM_OCPolarity_High;				//OC1有效电平为高电平TIM_OCInitStruct.TIM_OutputState 	= TIM_OutputState_Enable;			//输出比较使能/*输出PWM的波形占空比PWM占空比:	Duty = CCR / (ARR + 1)*/TIM_OCInitStruct.TIM_Pulse 			= 0;								//CCR,捕获/比较器,本工程中有封装的函数用于改变占空比,此处先设置为0TIM_OC1Init(TIM2,&TIM_OCInitStruct);

初始化输出比较单元时,在初始化TIM输出比较结构体之前调用TIM_OCStructInit()函数给结构体的每个成员初始化赋默认值,防止在实验中未使用到输出比较单元中的某些功能且未初始化这些功能对应的结构体成员时,发生意外错误。

将输出比较单元的输出模式设置为PWM模式1。

设置输出极性为高电平有效,即PWM模式1下计数器的值小于CCR时为高电平,反之为低电平。

此处是通过设置捕获比较使能寄存器TIMx_CCER位1CC1P设置输入/捕获1输出极性

此处将CCR寄存器的值设置为0,实验中使用封装的函数 void PWM_SetComparel(uint16_t Compare)来调用TIM_SetCompare1()函数来改变CCR寄存器的值实现占空比的改变。

/*** @brief	指定CCR寄存器的值改变PWM Duty* @param  None* @retval None* @note   None
**/
void PWM_SetComparel(uint16_t Compare)
{TIM_SetCompare1(TIM2,Compare);
}

输出PWM波形

uint8_t duty;							//控制PWM占空比

定义一个uint8_t类型的变量duty来控制PWM占空比。

while(1){/*输出PWM波形(此处使用一个变量即可,多一个变量将会多占用存储空间)*/for(duty=0;duty<=100;duty++){PWM_SetComparel(duty);Delay_ms(10);}for(duty=0;duty<=100;duty++){PWM_SetComparel(100-duty);Delay_ms(10);}}

通过两个for循环实现PWM占空比从0->100,然后从100->0的变化达到呼吸灯效果,每次改变占空比后使用延时函数延时10ms,防止变化太快实现效果不明显。不同占空比的高电平驱动LED实现LED呼吸灯效果。

此处使用一个变量即可,占空比从100->0使用100-duty即可,for循环的条件不用改变。多定义一个变量会占用额外的空间

输出比较通道重映射 

//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);						//开启AFIO时钟
//	
//	/*
//		引脚重映射
//	*/
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);					//将TIM2的CH1从PA0重映射到PA15
//	/*
//		解除引脚复用
//	*/
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);					//PA15引脚作为JTD1功能使用,此处解除AFIO的JTAG复用

在开启TIM2和GPIOA时钟后,开启AFIO时钟,调用GPIO_PinRemapConfig()将TIM2_CH1从PA0引脚重映射到PA15引脚。由于PA15默认为JTAG调试功能,调用GPIO_PinRemapConfig()函数解除引脚复用,PA15引脚作为JTD1功能使用,解除AFIO的JTAG复用。(如果不使用重映射功能,则忽略这部分)

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

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

相关文章

算法基础——栈

一、栈的概念 栈是⼀种只允许在⼀端进⾏数据插⼊和删除操作的线性表。 进⾏数据插⼊或删除的⼀端称为栈顶&#xff0c;另⼀端称为栈底。不含元素的栈称为空栈。进栈就是往栈中放⼊元素&#xff0c;出栈就是将元素弹出栈顶。 二、栈的模拟实现 1. 创建 本质还是线性表&#…

JVM类文件结构详解

文章目录 前言代码示例1.魔数2.版本3.常量池4.访问标识与继承信息访问标识继承信息 5.Field 信息6 Method 信息构造方法分析(method-main)main方法分析(method-main) 7.附加属性 前言 在 Java 中&#xff0c;JVM 可以理解的代码就叫做字节码&#xff08;即扩展名为 .class 的文…

5.安全相关(双手启动、安全触边传感器)

一、关于双手启动按钮的使用规范 本文介绍双手启动按钮的使用。概括来讲&#xff1a; 双手按下之间的时间差间隔应该在0.5-2秒之间。一旦释放任何一个按钮&#xff0c;启动信号输出结束。只有两个按钮都被释放之后&#xff0c;才能再次触发双手启动信号。如果某按钮被按下超过…

电脑上不了网普通用户排除方法

1&#xff1a;首先通过电脑的运行/CMD/ipconfig /all 命令查看电脑的ip地址是否正常如图&#xff1a; 2&#xff1a;在命令行中运行&#xff1a;ping 127.0.0.1 如图则正常&#xff0c;否则要重新安装网卡驱动 程序。 3&#xff1a;用ping命令&#xff0c;ping一下同网段的电…

【中文翻译】第3章(1/3)-The Algorithmic Foundations of Differential Privacy

为方便阅读&#xff0c;故将《The Algorithmic Foundations of Differential Privacy》翻译项目内容搬运至此&#xff1b; 教材原文地址&#xff1a;https://www.cis.upenn.edu/~aaroth/Papers/privacybook.pdf 中文翻译版 Github 项目地址1&#xff1a;https://github.com/gu…

2.1词法分析任务

编译器结构 前端 词法分析器的任务 字符流到记号流 eg: 把这些单词就叫做记号 EOF结束符号&#xff01;

Linux操作系统7- 线程同步与互斥5(POSIX条件变量生产者消费者模型的进一步使用)

上篇文章&#xff1a;Linux操作系统7- 线程同步与互斥4&#xff08;基于POSIX条件变量的生产者消费者模型&#xff09;-CSDN博客 本篇代码仓库&#xff1a; 支持处理简单任务的生产者消费者模型代码 生产者-消费者-保存者三线程两队列模型 多生产多消费的生产者消费者模型 进一…

【嵌入式学习2】C语言 - VScode环境搭建

目录 ## 语言分类 ## c语言编译器 ## VScode相关配置 ## 语言分类 编译型语言&#xff1a;C&#xff0c;C解释型语言&#xff1a;python&#xff0c;JS ## c语言编译器 分类GCC 系列MinGWCygwinMSVC系列一套编程语言编译器将GCC编译器和GNU Binutils移植到Win32平台下的产物…

使用Doris broker load导入数据到带Kerberos的HA HDFS的命令详解

以下是关于 Doris Broker Load 结合 Kerberos 认证的 HDFS 数据导入的详细解析&#xff1a; 一、Broker Load 核心原理 Broker Load 是 Doris 中用于从 HDFS/S3 等远程存储系统异步导入大数据的工具&#xff0c;其核心流程如下&#xff1a; 任务提交&#xff1a;用户通过 SQL…

数字化转型 2.0:AI、低代码与智能分析如何重塑企业竞争力?

引言&#xff1a;数字化转型进入2.0时代 在过去的十几年里&#xff0c;企业的数字化转型&#xff08;1.0&#xff09;主要围绕信息化和自动化展开&#xff0c;例如引入ERP、CRM等系统&#xff0c;提高办公效率&#xff0c;减少人为失误。然而&#xff0c;随着市场竞争加剧&…

指针,数组 易混题解析(一)

目录 一.相关知识点 1.数组名是什么&#xff1f; 两个例外&#xff1a; 2.strlen 3.sizeof 4. * ( ) 与 [ ] 的互换 二.一维数组 三.字符数组 1. 字符 &#xff08;1&#xff09;sizeof &#xff08;2&#xff09;strlen 2.字符串 &#xff08;1&#xff09;si…

ABC392题解

A 算法标签: 模拟 #include <iostream> #include <algorithm> #include <cstring>using namespace std;int main() {ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);int w[3];for (int i 0; i < 3; i) cin >> w[i];sort(w, w 3);if (w[0]…

Quartus + VScode 实现模块化流水灯

文章目录 一、通过VScode编写Verilog代码二、模块化编程三、代码示例 一、通过VScode编写Verilog代码 1、下载Vscode 2、下载相关插件 搜索Verilog就会弹出有如图所示的插件&#xff0c;下载并安装 3、创建Quartus项目 4、创建完成后点击Tools&#xff0c;选择Options 然后在…

简单讲一下控制系统所用的PID公式

2025年3月23日电子电工实验室讲课大纲 哈喽&#xff0c;小伙伴们大家好&#xff0c;今天我们来讲一下PID&#xff01;首先我们的针对的场景是什么——循迹小车&#xff01; 就是我们刚入手STM32时&#xff0c;我们可能会制作一个循迹小车。我们想让那个小车走直线&#xff0c;但…

直观理解ECC椭圆曲线加密算法

数学还是挺有逻辑的&#xff0c;给出计算的操作步骤 就能得出想要结果 背景&#xff1a; ● ECC 是一种极其巧妙的 非对称加密算法 , 其完美利用了 椭圆曲线几何累加 不可逆的性质 ● 拥有 密钥体积小&#xff0c;计算速度快的优势&#xff0c;被广泛用于各种区块链&#xff0c…

深度解析 Android Matrix 变换(二):组合变换 pre、post

前言 在上一篇文章中&#xff0c;我们讲解了 Canvas 中单个变换的原理和效果&#xff0c;即缩放、旋转和平移。但是单个旋转仅仅是基础&#xff0c;Canvas 变换最重要的是能够随意组合各种变换以实现想要的效果。在这种情况下&#xff0c;就需要了解如何组合变换&#xff0c;以…

c++之迭代器

一.迭代器的基本概念 1.什么是迭代器 迭代器是一种对象&#xff0c;它提供了一种访问容器中各个元素的方法&#xff0c;同时隐藏了容器内部的实现细节。简单来说&#xff0c;迭代器就像是一个指针&#xff0c;它可以指向容器中的某个元素&#xff0c;并且能够通过一些操作&am…

在 .NET 9.0 Web API 中实现 Scalar 接口文档及JWT集成

示例代码&#xff1a;https://download.csdn.net/download/hefeng_aspnet/90408075 介绍 随着 .NET 9 的发布&#xff0c;微软宣布他们将不再为任何 .NET API 项目提供默认的 Swagger gen UI。以前&#xff0c;当我们创建 .NET API 项目时&#xff0c;微软会自动添加 Swagger…

【操作系统笔记】操作系统的功能

上节课,我们学习了《什么是操作系统》。接下来,我们来看看操作系统有哪些功能? 这里讲的内容有两部分,一个是操作系统的目标,另外一个就是操作系统的功能。这两个细节可能会在考试的时候考到,但是最近好些年很少考到了。为了理解,我们还是一起来看一下。 操作系统的目标…

C/C++蓝桥杯算法真题打卡(Day7)

一、P8723 [蓝桥杯 2020 省 AB3] 乘法表 - 洛谷 算法代码&#xff1a; #include<bits/stdc.h> // 包含标准库中的所有头文件&#xff0c;通常用于竞赛编程中简化代码 using namespace std; // 使用标准命名空间&#xff0c;避免每次调用标准库函数时都要加std:: ty…