STM32HAL库高级定时器实验——高级定时器输出比较实验

前言

本实验使用高级定时器输出比较模式下的翻转功能,通过定时器 4 个通道分别输出 4 个 50%占空比、不同相位的 PWM。

实验原理

输出比较模式下翻转功能作用是:当计数器的值等于捕获/比较寄存器影子寄存器的值时,OC1REF 发生翻转,进而控制通道输出(OCx)翻转。通过翻转功能实现输出 PWM 的具体原理如下: PWM 频率由自动重载寄存器(TIMx_ARR)的值决定,在这个过程中,只要自动重载寄存器的值不变,那么 PWM 占空比就固定为 50%。我们可以通过捕获/比较寄存器(TIMx_CCRx)的值改变 PWM 的相位。生成 PWM 的原理如图:
在这里插入图片描述
总结:PWM波周期或频率由ARR决定,占空比固定50%,相位由CCRx决定
我们设置固定的 ARR 值为 999,那么 PWM 占空比固定为 50%,通过改变 4 个通道的捕获/比较寄存器(TIMx_CCRx)的值使得每个通道输出的 PWM 的相位都不一样,注意捕获/比较寄存器的值设置范围是: 0 ~ ARR。比如: TIMx_CCR1=250-1, TIMx_CCR2=500-1, TIMx_CCR3=750-1, TIMx_CCR4=1000-1,那么可以得到通道 1~通道 4 输出的 PWM 的相位分别是: 25%、 50%、 75%、 100%。翻转功能输出的 PWM 周期,这里用 T 表示,其计算公式如下:
T= 2*(arr+1)*((psc+1)/ Tclk)
其中:
T: 翻转功能输出的 PWM 周期(单位为 s)。
Tclk: 定时器的时钟源频率(单位为 MHz)。
arr:自动重装寄存器(TIMx_ARR)的值。
psc:预分频器寄存器(TIMx_PSC)的值。

涉及到的寄存器

高级定时器输出比较模式除了用到定时器的时基单元:计数器寄存器(TIMx_CNT)、预分频器寄存器(TIMx_PSC)、 自动重载寄存器(TIMx_ARR)之外。主要还用到以下这些寄存器:

控制寄存器 1(TIMx_CR1)

在这里插入图片描述

上图中我们只列出了本实验需要用的一些位,其中: 位 7(APRE)用于控制自动重载寄存器是否具有缓冲作用,在基本定时器的时候已经讲过,请回顾。 本实验中,我们把该位置 1。
位 4(DIR)用于配置计数器的计数方向, 本实验默认置 0 即可。
位 CEN 位,用于使能计数器的工作,必须要设置该位为 1,才可以开始计数。

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

TIM1/TIM8 的捕获/比较模式寄存器( TIMx_CCMR1/2),该寄存器一般有 2 个:TIMx_CCMR1 和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 CH2,而 TIMx_CCMR2 控制 CH3和 CH4。 TIMx_CCMR1 寄存器描述如图:在这里插入图片描述

该寄存器的有些位在不同模式下,功能不一样,我们现在用到输出比较模式。关于该寄存器的详细说明,请参考《STM32F10xxx 参考手册_V10(中文版) .pdf》 第 240 页, 13.4.7 节。
本实验我们用到了定时器 8 输出比较的 4 个通道,所以我们需要配置 TIM1_CCMR1 和TIM1_CCMR2。以 TIM1_CCMR1 寄存器为例,模式设置位 OC1M[2:0]就是对应着通道 1 的模式设置,此部分由 3 位组成,总共可以配置成 8 种模式,我们使用的是翻转功能,所以这 3 位必须设置为 011。通道 2 也是如此,将位 OC2M[2:0] 设置为 011。通道 3 和通道 4 就要设置TIM1_CCMR2 寄存器的位 OC3M[2:0]和位 OC4M[2:0]。除此之外,我们还要设置输出比较的预装载使能位,如通道 1 对应输出比较的预装载使能位 OC1PE 置 1, 其他通道也要把相应位置 1。

捕获/比较使能寄存器(TIMx_ CCER)

在这里插入图片描述
该寄存器比较简单,要让 TIM8 的 4 个通道都输出,我们需要把对应的捕获/比较 1 输出使
能位置 1。通道 1 到通道 4 的使能位分别是: CC1E、 CC2E、 CC3E、 CC4E,我们把这 4 个位置
1,使能通道输出。

捕获/比较寄存器 1/2/3/4(TIMx_ CCR1/2/3/4)

捕获/比较寄存器(TIMx_ CCR1/2/3/4),该寄存器总共有 4 个,对应 4 个通道 CH1~CH4。
在这里插入图片描述
这里,我们通过改变 TIMx_ CCR1/2/3/4 寄存器的值来改变 4 个通道输出的 PWM 的相位。

TIM1/TIM8 断路和死区寄存器(TIMx_ BDTR)

本实验用的是高级定时器,我们还需要配置:断路和死区寄存器(TIMx_BDTR),该寄存器各位描述如图:
在这里插入图片描述

该寄存器,我们只需要关注位 15(MOE),要想高级定时器的通道正常输出,则必须设置MOE 位为 1,否则不会有输出。

硬件设计

例程功能

通过定时器8通道1/2/3/4输出相位分别为25%、50%、75%、100%的PWM

硬件资源

本实验基于正点原子战舰V4开发板

  • 1) LED 灯
    LED0 – PB5
  • 2) PC6 复用为 TIM8_CH1
    PC7 复用为 TIM8_CH2
    PC8 复用为 TIM8_CH3
    PC9 复用为 TIM8_CH4

程序设计

实验涉及的HAL库函数

定时器在 HAL 库中的驱动代码在前面已经介绍了部分,请回顾,这里我们再介绍几个本实验用到的函数。

1. HAL_TIM_OC_Init 函数

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

HAL_StatusTypeDef HAL_TIM_OC_Init(TIM_HandleTypeDef *htim);

函数描述:
用于初始化定时器的输出比较模式。
函数形参:
形参 1 是 TIM_HandleTypeDef 结构体类型指针变量,基本定时器的时候已经介绍。
函数返回值:
HAL_StatusTypeDef 枚举类型的值。

2. HAL_TIM_OC_ConfigChannel 函数

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

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

函数描述:
该函数用于初始化定时器的输出比较通道。
函数形参:
形参 1 是 TIM_HandleTypeDef 结构体类型指针变量,用于配置定时器基本参数。
形参 2 是 TIM_OC_InitTypeDef 结构体类型指针变量,用于配置定时器的输出比较参数。在通用定时器 PWM 输出实验已经介绍过 TIM_OC_InitTypeDef 结构体指针类型。
形参 3 是定时器通道,范围: TIM_CHANNEL_1 到 TIM_CHANNEL_4。
函数返回值:
HAL_StatusTypeDef 枚举类型的值。

3. HAL_TIM_OC_Start 函数

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

HAL_StatusTypeDef HAL_TIM_OC_Start(TIM_HandleTypeDef *htim, uint32_t Channel);

函数描述:
用于启动定时器的输出比较模式。
函数形参:
形参 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_OC_Start 函数内部也调用了该函数。

配置步骤

在这里插入图片描述
在这里插入图片描述

代码

tim.c
/* 高级定时器8输出比较模式初始化函数 */
void tim8_comp_pwm_init(uint16_t arr, uint16_t psc)
{
    TIM_OC_InitTypeDef tim8_oc_comp_pwm={0};
    tim8_comp_pwm_handle.Instance = TIM8;
    tim8_comp_pwm_handle.Init.Prescaler = psc;
    tim8_comp_pwm_handle.Init.Period = arr;
    tim8_comp_pwm_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    HAL_TIM_OC_Init(&tim8_comp_pwm_handle);

    tim8_oc_comp_pwm.OCMode = TIM_OCMODE_TOGGLE;
    tim8_oc_comp_pwm.OCPolarity = TIM_OCPOLARITY_HIGH;

    HAL_TIM_OC_ConfigChannel(&tim8_comp_pwm_handle, &tim8_oc_comp_pwm, TIM_CHANNEL_1);
    HAL_TIM_OC_ConfigChannel(&tim8_comp_pwm_handle, &tim8_oc_comp_pwm, TIM_CHANNEL_2);
    HAL_TIM_OC_ConfigChannel(&tim8_comp_pwm_handle, &tim8_oc_comp_pwm, TIM_CHANNEL_3);
    HAL_TIM_OC_ConfigChannel(&tim8_comp_pwm_handle, &tim8_oc_comp_pwm, TIM_CHANNEL_4);
    
    __HAL_TIM_ENABLE_OCxPRELOAD(&tim8_comp_pwm_handle, TIM_CHANNEL_1);
    __HAL_TIM_ENABLE_OCxPRELOAD(&tim8_comp_pwm_handle, TIM_CHANNEL_2);
    __HAL_TIM_ENABLE_OCxPRELOAD(&tim8_comp_pwm_handle, TIM_CHANNEL_3);
    __HAL_TIM_ENABLE_OCxPRELOAD(&tim8_comp_pwm_handle, TIM_CHANNEL_4);
    
    HAL_TIM_OC_Start(&tim8_comp_pwm_handle, TIM_CHANNEL_1);
    HAL_TIM_OC_Start(&tim8_comp_pwm_handle, TIM_CHANNEL_2);
    HAL_TIM_OC_Start(&tim8_comp_pwm_handle, TIM_CHANNEL_3);
    HAL_TIM_OC_Start(&tim8_comp_pwm_handle, TIM_CHANNEL_4);
}

void HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM8)
    {
        GPIO_InitTypeDef GPIO_InitStruct;

        __HAL_RCC_TIM8_CLK_ENABLE();
        __HAL_RCC_GPIOC_CLK_ENABLE();

        GPIO_InitStruct.Pin = GPIO_PIN_6;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOC,&GPIO_InitStruct);
        
        GPIO_InitStruct.Pin = GPIO_PIN_7;
        HAL_GPIO_Init(GPIOC,&GPIO_InitStruct);

        GPIO_InitStruct.Pin = GPIO_PIN_8;
        HAL_GPIO_Init(GPIOC,&GPIO_InitStruct);

        GPIO_InitStruct.Pin = GPIO_PIN_9;
        HAL_GPIO_Init(GPIOC,&GPIO_InitStruct);
    }
}
main.c
extern TIM_HandleTypeDef tim8_comp_pwm_handle;

int main(void)
{
    uint8_t t = 0;
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟,72M */
    delay_init(72);                             /* 初始化延时函数 */
    led_init();                                 /* 初始化LED */
    tim8_comp_pwm_init(1000-1,72-1);

    __HAL_TIM_SET_COMPARE(&tim8_comp_pwm_handle, TIM_CHANNEL_1, 250-1);
    __HAL_TIM_SET_COMPARE(&tim8_comp_pwm_handle, TIM_CHANNEL_2, 500-1);
    __HAL_TIM_SET_COMPARE(&tim8_comp_pwm_handle, TIM_CHANNEL_3, 750-1);
    __HAL_TIM_SET_COMPARE(&tim8_comp_pwm_handle, TIM_CHANNEL_4, 1000-1);

    while(1)
    {
        delay_ms(10);
        t++;
        if(t>50)
        {
            t=0;
            LED0_TOGGLE();
        }

    }
}
STM32 HAL库中配置定时器输出PWM的基本步骤如下: 首先,你需要包括必要的头文件并初始化HAL库: ```c #include "stm32f1xx_hal.h" #include "tim.h" void SystemClock_Config(void); void TIMx_PWM_Init(TIM_TypeDef *TIMx); ``` 然后,在`SystemClock_Config()`函数中设置系统时钟,并确保定时器时钟已正确配置。 接下来,定义一个函数`TIMx_PWM_Init(TIM_TypeDef *TIMx)`,这里`TIMx`通常是你想要配置的定时器,如TIM2、TIM3等,它会配置基本的PWM模式: ```c static void TIMx_PWM_Init(TIM_TypeDef *TIMx) { TIM_HandleTypeDef htim; // 初始化结构体 htim.Instance = TIMx; htim.Init.Prescaler = ...; // 定时器预分频值,取决于系统时钟和期望的PWM周期 htim.Init.CounterMode = TIM_COUNTERMODE_UP; htim.Init.Period = ...; // PWM周期,即最高频率的倒数 // 初始化中断 htim.Init.OwnInterrupt = ENABLE; htim.Init.EncoderInterface = TIM_ENCODER_NOINTERFERENCE; htim.Init.Interrupt = TIM_IT_Update; if (HAL_TIM_Base_Init(&htim) != HAL_OK) Error_Handler(); // 处理错误 // 启动定时器 if (HAL_TIM_Base_Start(&htim) != HAL_OK) Error_Handler(); // 开启更新中断 HAL_NVIC_EnableIRQ(TIMx_IRQn); } ``` 最后,你需要在合适的地方调用这个初始化函数,并记得开启相应的定时器中断(例如,TIM2_IRQn)。 注意:具体的数值需要根据你的硬件配置和应用需求进行计算和调整。上述代码只是一个基础模板,实际使用时可能还需要根据你的项目具体情况进行修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值