STM32定时器与PWM对LED灯的控制

文章目录

  • 一、定时器——Timer
    • (一)概念
    • (二)分类
    • (三)功能
    • (四)结构
      • 1.模块一——时基单元
      • 2.模块二——输出比较模块
  • 二、实验内容
    • (一)标准库点亮LED灯
      • 1.实验说明
      • 2.设计思路
      • 3.代码部分
      • 4.实验现象
      • 5.keil仿真
    • (二)标准库点亮呼吸灯
      • 1.实验说明
      • 2.设计思路
      • 3.代码部分
      • 4.实验现象
      • 5.kei仿真
  • 三、总结

一、定时器——Timer

(一)概念

定时器,见名知意,是一种专门负责定时功能的片上外设。它的作用是设定一个时间,时间到后通过中断等方式通知STM32执行某些程序。因此,定时器的本质也可认为是计数器。
注意:定时器并不能直接获得现实世界的实时时间,而是当做一个计数器或者计时器来用。

(二)分类

STM32F103C8T6芯片中的定时器有三种八个。(TIM1到TIM8)
在这里插入图片描述

1.基本定时器:TIM6、TIM7。

2.通用定时器:TIM2、TIM3、TIM4、TIM5。

3.高级定时器:TIM1、TIM8。

其中高级定时器挂在总线APB2上,基本定时器和通用定时器挂在总线APB1上。

(三)功能

1.基本定时器:就是时基单元的功能。
2.通用定时器:在基本定时器的基础上增加了输入捕获功能和输出比较功能。
3.高级定时器:在通用定时器的基础上增加了从模式控制器和高级输出控制功能。

我们实验主要采用通用定时器,但我们要尽量了解高级定时器

(四)结构

STM32中单个高级定时器的内部结构如下图所示
在这里插入图片描述

主要可分为四大模块:时基单元输入捕获输出比较(包括绿色框中的高级功能)和从模式控制器。(在这里只介绍常用的时基单元与输出比较模块)

1.模块一——时基单元

在这里插入图片描述
(1)预分频器
RCC传来的脉冲频率很高,一般为72MHz或者为36MHz,而计数器是16位的,因此而一个计数器最多只能计数2的16次方个信号,即65536个脉冲,所以我们理应对该脉冲信号进行分频,分频之后的频率=原频率/(PSC设置的值+1)

(2)计数器与自动重装寄存器
这二者可以看成是捆绑在一起的,自动重装寄存器相当于是计数器的管理员,当自动重装寄存器的值设为n时,要经过n+1个脉冲,计数器的值才会向外输出1个脉冲。即计数器向外输出的脉冲个数=传入寄存器内部的脉冲个数/(ARR设置的值+1)

(3)重复寄存器
仅存在高级定时器中,还是相当于一个分频器。

小节:其实这三者都能起到分频作用,而中间的计数器与自动重装寄存器除此之外还能计数而已。它是时基单元与CCR寄存器建立联系的桥梁。

2.模块二——输出比较模块

二、实验内容

(一)标准库点亮LED灯

1.实验说明

使用STM32F103的 Tim2~Tim5其一定时器的某一个通道pin(与GPIOx管脚复用,见下图),连接一个LED,用定时器计数方式,控制LED以2s的频率周期性地亮-灭。
在这里插入图片描述

2.设计思路

(1)中断的条件
设置为UVE(update event),设置为定时器中的事件更新触发中断。说人话就是,定时器中的计数器溢出一次就出发中断。显然是等周期触发中断,我设置为1ms触发一次中断。每触发一次中断就计时一次,可以用全局变量currentTick来记录中断次数,也可以等同为程序运行的时间。
(2)延时函数的设计
先定义一个我们想要的延时变量命名为ms,其单位也是ms。
再定义一个变量uint64_t expireTime = App_Timer_GetTick() + ms,作为刚开始的定值变量。
接下里在while循环里不断让App_Timer_GetTick()返回的动态变量currentTick去追逐静态的高值变量expireTime,差值恰好为我们想要的延时ms。

3.代码部分

代码中的注释已经很明确了,这里不再逐句赘述。
(1)main.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "app_timer.h"


int main()
{
	App_Timer_Init();
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);

	
	while(1)
	{
		GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_SET);
		
		App_Timer_Delay(2000);
		
		GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_RESET);
		
		App_Timer_Delay(2000);
		
	}
}

2.app_timer.c文件

#include "app_timer.h"
#include "stdint.h"

static volatile uint64_t currentTick = 0;

void App_Timer_Init()
{
	//对定时器3的复位
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM3,ENABLE);
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM3,DISABLE);
	//1.使能定时器3的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
	//2.使能ARR寄存器的预加载特性
	TIM_ARRPreloadConfig(TIM3,ENABLE);
	
	
	//3.初始化实际单元的参数
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseInitStruct.TIM_Prescaler = 71;      //就是PSC里面的值,先72分频为,72MHz/72 = 1MHz;————>1us
	TIM_TimeBaseInitStruct.TIM_Period = 999;		//1us——————>1ms
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0; //就是RCR里面的值,重复计数器写0,相当于不再分频。
	//TIM_TimeBaseInitStruct.TIM_ClockDivision ;   //死区时间、输入滤波,暂时不设置
	//TIM_TimeBaseInitStruct.TIM_RepetitionCounter;  //定时器3没有重复计数器,不需要设置
	
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
	
	//这些参数还在影子寄存器里,手动产生中断
	TIM_GenerateEvent(TIM3,TIM_EventSource_Update);
	
	
	
	
	//4.使能Update中断
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);    //重点!!!!中断触发的条件————>更新事件.
								//而更新事件通常指的是定时器的计数器达到自动重载寄存器(ARR)的值时,即完成了一个计数周期,此时硬件会自动置位一个中断标志位(TIM_FLAG_Update)
								//显然就是:1ms触发一次中断!
	
	//5.配置NVIC模块
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
 	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0 ;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0 ;
	
	
	NVIC_Init(&NVIC_InitStruct);
	
	//6.使能定时器
	TIM_Cmd(TIM3,ENABLE);
	
}	


void TIM3_IRQHandler()
{
	if(TIM_GetFlagStatus(TIM3,TIM_FLAG_Update)== SET)	
	//这个函数的本意是得到具体定时器的具体部位的标志位
	//在这里是得到定时器3的更新标志位:当定时器计数器达到自动重载寄存器(ARR)的值并发生更新事件时,硬件会自动置。在这里是1ms
	{
		TIM_ClearFlag(TIM3,TIM_FLAG_Update);   	//中断函数的第一步,清除中断标志位
		currentTick++;					//currentTick计的是毫秒值。每经过1ms,currentTick就加1.
	}

}


uint64_t App_Timer_GetTick()   //获取当前时刻,单位ms
{
	return currentTick;
}

void App_Timer_Delay(uint32_t ms)   //毫秒级延时
{
	uint64_t expireTime = App_Timer_GetTick() + ms;
	while(App_Timer_GetTick() < expireTime);
}
//这个中断函数设计的很巧妙,expireTime是定值,刚进入while循环时,这个大的定值一直与currentTick这个动态值的差值刚好是传入的ms,当currentTick经过ms时间后,循环结束。
//这个动态小值追逐定值大值的过程就是花费的我们设置的延时时间。

4.实验现象

20240531——001

5.keil仿真

在这里插入图片描述
在这里插入图片描述

延时恰好为2s,设计正确。

(二)标准库点亮呼吸灯

1.实验说明

采用定时器PWM模式,让 LED 以呼吸灯方式渐亮渐灭,周期为1~2秒,自己调整占空比变化到一个满意效果;使用Keil虚拟示波器,观察 PWM输出波形。

2.设计思路

选择定时器的输出比较模式中的PWM模式,输出一个正弦电压到引脚即可。

3.代码部分

(1)main.c文件

#include "stm32f10x.h"
#include "stm32f10x_pal.h"

int main(void)
{
	PAL_Init();
	
	// #1. 将PA8初始化为复用推挽模式
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	// #2. 开启定时器1的时钟
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM1, ENABLE);
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM1, DISABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
	
	// #3. 初始化时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
	TIM_TimeBaseInitStruct.TIM_Period = 999;
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStruct);
	
	// #4. 初始化输出比较通道1
	TIM_OCInitTypeDef 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(TIM1, &TIM_OCInitStruct);
	
	// #5. 闭合MOE
	TIM_CtrlPWMOutputs(TIM1, ENABLE);
	
	// #6. 手动产生UEV
	TIM_GenerateEvent(TIM1, TIM_EventSource_Update);
	
	// #7. 使能定时器
	TIM_Cmd(TIM1, ENABLE);
	
	while(1)
	{
		float t = PAL_GetTick() * 0.001;
		float duty = 0.5 + 0.5 * sin(0.6 *3.14 * t);
		uint16_t ccr = duty * 999;
		TIM_SetCompare1(TIM1, ccr);
	}
}

4.实验现象

20240531——002

5.kei仿真

在这里插入图片描述

我不太明白为啥是频率快的方波,也能达到周期为1~2S的呼吸灯的效果。

三、总结

通过本次实验,学习了定时器的有关概念及其结构。我用标准库通过定时器中断的方式点亮了LED灯。在此过程中我感觉定时器的很多用途都得与中断联系在一起。总的来说,标准库点灯会遇到很多麻烦,和异常的现象,比如keil波形是快频率的方波,实验现象却是呼吸灯的效果。

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

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

相关文章

99.网络游戏逆向分析与漏洞攻防-ui界面的设计-角色信息显示的界面与功能

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

新型 Meterpreter 后门能够在图片中隐藏恶意代码

据Cyber Security News消息&#xff0c;ANY.RUN 沙盒分析了一种被称为Meterpreter 的新型后门恶意软件&#xff0c;能利用复杂的隐写技术将恶意有效载荷隐藏在看似无害的图片文件中。 基于Meterpreter的攻击从一个包含 PowerShell 脚本的 .NET 可执行文件开始&#xff0c;该脚…

解决VSCode右键没有Open In Default Browser问题

在VSCode进行Web小程序测试时&#xff0c;我们在新建的HTML文件中输入 !会自动生成页面代码骨架&#xff0c;写入内容后&#xff0c;我们想要右键在浏览器中预览。发现右键没有“Open In Default Browser”选项。原因是没有安装插件。 下面是解决方案&#xff1a;首先在VSCode找…

【CSS布局】响应式设计原则以及双飞翼布局圣杯布局

历史小剧场 温体仁未必是奸臣&#xff0c;钱谦益未必是好人&#xff0c;不需要惊讶&#xff0c;历史往往跟你所想的并不一样。英雄可以写成懦夫&#xff0c;能臣可以写成奸臣&#xff0c;史实并不重要&#xff0c;重要的是&#xff0c;谁来写。----《明朝那些事儿》 响应式设计…

【计算机毕设】基于SpringBoot的学生心理咨询评估系统设计与实现 - 源码免费(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 随着社会的快速发展和竞争压力的增加&#xff0c;学生心理健康问题日益突出。设计和实现一个基于SpringBoot的学生心理咨询评估系统&#xff0c;旨在…

【Seafile】Seafile容器版文件删除后存储空间不释放问题解决

Seafile是一款非常优秀的网盘系统&#xff0c;我们可以根据官方文档&#xff0c;在本地虚拟机研究Seafile免费版的安装和使用&#xff0c;安装建议采用使用docker容器的方式。 不过在使用过程中&#xff0c;刚接触的小伙伴可能会遇到这样的问题&#xff1a; 删除网盘里面的文…

煤矿输送设备无人化运维巡检解决方案

一、煤矿行业目前存在的挑战和难题 煤矿行业面临着复杂的环境&#xff0c;如粉尘、潮湿、高温、高瓦斯等&#xff0c;对巡检设备和人员安全有威胁。并且设备分布广、需要长时间作业&#xff0c;全面巡检难度大、对巡检工作的耐力和持续性要求高。而煤矿输送设备无人化运维巡检…

OAK相机如何将 YOLOv10 模型转换成 blob 格式?

编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查看首发地址链接。 Hello&#xff0c;大家好&#xff0c;这里是OAK中国&#xff0c;我是Ashely。 专…

MedSAM 学习笔记(续):训练自定义数据集

1、下载官方权重 官方的预训练权重:https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth 下载后保存在:work_dir/SAM/sam_vit_b_01ec64.pth 目录 2、摆放数据集 因为MedSAM 分割模型需要对3D数据集进行切片处理,也就是对nii.gz 数据处理成 npy 格式 …

计算机图形学入门05:投影变换

1.投影变换 上一章已经介绍了投影变换&#xff0c;就是将三维图像投影到二维平面上&#xff0c;而投影变换又分为正交投影(Orthographic Projection)和透视投影(Perspective Projection)。如下图&#xff1a; 正交投影 没有近大远小的现象&#xff0c;无论图形与视点距离是远是…

通过定时器和脉冲控制LED

一、定时器 &#xff08;一&#xff09;定时器简介 STM32定时器是STM32微控制器中的重要块&#xff0c;用于生成精确的时间基准。它可以用于测量时间间隔、产生脉冲、实现定时中断等功能。通过配置寄存器&#xff0c;用户可以灵活地控制定时器的工作模式和参数&#xff0c;实现…

Visual Studio Code使用(C++项目新建,运行)

VS Code 直接在官网下载安装。 接下来安装插件&#xff0c;下图是C所需的对应插件 1.新建项目 VS Code下载安装完成后&#xff0c;直接进入欢迎页&#xff1a; 在访达/文件夹中新建一个文件夹&#xff0c;欢迎页点击【打开】&#xff0c;选择刚刚新建的文件夹。点击第一个图…

材质技术在AI去衣中的作用

随着人工智能技术的飞速发展&#xff0c;越来越多的应用场景开始涌现。其中&#xff0c;AI去衣技术作为一种新兴的图像处理技术&#xff0c;已经在很多领域得到了广泛的应用。而在AI去衣技术中&#xff0c;材质技术起到了至关重要的作用。本文将详细介绍材质技术在AI去衣中的作…

autocad背景色、引线文字大小

一、改变背景 在命令行输入op&#xff0c;回车&#xff0c;弹出配置对话框&#xff1a; 二、改变引线文字大小 选中引线&#xff0c;右键选择【特性】&#xff0c;在文字选项卡中设置文字高度&#xff1a;

【机器学习】探索未来科技的前沿:人工智能、机器学习与大模型

文章目录 引言一、人工智能&#xff1a;从概念到现实1.1 人工智能的定义1.2 人工智能的发展历史1.3 人工智能的分类1.4 人工智能的应用 二、机器学习&#xff1a;人工智能的核心技术2.1 机器学习的定义2.2 机器学习的分类2.3 机器学习的实现原理2.4 机器学习的应用2.5 机器学习…

JVM-之GC日志

一、 开启gc 日志 在项目中开启GC 日志打印后会查看gc 日志如下 nohup java -Xms768m -Xmx768m -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath./dumplog/dumplog.log -Xloggc:./dumplog/gc.log -XX:PrintGCDetails -XX:PrintGCDateStamps -XX:PrintHeapAtGC -jar xxxx…

校园安保巡逻机器人

2023年8月5日&#xff0c;陕西西安一高校实验室起火冒烟&#xff0c;导致学校化学实验室发生火灾。2022年8月3日&#xff0c;一名歹徒持械闯入江西吉安安福县城的一家私立幼儿园&#xff0c;对着无辜的幼儿行凶伤人&#xff0c;造成3死6伤。 像这样的事故有不断地发生&#xf…

[个人笔记] 记录docker-compose使用和Harbor的部署过程

容器技术 第三章 记录docker-compose使用和Harbor的部署过程 容器技术记录docker-compose使用和Harbor的部署过程Harborhttps方式部署&#xff1a;测试环境部署使用自签名SSL证书https方式部署&#xff1a;正式环境部署使用企业颁发的SSL证书给Docker守护进程添加Harbor的SSL证…

世界500强是如何解决邮件大附件影响业务问题的

电子邮件已成为众多企业沟通和文件传输的重要工具。然而&#xff0c;邮件发送大附件时&#xff0c;企业往往会遇到一系列挑战。本文将探讨邮件发送大附件的优劣势&#xff0c;分析其对业务可能造成的问题&#xff0c;并介绍500强企业是如何解决这些问题的。 邮件发送大附件的优…

NSS题目练习5

[NISACTF 2022]babyupload 打开后尝试上传php&#xff0c;jpg&#xff0c;png文件都没成功 查看源代码发现有个/source文件 访问后下载压缩包发现有一个python文件 搜索后知道大致意思是&#xff0c;上传的文件不能有后缀名&#xff0c;上传后生成一个uuid&#xff0c;并将uuid…