IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 面向应用学习stm32(6)-TIM通用定时器-PWM输出和输入 -> 正文阅读

[嵌入式]面向应用学习stm32(6)-TIM通用定时器-PWM输出和输入

前导:本文的目的与,意在于面向应用的学习单片机,故不会涉及太多的原理知识,例如寄存器之类的。

主要目的在于面向应用的学习单片机,学会单片机的基础用法,开发板采取野火的指南者f103。

作者大二小白,写的不好的地方轻点喷,欢迎评论区交流

全部工程代码开源在Gitee仓库


image-20220507132940522

1 通用定时器简介

通用定时器框图
image-20220507135917668

image-20220507140109659

1.1简介

通用定时器包括TIM2、TIM3、TIM4和TIM5。

不同于基本定时器TIM6、7

通用定时器具有向上向下的计数功能,并且有4路的IO口引出,后文会说成四个通道,可以进行输入捕获和输出比较,PWM等等的,PWM其实就是输出比较的一个特例。也有PWM输入模式,也是输入捕获的一个特例

1.2 输出比较

输出比较:当计数器CNT的值和比较器CCR(Capture Compare Register)的值相等的时候,输出信号的极性就会改变。例如翻转极性,拉高极性,或者拉低极性。直到CNT的值等于ARR的值时,再次发生跳变。此时一个周期完整结束

例如 psc配置71,period(ARR)配置999。也就是我们之前的1ms计一次。那么CCR的值配置为200。

假设最开始是高电平,每次CNT+1,加到200的时候,电平翻转,再走完剩下的800,电平再次翻转。实现百分之20占空比的方波

PWM模式原理。

img

? 在这里插入图片描述

1.3 输入捕获

输入捕获说的通俗一点就是用定时器来记录某一段脉冲的时间,或者者我们只捕获脉冲的上升沿或者下降沿。

基本工作过程就是捕捉上升沿或者下降沿,然后触发中断

之前的两次按键间隔计时,在那边我们用的是计时计算,也就是1ms进一次中断,通过使能和失能定时器

如果是要计算一次按键按下的时间的话,我们就可以利用输入捕获,按键按下,产生上升沿,维持一定宽度的矩形脉冲,按键松手的时候,产生下降沿,结束这个矩形脉冲。然后我们就可以通过捕获的数据,计算按键按下的时间

2 实验

2.1 输出PWM

2.1.1提要

采用比较输出模式生成pwm的实现方法较为复杂,博主还没研究好,这里先不提及。

我们先用PWM模式生成pwm,较为直观简单

两种方式的差别是 PWM它只能生成四路频率相同的PWM,当你设定了PSC,ARR(自动重装载寄存器),这时PWM的频率就被定下来了。你可以通过改变各个通道的CCR寄存器来改变占空比。

但是想生成多路不同频率的PWM的话,使用PWM模式这个方法只能使用多个定时器了,但输出比较的模式,可以生成多路不同频率及占空比的PWM。

2.1.2 思路

  • 开启IO和TIM的时钟,使用复用功能
  • 配置定时器的基本功能
  • 配置定时器的输出功能,设置占空比
  • 使能

2.1.3 代码编写

基本配置部分,我们在前面的定时器里已经说过了,这里不多赘述。

查阅数据手册,使用PB0,PB1输出方波,可以发现PB0和PB1分别挂在了TIM3通道3,和TIM3通道4上

image-20220507144307887

开启复用

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; 
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

定时器基本配置

	TIM_TimeBaseStructure.TIM_Period = arr;
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 

输出功能配置

这里我们去头文件里找,发现有这个结构体,我们需要1,2,4,5个成员,分别配置,输出模式,输出使能,CCR寄存器,输出有效极性。

image-20220507142259437

找参数的方法之前也说过了,就不提了,关于参数里的TIM_OCMode_PWM1和TIM_OCMode_PWM2的差别。

有以下说法,理解为两个模式产生的效果是相反的

image-20220507142836960

代码配置如下

	//设置PWM模式1
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 
	//设置PWM输出使能
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	//设置脉冲(CCR)的值
	TIM_OCInitStructure.TIM_Pulse = 500; 
	//设置PWM输出极性
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

然后我们需要把这个输出结构体,初始化到指定的通道中(这些函数都可以在手册或者stm32f10x_tim.h里找)

	TIM_OC3Init(TIM3, &TIM_OCInitStructure);  
	TIM_OC4Init(TIM3, &TIM_OCInitStructure);  
	//使能通道3的预装载寄存器
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  
	//使能通道4的预装载寄存器
	TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  
	TIM_Cmd(TIM3, ENABLE); 

关于倒数二三行可以参考TIM_OC3PreloadConfig的作用

完整配置代码如下

void TIM3_Init(int psc,int arr)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	TIM_TimeBaseStructure.TIM_Period = arr;
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 
	//设置PWM模式2
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 
	//设置PWM输出使能
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	//设置脉冲(CCP)的值
	TIM_OCInitStructure.TIM_Pulse = 500; 
	//设置PWM输出极性
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	//初始化TIM3输出结构体
	TIM_OC3Init(TIM3, &TIM_OCInitStructure);  
	TIM_OC4Init(TIM3, &TIM_OCInitStructure);  
	//使能通道3的预装载寄存器
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  
	//使能通道4的预装载寄存器
	TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  
	TIM_Cmd(TIM3, ENABLE); 
}

主函数中

int main (void)
{
	KEY_Init();//LED初始化
	LED_Init();//按键初始化
	
	TIM3_Init(72-1,1000-1); //72 * 1000 / 72 000 000 = 0.001s = 1ms
	
	SysTick_Config(SystemCoreClock / 1000);
	ILI9341_Init();//液晶初始化    
	LCD_SetColors(BLACK,WHITE);//设置白底黑字
	LCD_SetFont(&Font8x16);//设置字体大小
	ILI9341_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH);//清屏
  ILI9341_GramScan ( 6 );//设置显示模式
	LED_Color(LED_OFF);//关灯
		
	while (1)
	{

	}
}

到这里,我们就实现了PB0和PB1,百分之50的PWM占空比输出

2.1.4 改变占空比

可是如果要改变通道的占空比呢?

之前说过,PWM模式本质上是输出比较模式的一个特例,通过比较CNT和CCR的值来改变的电平

而有这样一套函数就是专门用来设置CCR的值的

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);

我们这里用的是通道3,4,arr设置的是1000,假设要改成1个通道输出20占空比的方波,一个输出60,那就这样改

TIM_SetCompare3(TIM3,200);
TIM_SetCompare4(TIM3,600);

主函数如下

int main (void)
{
	//...省略部分...
		
	TIM_SetCompare3(TIM3,200);
	TIM_SetCompare4(TIM3,600);
	while (1)
	{

	}
}

image-20220507144828003

image-20220507144843294

2.2 PWM控制颜色的亮度

首先由于小灯泡是置低电平触发,所以我们先把初始化配置里的输出极性改为Low

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;

主函数设置延时就可以了

	while (1)
	{		
		for(i=0;i<100;i+=5)
		{
			TIM_SetCompare3(TIM3,i);
			Delay(0xFFFFF);
		}
		for(i=100;i>0;i-=5)
		{
			TIM_SetCompare3(TIM3,i);
			Delay(0xFFFFF);
		}
	}

2.3 捕获PWM周期和占空比

2.3.1 PWM输入模式简介

PWM输入模式,这个模式是STM32输入捕获检测脉宽和频率的一种硬件处理机制,说白了就是STM32芯片专门用来进行对PWM进行捕获的一个功能;此方法相比较于传统的PWM的捕获方法,大大减小了代码量,提高了检测效率。

PWM输入模式中,需要两个通道的协同工作,官方给出的是CH1和CH2为一组。

为什么需要两个通道的协同工作来计算占空比的周期?

假设PWM波从0电平开始跳变 我们假设从机是通道2,主机通道1

  • 第一个上升沿到来时,1,2通道同时检测到上升沿,通道1设置为复位模式。然后将TIM的CNT计数值复位到0,所以不会产生中断。
  • 下一个到来的是下降沿,此时通道2发生捕获事件产生中断,将计数值存入自己的CCR2中
  • 第二个上升沿来时,通道1发生捕获事件产生中断,将计数值存入自己的CCR1中,然后再次复位清零计数值
  • 这样,一次捕获完成了。
    • 占空比就是CCR1/CCR2 * 100%
    • 频率就是SysytemClockCore/输入的psc/CCR2

image-20220508090933531

image-20220508090956981

2.3.2 使用方法

只给其中一个通道分配gpio时钟即可,另一个在内部使用,我们无需顾虑。给一个通道分配gpio时钟后,设置另一个为从机且复位模式。

代码思路

  • 配置中断
  • 配置时基结构体TIM_TimeBaseInitTypeDef
  • 配置输入捕获结构体
  • 设置捕获通道,触发方式
  • 设置捕获管角映射
  • 初始化到PWM输入模式中
  • 设置主从模式触发源
  • 设置从机复位
  • 使能主从模式并初始化定时器

2.2.3 代码

void Tim4_Capture_Init()
{
		NVIC_InitTypeDef 					NVIC_InitStructure; 
		TIM_ICInitTypeDef  				TIM_ICInitStructure;
		TIM_TimeBaseInitTypeDef  	TIM_TimeBaseStructure;	
		GPIO_InitTypeDef 					GPIO_InitStructure;
	
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);			

//   // 为什么这里不配置才能用,配置就没法用了
//		GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 ;
//		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//		GPIO_Init(GPIOB, &GPIO_InitStructure);	
	
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);		    		
    	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; 					
   	 	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	
   	 	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		  
   		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   		NVIC_Init(&NVIC_InitStructure);		

		//设置测量下限频率为1k,如果要设置到最低就用0xFFFF
		//TIM_TimeBaseStructure.TIM_Period= 1000;			
		TIM_TimeBaseStructure.TIM_Period= 0xFFFF;							
		TIM_TimeBaseStructure.TIM_Prescaler= 72-1;									

		TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);				
		
		TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//设置捕获通道	
		TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	 //设置上升沿触发	
		TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;	//设置管角映射
    
    	//输入预分频。意思是控制在多少个输入周期做一次捕获,
    	//如果输入的信号频率没有变,测得的周期也不会变。比如选择4分频
    	//则每四个输入周期才做一次捕获		
		TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
		TIM_ICInitStructure.TIM_ICFilter = 0x0;		//不滤波							
		TIM_PWMIConfig(TIM4, &TIM_ICInitStructure);	
		//设置触发源为IC1 要和前面的通道一样,通道2就默认变成了从模式													
		TIM_SelectInputTrigger(TIM4, TIM_TS_TI1FP1);
		TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset);//设置从机复位模式
		TIM_SelectMasterSlaveMode(TIM4,TIM_MasterSlaveMode_Enable); //使能主从模式
		//初始化清除标志位
		TIM_ITConfig(TIM4, TIM_IT_CC1, ENABLE);					
		TIM_ClearITPendingBit(TIM4, TIM_IT_CC1);				
		TIM_Cmd(TIM4, ENABLE);		 										
}

中断代码

//...省略全局变量...
void TIM4_IRQHandler(void)
{
  //清除标志位
  TIM_ClearITPendingBit(TIM4, TIM_IT_CC1);
  //获取通道1和2的值
  IC1Value = TIM_GetCapture1(TIM4);
  IC2Value = TIM_GetCapture2(TIM4);
  if (IC1Value != 0)
  {
    DutyCycle = (float)((IC2Value+1) * 100) / (IC1Value+1);  
    //这里的72就是刚刚输入捕获那边配置的分频数,如果配置的是36-1就要除36-1
    Frequency = (SystemCoreClock/72-1)/(float)(IC1Value+1);  
  }
  else
  {
    DutyCycle = 0;
    Frequency = 0;
  }
}
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-05-09 12:53:52  更:2022-05-09 12:53:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/30 1:08:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码