STM32HAL库高级定时器实验——高级定时器输出指定个数PWM

实验原理

要实现定时器输出指定个数 PWM,只需要掌握下面几点内容:
第一,如果大家还不清楚定时器是如何输出 PWM 的,请回顾通用定时器 PWM 输出实验
的内容,这部分的知识是一样的。但是需要注意的是:我们需要把 MOE 位置 1,这样高级定时
器的通道才能输出。
第二,要清楚重复计数器特性,设置重复计数器寄存器 RCR 的值为 N,那么更新事件将在 定时器发生 N+1次上溢或下溢时发生。换句话来说就是,想要指定输出 N 个 PWM,只需要把 N-1 写入 RCR寄存器。因为在边沿对齐模式下,定时器溢出周期对应着 PWM 周期,我们只要 在更新事件发生时,停止输出 PWM 就行。
第三,为了保证定时器输出指定个数的 PWM 后,定时器马上停止继续输出,我们使能更 新中断,并在定时器中断里关闭计数器。
即:
1,配置边沿对齐模式输出PWM
2,指定输出N个PWM,则把N-1写入RCR
3,在更新中断内,关闭计数器

涉及的寄存器

控制寄存器 1(TIMx_CR1)

在这里插入图片描述
位 7(APRE)用于控制自动重载寄存器是否具有缓冲作用,在基本定时器的时候已经讲过,请回顾。 在本实验中我们把该位要置 1,这样就算改变 ARR 寄存器的值,该值也不会马上生效,而是等待之前设置的 PWM 完整输出后
(发生更新事件)才生效。
位 4(DIR)用于配置计数器的计数方向,这里我们默认置 0。
位 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 寄存器描述如图 :
在这里插入图片描述
该寄存器的有些位在不同模式下,功能不一样,我们前面已经说过。比如我们要让 TIM1 的CH1 输出 PWM 波为例,该寄存器的模式设置位 OC1M[2:0]就是对应着通道 1 的模式设置,此部分由 3 位组成,总共可以配置成 8 种模式,我们使用的是 PWM 模式,所以这 3 位必须设置为110 或者 111,分别对应 PWM 模式 1 和 PWM 模式 2。

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

在这里插入图片描述
该寄存器比较简单,要让 TIM1 的 CH1 输出 PWM 波,这里我们要使能 CC1E 位,该位是通道 1 输入/输出使能位,要想 PWM 从 IO 口输出,这个位必须设置为 1。 CC1P 位是设置通道1 的输出极性,我们设置 0,即 OC1 高电平有效。

事件产生寄存器(TIMx_ EGR)

TIM1/TIM8 的事件产生寄存器, 该寄存器作用是让用户用软件方式产生各类事件。
在这里插入图片描述
UG 位是更新事件的控制位,作用和定时器溢出时产生的更新事件一样,区别是这里是通过软件产生的,而定时器溢出是硬件自己完成的。只有开启了更新中断,这两种方式都可以产更新中断。本实验用到该位去产生软件更新器事件,在需要的时候把 UG 位置 1 即可,会由硬件自动清零。

重复计数器寄存器(TIMx_ RCR)

重复计数器寄存器用于设置重复计数器值,因为它具有影子寄存器,所以它本身只是起缓冲作用。
在这里插入图片描述
该寄存器的 REP[7:0]位是低 8 位有效,即最大值 255。因为这个寄存器只是起缓冲作用,如果大家对该寄存器写入值后,想要立即生效,可以通过对 UG 位写 1,产生软件更新事件。

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

在这里插入图片描述

在输出模式下,捕获/比较寄存器影子寄存器的值与 CNT 的值比较,根据比较结果产生相应动作,利用这点,我们通过修改这个寄存器的值,就可以控制 PWM 的占空比了。

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

高级定时器 TIM1/8 的通道用作输出时,还必须配置断路和死区寄存器(TIMx_BDTR) 的位 MOE,该寄存器各位描述如图:在这里插入图片描述
本实验, 我们只需要关注该寄存器的位 15(MOE),要想高级定时器的 PWM 正常输出,则必须设置 MOE 位为 1,否则不会有输出。

硬件设计

例程功能

通过 TIM8_CH1(由 PC6 复用) 输出 PWM, 然后为了指示 PWM 的输出情况,我们用杜邦线将 PC6 和 PE5引脚的排针连接起来, 从而实现PWM输出控制 LED1(硬件已连接在 PPE5 引脚上)的亮灭。 注意的点是: PE5 要设置成浮空输入,避免引脚冲突,我们在 main 函数中设 置好了,请看源码。 上电默认输出 5 个 PWM 波,连接好杜邦线后可以看见 LED1 亮灭五次。 之后按一下按键 KEY0,就会输出 指定个数 个 PWM 波控制 LED1 亮灭五次。 LED0 闪烁提示系统正在运行。

硬件资源

1) LED 灯:
LED0 – PB5
LED1 – PE5
2) 独立按键:
KEY0 – PE4
3)定时器 8,使用 TIM8 通道 1, 由 PC6 复用。 用杜邦线将 PC6 和 PE5 引脚连接起来。

程序设计

HAL库函数

本实验用到的hal库函数可以参考通用定时器的实验

配置步骤

在这里插入图片描述
相关hal库函数
加粗样式

代码实现

tim.c
/* 通用定时器6 PWM输出通道1句柄 */
TIM_HandleTypeDef g_tim8_pwm_ch1_handle;

static uint8_t pwn_remain = 0;

/* 高级定时器8通道1 PWM输出初始化函数 */
void TIM_8_PWM_CH1_init(uint16_t psc,uint16_t arr)
{
    TIM_OC_InitTypeDef tim1_oc_handle={0};

    g_tim8_pwm_ch1_handle.Instance = TIM8;                        /* 定时器8 */
    g_tim8_pwm_ch1_handle.Init.Prescaler = psc;                  /* 预分频器 */ 
    g_tim8_pwm_ch1_handle.Init.Period = arr;                     /* 自动重装载值 */
    g_tim8_pwm_ch1_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    g_tim8_pwm_ch1_handle.Init.RepetitionCounter = 0;
    HAL_TIM_PWM_Init(&g_tim8_pwm_ch1_handle);

    tim1_oc_handle.OCMode = TIM_OCMODE_PWM1;                    /* PWM模式1 */
    tim1_oc_handle.Pulse = arr/2;                               /* 占空比 */

    tim1_oc_handle.OCPolarity = TIM_OCPOLARITY_HIGH;            /* 极性高 */
    HAL_TIM_PWM_ConfigChannel(&g_tim8_pwm_ch1_handle, &tim1_oc_handle, TIM_CHANNEL_1);

    __HAL_TIM_ENABLE_IT(&g_tim8_pwm_ch1_handle, TIM_IT_UPDATE);   /* 使能更新中断 */
    HAL_TIM_PWM_Start(&g_tim8_pwm_ch1_handle, TIM_CHANNEL_1);            /* 启动定时器 */

}

/* 定时器输出PWM MSP初始化函数 */
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance==TIM8)
    {
        GPIO_InitTypeDef gpio_init_struct;
        __HAL_RCC_GPIOC_CLK_ENABLE();                               /* 时钟使能 */
        __HAL_RCC_TIM8_CLK_ENABLE();                                 /* 时钟使能 */

        gpio_init_struct.Pin = GPIO_PIN_6;                   /* 引脚 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;            /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速 */
        HAL_GPIO_Init(GPIOC, &gpio_init_struct);       /* */   
        
        HAL_NVIC_SetPriority(TIM8_UP_IRQn,1,3);                 /*  */
        HAL_NVIC_EnableIRQ(TIM8_UP_IRQn);                      /*  */

    
    }
}

/* 高级定时器TIM8设置PWM个数 */
void TIM8_PWMS_ch1_set(uint8_t npwm)
{
    if(npwm == 0)
    {
        return;
    }
    pwn_remain=npwm;
    HAL_TIM_GenerateEvent(&g_tim8_pwm_ch1_handle, TIM_EVENTSOURCE_UPDATE); 
    __HAL_TIM_ENABLE(&g_tim8_pwm_ch1_handle);
    
}

/* TIM8中断服务函数 */
void TIM8_UP_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_tim8_pwm_ch1_handle);
}

/* 定时器更新中断回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM8)
    {
        if(pwn_remain)
        {
            TIM8->RCR=pwn_remain-1;
            HAL_TIM_GenerateEvent(&g_tim8_pwm_ch1_handle, TIM_EVENTSOURCE_UPDATE); 
            __HAL_TIM_ENABLE(&g_tim8_pwm_ch1_handle);
            pwn_remain=0;
        }
        else
        {
            TIM8->CR1 &= ~(1<<0);
        }

    }
}
led相关代码

led.c

void led_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    LED0_GPIO_CLK_ENABLE();                                 /* LED0时钟使能 */
    LED1_GPIO_CLK_ENABLE();                                 /* LED1时钟使能 */

    gpio_init_struct.Pin = LED0_GPIO_PIN;                   /* LED0引脚 */
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速 */
    HAL_GPIO_Init(LED0_GPIO_PORT, &gpio_init_struct);       /* 初始化LED0引脚 */

    gpio_init_struct.Pin = LED1_GPIO_PIN;                   /* LED1引脚 */
    HAL_GPIO_Init(LED1_GPIO_PORT, &gpio_init_struct);       /* 初始化LED1引脚 */
    

    LED0(1);                                                /* 关闭 LED0 */
    LED1(1);                                                /* 关闭 LED1 */
}

led.h

/* 引脚 定义 */

#define LED0_GPIO_PORT                  GPIOB
#define LED0_GPIO_PIN                   GPIO_PIN_5
#define LED0_GPIO_CLK_ENABLE()          do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)             /* PB口时钟使能 */

#define LED1_GPIO_PORT                  GPIOE
#define LED1_GPIO_PIN                   GPIO_PIN_5
#define LED1_GPIO_CLK_ENABLE()          do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)             /* PE口时钟使能 */

/******************************************************************************************/
/* LED端口定义 */
#define LED0(x)   do{ x ? \
                      HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_SET) : \
                      HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_RESET); \
                  }while(0)      /* LED0翻转 */

#define LED1(x)   do{ x ? \
                      HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_SET) : \
                      HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_RESET); \
                  }while(0)      /* LED1翻转 */

/* LED取反定义 */
#define LED0_TOGGLE()   do{ HAL_GPIO_TogglePin(LED0_GPIO_PORT, LED0_GPIO_PIN); }while(0)        /* 翻转LED0 */
#define LED1_TOGGLE()   do{ HAL_GPIO_TogglePin(LED1_GPIO_PORT, LED1_GPIO_PIN); }while(0)        /* 翻转LED1 */

/******************************************************************************************/
/* 外部接口函数*/
void led_init(void);                                                                            /* 初始化 */

main.c
int main(void)
{
    uint8_t t=0;
    uint8_t key;
    GPIO_InitTypeDef gpio_init_struct;
    
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟,72M */
    delay_init(72);                             /* 初始化延时函数 */
    led_init();                                 /* 初始化LED */
    key_init();                                 /* 初始化按键 */
    
    //避免冲突
    __HAL_RCC_GPIOE_CLK_ENABLE();                /* 使能GPIOE时钟 */
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    gpio_init_struct.Pin = GPIO_PIN_5;                   /* LED1引脚 */
    gpio_init_struct.Mode = GPIO_MODE_INPUT;            /* 输入 */
    gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速 */
    HAL_GPIO_Init(GPIOE, &gpio_init_struct);       /* 初始化LED1引脚 */

    
    TIM_8_PWM_CH1_init(7200-1,5000-1);
    TIM8_PWMS_ch1_set(5);
    while(1)
    {
        key = key_scan(0);
        if(key==KEY0_PRES)
        {
            TIM8_PWMS_ch1_set(8);
        }
        t++;
        delay_ms(10);

        if(t>50)
        {
            t=0;
            LED0_TOGGLE();

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值