嵌入式学习笔记——PWM与输入捕获(上)

输出比较与输入捕获

  • 前言
  • 输出比较(PWM)
    • PWM简介
    • 输出比较详细框图
      • 1. 定时器部分
      • 2. 比较器控制部分
      • 3.输出控制部分
    • 寄存器简介
    • 输出比较代码
      • 伪代码
      • 实际代码
      • 实际效果
  • 总结

前言

上一篇中,主要介绍了有关通用定时器的一些概述性内容,本文主要是具体介绍一下输出比较与输入捕获的使用过程,包括其详细结构以及寄存器;其中输出捕获以常用的PWM为例,输入捕获以捕获按下按键的时间为例。

输出比较(PWM)

嵌入式学习笔记——PWM与输入捕获(上)_第1张图片
首先来看看输出比较部分,前面提到了通用定时器的通道具有输入捕获、输出比较、PWM模式、单脉冲输出模式,本质上除了输入捕获以外,其他三个都是属于输出比较,都是通过编程控制输出高低电平的具体时间、周期,循环或者单次,只是具体配置的输出方式不一样;其中,PWM是最常用的控制方式,在电机控制、调节LED、屏幕亮度、电源电压控制等等邻域都有应用。那么什么是PWM呢?

PWM简介

PWM名称叫做脉冲宽度调制技术,是一种常用于电子电路和控制系统中的信号调制技术
在实际应用中,PWM广泛运用于电机驱动、灯光控制、电源电压控制等领域。因为PWM技术能够提供精准的控制,使得输出信号的质量更加高效和稳定,同时也减少了系统能耗和噪声干扰。
其实,通俗点说,PWM就是一个一直循环的方波信号,只是这个方波信号的周期,以及高低电平的时间都是可以调节的,也正是由于其周期性,所以需要借助定时器来实现,使用定时器去操作PWM控制的本质是操作高低电平的时间。
嵌入式学习笔记——PWM与输入捕获(上)_第2张图片

输出比较详细框图

在编程手册的15.3.4 捕获/比较通道有关于输入输出的详细电路图,如下所示是通道1的主电路图,其中左侧是输入捕获部分的,右侧是输出比较部分的,这里先看输出比较部分的。
嵌入式学习笔记——PWM与输入捕获(上)_第3张图片
仔细观察会发现在这个框图的内容应该不全,上一篇的中的输出部分还有一个输出控制器,在这个图中找不到,实际上不是少了,只是为了方便排版,官方分为了几个图,找到“图 146. 捕获/比较通道的输出阶段(通道 1)”,它所对应的电路就是输出的剩下部分,这里笔者直接将两者做个拼接,拼接后如下图所示:
嵌入式学习笔记——PWM与输入捕获(上)_第4张图片
这个电路图才是输出比较的实际框图,还是老规矩,为了好理解进行分块介绍。
嵌入式学习笔记——PWM与输入捕获(上)_第5张图片

1. 定时器部分

首先还是来看定时器的部分,其实定时器部分的配置和框图和之前的基本定时一样的,这里只是更详细了,对照下图再捋一下定时器的配置流程:
1.开启对应的时钟使能;
2.进行时基配置,包括计数器、预分频和重装载的配置;
关于计数器的配置其实很简单,没有什么多说的,这里要补充一点计数器和比较器的工作方式。
嵌入式学习笔记——PWM与输入捕获(上)_第6张图片
如上图所示,计数器的计数器会和比较器的CCR1进行比较,假设我们编程控制,要求当计数器的CNT大于CCR1的时候输出高电平;当CNT<=CCR1时输出低电平,这在对应通道的IO口我们就可以看见右侧IO逻辑的输出波形。
同样的,我们也可以编程为当CNT小于CCR1时输出高电平,当CNT大于等于CCR1时输出低电平,此时IO的输出波形如下图所示:
嵌入式学习笔记——PWM与输入捕获(上)_第7张图片
也就是说,GPIO在啥时候输出啥电平是可以由我们进行编程控制的。

2. 比较器控制部分

关于比较器的控制部分,其实和基本定时器的流程差不多,这里我们先看下图中的红框位置,他的输出是指向比较预装载寄存器和比较影子寄存器之间的,这就说明,由他来控制是否将预装在寄存器里面的值写入影子寄存器进而是参数对计数器产生效果。
嵌入式学习笔记——PWM与输入捕获(上)_第8张图片

  1. 要让这个与门输出为真,才可以让预装载值写入影子寄存器,那么要让与门输出为真,就要求后面的三个输入都是真才行;
  2. 接下来看与门后面的三个输入,首先是1号位置,它的输出在进入与门之前会有一个非对它进行取反,也就说如果1号位置是非0的时候 ,第一个输入红框与门的信号就是0,这样是无法写入数据的,那么1号位置代表的是什么呢,查询对应的寄存器就可以知道了,如下图所示:
    嵌入式学习笔记——PWM与输入捕获(上)_第9张图片
    可以发现这个寄存器就是写入预装载值的,在写入CCR1数据的时候1号位置会输出1,使得红框的与门被卡住,避免将错误数据写入影子寄存器;而写入完成后1号位置会输出0,此时1号输入红框与门的为真,可以写入,对于编程人员来说,只需要对CCR1进行赋值就可以了,写入完成这些不需要我们判断。
  3. 然后是2号位置,这里是一个或非门,它的逻辑是输入全为假则为真,输入有真则为真,也就是说,此路想要让红框的与门输出为真的话,就必须保证CC1S这个寄存器的值为0才可以,那么这个寄存器的值为0代表了什么呢
    还是在编程手册中对应搜索即可。
    嵌入式学习笔记——PWM与输入捕获(上)_第10张图片
    可以发现,这个寄存器是用来选择配置为输入模式还是输出模式的,由于PWM是输出比较,所以得配置为“00”,这样也就使得或非门输出为真,进而使得与门的第二个数为真。
    4 .最后就是红框与门的第三个输入了,也就是图中的3号位置,可以看出它是一个或门,有两个输入,且上面的输入也是带有非门标志的,那么这个或门要输出真的话就要求上面的输入事件为假或者下面的输入为真,那么这两个输入的事件对应的是什么呢,还是看编程手册,如下图所示:
    首先是OCIPE,这一位就是使能预装载的,也就是说编程时可以通过控制这一位来实现预装载要不要开启。
    嵌入式学习笔记——PWM与输入捕获(上)_第11张图片
    然后是下面这个事件,我们先不看,猜一下,既然上个事件是使能或者禁止预装载,那么这个预装载配套的还有个东西——就是我们前面使用基本定时器的时候提到过的更新事件;如果开启了预装载寄存器,那么我们要想真正的将数据写入计数器的话,就必须手动产生一次更新时间才可以,所以最后一个肯定是和更新事件有关的UEV(更新事件)。
    这里的配置一般都会开启预装载寄存器的。

3.输出控制部分

经过计数器和比较比较后的输出结果会来到下图所示的输出模式控制器,这部分的电路主要就是控制输出的模式,是高有效还是低有效这些那么具体怎么控制的呢。
嵌入式学习笔记——PWM与输入捕获(上)_第12张图片

  1. 首先需要看的就是1号框内,有一个数据选择器,这里要注意,原本的编程手册这个地方有点小问题,就是选择器的选择寄存器,原图中标的寄存器找不到,实际是笔者图中改的TIMx_CCMR1的OC1CK,描述如下:
    嵌入式学习笔记——PWM与输入捕获(上)_第13张图片
    ETRF前面提到过叫做外部触发输入 (ETRF),由于PWM输出过程中不需要由外部触发的作用,所以默认配置为0;
  2. 然后是2号框对应的OC1M,这位对应位置也在CCMR1上,可以发现这个寄存器分为了两部分功能,其中上面第一列是控制输出功能的,中间第二列是控制输入模式的,他们是共用一个寄存器的。
    嵌入式学习笔记——PWM与输入捕获(上)_第14张图片
    关于OC1M的作用,如下图所示:
    嵌入式学习笔记——PWM与输入捕获(上)_第15张图片
    此三位是用来选择模式的,由于我们要使用PWM模式,所以就需要配置为110或者111,关于PWM的模式1和PWM的模式2,其实就是上面提到的有效电平的不同罢了,当计数器为向上计数时:
    PWM模式1 CNT PWM模式2 CNT>CCR的时候为有效电平
    也就是说,这两个模式,在其他配置都一样的情况下会生成互补的输出波形。
  3. 然后就是3号红框的位置这里又是一个数据选择器,但是输入信号是同一个,只是其中一个进行了取反操作,猜一下,这个位是用来控制输出极性的
    具体描述如下图所示:
    在这里插入图片描述
    此位就是用来控制输出博信是高有效还是低有效的,那么需要注意的是,上面的PWM模式最终也是控制高低电平的,这个也是用来控制高低电平在配置的过程中要小心。
  4. 最后就是4号框所在的控制器,使能位,置一才可以开启对应的输出。
    嵌入式学习笔记——PWM与输入捕获(上)_第16张图片
    输出比较需要配置的流程:
    输出通道需要配置的
    ①选择输出模式
    ②比较寄存器的影子寄存器
    ③选择PWM模式
    ④选择有效电平
    ⑤输出使能

寄存器简介

上面的框图讲解已经引出了大部分寄存器了,这里再来做个总结,将所需要用的寄存器挑出来。通用定时和高级定时器的寄存器数量还是蛮多的,通用定时器就有了20个寄存器,当然不是每次都需要20个寄存器,只需要根据对应功能去配置对应位即可。 PWM需要使用的寄存器如下所示:

1.TIMx 控制寄存器 1 (TIMx_CR1)
写法: TIMx->CR1
位 0 CEN:计数器使能
位 1 UDIS:更新禁止 (Update disable)
位 2 URS:更新请求源 (Update request source)
位 3 OPM:单脉冲模式 (One-pulse mode)
位 4 DIR:方向 (Direction)
位 7 ARPE:自动重载预装载使能 (Auto-reload preload enable)
位 9:8 CKD:时钟分频 (Clock division)
输入捕获的时候涉及到采样频率

2.TIMx 状态寄存器 (TIMx_SR)
写法:TIMx->SR
位 0 UIF:更新中断标志 (Update interrupt flag)
位 1 CC1IF:捕获/比较 1 中断标志 (Capture/compare 1 interrupt flag)

3.TIMx 事件生成寄存器 (TIMx_EGR)
写法:TIMx->EGR
位 0 UG:更新生成 (Update generation)

4.TIMx 捕获/比较模式寄存器 1 (TIMx_CCMR1)
写法:TIMx->CCMR1
用于配置通道1 通道2
位 1:0 CC1S:捕获/比较 1 选择 (Capture/Compare 1 selection)
选择输入还是输出
位 3 OC1PE:输出比较 1 预装载使能 (Output compare 1 preload enable)
比较影子寄存器使能
位 6:4 OC1M:输出比较 1 模式 (Output compare 1 mode)
选择对应输出模式 PWM1

5.TIMx 捕获/比较模式寄存器 2 (TIMx_CCMR2)
写法:TIMx->CCMR2
功能和CCMR1完全一致 操作的是通道3 和 通道4

6.TIMx 捕获/比较使能寄存器 (TIMx_CCER)
写法:TIMx->CCER
位 0 CC1E:捕获/比较 1 输出使能 (Capture/Compare 1 output enable)。
通道使能:置1使能
位 1 CC1P:捕获/比较 1 输出极性 (Capture/Compare 1 output Polarity)。
配置对应有效电平

7.TIMx 捕获/比较寄存器 1 (TIMx_CCR1)
写法:TIMx->CCR1
用法直接赋值
不难发现整个配置过程中,时基配置部分和前面的基本定时器一样的,就是修改了一下定时的名字。通用定时的PWM功能只是在其基础上怎加了部分寄存器。

输出比较代码

根据上面的框图讲解,以及寄存器的介绍,总结一下使用定时器输出PWM的配置流程:

伪代码

PWM初始化配置
{
   //开始对应时钟  IO口  定时器

   GPIO的控制器
   //模式寄存器
   //复用功能寄存器   查映射表

   配置定时器的时基单元部分
   //更新禁制
   //更新请求源
   //单脉冲
   //方向
   //重装载值影子寄存器
   //预分频配置
   //重装载值配置
   //人为产生更新事件

   配置输出通道
   //通道配置成输出模式
   //比较寄存器的影子寄存器
   //配置PWM模式
   //配置输出的有效电平
   //通道使能

   //计数器使能   
}
//具体修改PWM输出的占空比,就是通过控制比较寄存器的值CCR来实现的
TIMx->CCR1=****;

实际代码

首先找到对应需要PWM驱动的外设引脚,这里笔者用的是PA6、PA7,在原理图上即可查看
嵌入式学习笔记——PWM与输入捕获(上)_第17张图片
然后再引脚映射表查找对应的映射号和对应的定时器通道,分别是:AF2、TIM3->CH1、TIM3->CH2;
嵌入式学习笔记——PWM与输入捕获(上)_第18张图片
然后就是代码了:如下所示:

/*******************************************
*函数名    :Time3_PWM_Init
*函数功能  :定时器三的PWM初始化配置
*函数参数  :u16 arr u16 psc
*函数返回值:无
*函数描述  :
PA6---------CH1
PA7---------CH2
*********************************************/
void Time3_PWM_Init(u16 psc,u16 arr )
{
	RCC->AHB1ENR |=(1<<0);//打开PA的时钟
	RCC->APB1ENR |=(1<<1); //打开Time3的时钟
	
	//GPIO的配置
	GPIOA->MODER &=~(0xf<<12);//清零
	GPIOA->MODER |= (0xa<<12);//复用模式
	
	GPIOA->AFR[0] &=~(0xf<<24);//清零
	GPIOA->AFR[0] |= (0x2<<24);//将PA6映射到Time3的通道1
	//GPIOA->AFR[0] &=~(0xf<<28);//清零
	GPIOA->AFR[0] &=~(0XF<<28); //清零
	GPIOA->AFR[0] |=(2<<28);    //将PA7复用到TIM3_CH1
	
/*-----------时基配置-----------------------------------------------------------------*/
	TIM3->CR1 &=~(0Xf<<1);	//更新禁止,更新请求源,关闭单脉冲,向上计数
	TIM3->CR1 |=(1<<7);     //重装载影子寄存器
	TIM3->SMCR &=~(7<<0);  //配置内部时钟
	TIM3->PSC = psc-1;
	TIM3->ARR = arr-1;
	TIM3->EGR |= (1<<0);//更新事件,写入预分频和重装载值
	
/*--------------通道部分-------------------------------------------------------------*/
//通道一
	TIM3->CCMR1 &=~ (3<<0);//配置为输出模式
	TIM3->CCMR1 |=(1<<3); //比较寄存器的影子寄存器
	TIM3->CCMR1 &=~(0X7<<4);
	TIM3->CCMR1 |=(0X6<<4);//PWM模式1
	
	TIM3->CCER |=(1<<1);//低电平有效
	TIM3->CCER |=(1<<0);//输出通道使能

//通道二
	TIM3->CCMR1 &=~ (3<<8);//配置为输出模式
	TIM3->CCMR1 |=(1<<11); //比较寄存器的影子寄存器
	TIM3->CCMR1 &=~(0X7<<12);
	TIM3->CCMR1 |=(0X6<<12);//PWM模式1
	
	TIM3->CCER |=(1<<5);//低电平有效
	TIM3->CCER |=(1<<4);//输出通道使能

	TIM3->CR1 |=(1<<0);     //计数器使能	
}

#ifndef _PWM_H
#define _PWM_H
#include "stm32f4xx.h"
#define Time_PWM_Duty1 TIM3->CCR1
#define Time_PWM_Duty2 TIM3->CCR2
void Time3_PWM_Init(u16 psc,u16 arr );
#endif

实际效果

实现板子上两个灯PA6和PA7的呼吸灯效果;
占空比控制函数:

/*******************************
函数名:TIM7_IRQHandler
函数功能:定时器7中断服务函数函数
函数形参:无
函数返回值:void
备注:1ms中断
********************************/
void TIM7_IRQHandler(void)
{
	static u8 LED_Flag=0;
	static u16 Time7_Cnt[10]; 
	if(TIM7->SR & (1<<0))
	{
		TIM7->SR &=~(1<<0);
		Time7_Cnt[0]++;
		Time7_Cnt[1]++;
		if(Time7_Cnt[0]>=500)
		{
			
			Time7_Cnt[0]=0;
		}
		if(Time7_Cnt[1]>2)
		{
			Time7_Cnt[1]=0;
			if(LED_Flag==0)//逐渐亮
			{		
				Time_PWM_Duty1++;
				Time_PWM_Duty2++;
				if(Time_PWM_Duty1>900)
				{
					LED_Flag=1;
					Time_PWM_Duty1=990;
					Time_PWM_Duty2=990;
				}
			}
			if(LED_Flag==1)//逐渐暗
				{
					Time_PWM_Duty1--;
					Time_PWM_Duty2--;
					if(Time_PWM_Duty1<50)
					{
						LED_Flag=0;
						Time_PWM_Duty1=10;
						Time_PWM_Duty2=10;
					}
				}
		}	
	}
}

呼吸灯:

总结

关于输出比较模式的PWM输出就介绍这么多,由于篇幅太长影响看的体验,输入捕获再单开一篇,下篇进行介绍。

你可能感兴趣的:(嵌入式,—M4,学习,单片机,stm32,嵌入式,arm开发)