当前位置: 首页 > news >正文

【STM32F103】TIM定时器PWM

定时器分类

STM32F1中除了互联型产品(STM32F103C8T6为64KB Flash 中容量产品),其余有8个定时器。

可以8个定时器分为高级,通用,基本三种。

高级定时器有两个,分别是TIM1和TIM8。

通用定时器有四个,是TIMx(x=2~4)

基本定时器有两个,是TIMx(x=6~7)

功能上高级>通用>基本

不过高级定时器一般也用不着,如果只是普通的计时的话基本定时器也就够用了,但是基本定时器没有输出比较,没法实现PWM(脉冲宽度调制),因此我们主要使用的是通用定时器,并且STM32F103中通用定时器的资源也是最多的。

下图截自《ARM Cortex-M3嵌入式原理及应用(基于STM32F103微控制器)》的第150页。

通用定时器

 下图截自《STM32F10xxx参考手册(中文)》第253页

不太严谨地介绍一下定时器的工作流程。时钟源每传来n(n=预分频器的值)次脉冲,都会使得计数器工作一次。如果选择的是向上计数模式,那么计数器每工作一次都会使得计数器的值+1,直到值等于了自动重装载寄存器的值,那么触发一次中断,并且计数器清零。

如果选择的是向下计数模式,那么计数器每工作一次都会使得计数器的值-1,直到值等于0,那么触发一次中断,并且计数器的值变为自动重装载寄存器的值。

如果选择向上向下计数模式,那么计数器工作一次会使得计数器的值+1或者-1,计数器的值不会被重置,会一直在0和自动重装载寄存器的值之间上下跳动。

一般情况下这三种工作模式对于计数的效果都是一样的,但是当我们需要利用到计数器里的值的时候就有差别了(例如PWM),通常我们使用的是向上计数模式,这也比较符合我们的直觉。

 上述的介绍中提到的是以下三个寄存器。

可以看出它们都是只有16位,也就是说可以设置的最大数值为2^16-1(65535)。

并且从工作流程中也可以得知,定时器溢出的频率公式为

时钟源频率/(自动重装载寄存器的值+1)/(预分频器的值+1)=CK_INT/(ARR+1)/(PSC+1)

PWM脉冲宽度调制

STM32F103中每个通用定时器都可以输出4路PWM,而每个高级定时器都可以输出7路PWM,所以理论上,STM32最多是可以同时产生30路PWM。

PWM简单来说就是一段有高电平也有低电平的脉冲信号。

我们一般点亮一个LED灯,就是把LED灯的短脚接地,然后通过GPIO口从LED灯的长脚传入高电平,我们假设此刻LED的亮度为100。如果我们改用PWM来代替原本的高电平,并且PWM传入的脉冲信号中高低电平各占50%,那么此刻LED的亮度就为50%了,因为人眼大概只能接受每秒25帧左右的图像,因此只要PWM够快,那么我们就看不出LED实际上是亮灭亮灭的,由于人眼的残留影像,我们看到的只是LED灯变得暗了些。

之所以把PWM和定时器放在一起,是因为定时器的硬件中就含有捕获比较寄存器。

我们可以给这个寄存器设置一个阈值,当定时器的计数器的值小于这个阈值的时候我们就输出高电平,反之输出低电平(具体看选择的PWM输出模式)。

综上我们可以得知,PWM输出的频率等于定时器的溢出中断频率。

固件库函数

定时器相关的函数还是非常多的,接下来就以计数1ms为目的来说明相关函数。

首先需要打开相关定时器的外设时钟,不过不属于定时器库函数里的,就不介绍。

下一步选择时钟源,默认是使用内部时钟源(72MHz),所以也可以不指定。

然后初始化时基单元(配置自动重装载寄存器,预分频器,时钟分频,计数模式)

接着是中断使能以及配置NVCI相关的中断优先级

最后是使能计数器就可以开始计数了。

TIM_InternalClockConfig

上面三个函数是常用的用来指定时钟源的函数。

第一个也是默认的,使用的是内部时钟作为时钟源。

第二个可以将其他定时器的溢出信号作为时钟源,也就是两个定时器联动,这样可以将最大的计时数平方一次,也就是可以记更久的时间,不过我们也是可以使用软件来完成这种效果的。

第三个可以将时钟源接到外部的时钟。

TIM_TimeBaseInit

初始化时基单元,参数一指定定时器资源,参数二传入一个TIM_TimeBaseInitTyepDef类型的结构体变量用于初始化配置。

TIM_Prescale:预分频器的值,0~65535。

TIM_CounteMode:计数器模式,一般使用向上计数TIM_CounterMode_Up

TIM_Period:自动重装载计数器的值,0~65535。

TIM_ClockDivision:时钟分频,TIM_CKD_DIV1

TIM_RepetitionCounter:重复计数器的值,只有高级定时器才用的到,基本定时器和通用定时器随便给个0就行。

TIM_ITConfig

中断使能,参数一选择定时器资源,参数二选择中断源,参数三给个ENABLE。

中断源一般就指定第一个TIM_IT_Update

这样每计数一轮都会触发一次中断。

TIM_Cmd

使能计数器。

使能之后计数器就开始运作了,每次记满一轮之后就会进入一次中断,我们可以从“startup_stm32f10x_md.s”中找到对应的中断函数。

由于可进入这个中断函数的中断源有多个(参考上面中断使能的参数表),因为我们需要查询中断标志位是否是我们所规定的TIM_IT_Update触发的,是的话我们还需要手动把标志位清除(其他中断源也是一样)。

TIM_GetITStatus

查询中断标志位,参数二给的一样的TIM_IT_Update

TIM_ClearITPendingBit

清除中断标志位,参数二给的一样的TIM_IT_Update

上面的函数就是用来计数用的,接下来就是如何输出PWM的了。

把上面计数的流程走一遍,然后把中断相关的都删掉,再加上配置输出比较单元以及初始化GPIO口的函数即可。

TIM_OC1Init

配置通道1的输出比较单元。这里需要注意的是不同定时器资源的不同通道对应的GPIO输出口是不一样的,TIM2的通道1对应的是GPIOA的0号引脚,具体可以查询引脚定义表。并且需要配置为复用推挽输出模式GPIO_Mode_AF_PP

参数一指定定时器资源,参数二传入TIM_OCInitTypeDef类型的参数。

一共有八个成员变量,但是我们只需要用到我圈起来的4个,其余为高级定时器使用的。

TIM_OCMode:设置输出比较的模式,一般我们选择TIM_OCMode_PWM1,在这个模式下,当计数器的值小于阈值时输出设置的极性(下面会设置),反之输出相反的极性。

TIM_OCPolarity:输出比较的极性,这个根据需求来设置,设置为高电平的参数为TIM_OCPolarity_High

TIM_OutputState:设置输出使能,TIM_OutputState_Enable

TIM_Pulse:设置阈值,0~65535。因此我们定时器的自动重装载寄存器的值最好不要超过这个阈值,并且由于容易计算PWM的占空比,一般我会把自动重装载寄存器的值设为10的倍数。

以上就足够我们输出PWM了,如果我们需要实现呼吸灯的效果,那么还需要动态地调整PWM的阈值。

TIM_SetCompare1

设置通道1的输出比较阈值。

参数一指定定时器资源,参数二重新设置阈值大小。 

通用定时器计数1ms代码

#include "stm32f10x.h"                  // Device header
#include "OLED.h"uint32_t count=0;int main(void){OLED_Init();RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);     //打开TIM2的外设时钟TIM_InternalClockConfig(TIM2);                          //选择内部时钟(72MHz)作为时钟源TIM_TimeBaseInitTypeDef itd;itd.TIM_ClockDivision=TIM_CKD_DIV1;                     //时钟1分频itd.TIM_CounterMode=TIM_CounterMode_Up;                 //向上计数模式itd.TIM_Period=1000-1;                                  //设置自动重装器的值itd.TIM_Prescaler=72-1;                                 //设置预分频器的值//TIM2的计数器溢出频率(每秒中断次数)为//(72MHz/1(1分频)/(1000-1+1)(自动重装器的值+1)/(72-1+1)(预分频器的值+1))=1000(ms级,即1ms溢出一次)itd.TIM_RepetitionCounter=0;                            //重复计数器的值,但是仅高级定时器有效TIM_TimeBaseInit(TIM2,&itd);TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);                //开启定时器中断,TIM为中断源//配置中断优先级NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);         //抢占优先级和响应优先级各占两位NVIC_InitTypeDef itd1;itd1.NVIC_IRQChannel=TIM2_IRQn;                         //指定TIM2的中断通道itd1.NVIC_IRQChannelCmd=ENABLE;itd1.NVIC_IRQChannelPreemptionPriority=2;               //抢占优先级为2itd1.NVIC_IRQChannelSubPriority=2;                      //响应优先级为2NVIC_Init(&itd1);TIM_Cmd(TIM2,ENABLE);                                   //使能定时器while(1){OLED_ShowNum(2,1,count,6);}
}void TIM2_IRQHandler(void){if(TIM_GetITStatus(TIM2,TIM_FLAG_Update)){          //判断是否是TIM中断源引发的中断++count;    TIM_ClearITPendingBit(TIM2,TIM_FLAG_Update);    //清除标志位}
}

效果

 通用定时器使用PWM实现LED呼吸灯

#include "stm32f10x.h"                  // Device header
#include "Delay.h"int main(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);     //打开TIM2的外设时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef gitd;gitd.GPIO_Mode=GPIO_Mode_AF_PP;                         //配置为复用推挽输出gitd.GPIO_Pin=GPIO_Pin_0;gitd.GPIO_Speed=GPIO_Speed_2MHz;GPIO_Init(GPIOA,&gitd);TIM_InternalClockConfig(TIM2);                          //选择内部时钟(72MHz)作为时钟源TIM_TimeBaseInitTypeDef itd;itd.TIM_ClockDivision=TIM_CKD_DIV1;                     //时钟1分频itd.TIM_CounterMode=TIM_CounterMode_Up;                 //向上计数模式itd.TIM_Period=100-1;                                   //设置自动重装器的值itd.TIM_Prescaler=72-1;                                 //设置预分频器的值itd.TIM_RepetitionCounter=0;                            //重复计数器的值,但是仅高级定时器有效TIM_TimeBaseInit(TIM2,&itd);TIM_OCInitTypeDef itd1;itd1.TIM_OCMode = TIM_OCMode_PWM1;                      //比较输出模式为PWM1itd1.TIM_OCPolarity = TIM_OCPolarity_High;              //输出极性为高电平itd1.TIM_OutputState=TIM_OutputState_Enable;            //使能itd1.TIM_Pulse=0;                                       //初始化输出比较的阈值          TIM_OC1Init(TIM2,&itd1);TIM_Cmd(TIM2,ENABLE);                                   //使能定时器while(1){for(int i=0;i<=100;++i){ TIM_SetCompare1(TIM2,i);Delay_ms(10);}for(int i=100;i>=0;--i){TIM_SetCompare1(TIM2,i);Delay_ms(10);}}
}

效果

参考

《STM32F10xxx参考手册(中文)》

《STM32F103xx固件函数库用户手册》

b站江科大自化协

《STM32库开发实战指南(基于STM32F103)》

《ARM Cortex-M3嵌入式原理及应用(基于STM32F103微控制器)》

相关文章:

【STM32F103】TIM定时器PWM

定时器分类 STM32F1中除了互联型产品&#xff08;STM32F103C8T6为64KB Flash 中容量产品&#xff09;&#xff0c;其余有8个定时器。 可以8个定时器分为高级&#xff0c;通用&#xff0c;基本三种。 高级定时器有两个&#xff0c;分别是TIM1和TIM8。 通用定时器有四个&…...

图论及其应用的一些论断---选择题

在任意一个网络N=(X,Y,I,A,c)中,最大流的值等于最小割的容量。在任意6个人的集会上,要么有3个人互相认识,要么有3个人互不认识。若G为无向简单图,则图G的边数ε,点数v之间有: ε < = ( v 2 ) ε<=\binom{v}{2} ε<=...

腾讯云轻量应用服务器镜像操作系统如何选择?

腾讯云轻量应用服务器镜像怎么选择&#xff1f;镜像是指轻量服务器的操作系统&#xff0c;可以选择宝塔Linux面板8.0.4腾讯云专享版&#xff0c;如果需要Win系统建议选择Windows Server 2012 R2 中文版&#xff0c;腾讯云服务器网txyfwq.com分享腾讯云轻量应用服务器镜像操作系…...

鸿蒙原生应用/元服务开发-发布基础类型通知类型与接口

基础类型通知主要应用于发送短信息、提示信息、广告推送等&#xff0c;支持普通文本类型、长文本类型、多行文本类型和图片类型。 表 基础类型通知中的内容分类 目前系统仅通知栏订阅了通知&#xff0c;将通知显示在通知栏里。基础类型通知呈现效果示意图如下所示。 图1基础类…...

Apisix常见问题

1.通过接口操作路由时X-API-KEY cd /usr/local/apisix/conf vim config-default.yaml注释掉这一部分 #allow_admin: # http://nginx.org/en/docs/http/ngx_http_access_module.html#allow# - 0.0.0.0/24 # If we dont set any IP list, then a…...

Docker 安装Mysql

目录 Docker Mysql安装 ✨安装和配置mysql ✨远程连接mysql远程连接 MySQL 是世界上最流行的开源数据库。根据 DB-Engines的调查数据&#xff0c;MySQL 是第二受欢迎的数据库&#xff0c;仅次于 Oracle 数据库。MySQL在过去由于性能高、成本低、可靠性好&#xff0c;已经成…...

Pillow图像处理(PIL.Image类的详细使用)

文章目录 Opencv、Matplotlib(plt)、Pillow(PIL)、Pytorch读取数据的通道顺序Python图像处理库&#xff08;PIL、Pillow、Scikit-image、Opencv&#xff09;Pillow 官方文档&#xff08;超详细&#xff0c;超推荐&#xff09;一、PIL库与Pillow库的区别二、Pillow库&#xff08…...

嵌入式开发——ADC开发

学习目标 了解ADC开发流程掌握采样方式能够使用ADC进行芯片内部通道进行采样能够使用ADC对外部电路进行采样学习内容 GD32F4的ADC 特点: 16个外部模拟输入通道;1个内部温度传感通道(VSENSE);1个内部参考电压输入通道(VREFINT);1个外部监测电池VBAT供电引脚输入通道。ADC开…...

FreeSWITCH t38测试

主叫 192.168.100.205 被叫 192.168.100.121 主叫侧发送multipage.tif 被叫侧接收传真&#xff0c;保存为recv.tif 主叫侧: originate [fax_enable_t381][fax_verbose1][fax_disable_v170][fax_ident77777777][fax_enable_t38_request1]sofia/internal/1234192.168.100.121:…...

跑腿配送系统技术探析

概述 跑腿配送系统是一种基于现代科技的服务平台&#xff0c;通过智能化的技术手段&#xff0c;实现用户需求的快速响应和高效配送。本文将探讨该系统的核心技术原理&#xff0c;以及在实际开发中的一些代码示例。 技术原理 1. 用户请求与任务分配 跑腿配送系统的第一步是…...

【数据不完整?用EM算法填补缺失】期望值最大化 EM 算法:睹始知终

期望值最大化算法 EM&#xff1a;睹始知终 算法思想算法推导算法流程E步骤&#xff1a;期望M步骤&#xff1a;最大化陷入局部最优的原因 算法应用高斯混合模型&#xff08;Gaussian Mixture Model, GMM&#xff09;问题描述输入输出Python代码实现 算法思想 期望值最大化方法&a…...

PMP证书可以挂靠吗?

PMP证书不是国内的证书&#xff0c;挂靠不了呀&#xff0c;想挂靠&#xff0c;可以考软考/一建等&#xff0c;里面也有项目管理相关的证书。 PMP证书虽然不能挂靠&#xff0c;但是用处还是很大的&#xff0c;例如提升个人能力、薪资待遇&#xff0c;还有持证可享一些城市的福利…...

HTML语义化的理解

HTML语义化是指在编写HTML代码时&#xff0c;合理地选择适当的标签和属性来描述页面的结构和内容&#xff0c;使得代码更具有可读性、可维护性和可访问性。 可读性&#xff1a;通过使用语义化的标签&#xff0c;可以清晰地表达页面的结构和内容&#xff0c;使得代码更易于阅读和…...

(Java企业 / 公司项目)注册,配置中心Nacos的怎么使用?(含相关面试题)(一)

在企业项目中使用Nacos实现的功能操作&#xff0c;以及如何在自己的环境中搭建Nacos环境&#xff0c;包含demo 一. 官网介绍&#xff1a;home (nacos.io) 文档地址&#xff1a;Nacos 快速开始 二. 准备Nacos环境 在公司里面很多的服务以及环境都是自己搭建的所以我在这里就从…...

计算机网络---知识点

ARPANET----NFSNET—ANSNET—Internet发展及协议 移动互联网 物联网 无线自组网、无线传感器网络、无线个域网 ISO/OSI网络体系结构 TCP/IP网络体系结构 对等通信、PDU 电路交换、报文交换、分组报文交换 虚电路、数据报 信道复用技术 网络性能的主要指标&#xff08…...

力扣42. 接雨水

双指针法 思路&#xff1a; 将数组前后设置为 left、right 指针&#xff0c;相互靠近&#xff1b;在逼近的过程中记录两端最大的值 leftMax、rightMax&#xff0c;作为容器的左右边界&#xff1b;更新指针规则&#xff1a; 如果数组左边的值比右边的小&#xff0c;则更新 left…...

SpringSecurity-2.7中跨域问题

SpringSecurity-2.7中跨域问题 访问测试 起因 写这篇的起因是会了解到 SSM(CrosOrigin)解决跨域,但是会在加入SpringSecurity配置后,这个跨域解决方案就失效了,而/login这个请求上是无法添加这个注解或者通过配置(WebMvcConfig)去解决跨域,所以只能使用SpringSecurity提供的.c…...

Java解决字典序最小回文串

Java解决字典序最小回文串 01 题目 给你一个由 小写英文字母 组成的字符串 s &#xff0c;你可以对其执行一些操作。在一步操作中&#xff0c;你可以用其他小写英文字母 替换 s 中的一个字符。 请你执行 尽可能少的操作 &#xff0c;使 s 变成一个 回文串 。如果执行 最少 操…...

【力扣100】207.课程表

添加链接描述 class Solution:def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:# 思路是计算每一个课的入度&#xff0c;然后使用队列进行入度为0的元素的进出# 数组&#xff1a;下标是课程号&#xff0c;array[下标]是这个课程的入度# 哈希…...

2024年生成式AI支出将翻倍,到2027年将超1500亿美元

据国际数据公司&#xff08;IDC&#xff09;的预测&#xff0c;2023年全球企业在生成式人工智能(GenAI)解决方案上的投资已达194亿美元&#xff0c;预计在2024年将翻番。该预测还指出&#xff0c;包括GenAI软件、相关硬件和服务在内的支出将在2027年达到1511亿美元&#xff0c;…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...