STM32 PWM驱动设计

news2025/1/19 20:17:02

单片机学习!

目录

文章目录

前言

一、PWM驱动配置步骤

二、代码示例及注意事项

2.1 RCC开启时钟

2.2 配置时基单元

2.3 配置输出比较单元

2.4 配置GPIO

2.5 运行控制

三、PWM周期和占空比计算

总结


前言

        PWM本质是利用面积等效原理来改变波形的有效值。


一、PWM驱动配置步骤

第一步、RCC开启时钟,将需要的TIM外设和GPIO外设的时钟打开。

第二步、配置时基单元,包括时钟源选择的配置。

第三步、配置输出比较单元,包括CCR的值、输出比较模式、极性选择、输出使能这些参数。(在库函数中也是用结构体统一来配置)。

第四步、配置GPIO,把PWM对应的GPIO口初始化为复用推挽输出的配置。

第五步、运行控制,启动计数器就可以输出PWM了。

二、代码示例及注意事项

2.1 RCC开启时钟

        将需要的TIM外设GPIO外设的时钟打开。打开时钟后定时器的基准时钟和整个外设的工作时钟就会同时打开。

代码示例:

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//使用APB1的开启时钟函数,因为TIM2是APB1总线的外设。
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

2.2 配置时基单元

        配置时基单元,包括时钟源选择的配置。

  1. 时基单元的选择时钟源。对于定时中断可选择内部时钟源。
  2. 配置时基单元,包括预分频器、自动重装器、计数模式等,这些参数可用结构体配置。

代码示例:

	TIM_InternalClockConfig(TIM2);

    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision= TIM_CKD_DIV1;//指定时钟分频
	TIM_TimeBaseInitStructure.TIM_CounterMode= TIM_CounterMode_Up;//计数器模式
	TIM_TimeBaseInitStructure.TIM_Period= 100-1;//ARR自动重装器的值
	TIM_TimeBaseInitStructure.TIM_Prescaler= 720-1;//PSC预分频器的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter= 0;//重复计数器的值
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);

结构体成员:

  • TIM_ClockDivision:指定时钟分频,用于信号经过滤波器时的滤波采样频率。
  • TIM_CounterMode:计数器模式,这里选择向上计数模式TIM_CounterMode_Up。
  • TIM_Period :ARR自动重装器的值。
  • TIM_Prescaler:PSC预分频器的值。
  • TIM_RepetitionCounter:重复计数器的值,只有高级定时器才有,本文初始化通用寄存器,所以值给0。

        时基单元中关键寄存器参数ARR、PSC都有设置,但是这里没有CNT计数器的参数,CNT参数的配置可根据需要在函数 TIM_SetCounter 和函数 TIM_GetCounter 中操作。

        决定定时时间的参数是 TIM_Period 和 TIM_Prescaler 。定时时间可用计数器溢出频率公式计算,定时频率=72M/(PSC+1)/(ARR+1)。

2.3 配置输出比较单元

        配置输出比较单元,包括CCR的值、输出比较模式、极性选择、输出使能这些参数。(在库函数中也是用结构体统一来配置)。

void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

        这里4个初始化函数对应4个输出比较通道(单元),不同的通道对应的GPIO口也不一样,函数的选择需要根据GPIO口的配置选择。代码示例中使用PA0口,对应第一个输出比较通道,选择TIM_OC1Init函数。

代码示例:

	TIM_OCInitTypeDef TIM_OCInitStructture;
	
	TIM_OCStructInit(&TIM_OCInitStructture);
	TIM_OCInitStructture.TIM_OCMode=TIM_OCMode_PWM1; //设置输出比较的模式。
	TIM_OCInitStructture.TIM_OCPolarity=TIM_OCPolarity_High; //设置输出比较的极性
	TIM_OCInitStructture.TIM_OutputState=TIM_OutputState_Enable; //设置输出使能
	TIM_OCInitStructture.TIM_Pulse=50; //设置CCR
	TIM_OC1Init(TIM2,&TIM_OCInitStructture);   

        代码示例中的程序并没有给结构体的所有成员赋值,而是用TIM_OCStructInit函数先给结构体成员赋一个初始值,再修改部分的结构体成员。这个结构体变量是局部变量,若不给成员赋初始值,它成员的值就是不确定的,这会导致一些问题。比如当你想把高级定时器当作通用定时器输出PWM时,只配置了通用定时器需要的结构体成员,剩下这些没赋值成员就会导致高级定时器输出PWM出现一些奇怪的问题。

        TIM_OCStructInit函数需要把结构体变量的地址传进去,才能给结构体赋初始值。

1. TIM_OCMode设置输出比较的模式。

        TIM_OCMode输出比较模式参数对应含义:

  • TIM_OCMode_Timing 冻结模式                
  • TIM_OCMode_Active 相等时置有效电平                 
  • TIM_OCMode_Inactive 相等时置无效电平               
  • TIM_OCMode_Toggle 相等时电平翻转                 
  • TIM_OCMode_PWM1 PWM模式1                   
  • TIM_OCMode_PWM2 PWM模式2                   
  • TIM_ForcedAction_Active 强制输出有效电平
  • TIM_ForcedAction_InActive 强制输出无效电平

        注:强制输出两种模式的参数不可以在初始化时使用

2. TIM_OCPolarity设置输出比较的极性。

  • TIM_OCPolarity_High 高级性,是极性不翻转,REF波形直接输出。意思是有效电平是高电平,REF有效时,输出高电平。
  • TIM_OCPolarity_Low  低级性,REF电平取反,意思是有效电平为低电平。

3. TIM_OutputState设置输出使能。

4.TIM_Pulse置CCR。 ARR,PSC,CCR共同决定PWM的周期和占空比
 

        以上配置已经把输出比较通道初始化好了,在TIM2的OC1通道上就可以输出PWM波形了,最终这个波形需要通过GPIO口才可以输出。那TIM2的OC1通道是借用哪个GPIO口呢?下文来选择并配置GPIO。


注:TIM_OC1Init是通道1的初始化函数,若通道1、2、3、4都需要的话可以直接在后面加TIM_OC2Init、TIM_OC3Init、TIM_OC4Init,这样就可以同时使用4个通道来输出4个PWM了。

代码示例:

	TIM_OCInitTypeDef TIM_OCInitStructture;
	
	TIM_OCStructInit(&TIM_OCInitStructture);
	TIM_OCInitStructture.TIM_OCMode=TIM_OCMode_PWM1; //设置输出比较的模式。
	TIM_OCInitStructture.TIM_OCPolarity=TIM_OCPolarity_High; //设置输出比较的极性
	TIM_OCInitStructture.TIM_OutputState=TIM_OutputState_Enable; //设置输出使能
	TIM_OCInitStructture.TIM_Pulse=50; //设置CCR
	TIM_OC1Init(TIM2,&TIM_OCInitStructture);
    TIM_OC2Init(TIM2,&TIM_OCInitStructture);
    TIM_OC3Init(TIM2,&TIM_OCInitStructture);
    TIM_OC4Init(TIM2,&TIM_OCInitStructture);   

        同一个定时器的不同通道输出的PWM,因为不同通道是共用一个计数器的,所以它们的频率必须是一样的;它们的占空比由各自的CCR决定,所以占空比可以各自设定;他们的相位由于计数器更新,所有PWM同时跳变,所以它们的相位是同步的。

        这就是同一个定时器不同通道输出PWM的特点。如果使用多个设备如电机或者舵机,那使用同一个定时器不同通道的PWM就完全可以。


2.4 配置GPIO

        输出比较通道借用GPIO口可以在引脚定义表中查看:

        默认复用功能这一列就是片上外设的端口和GPIO的连接关系。可以找到 TIM2_CH1_ETR 在 PA0 这一行,这说明 TIM2 的 ETR 引脚和通道1的引脚都是借用了 PA0 这个引脚位置,就是TIM2的引脚复用在了PA0引脚上。所以使用TIM2的OC1通道也就是CH1通道输出PWM,就只能在PA0引脚上输出,而不能任意选择引脚输出。

同理:

  • 使用TIM2_CH2只能在PA1端口输出
  • 使用TIM2_CH3只能在PA2端口输出
  • 使用TIM2_CH4只能在PA3端口输出

表中其他外设也是同理:如使用SPI1_MISO只能在PA6端口输出。虽然引脚与外设都是规定好的,但是还可以根据情况做一次改动。在引脚定义表重定义(重映射)这一列,还可以对应更改。如既要使用 USART2_TX 引脚又要使用 TIM2_CH3 通道,但是它俩都在PA2端口输出,这就冲突了没法同时使用。这时可以在重定义列表里找一下,有 TIM2_CH3 ,那么 TIM2_CH3 就可以从PA2端口输出换为从PB10端口输出。这样就避免了两个外设引脚的冲突。但是如果重映射的列表里找不到,那外设复用的GPIO就不能挪位置。配置重映射需要用AFIO来完成。

        配置GPIO,把PWM对应的GPIO口初始化为复用推挽输出的配置。

代码示例:

	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_PP;//这里选择复用推挽输出。
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);

        这里选择复用推挽输出。
        原因:对于普通的开漏/推挽输出,引脚的控制权是来自于输出数据寄存器的,定时器控制引脚需要使用复用开漏/推挽输出的模式,复用开漏/推挽输出模式中输出数据寄存器将被断开,输出控制权将转移给片上外设,通过引脚定义表可知,这里片上外设引脚连接的就是TIM2的CH1通道。所以只有把GPIO设置为复用推挽输出,引脚的控制权才能交给片上外设,PWM波形才能通过引脚输出。
  

2.5 运行控制

        运行控制:整个模块配置完成后,还需要使能一下计数器,PWM波形就能通过PA0输出了。

代码示例:

    TIM_Cmd(TIM2,ENABLE);

三、PWM周期和占空比计算

计算公式:

  • PWM频率:    Freq = CK_PSC / (PSC + 1) / (ARR + 1)      PWM频率等于计数器更新频率。
  • PWM占空比:    Duty = CCR / (ARR + 1)
  • PWM分辨率:    Reso = 1 / (ARR + 1)

        ARR、PSC、CCR共同决定PWM的周期和占空比

代码示例中产生的是一个频率为1KHHz,占空比为50%,分辨率为1%的PWM波形。

代入公式:

  • 72M/(PSC+1)/(ARR+1)=1000
  • CCR/(ARR+1)=50%
  • 1/(ARR+1)=1%

得出:

  • (ARR+1)=100
  • CCR=50
  • (PSC+1)=720

对应代码中:

  • ARR 给 100-1;
  • PSC 给 720-1;
  • CCR 给 50。

        代码运行时,PWM占空比也可更改,可选择使用TIM_SetCompare1、TIM_SetCompare2、TIM_SetCompare3、TIM_SetCompare4 函数改变CCR寄存器的值来调节PWM占空比。但是PWM的占空比是由ARR、PSC、CCR共同决定。


总结

        以上就是今天要讲的内容,本文仅仅简单介绍了PWM驱动配置。

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

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

相关文章

2024最新版Java Development Kit (JDK)安装使用指南

2024最新版Java Development Kit (JDK)安装使用指南 Installation and Configuration Guide of the latest version Java Development Kit (JDK) in 2024 By JacksonML 0. 序言 What is Java? Java is a programming language and computing platform first released by Su…

day23 其他事件(页面加载事件、页面滚动事件)

目录 页面加载事件页面/元素滚动事件页面滚动事件——获取位置 页面加载事件 加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件为什么使用: 有时候需要等页面资源全部处理完毕再做一些事老代码喜欢把script写在head中&…

【JavaScript 漫游】专栏介绍

专栏介绍 本专栏旨在记录 JavaScript 核心语法,作为笔者日常学习和工作中的参考手册和代码示例仓库。 内容上力求覆盖 ES5、DOM、BOM 和 ES6 规范的所有内容。对于常用且重要的知识点,应该详细描述并附带有大量的代码示例。对于在工作场景中很少用到的…

12.14 回退流(血干JAVA系列)

回退流 12.14 回退流【例12.67】操作回退流 12.14 回退流 表 12-23 PushbacklnputStream 类的常用方法 表12-24回退流与输入流的对应 【例12.67】操作回退流 package jiaqi;import java.io.ByteArrayInputStream; import java.io.PushbackInputStream;public class demo43…

E5071C 是德科技网络分析仪

181/2461/8938产品概述: E5071C ENA 矢量网络分析仪,9 kHz 至 20 GHz,配有增强型 TDR 测量选件。 E5071C 是大规模无源元器件测试的理想解决方案。 它具有出色的测量性能,有助于提高测试吞吐量,尤其是与 E5092A 多端…

1948-2022年金融许可信息明细数据

1948-2022年金融许可信息明细数据 1、时间:1948-2022年 2、来源:银监会(银监会许可证发布系统) 3、指标:来源表、机构编码、机构名称、所属银行、机构类型、业务范围、机构住所、地理坐标、行政区划代码、所属区县、…

视频调色 -- 达芬奇DaVinci Resolve Studio 18中文

达芬奇DaVinci Resolve Studio 18是一款功能强大的视频后期处理软件,集视频编辑、色彩校正、音频后期制作等多项功能于一身。该软件支持多种轨道编辑和时间线管理,使视频剪辑和处理更加高效。其高精度的色彩校正技术,能够精确地对影片进行校色…

PHP语法

#本来是在学命令执行,所以学了学,后来发现,PHP语法和命令执行的关系好像没有那么大,不如直接学php的一些命令执行函数了。# #但是还是更一下,毕竟还是很多地方都要求掌握php作为脚本语言,所以就学了前面的…

Linux第37步_解决“Boot interface 6 not supported”之问题

在使用USB OTG将“自己移植的固件”烧写到eMMC中时,串口会输出“Boot interface 6 not supported”,发现很多人踩坑,我也一样。 见下图: 解决办法: 1、打开终端 输入“ls回车”,列出当前目录下所有的文件…

自然语言处理:transfomer架构

介绍 transfomer是自然语言处理中的一个重要神经网络结构,算是在传统RNN和LSTM上的一个升级,接下来让我们来看看它有处理语言序列上有哪些特殊之处 模型整体架构 原论文中模型的整体架构如下,接下来我们将层层解析各层的作用和代码实现 该…

docker环境搭建及其安装常用软件

centos安装docker Install Docker Engine on CentOS | Docker Docs 下载docker sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce docker-ce-cli containerd.io…

Qt6入门教程 12:QAbstractButton

目录 一.状态 二.信号 三.使用 1.自定义按钮 2.多选 3.互斥 QAbstractButton类实现了一个抽象按钮,并且让它的子类来指定如何处理用户的动作,并指定如何绘制按钮。QAbstractButton类是所有按钮控件的基类。 QAbstractButton提供…

SpringCloud-高级篇(十七)

(1)添加Redis缓存 -缓存预热 前面实现了openResty查询tomcat,但是缓存架构是,先查询Redis,Redis没有在查询tomcat,下面实现Redis的缓存功能了: --appendonly yes 运行的时候基于日志的方式做数…

寒假思维训练计划day16 A. Did We Get Everything Covered?

今天更新一道1月27号晚上div2的C题作为素材,感觉用到了我的构造题总结模型,我总结了一系列的模型和例题。 摘要: Part1 定义"边界贪心法" Part2 题意 Part3 题解 Part4 代码 Part5 思维构造题模型和例题 Part1 边界贪心…

【分布式技术专题】「探索高性能远程通信」基于Netty的分布式通信框架实现(附通信协议和代码)(上)

基于Netty的分布式通信框架实现 前提介绍回顾Dubbo分布式通信框架组成元素程序执行流程消息协议设计实现机制ChannelInboundHandlerAdapter自定义事件处理 ChannelOutboundHandlerAdapter 编(解)码处理器编码过程阶段ChannelOutboundHandlerAdapter序列化实现ChannelOutboundHa…

Android App开发-简单控件(4)——按钮触控和图像显示

3.4 按钮触控 本节介绍了按钮控件的常见用法,包括:如何设置大小写属性与点击属性,如何响应按钮的点击事件和长按事件,如何禁用按钮又该如何启用按钮,等等。 3.4.1 按钮控件Button 除了文本视图之外,按钮…

C++: 内联函数

目录 概念: 与宏的对比: 函数膨胀: 内联函数的特性: 概念: 以inline修饰的函数叫做内联函数,编译时C编译器会在调用内联函数的地方展开,没有函数调 用建立栈帧的开销,内联函数…

DLL劫持之IAT类型(Loadlibrary)

Loadlibrary Loadlibrary的底层是LoadLibraryEx 第三个参数: DONT_RESOLVE_DLL_REFERENCES : 这个标志用于告诉系统将DLL映射到调用进程的地址空间中,但是不调用DllMain并且不加载依赖Dll(只映射自己本身)。 LOAD_LIBRARY_AS_DA…

【代码随想录-数组】螺旋矩阵 II

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

高级IO之epoll模型

一、epoll模型介绍 epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,用于监视一个或多个文件描述符,以查看它们是否可以进行读取、写入或异常处理。它能够显著提高程序在大量并发连接…