STM32F429高级定时器输出PWM

一.STM32F429定时器介绍

STM32F429 一共有14个定时器,其中有 2 个高级定时器(TIM1 和 TIM8),具体如下表所示:

二.高级定时器输出指定个数PWM

1.输出PWM原理

    脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用

微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。我们可以让定时器产生

PWM,在计数器频率固定时,PWM 频率或者周期由自动重载寄存器(TIMx_ARR)的值决定,其占空比由捕获/比较寄存器(TIMx_CCRx)的值决定。PWM 产生原理示意图如下图所示:

  上图中,定时器工作在递增计数模式,纵轴是计数器的计数值 CNT,横轴表示时。当

CNT<CCRx 时,IO 输出低电平(逻辑 0);当 CNT>=CCRx 时,IO 输出高电平(逻辑 1);当CNT=ARR 时,定时器溢出,CNT 的值被清零,然后继续递增,依次循环。在这个循环中,改变 CCRx 的值,就可以改变 PWM 的占空比,改变 ARR 的值,就可以改变 PWM 的频率,这就是 PWM 输出的原理。

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

STM32F429 的定时器除了 TIM6 和 TIM7 外,其他的定时器都可以产生 PWM 输出。其中

高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4 路的 PWM 输出!

2.TIMER8输出指定个数PWM设置

要清楚重复计数器特性,设置重复计数器寄存器 RCR 的值为 N,那么更新事件将

在定时器发生 N+1 次上溢或下溢时发生。换句话来说就是,想要指定输出 N 个 PWM,只需要把 N-1写入 RCR寄存器。因为在边沿对齐模式下,定时器溢出周期对应着 PWM周期,我们只要在更新事件发生时,停止输出 PWM 就行。

为了保证定时器输出指定个数的 PWM 后,定时器马上停止继续输出,我们使能更

新中断,并在定时器中断里关闭计数器。

三.PWM输出例程

1.代码功能

通过 TIM8_CH1(由 PC6 复用)输出 PWM,为了指示 PWM 的输出情况,我们用杜邦线连接 PC6 和 PB0,来实现 PWM 输出控制 LED1 的亮灭。上电默认输出 5 个 PWM 控制 LED1亮灭五次。之后按一下按键 KEY0,就会输出 5 个 PWM 控制 LED1 亮灭五次

2.代码流程图

3.代码

(1)main.c

int main(void)

{

    uint8_t key = 0;

    uint8_t t = 0;

    GPIO_InitTypeDef gpio_init_struct;

    HAL_Init();                                     /* 初始化HAL库 */

    sys_stm32_clock_init(360, 25, 2, 8);            /* 设置时钟,180Mhz */

    delay_init(180);                                /* 延时初始化 */

    usart_init(115200);                             /* 初始化USART */

    led_init();                                     /* 初始化LED */

    key_init();                                     /* 初始化按键 */

    atim_timx_npwm_chy_init(10000 - 1, 9000 - 1);   /* 20Khz的计数频率,2Hz的PWM频率 */

    /* 将 LED1 引脚设置为输入模式, 避免和PC6冲突 */

    gpio_init_struct.Pin = LED1_GPIO_PIN;

    gpio_init_struct.Mode = GPIO_MODE_INPUT;        /* LED1引脚设置浮空输入模式 */

    gpio_init_struct.Pull = GPIO_PULLUP;

    gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

    HAL_GPIO_Init(LED1_GPIO_PORT, &gpio_init_struct);

    ATIM_TIMX_NPWM_CHY_CCRX = 5000;                 /* 设置PWM占空比为50%,这样可以控制每一个PWM周期,LED1(GREEN)

                                                       有一半时间是亮的,一半时间是灭的,LED1亮灭一次,表示一个PWM波

                                                     */

    atim_timx_npwm_chy_set(5);                      /* 输出5个PWM波(控制LED1(GREEN)闪烁5次) */

    while (1)

    {

        key = key_scan(0);

        if (key == KEY0_PRES)                      /* KEY0按下 */

        {

            atim_timx_npwm_chy_set(5);             /* 输出5个PWM波(控制TIM8_CH1, 即PC6输出5个脉冲) */

        }

        t++;

        delay_ms(10);

        if (t > 50)                                /* 控制LED0闪烁, 提示程序运行状态 */

        {

            t = 0;

            LED0_TOGGLE();

        }

    }

}

(2)timer.h

#ifndef __ATIM_H

#define __ATIM_H

#include "./SYSTEM/sys/sys.h"

/******************************************************************************************/

/* 高级定时器 定义 */

 /* TIMX 输出指定个数PWM 定义

 * 这里输出的PWM通过PC6(TIM8_CH1)输出, 我们用杜邦线连接PC6和PB0, 然后在程序里面将PB0设置成浮空输入

 * 就可以 看到TIM8_CH1控制LED1(GREEN)的亮灭, 亮灭一次表示一个PWM波

 */

#define ATIM_TIMX_NPWM_CHY_GPIO_PORT            GPIOC

#define ATIM_TIMX_NPWM_CHY_GPIO_PIN             GPIO_PIN_6

#define ATIM_TIMX_NPWM_CHY_GPIO_CLK_ENABLE()    do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)    /* PC口时钟使能 */

#define ATIM_TIMX_NPWM_CHY_GPIO_AF              GPIO_AF3_TIM8

#define ATIM_TIMX_NPWM                          TIM8

#define ATIM_TIMX_NPWM_IRQn                     TIM8_UP_TIM13_IRQn

#define ATIM_TIMX_NPWM_IRQHandler               TIM8_UP_TIM13_IRQHandler

#define ATIM_TIMX_NPWM_CHY                      TIM_CHANNEL_1                                  /* 通道Y,1<= Y <=4 */

#define ATIM_TIMX_NPWM_CHY_CCRX                 TIM8->CCR1                                     /* 通道Y的输出比较寄存器 */

#define ATIM_TIMX_NPWM_CHY_CLK_ENABLE()         do{ __HAL_RCC_TIM8_CLK_ENABLE(); }while(0)     /* TIM8 时钟使能 */

/******************************************************************************************/

void atim_timx_npwm_chy_init(uint16_t arr, uint16_t psc);   /* 高级定时器 输出指定个数PWM初始化函数 */

void atim_timx_npwm_chy_set(uint32_t npwm);                 /* 高级定时器 设置输出PWM的个数 */

#endif

(3)timer.c

TIM_HandleTypeDef g_timx_npwm_chy_handle;           /* 定时器x句柄 */

/* g_npwm_remain表示当前还剩下多少个脉冲要发送

 * 每次最多发送256个脉冲

 */

static uint32_t g_npwm_remain = 0;

/**

 * @brief       高级定时器TIMX 通道Y 输出指定个数PWM 初始化函数

 * @note

 *              高级定时器的时钟来自APB2,当PPRE2≥2分频的时候

 *              高级定时器的时钟为APB2时钟的2倍, 而APB2为90M, 所以定时器时钟 = 180Mhz

 *              定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.

 *              Ft=定时器工作频率,单位:Mhz

 *

 * @param       arr: 自动重装值

 * @param       psc: 时钟预分频数

 * @retval      无

 */

void atim_timx_npwm_chy_init(uint16_t arr, uint16_t psc)

{

    GPIO_InitTypeDef gpio_init_struct;

    TIM_OC_InitTypeDef timx_oc_npwm_chy = {0};                                                 /* 定时器输出 */

    ATIM_TIMX_NPWM_CHY_GPIO_CLK_ENABLE();                                                      /* TIMX 通道IO口时钟使能 */

    ATIM_TIMX_NPWM_CHY_CLK_ENABLE();                                                           /* TIMX 时钟使能 */

    g_timx_npwm_chy_handle.Instance = ATIM_TIMX_NPWM;                                          /* 定时器x */

    g_timx_npwm_chy_handle.Init.Prescaler = psc;                                               /* 定时器分频 */

    g_timx_npwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;                              /* 递增计数模式 */

    g_timx_npwm_chy_handle.Init.Period = arr;                                                  /* 自动重装载值 */

    g_timx_npwm_chy_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;             /* 使能TIMx_ARR进行缓冲 */

    g_timx_npwm_chy_handle.Init.RepetitionCounter = 0;                                         /* 重复计数器初始值 */

    HAL_TIM_PWM_Init(&g_timx_npwm_chy_handle);                                                 /* 初始化PWM */

    gpio_init_struct.Pin = ATIM_TIMX_NPWM_CHY_GPIO_PIN;                                        /* 开启通道y的GPIO口 */

    gpio_init_struct.Mode = GPIO_MODE_AF_PP;                                                   /* 复用推挽输出 */

    gpio_init_struct.Pull = GPIO_PULLUP;                                                       /* 上拉 */

    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                                             /* 高速 */

    gpio_init_struct.Alternate = ATIM_TIMX_NPWM_CHY_GPIO_AF;                                   /* 端口复用 */

    HAL_GPIO_Init(ATIM_TIMX_NPWM_CHY_GPIO_PORT, &gpio_init_struct);

    timx_oc_npwm_chy.OCMode = TIM_OCMODE_PWM1;                                                 /* 选择PWM1模式 */

    timx_oc_npwm_chy.Pulse = arr / 2;                                                          /* 设置比较值,此值用来确定占空比

                                                                                                  这里默认设置比较值为自动重装载值的一半,即占空比为50%

                                                                                                */

    timx_oc_npwm_chy.OCPolarity = TIM_OCPOLARITY_HIGH;                                         /* 输出比较极性为高 */

    HAL_TIM_PWM_ConfigChannel(&g_timx_npwm_chy_handle, &timx_oc_npwm_chy, ATIM_TIMX_NPWM_CHY); /* 配置TIMx通道y */

    HAL_NVIC_SetPriority(ATIM_TIMX_NPWM_IRQn, 1, 3);                                           /* 设置中断优先级,抢占优先级1,子优先级3 */

    HAL_NVIC_EnableIRQ(ATIM_TIMX_NPWM_IRQn);                                                   /* 开启ITMx中断 */

    __HAL_TIM_ENABLE_IT(&g_timx_npwm_chy_handle, TIM_IT_UPDATE);                               /* 允许更新中断 */

    HAL_TIM_PWM_Start(&g_timx_npwm_chy_handle, ATIM_TIMX_NPWM_CHY);                            /* 开启对应PWM通道 */

}

/**

 * @brief       高级定时器TIMX NPWM设置PWM个数

 * @param       npwm : PWM的个数, 1~2^32次方个

 * @retval      无

 */

void atim_timx_npwm_chy_set(uint32_t npwm)

{

    if (npwm == 0) return;

    g_npwm_remain = npwm;                                                           /* 保存脉冲个数 */

    HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle, TIM_EVENTSOURCE_UPDATE);         /* 产生一次更新事件,在中断里面处理脉冲输出 */

    __HAL_TIM_ENABLE(&g_timx_npwm_chy_handle);                                      /* 使能定时器TIMX */

}

/**

 * @brief       高级定时器TIMX NPWM中断服务函数

 * @param       无

 * @retval      无

 */

void ATIM_TIMX_NPWM_IRQHandler(void)

{

    uint16_t npwm = 0;

    /* 以下代码没有使用定时器HAL库共用处理函数来处理,而是直接通过判断中断标志位的方式 */

    if (__HAL_TIM_GET_FLAG(&g_timx_npwm_chy_handle, TIM_FLAG_UPDATE) != RESET)

    {

        if (g_npwm_remain >= 256)                                                   /* 还有大于256个脉冲需要发送 */

        {

            g_npwm_remain = g_npwm_remain - 256;

            npwm = 256;

        }

        else if (g_npwm_remain % 256)                                               /* 还有位数(不到256)个脉冲要发送 */

        {

            npwm = g_npwm_remain % 256;

            g_npwm_remain = 0;                                                      /* 没有脉冲了 */

        }

        if (npwm)                                                                   /* 有脉冲要发送 */

        {

            ATIM_TIMX_NPWM->RCR = npwm - 1;                                         /* 设置重复计数寄存器值为npwm-1, 即npwm个脉冲 */

            HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle, TIM_EVENTSOURCE_UPDATE); /* 产生一次更新事件,在中断里面处理脉冲输出 */

            __HAL_TIM_ENABLE(&g_timx_npwm_chy_handle);                              /* 使能定时器TIMX */

        }

        else

        {

            ATIM_TIMX_NPWM->CR1 &= ~(1 << 0);                                       /* 关闭定时器TIMX,使用HAL Disable会清除PWM通道信息,此处不用 */

        }

        __HAL_TIM_CLEAR_IT(&g_timx_npwm_chy_handle, TIM_IT_UPDATE);                 /* 清除定时器溢出中断标志位 */

    }

}

四.STM32单片机输出PWM应用场景

   PWM通过改变信号的脉冲宽度(即占空比)来模拟不同的电压或电流水平,从而实现对设备的精确控制。以下是一些具体的应用场景:

1.电机控制

PWM信号常用于电机的速度调节。通过改变PWM信号的占空比,可以控制电机的平均电压,从而调节电机的转速。这种方式比直接控制电压或电流更为高效和精确。

2.灯光控制

在LED灯、呼吸灯等照明设备中,PWM信号可以用来调节灯光的亮度。通过改变PWM信号的占空比,可以控制LED灯的平均电流,从而调节其亮度。这种方式可以实现平滑的亮度调节,避免直接调节电压可能带来的闪烁问题。

3.音频控制

在音频设备中,PWM信号可以用于模拟音频信号的音量控制。通过改变PWM信号的占空比,可以控制音频信号的等效电压,从而调节音量。然而,需要注意的是,由于PWM信号本身是离散的,因此在音频领域通常使用更高级的DAC(数模转换器)来实现更平滑的音量控制。

4.温度控制

在需要精确控制温度的设备中,如电加热器、空调等,PWM信号可以用来调节加热元件的功率。通过改变PWM信号的占空比,可以控制加热元件的平均功率输出,从而实现对温度的精确控制。

5.舵机控制

在机器人、智能车等项目中,PWM信号常用于控制舵机的角度。通过改变PWM信号的占空比,可以控制舵机内部电机的转动角度,从而实现对机械臂、车轮等部件的精确控制。

6.电源管理

在需要精确控制电源输出的场合,如开关电源、电池充电器等,PWM信号可以用来调节输出电压或电流。通过改变PWM信号的占空比和频率,可以实现对电源输出的精确控制,提高电源效率和稳定性。

相关推荐

  1. GD32F4xx 通用定时器输出PWM

    2024-07-17 11:20:04       29 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-17 11:20:04       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-17 11:20:04       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-17 11:20:04       58 阅读
  4. Python语言-面向对象

    2024-07-17 11:20:04       69 阅读

热门阅读

  1. DDD学习笔记七

    2024-07-17 11:20:04       24 阅读
  2. 怎么把VMamba作为Feature Extractor集成到现有模型

    2024-07-17 11:20:04       25 阅读
  3. AI时代的技术应用与创新:探索未来

    2024-07-17 11:20:04       17 阅读
  4. 新版本 Android Studio 没有BuildConfig ?

    2024-07-17 11:20:04       30 阅读
  5. 前缀匹配工具之IP-Prefix

    2024-07-17 11:20:04       28 阅读
  6. 高精度减法(C++)

    2024-07-17 11:20:04       24 阅读
  7. 谈人工智能在电子档案系统的应用

    2024-07-17 11:20:04       18 阅读
  8. Android 音频通道切换HDMI,蓝牙,喇叭

    2024-07-17 11:20:04       26 阅读