stm32 定时器输出比较(OC)与PWM的理解和应用

news2025/6/7 15:13:29

不积跬步,无以至千里;不积小流,无以成江海。
大家好,我是闲鹤,公众号 xxh_zone,十多年开发、架构经验,先后在华为、迅雷服役过,也在高校从事教学3年;目前已创业了7年多,主要从事物联网/车联网相关领域和业务。
喜欢交友、骑行、写毛笔字、弹吉他、折腾硬件和写代码。

相关阅读:

  • STM32 外部中断的理解
  • STM32 定时器中断

1. 定时器TIM简介

  • TIM 是 stm32 微控制器中的定时器模块。stm32 包含多个定时器模块,每个定时器模块有不同的功能和配置,适用于各种应用场景。
  • 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
  • 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从模式等多种功能

定时器分类
图1
基本定时器结构(时基单元):
图2

2. TIM 定时器相关概念

  • 计数模式
    • 向上计数模式(Up-counter mode)
    • 向下计数模式(Down-counter mode)
    • 中心对齐模式(Center-aligned mode)
  • 预分频器(Prescaler)
    主要作用是控制(降低)计数器时钟频率。
计数器时钟频率 = 定时器输入时钟频率(CK_PSC) / (预分频值 + 1) = CK_PSC / (PSC + 1)


通常 CK_PSC 为内部时钟CK_INT,即为72MHz

  • 自动重装寄存器(ARR)
    设置计数器的周期值。当计数器达到该值时,会产生一个更新时间(中断或 DMA 请求),计数器重新从 0 开始计数。
  • 输出比较(Output Compare, OC)
    定时器的输出比较功能可以用来产生精准的输出信号。通过设置比较寄存器(CCR),可以控制输出引脚的状态。
  • 输入捕获(Input Capture)
    定时器的输入捕获功能可以测量外部信号的周期和脉宽。
  • PWM 模式(Pulse Width Modulation)
    定时器 PWM 模式可以产生占空比可调的 PWM 信号,常用于电机控制、LED调光等。

3. 输出比较(Output Compare, OC)

框图如下:
图3

  • 输出比较可以通过比较 CNT 与 CCR 寄存器值的关系,来对输出电平进行置1、置0或翻转等操作,用于输出一定频率和占空比的 PWM 波形;
  • 每个高级定时器和通用定时器都拥有4个输出比较通道
  • 高级定时器的前3个通道额外拥有死区生成和互补输出的功能

输出比较产生的信号模式:
图4

4. PWM

PWM: Pulse Width Modulation 脉冲宽度调制。在具有惯性的系统中,可以通过一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常用于电机控速等领域。

  • 频率:PWM 信号的周期倒数。
f = 1 / Ts = CK_PSC / (PSC + 1) / (ARR + 1)
  • 占空比:高电平时间(Ton)与周期时间(Ts)的比值,通常用百分比表示。
占空比 = Ton / Ts = Ton / (ARR + 1) = CCR / (ARR + 1)

占空比决定了输出信号在一个周期内为高电平的时间比例

  • 分辨率:分辨率是占空比的最小变化步距。分辨率通常由定时器的计数位数(如8位、16位等)决定,影响 PWM 信号的精细度。
分辨率 = 1 / (计数器最大值 + 1) = 1 / (ARR + 1)


见图:
图5
PWM 基本结构图,也是作为后续编程的参考图:
图6

5. 应用举例

5.1 使用 PWM 驱动 LED 呼吸灯

这个例子主要通过 PWM 实现 LED 呼吸灯的效果。
基本思想:通过 TIM2 定时器,输出 PWM 脉宽给到 LED ,LED 呈现呼吸闪烁的过程。
实现思维逻辑:
根据 PWM 基本结构图,基本步骤为:

  1. 初始化时基单元
  2. 配置 OC
  3. 由于需要控制一个 LED 灯,所以需要配置一个 GPIO 用于输出 PWM 的信号
  4. 运行控制逻辑

硬件电路:
由于使用的是 TIM2 定时器的通道1即 TIM2_CH1_ETR 输出信号,而 TIM2_CH1_ETR 输出复用的是 PA0 口,如图:
图7PA0 口插上 LED 的正极,负极接在 GND 上。
代码:
PWM.c

void PWM_Init(void)
{
	// 初始化时基单元
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // TIM2 在 APB1 总线
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; // 时基单元结构体
	TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频
	TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; // 计数器模式
	TIM_TimeBaseStruct.TIM_Period = 100 - 1; // 计数周期 ARR
	TIM_TimeBaseStruct.TIM_Prescaler = 7200 - 1 ; // 预分频器 PSC
	TIM_TimeBaseStruct.TIM_RepetitionCounter = 0; // 重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);
	
	// OC 配置 定时器通道配置
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // OC 输出模式 见图7
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // 输出极性 见图7 REF 置有效电平
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // 输出使能状态
	TIM_OCInitStruct.TIM_Pulse = 0; // CCR 比较寄存器 0 ~ 100
	TIM_OC1Init(TIM2, &TIM_OCInitStruct);
	
	// GPIO 配置
	// 这里需要复用 PA0 口,所以 AFIO 一并启用
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
	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 
	TIM_Cmd(TIM2, ENABLE);
}

void SetCompare1(uint16_t Compare1)
{
	TIM_SetCompare1(TIM2, Compare1);
}

main.c:

int main() 
{
	PWM_Init();

	int8_t i = 0;
	while(1) {
		for(i = 0; i <= 100; i++) {
			SetCompare1(i);
			Delay_ms(10);
		}
		for(i = 100; i >= 0; i--) {
			SetCompare1(i);
			Delay_ms(10);
		}
	}
}

5.1 使用 PWM 驱动 舵机

PWM 驱动舵机与 PWM 驱动 LED 呼吸灯整个流程基本一致,不同的是要根据舵机的特性来控制 PWM 输出的脉宽信号。
舵机的特性:

舵机预分频器计算:

  1. 预分频器:72,TIM_CLK = 72MHz / 72 = 1MHz
  2. 定时周期: TIM_Period = PWM周期 * TIM_CLCK频率 - 1
    其中 PWM 周期为 20ms, 定时器时钟频率为 1MHz,因此 TIM_Period = 20ms * 1MHZ - 1 = 20000 - 1

角度到脉宽的线性变换公式:

代码:
大部分的代码都与 LED 呼吸灯类似,就是几个参数有所改变:

void PWM_Init(void)
{
	//
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
	TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseStruct.TIM_Period = 20000 - 1;
	TIM_TimeBaseStruct.TIM_Prescaler = 72 - 1;
	TIM_TimeBaseStruct.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);
	
	// OC
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStruct.TIM_Pulse = 0;
	TIM_OC1Init(TIM2, &TIM_OCInitStruct);
	
	//
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
	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);
	
	TIM_Cmd(TIM2, ENABLE);
}

void Servo_Angle(float angle)
{
	TIM_SetCompare1(TIM2, angle / 180.0 * 2000 + 500);
}

main.c

int main() 
{
	OLED_Init();
	PWM_Init();
	Key_Init();

	float angle = 0;
	Servo_Angle(0);
	char str[100] = {0};
	int8_t is_rt = 0;
	while(1) {
		if(Key_Read() ==1) {
			if(is_rt == 1) angle -= 20;
			else angle += 20;
		}
		Servo_Angle(angle);
		sprintf(str, "angle: %d    ", (int)angle);
		OLED_ShowString(1, 1, str);
		if(angle == 180) is_rt = 1;
		if(angle == 0) is_rt = 0;
		
	}
}

相关阅读:

  • STM32 外部中断的理解
  • STM32 定时器中断

近期文章:
百万级物联网框架设计
从华为无线网络框架说Dispatch服务
【自动化运维】不要相信人,把所有的东西都交给机器去处理

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

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

相关文章

f1c100s 荔枝派 系统移植

一。交叉编译环境配置 1.1下载交叉工具链&#xff1a;https://releases.linaro.org/components/toolchain/binaries/7.2-2017.11/arm-linux-gnueabi/ 1.2解压安装 在home目录下新建 工程目录&#xff1a;mkdir f1c100s_project 将windows下的gcc-linaro-7.2.1-2017.11-x86…

微信小程序毕业设计-预约挂号系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

自导自演的39亿大雷?

深夜炸雷&#xff01;39亿存款或无法收回&#xff0c;11万股民懵了&#xff01;A股又出了离谱事件。 4日深夜&#xff0c;ST亿利公告称&#xff0c;公司存放在亿利财务公司的39亿存款已被划分为次级贷款&#xff0c;也就是不良贷款的一种&#xff0c;存在重大可收回性风险。又是…

【第一节】数据结构和算法绪论

目录 一、数据结构的起源与发展 二、什么是数据结构 三、数据的逻辑结构和存储结构 四、数据类型和数据结构 五、算法 六、算法与数据结构的关系 七、算法时间复杂度和空间复杂度 一、数据结构的起源与发展 数据结构的起源可以追溯至1968年。当时&#xff0c;美国的唐欧…

OpenCV的“画笔”功能

类似于画图软件的自由笔刷功能&#xff0c;当按住鼠标左键&#xff0c;在屏幕上画出连续的线条。 定义函数&#xff1a; import cv2 import numpy as np# 初始化参数 drawing False # 鼠标左键按下时为True ix, iy -1, -1 # 鼠标初始位置# 鼠标回调函数 def mouse_paint(…

秀肌肉-海外短剧系统的案例展示

多语种可以选择&#xff0c;分销功能&#xff0c;多种海外支付方式&#xff0c;多种登录模式可供选择&#xff0c;总之你想到的我们都做了&#xff0c;你没想到的我们也都做了

Activity->Activity中动态添加Fragment->Fragment回退栈BackStack

Fragment回退栈 Fragment回退栈用于管理Fragment的导航历史(添加、删除、替换)。每个Activity都有一个包含其所有Fragment的FragmentManager&#xff0c;调用其addToBackStack方法时&#xff0c;这个事务就会被添加到FragmentManager的回退栈中当用户按下返回键时&#xff0c;…

kotlin 调用java的get方法Use of getter method instead of property access syntax

调用警告 Person.class public class Person {private String name;Person(String name) {this.name name.trim();}public String getName() {return name;}public void setName(String name) {this.name name;}public String getFullName() {return name " Wang&quo…

ChatTTS+Python编程搞定语音报时小程序

文字转语音神器Python编程搞定语音报时小程序 今天一个好哥们发了一个文字转语音的AI神器的短视频。这个神器的网站是[ChatTTS - Text-to-Speech for Conversational Scenarios][https://chattts.com/]&#xff0c;如下图所示&#xff1a; 这个开源项目可以从github.com上下载…

VisualStudio2022离线包安装后无法进行版本升级(Visual Studio installer 卡在正在下载进度条0%)

目录 一、问题现象二、问题分析三、解决办法恢复正常 一、问题现象 表现1(未安装时&#xff0c;安装) 表现2&#xff08;已安装&#xff0c;升级&#xff09; 二、问题分析 原因&#xff1a;无法访问 aka.ms 测试方法:在浏览器中输入&#xff1a; https://aka.ms/vs/16/rele…

[笔试训练](三十四)100:[NOIP2008]ISBN号码101:kotori和迷宫102:矩阵最长递增路径

目录 100:[NOIP2008]ISBN号码 101:kotori和迷宫 102:矩阵最长递增路径 100:[NOIP2008]ISBN号码 题目链接:[NOIP2008]ISBN号码_牛客题霸_牛客网 (nowcoder.com) 题目&#xff1a; 题解: 简单模拟 #include <iostream> #include<string> using namespace std; str…

文件上传题目练习

[HNCTF 2022 Week1]easy_upload 先尝试上传一个php文件&#xff0c;发现直接就成功了 用蚁剑测试连接成功 找到flag [NISACTF 2022]bingdundun~ 白名单上传 这里因为尝试了很多绕过方式都不成功&#xff0c;去搜索了一下wp&#xff0c;发现要用到Phar://伪协议 补充&#xff…

【多模态】36、ShareGPT4V | 借助 GPT4V 的能够来生成更丰富的 caption 用于提升 LMM 模型的能力

文章目录 一、背景二、方法2.1 ShareGPT4V 数据集构建2.2 ShareGPT4V-PT 数据生成2.3 ShareGPT4V-7B Model 三、效果3.1 benchmark3.2 定量分析3.3 多模态对话 四、一些例子 论文&#xff1a;ShareGPT4V: Improving Large Multi-Modal Models with Better Captions 代码&#…

Python logging 模块详解

Python 的 logging 模块提供了一个强大而灵活的日志系统。它是 Python 标准库的一部分&#xff0c;因此可以在任何 Python 程序中使用。logging 模块提供了许多有用的功能&#xff0c;包括日志消息的级别设置、日志消息的格式设置、将日志消息输出到不同的目标&#xff0c;以及…

【机器学习】GLM4-9B-Chat大模型/GLM-4V-9B多模态大模型概述、原理及推理实战

​​​​​​​ 目录 一、引言 二、模型简介 2.1 GLM4-9B 模型概述 2.2 GLM4-9B 模型架构 三、模型推理 3.1 GLM4-9B-Chat 语言模型 3.1.1 model.generate 3.1.2 model.chat 3.2 GLM-4V-9B 多模态模型 3.2.1 多模态模型概述 3.2.2 多模态模型实践 四、总结 一、引言…

《精通ChatGPT:从入门到大师的Prompt指南》大纲目录

第一部分&#xff1a;入门指南 第1章&#xff1a;认识ChatGPT 1.1 ChatGPT是什么 1.2 ChatGPT的应用领域 1.3 为什么需要了解Prompt 第2章&#xff1a;Prompt的基本概念 2.1 什么是Prompt 2.2 好Prompt的特征 2.3 常见的Prompt类型 第二部分&#xff1a;Prompt设计技巧 第…

SOA的发展历史

1.SOA的发展历程 回顾SOA发展历程&#xff0c;我们把其大致分为了三个阶段&#xff0c;下面将分别介绍每个阶段的重要标准和规范。 1.1.萌芽阶段 这一阶段以XML技术为标志&#xff0c;时间大致从20世纪90年代末到21世纪初。XML系W3C所建&#xff0c;源自流行的标准通用标记语…

多表连接查询和子查询

一、连接查询 连接查询是SQL语言最强大的功能之一&#xff0c;它可以执行查询时动态的将表连接起来&#xff0c;然后从中查询数据。 1.1、连接两表的方法 在SQL中连接两表可以有两种方法&#xff0c;一种是无连接规则连接&#xff0c;另一种是有连接规则连接。 无连接规则连…

多卡聚合智能融合通信设备在无人机无线视频传输应用

无人驾驶飞机简称“无人机”&#xff0c;是利用(无线电)遥控设备和自备的程序控制装置操纵的不载人飞行器&#xff0c;现今无人机在航拍、农业、快递运输、测绘、新闻报道多个领域中都有深度的应用。 无人机无线视频传输保证地面人员利用承载的高灵敏度照相机可以进行不间断的画…

Accelerate 笔记:保存与加载文件

保存和加载模型、优化器、随机数生成器和 GradScaler 使用 save_state() 将上述所有内容保存到一个文件夹位置使用 load_state() 加载之前通过 save_state() 保存的状态通过使用 register_for_checkpointing()&#xff0c;可以注册自定义对象以便自动从前两个函数中存储或加载 …