STM32 PWM驱动设计

单片机学习!

目录

文章目录

前言

一、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驱动配置。

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

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

相关文章

【electron】打包问题处理

目录 项目无法在win7执行场景尝试处理 项目无法在win7执行 场景 使用electron25.0.1、electron-builder24.2.1,打出来的项目在win7系统上跑不起来,报错无法定位程序输入点DiscardVirtualMemoty于动态链接库KERNEL32.dll上。 尝试处理 通过百度发现ele…

编曲学习:和声音程 调式体系 唱名法 调式调性

34届和声音程 调式体系 唱名法 调式调性https://app8epdhy0u9502.pc.xiaoe-tech.com/live_pc/l_65af994be4b064a8cb1c3a5f?course_idcourse_2XLKtQnQx9GrQHac7OPmHD9tqbv 34届独立音乐人编曲训练营https://app8epdhy0u9502.pc.xiaoe-tech.com/p/t_pc/course_pc_detail/camp_p…

Tomcat 简介安装

目录 1、概念介绍 Tomcat 组件 HTTP 请求过程 Tomcat 目录结构 Tomcat 命令 配置文件说明 2、安装环境 安装JDK 安装Tomcat 创建管理用户 3、搭建虚拟主机 1、概念介绍 端口:8080 Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器, 按照 Sun 提供…

DevOps系列文章之 GitLabCI汇总

GitlabCI环境搭建 前提 先安装 docker Docker容器化安装 docker pull gitlab/gitlab-ee:12.4.0-ee.0 创建挂载目录 mkdir -p /srv/gitlab mkdir -p /srv/gitlab/config # 映射到 Glitlab 容器中的配置目录 mkdir -p /srv/gitlab/logs # 映射到 Glitlab 容器中的日志目录 m…

ArXiv| Graph-Toolformer: 基于ChatGPT增强提示以赋予大语言模型图数据推理能力

ArXiv| Graph-Toolformer: 基于ChatGPT增强提示以赋予大语言模型图数据推理能力. 来自加利福利亚大学戴维斯分校计算机科学系的IFM实验室发表在arXiv上的文章:“Graph-ToolFormer: To Empower LLMs with Graph Reasoning Ability via Prompt Augmented by ChatGPT”。 文章的…

P1045 [NOIP2003 普及组] 麦森数题解

题目 形如的素数称为麦森数,这时P一定也是个素数。但反过来不一定,即如果P是个素数,不一定也是素数。到1998年底,人们已找到了37个麦森数。最大的一个是P3021377,它有909526位。麦森数有许多重要应用,它与…

pcl之滤波器(三)

pcl滤波器 pcl一共是有十二个主要模块,详细了解可以查看官网。https://pcl.readthedocs.io/projects/tutorials/en/latest/#basic-usage 今天学习一下pcl的滤波器模块。 滤波器模块,官网一共是提供了6个例程,今天看第五个、第六个。 从一…

P2246 SAC#1 - Hello World(升级版)

网址如下: P2246 SAC#1 - Hello World(升级版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 刚开始是用递归做的,虽然用了哈希表优化,但是超时,只得了50 后面想到了一个新的算法,时间复杂度…

Java笔记 --- 三、方法引用

三、方法引用 概述 分类 引用静态方法 引用成员方法 本类中注意,静态方法中没有this,需要创建本类的对象 引用构造方法 其他的调用方式 使用类名引用成员方法 引用数组的构造方法

【遥感专题系列】影像信息提取之——基于专家知识的决策树分类

可以将多源数据用于影像分类当中,这就是专家知识的决策树分类器,本专题以ENVI中Decision Tree为例来叙述这一分类器。 本专题包括以下内容: 专家知识分类器概述知识(规则)定义ENVI中Decision Tree的使用 概述 基于知…

微信小程序(二十)Vant组件库的配置

教程很详细,直接上过程 上一篇 官方文档也有,但是因为版本的更新,官方文档并没有跟着改变,这里我写一份最新版能用的教程 (口头禅还是不能少的🤣🤣🤣) 灵魂拷问&#xf…

jsp原理与EL,JSTL表达式基础内容整理

2024年了,vue都到了灌篮高手的版本,真的没想到我还会在这个时间整理一篇关于jsp页面操作的文章。技术就是一个不用就忘的东西,既然工作中还有用武之地,那就整理一下以备不时之需。 长话短说,不展开叙述,只记…

嘿嘿,vue之输出土味情话

有点好玩&#xff0c;记录一下。通过按钮调用网站接口&#xff0c;然后解构数据输出土味情话。 lovetalk.vue: <!--vue简单框架--> <template> <!-- 这是一个div容器&#xff0c;用于显示土味情话 --> <div class"talk"> <!-- 当点…

华为机考入门python3--(4)牛客4-字符串分隔

分类&#xff1a;字符串 知识点&#xff1a; 复制符号* 复制3个0 0*3 000 字符串截取 截取第i位到j-1位 str[i:j] 题目来自【牛客】 input_str input().strip()# 先补齐 if len(input_str) % 8 ! 0: input_str 0 * (8 - len(input_str) % 8) # 每8个分 out…

第15次修改了可删除可持久保存的前端html备忘录:换了一个容器时钟,匹配背景主题:现代深色

第15次修改了可删除可持久保存的前端html备忘录&#xff1a;换了一个容器时钟&#xff0c;匹配背景主题&#xff1a;现代深色 备忘录代码 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta http-equiv&qu…

【安卓】不需要魔法使用AuthenticationApp解决Github报2FA双重验证警告的问题

如果你也收到了类似的警告信息&#xff0c;那就一起启用2FA吧​。 背景介绍 Github提供了四种2FA方式&#xff1a; AuthenticatorApp(今天要分享的就是这个)SMS/Text message: 由于SMS不支持国内手机号, 不可用Security keys: 由于该方式需要物理设备等&#xff0c;不好Githu…

数据库查询3

目录 1. 多表查询 1.1.1 介绍 1.1.2 分类 1.2 内连接 1.3 外连接 1.4 子查询 1.4.1 介绍 1.4.2 标量子查询 1.4.3 列子查询 1.4.4 行子查询 1.4.5 表子查询 2. 事务 2.1 操作 2.2 四大特性 数据库总结2 数据库总结1 1. 多表查询 1.1.1 介绍 多表查询&#xff…

vue3使用最新的属性defineModel实现父子组件数据响应式绑定

子父之间使用v-model双向绑定数据&#xff0c;子组件每次都要写emit和props觉得麻烦&#xff1f;以前&#xff0c;为了使组件支持与v-model双向绑定&#xff0c;它需要&#xff08;1&#xff09;声明prop&#xff0c;&#xff08;2&#xff09;在打算更新prop时发出相应的updat…

Keil导入文件的操作步骤

本文以STM32G431R8T6导入lcd.c文件为例 1 背景 作为最常用的单片机程序编辑工具&#xff0c;全球有超过10万的工程师在使用Keil&#xff0c;但初学者很有可能对Keil的各种信息和操作一无所知&#xff0c;我便是其中一员&#xff0c;由于最近看了很多Keil相关的教程&#xf…

3DGS 其二:Street Gaussians for Modeling Dynamic Urban Scenes

3DGS 其二&#xff1a;Street Gaussians for Modeling Dynamic Urban Scenes 1. 背景介绍1.1 静态场景建模1.2 动态场景建模 2. 算法2.1 背景模型2.2 目标模型 3. 训练3.1 跟踪优化 4. 下游任务 Reference&#xff1a; Street Gaussians for Modeling Dynamic Urban Scenes 1.…