栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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

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

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

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

文章目录
  • 1 通用定时器简介
    • 1.1简介
    • 1.2 输出比较
    • 1.3 输入捕获
  • 2 实验
    • 2.1 输出PWM
      • 2.1.1提要
      • 2.1.2 思路
      • 2.1.3 代码编写
      • 2.1.4 改变占空比
    • 2.2 PWM控制颜色的亮度
    • 2.3 捕获PWM周期和占空比
      • 2.3.1 PWM输入模式简介
      • 2.3.2 使用方法
      • 2.2.3 代码

1 通用定时器简介

通用定时器框图

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模式原理。

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上

开启复用

    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寄存器,输出有效极性。

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

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

代码配置如下

	//设置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)
	{

	}
}

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

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;
  }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/866984.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号