外设通用定时器PWM输出实验

输出比较部分框图

⑤ 输入捕获和输出比较公用部分

该部分需要结合第④部分或者第⑥部分共同完成相应功能。

⑥ 输出比较

图21.1.1.1中的第⑥部分是输出比较,一般应用是要和第⑤部分一起完成定时器输出功能。 TIMx_CH1~ TIMx_CH4 表示定时器的 4 个通道,这 4 个通道都是可以独立工作的。IO 端口通 过复用功能与这些通道相连。

图 21.1.9 中,灰色阴影部分是输入捕获功能部分。这里我们看到右边没有 阴影部分就是输出比较功能部分了。下面以通道 1 输出比较功能为例给大家介绍定时器如何实 现输出功能的。

首先程序员写 CCR1 寄存器,即写入比较值。这个比较值需要转移到对应的捕获/比较影子 寄存器后才会真正生效。什么条件下才能转移?图 21.1.9 中可以看到 compare_transfer 旁边的 与门,需要满足三个条件:CCR1 不在写入操作期间、CC1S[1:0] = 0 配置为输出、OC1PE 位置 0(或者 OC1PE 位置 1,并且需要发生更新事件,这个更新事件可以软件产生或者硬件产生)。

当 CCR1 寄存器的值转移到其影子寄存器后,新的值就会和计数器的值进行比较,它们的 比较结果将会通过第⑥部分影响定时器的输出。

上图中,可以看到输出模式控制器,由 OC1M[2:0]位配置输出比较模式,该位的描述请参 考《STM32F10xxx 参考手册_V10(中文版).pdf》相关定时器章节的 TIMx_CCMR1 寄存器。 F1 系列有 8 种输出比较模式之多,后面用到再来介绍。

oc1ref 是输出参考信号,高电平有效,为高电平时称之为有效电平,为低电平时称之为无 效电平。它的高低电平受到三个方面的影响:OC1M[3:0]位配置的输出比较模式、第⑤部分比较 器的比较结果、还有就是 OC1CE 位配置的 ETRF 信号。ETRF 信号可以将 Oc1ref 电平强制清 零,该信号来自 IO 外部。

一般来说,当计数器的值和捕获/比较寄存器的值相等时,输出参考信号 oc1ref 的极性就会 根据我们选择的输出比较模式而改变。如果开启了比较中断,还会发生比较中断。

CC1P 位用于选择通道输出极性。

CC1E 位置 1 使能通道输出。

OC1 信号就会从 TIMx_CH1 输出到 IO 端口,再到 IO 外部。

通用定时器的 PWM 输出模式

脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微 处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。我们可以让定时器产生PWM, 在计数器频率固定时,PWM 频率或者周期由自动重载寄存器(TIMx_ARR)的值决定,其占空 比由捕获/比较寄存器(TIMx_CCRx)的值决定。PWM 产生原理示意图如下图所示:

上图中,定时器工作在递增计数模式,纵轴是计数器的计数值 CNT,横轴表示时。当 CNT=CCRx 时,IO 输出高电平(逻辑 1);当 CNT=ARR 时,定时器溢出,CNT 的值被清零,然后继续递增,依次循环。在这个循环中,改 变 CCRx 的值,就可以改变 PWM 的占空比,改变 ARR 的值,就可以改变 PWM 的频率,这就 是 PWM 输出的原理。

定时器产生 PWM 的方式有许多种,下面我们以边沿对齐模式(即递增计数模式/递减计数 模式)为例,PWM 模式 1 或者 PWM 模式 2 产生 PWM 的示意图,如下图所示:

STM32F103 的定时器除了 TIM6 和 TIM7,其他的定时器都可以用来产生 PWM 输出。其 中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产 生多达 4 路的 PWM 输出!本实验我们以使用 TIM3 的 CH2 产生一路 PWM 输出为例进行学习。

配置步骤

 

相关寄存器

⚫ 捕获/比较模式寄存器 1/2(TIMx_CCMR1/2)

⚫ 捕获/比较使能寄存器(TIMx_CCER)

⚫ 捕获/比较寄存器 1/2/3/4(TIMx_CCR1/2/3/4)

捕获/比较寄存器(TIMx_CCR1/2/3/4),该寄存器总共有 4 个,对应 4 个通道 CH1~CH4。 我们使用的是通道 2,所以来看看 TIMx_CCR2 寄存器,描述如图 21.3.1.3 所示:

相关HAl库函数介绍

1. HAL_TIM_PWM_Init 函数

定时器的 PWM 输出模式初始化函数,其声明如下:

HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim);

⚫ 函数描述: 用于初始化定时器的 PWM 输出模式。

⚫ 函数形参: 形参 1 是 TIM_HandleTypeDef 结构体类型指针变量,基本定时器的时候已经介绍。

⚫ 函数返回值: HAL_StatusTypeDef 枚举类型的值。

⚫ 注意事项:该函数实现的功能以及使用方法和 HAL_TIM_Base_Init 类似,作用都是初始化定时器的 ARR 和 PSC 等参数。为什么 HAL 库要提供这个函数而不直接让我们使用 HAL_TIM_Base_Init 函数呢?这是因为 HAL 库为定时器的针对 PWM 输出定义了单独的 MSP 回调函数 HAL_TIM_PWM_MspInit,所以当我们调用 HAL_TIM_PWM_Init 进行 PWM 初始化之后,该函数内部会调用 MSP 回调函数 HAL_TIM_PWM_MspInit。当我们使用 HAL_TIM_Base_Init 初始 化定时器参数的时候,它内部调用的回调函数是 HAL_TIM_Base_MspInit,这里大家注意区分。

2. HAL_TIM_PWM_ConfigChannel 函数

定时器的 PWM 通道设置初始化函数。其声明如下:

HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim,
TIM_OC_InitTypeDef *sConfig, uint32_t Channel);

⚫ 函数描述: 该函数用于设置定时器的 PWM 通道。

⚫ 函数形参: 形参 1 是 TIM_HandleTypeDef 结构体类型指针变量,用于配置定时器基本参数。 形参 2 是 TIM_OC_InitTypeDef 结构体类型指针变量,用于配置定时器的输出比较参数。 重点了解一下 TIM_OC_InitTypeDef 结构体指针类型,其定义如下:

typedef struct
{
     uint32_t OCMode; /* 输出比较模式选择,寄存器的时候说过了,共 8 种模式 */
     uint32_t Pulse; /* 设置比较值 */
     uint32_t OCPolarity; /* 设置输出比较极性 */
     uint32_t OCNPolarity; /* 设置互补输出比较极性 */
     uint32_t OCFastMode; /* 使能或失能输出比较快速模式 */
     uint32_t OCIdleState; /* 选择空闲状态下的非工作状态(OC1 输出) */
     uint32_t OCNIdleState; /* 设置空闲状态下的非工作状态(OC1N 输出) */
} TIM_OC_InitTypeDef; 

我们重点关注前三个结构体成员。成员变量 OCMode 用来设置模式,这里我们设置为 PWM 模式 1。成员变量 Pulse 用来设置捕获比较值。成员变量 TIM_OCPolarity 用来设置输出极性。 其他成员 TIM_OutputNState,TIM_OCNPolarity,TIM_OCIdleState 和 TIM_OCNIdleState 后面 用到再介绍。

形参 3 是定时器通道,范围:TIM_CHANNEL_1 到 TIM_CHANNEL_4。这里我们使用的 是定时器 3 的通道 2,所以取值为 TIM_CHANNEL_2 即可。

⚫ 函数返回值: HAL_StatusTypeDef 枚举类型的值。

3. HAL_TIM_PWM_Start 函数

定时器的 PWM 输出启动函数,其声明如下:

HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);

⚫ 函数描述: 用于使能通道输出和启动计数器,即启动 PWM 输出。

⚫ 函数形参: 形参 1 是 TIM_HandleTypeDef 结构体类型指针变量。 形参 2 是定时器通道,范围:TIM_CHANNEL_1 到 TIM_CHANNEL_4。

⚫ 函数返回值: HAL_StatusTypeDef 枚举类型的值。

⚫ 注意事项: 对于单独使能定时器的方法,在上一章定时器实验我们已经讲解。实际上,HAL 库也同样 提供了单独使能定时器的输出通道函数,函数为:

void TIM_CCxChannelCmd(TIM_TypeDef *TIMx, uint32_t Channel,
uint32_t ChannelState);

HAL_TIM_PWM_Start 函数内部也调用了该函数。

4. HAL_TIM_ConfigClockSource 函数 

配置定时器时钟源函数,其声明如下:

HAL_StatusTypeDef HAL_TIM_ConfigClockSource(TIM_HandleTypeDef *htim, 
TIM_ClockConfigTypeDef *sClockSourceConfig);

⚫ 函数描述: 用于配置定时器时钟源。

⚫ 函数形参:

形参 1 是 TIM_HandleTypeDef 结构体类型指针变量。

形参 2 是 TIM_ClockConfigTypeDef 结构体类型指针变量,用于配置定时器时钟源参数。 TIM_ClockConfigTypeDef 定义如下:

typedef struct
{
 uint32_t ClockSource; /* 时钟源 */
 uint32_t ClockPolarity; /* 时钟极性 */
 uint32_t ClockPrescaler; /* 定时器预分频器 */
 uint32_t ClockFilter; /* 时钟过滤器 */
} TIM_ClockConfigTypeDef;

⚫ 函数返回值: HAL_StatusTypeDef 枚举类型的值。

⚫ 注意事项: 该函数主要配置 TIMx_SMCR 寄存器。默认情况下,定时器的时钟源是内部时钟。本实验 就是使用内部时钟的,所以我们不用对时钟源就行初始化,默认即可。这里只是让大家知道有 这个函数可以设定时器的时钟源。比如用 HAL_TIM_ConfigClockSource 初始化选择内部时钟, 方法如下:

TIM_HandleTypeDef timx_handle; /* 定时器 x 句柄 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; /* 选择内部时钟 */
HAL_TIM_ConfigClockSource(&timx_handle, &sClockSourceConfig);

后面的定时器初始化凡是用到内部时钟我们都没有去初始化,系统默认即可。

代码

gtim.c

#include "./BSP/GTIM/gtim.h"

TIM_HandleTypeDef g_timx_pwm_chy_handle;

/* 通用定时器PWM输出初始化函数 */
void gtim_timx_pwm_chy_init(uint16_t psc, uint16_t arr)
{
    TIM_OC_InitTypeDef timx_oc_pwm_chy;
    
    g_timx_pwm_chy_handle.Instance = TIM3;
    g_timx_pwm_chy_handle.Init.Prescaler = psc;
    g_timx_pwm_chy_handle.Init.Period = arr;
    g_timx_pwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    HAL_TIM_PWM_Init(&g_timx_pwm_chy_handle);
    
    timx_oc_pwm_chy.OCMode = TIM_OCMODE_PWM1;
    timx_oc_pwm_chy.Pulse = arr / 2;
    timx_oc_pwm_chy.OCPolarity = TIM_OCPOLARITY_LOW;
    HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle, &timx_oc_pwm_chy, TIM_CHANNEL_2);
    
    HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, TIM_CHANNEL_2);
}

/* 定时器输出PWN MSP初始化函数 */

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM3)
    {
        __HAL_RCC_GPIOB_CLK_ENABLE();
        __HAL_RCC_TIM3_CLK_ENABLE();
        __HAL_RCC_AFIO_CLK_ENABLE();
        
        GPIO_InitTypeDef gpio_init_struct;
        gpio_init_struct.Pin = GPIO_PIN_5;
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOB, &gpio_init_struct);
        
        __HAL_AFIO_REMAP_TIM3_PARTIAL();
    }
}

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/GTIM/gtim.h"

extern TIM_HandleTypeDef g_timx_pwm_chy_handle; /* 定时器x句柄 */

int main(void)
{
    uint16_t ledrpwmval = 0;    /* CCR比较值 */
    uint8_t dir = 1;            /* CCR比较值的改变方向 */
    
    HAL_Init();                         /* 初始化 HAL 库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    
    led_init();
    gtim_timx_pwm_chy_init(72 -1, 500 -1);  /* 72*500/72M=0.0005s,2KHz */
    
    while(1)
    {
        delay_ms(10);
        
        if(dir)
        {
            ledrpwmval++;       /* dir==1 ledrpwmval递增 */
        }
        else
        {
            ledrpwmval--;       /* dir==0 ledrpwmval递减 */
        }
        
        if(ledrpwmval > 500 - 1)
        {
            dir = 0;            /* ledrpwmval到达500后,方向为递减 */
        }
        if(ledrpwmval == 0)
        {
            dir = 1;            /* ledrpwmval到达0后,方向改为递增 */
        }
        
        /* 修改比较值控制占空比 */
        __HAL_TIM_SET_COMPARE(&g_timx_pwm_chy_handle, TIM_CHANNEL_2, ledrpwmval);
    }
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值