ter value)
3.4 预分频器PSC
预分频器的功能也很单一,就是分频:
位 15:0 PSC[15:0]:预分频器值 (Prescaler value)
计数器时钟频率 CK_CNT 等于 fCK_PSC / (PSC[15:0] + 1)。
PSC 包含在每次发生更新事件时要装载到实际预分频器寄存器的值。
3.5 自动重装载寄存器ARR
自动重装载寄存器的功能也很单一,就是保存一个数,在计数满的时候,重新开始计数
位 15:0 ARR[15:0]:自动重载值 (Auto-reload value)
ARR 为要装载到实际自动重载寄存器的值。
当自动重载值为空时,计数器不工作。
3.6 捕获/比较寄存器CCR
自动重装载寄存器的功能也很单一,也是保存一个数,用于与当前的CNT进行比较,注意 TIM2 和 TIM5是32位计数。
以CCR1寄存器(一共有CCR1~CCR4这4个通道)为例:
位31:16 CCR1[31:16]:捕获/比较 1 的高 16 位(对于 TIM2 和 TIM5)。
位15:0 CCR1[15:0]:捕获/比较 1 的低 16 位 (Low Capture/Compare 1 value)
如果通道 CC1 配置为输出:CCR1 是捕获/比较寄存器 1 的预装载值。如果没有通过 TIMx_CCMR寄存器中的OC1PE 位来使能预装载功能,写入的数值会被直接传输至当前寄存器中。否则只在发生更新事件时生效(拷贝到实际起作用的捕获/ 比较寄存器1)。实际捕获/比较寄存器中包含要与计数器 TIMx_CNT进行比较并在 OC1 输出上发出信号的值。
如果通道 CC1 配置为输入:CCR1 为上一个输入捕获 1 事件 (IC1) 发生时的计数器值。
44代码实现与分析
上面介绍了定时器的基础知识与PWM的输出原理,下面就来实际看一下,如何编写对应的代码(以STM32F407为例)。
4.1 定时器初始化
定时器的初始化,因为需要用到对应的引脚输出PWM,因此要先初始化GPIO引脚,然后,还要初始化定时器的时基(计数的时钟)以及输出通道(用于配置PWM的输出模式)。
4.1.1 复用引脚初始化
这里用到的是定时器3,根据STM32F407的数据手册“3 Pinouts and pin description”中的“Table 9. Alternate function mapping”复用引脚说明表,可以看到定时器3通道1对应的引脚位A6:
因此程序中对A6引脚可以这样配置,注意一定要配置引脚的复用功能:
GPIO_InitTypeDef GPIO_InitStructure; /*引脚配置 结构体*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能PORTA时钟GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_TIM3); /*GPIOA6复用为定时器3*/
/*复用引脚配置*/GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //GPIOA6GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; /*复用功能*/GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA6
4.1.2 时基初始化
时基初始化,主要是配置定时器的计数频率(psc)和自动重装置值(每次计数的周期,arr),比如TIM3_PWM_Init(500-1,84-1);
(关于psc与arr的知识点,可以再回顾一下上面1.3节的知识)
这里将arr的值设置为500,即计数器每计够500个数就会重新从0开始计数,这个500再乘以计数器计数的周期,就是PWM真正的周期,那计数器计数的频率是多少呢(频率的倒数为周期)?
这里将psc的值设置为84-1,即TIM3的输入频率为84MHz再将频率降低1/84,即使用1MHz的频率计数(1s能计1,000,000个数,也即1us计1个数),那么PWM的真正周期就是500*1us=500us(0.5ms),通过改变占空比的值(ccr),就可以调节PWM的输出占空比。
时基初始化配置如下:
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; /*时基 结构体*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //TIM3时钟使能
/*时基初始化*/TIM_TimeBaseStructure.TIM_Period=arr; /*ARR 自动重装载值(周期),例如500*/TIM_TimeBaseStructure.TIM_Prescaler=psc; /*PSC 定时器分频,例如84*/TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; /*时钟分割*/TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; /*向上计数模式*/TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure); /*初始化定时器3*/
最后一句的时基初始化,起始就是对定时的寄存器进行配置,该函数的内部实现如下:
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct){ uint16_t tmpcr1 = 0; tmpcr1 = TIMx->CR1;
if((TIMx == TIM1) || (TIMx == TIM8)|| /*gaoji定时器TIM和TIM8*/ (TIMx == TIM2) || (TIMx == TIM3)||(TIMx == TIM4) || (TIMx == TIM5)) /*通用定时器中的TIM2~TIM5*/ { /* 设置为计数器模式 */ tmpcr1 &= (uint16_t)(~(TIM_CR1_DIR | TIM_CR1_CMS)); tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode; } if((TIMx != TIM6) && (TIMx != TIM7)) /*基本定时器TIM6和TIM7无此功能*/ { /* 设置时钟分频 */ tmpcr1 &= (uint16_t)(~TIM_CR1_CKD); tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision; }
/* 配置CR1寄存器 */ TIMx->CR1 = tmpcr1;
/* 配置ARR寄存器,设置自动重转载值 */ TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ; /* 配置PSC寄存器,设置预分频值 */ TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler; if ((TIMx == TIM1) || (TIMx == TIM8)) /*gaoji定时器TIM和TIM8*/ { /* 配置RCR寄存器,设置重复计数值 */ TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter; }
/* 生成一个更新事件来立即重新加载预分频器和重复计数器(仅针对gaoji定时器TIM1和TIM8)值 */ TIMx->EGR = TIM_PSCReloadMode_Immediate; }