一、IC和OC
可以看到:定时器除了基本的定时中断功能,输入捕获、输出比较均是STM32定时器的功能
输入捕获IC(Input Capture)
输入捕获是一种用于测量外部信号脉冲宽度或频率的技术。它通过定时器模块捕获外部信号的特定事件(如脉冲的上升沿或下降沿),以便测量脉冲宽度、频率或其他相关参数。在STM32中,除了基本定时器TIM6和TIM7外,其他定时器通常都具有输入捕获功能。输入捕获常用于测量传感器输出、编码器信号、PWM输入信号等。
输出比较OC(Output Compare)
输出比较是通过比较定时器的计数器值与捕获/比较寄存器(CCR)的值,来操作输出电平的一种技术。++它可以用来生成一定频率和占空比的PWM波形,实现对外部电路的控制++。每个高级定时器和通用定时器在STM32中都拥有多个输出比较通道,这些通道可以独立配置,以生成不同的PWM波形。
捕获/比较寄存器,这个寄存器是输出比较和输入捕获共用的,两种功能在同一时刻只能选择一种执行。先将其用作输出比较来看,输出比较最重要的一个功能就是产生PWM波。也可以看到,通用计时器有四个输出比较通道:OC1~4
其中OC1的内部输出部分如下:
输出比较模式(TIMx_CCMRx寄存器中的OCxM位)和输出极性(TIMx_CCER寄存器中的 CCxP位)
在判断计数器CNT和捕获/比较寄存器CCR(capture /compare register)的值并确认输出模式后输出一个OC1REF(OC1 Reference),它指的是与输出比较通道1(OC1)相关联的参考信号或电平状态。
它有两条之路可走,一条是进入主模式控制器就能够作为触发输出TRGO输出给其他定时器或者DA/DC转换;另一条是进入极性选择器TIMx_CCER,极性选择器置0则信号走上一路不做变换直接输出到输出使能电路,如果置1则让它走下支路可以使信号oc1ref经过一个非门电路将其反转,高变低,低变高。所以,oc1ref的电平状态不仅取决与CNT和CCR的比较,还取决于我们选取怎么样的输出模式
其中输出模式总共有以下几种:
这里细心一点就会发现:PWM模式1和模式2是完全相反的,也就是说,在两种不同模式下,oc1ref在经过选择器时,一个模式ref照常输出,一个模式ref取反。模式1和模式2正好互补,互为相反,所以在运用起来差别也并不太大。这里用的最多的就是PWM模式1。
而从计数模式上来看,PWM也和TIMx在作定时器时一样,也有向上计数模式、向下计数模式和中心对齐模式,关于3种模式的具体资料,可以查看《STM32参考手册》的"14.3.9 PWM模式"一节。
二、PWM输出
脉冲宽度调制(PWM),是英文"Pulse Width Modulation"的缩写,简称++脉宽调制++ ,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对**++脉冲宽度的控制++**。一般用来控制步进电机的速度等等。
STM32的定时器除了基本定时器TIM6和TIM7之外,其他的定时器都可以用来产生PWM输出,其中高级定时器TIM1和TIM8可以同时产生7路的PWM输出,而通用定时器也能同时产生4路的PWM输出。
PWM输出管脚
PWM的输出管脚是确定好的,具体的引脚功能可以查看《STM32参考手册》的"8.3.7 定时器复用功能重映射"一节。比如说TIM3的第2个通道,在没有重映射的时候,指定的管脚是PA7,如果设置部分重映射之后,TIM3_CH2的输出就被映射到PB5上了,如果设置了完全重映射的话,TIM3_CH2的输出就被映射到PC7上了。
在此需要强调的是,不同的TIMx有分配不同的引脚,但是考虑到管脚复用功能 ,STM32提出了一个重映射的概念,就是说通过设置某一些相关的寄存器,来使得在其他非原始指定的管脚上也能输出PWM。但是这些重映像的管脚也是由参考手册给出的。
复用和重映射的区别:
(1) 可以看到PA9引脚、PA10引脚都有三种功能。其中第一项PA9和PA10是其默认功能,默认功能为GPIO功能,也即是作为通用的输入输出端口使用。这样我们就知道,当PA9引脚和PA10引脚不在作为默认的GPIO功能使用,而是作为USART1_TX/USART1_RX或者作TIM1_CH2/TIM1_CH3功能使用时,就是对PA9引脚和PA10引脚的复用。++简单来说就是:一个引脚有多种外设的特定功能就叫复用++
(2)重映射就是把原本属于这个引脚的复用映射到另外一个引脚上去,但是能不能够映射就要看上图引脚定义的重定义功能有没有原本引脚的复用功能存在。++简单来说:将某些外设的特定功能从默认的GPIO引脚移动到其他GPIO引脚上。++
++那么部分重映射和完全重映射也就比较好理解了,都是相对于没有重映射来说,部分引脚改变则位部分重映射,全部引脚均改变则为完全重映射。++
PWM输出信号
PWM输出的是一个方波信号,信号的频率是由TIMx的时钟频率和TIMx_ARR预分频器所决定的,而输出信号的占空比则是由TIMx_CRRx寄存器确定的。
因此,可以通过向CRR中填入适当的数来输出自己所需的频率和占空比的方波信号。
三、TIMER输出PWM实现步骤
代码编写依托于以下:
定时器部分:
PWM配置部分:
定时器配置上篇文章讲过了,以下是PWM部分:
1.开启时钟;
RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM2,ENABLE );
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA,ENABLE );
2.配置GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_15 ;
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_50MHz ;
GPIO_Init (GPIOA,&GPIO_InitStruct);
3.设置TIMx定时器的相关寄存器
TIM_InternalClockConfig(TIM2); //开启内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision =TIM_CKD_DIV1; //1分频
TIM_TimeBaseInitStruct.TIM_CounterMode =TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInitStruct.TIM_Period =; //给ARR赋值
TIM_TimeBaseInitStruct.TIM_Prescaler =; //给PSC赋值
TIM_TimeBaseInitStruct.TIM_RepetitionCounter =0; //重复计数器是高级定时器才会用到,这里给0即可
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);//初始化定义
4.设置TIMx定时器的PWM相关寄存器
TIM_OCInitTypeDef TIM_OCInitStruct;
// TIM_OCStructInit(&TIM_OCInitStruct); ------>>PWM结构体内部成员有些是高级定时器才会进行配置,所以我们只需要保持默认值即可,该函数的作用就是设置默认值,如果有子成员需要修改,再在下面进行修改即可;
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 =90; //给CCR赋值
TIM_OC1Init(TIM2,&TIM_OCInitStruct);
第1步需要注意的是通用定时器TIMx是由APB1提供时钟,而GPIO则是由APB2提供时钟。如果需要对PWM的输出进行重映像的话,还需要开启引脚复用时钟AFIO:
RCC_APB2PeriphClockCmd (RCC_APB2Periph_AFIO,ENABLE );
第2步设置GPIO时钟时,GPIO模式应该设置为复用推挽输出GPIO_Mode_AF_PP ,如果需要引脚重映射的话,则需要用**GPIO_PinRemapConfig()**函数进行设置。
为什么GPIO要配置为复用推挽模式? ------可以看到当I/O端口被配置为复用功能时,在开漏或推挽式配置中,输出缓冲器被打开,输出控制与片上外设直接连接,只有这样我们才能将定时器通道TIMx_Ch打通:
其中还需要注意,你想要映射到的那个引脚是否是调试引脚:
如果是调试引脚还需要使用GPIO_PinRemapConfig()函数关闭其调试端口的复用,才能够让它作为普通的GPIO口或复用定时器通道。
第3步设置TIMx定时器的相关寄存器。
第4步设置PWM相关寄存器,首先要设置PWM模式(默认情况下PWM是冻结的),然后设置占空比(根据前面所述公式进行计算),再设置输出比较极性:当设置为High时,输出信号不反相,当设置为Low时,输出信号反相之后再输出。最重要是要使能TIMx的输出状态和使能TIMx的PWM输出使能。
相关设置完成之后,就可以通过TIM_Cmd()来打开TIMx定时器,从而得到PWM输出了。
PWM函数如下:
cpp
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM2,ENABLE );
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA,ENABLE );
RCC_APB2PeriphClockCmd (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_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision =TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode =TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period =100-1; //ARR
TIM_TimeBaseInitStruct.TIM_Prescaler =720-1;//PSC
TIM_TimeBaseInitStruct.TIM_RepetitionCounter =0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
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 =50;//CCR
TIM_OC1Init(TIM2,&TIM_OCInitStruct);
TIM_Cmd (TIM2 ,ENABLE );
}
如果我想要把PA0的TIM2_Ch1_ETR功能映射到PA15:
代码示例如下:
cpp
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM2,ENABLE );
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA,ENABLE );
RCC_APB2PeriphClockCmd (RCC_APB2Periph_AFIO,ENABLE );
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE );
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE );
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_15 ;
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_50MHz ;
GPIO_Init (GPIOA,&GPIO_InitStruct);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision =TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode =TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period =100-1; //ARR
TIM_TimeBaseInitStruct.TIM_Prescaler =720-1;//PSC
TIM_TimeBaseInitStruct.TIM_RepetitionCounter =0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
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 =90;//CCR
TIM_OC1Init(TIM2,&TIM_OCInitStruct);
TIM_Cmd (TIM2 ,ENABLE );
}