1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html#
第十八章 通用定时器PWM输出实验
本章将介绍使用APM32F407的通用定时器输出PWM。通过本章的学习,读者将学习到通用定时器输出比较的使用。
本章分为如下几个小节:
18.1 硬件设计
18.2 程序设计
18.3 下载验证
18.1 硬件设计
18.1.1 例程功能
- LED0由暗变亮,再从亮变暗,依次循环
18.1.2 硬件资源 - LED
LED0 - PF9 - 定时器14
通道1 - PF9
18.1.3 原理图
本章实验使用的定时器14为APM32F407的片上资源,因此没有对应的连接原理图。
18.2 程序设计
18.2.1 Geehy标准库的TMR驱动
本章实验将使用TMR14从通道1(PF9引脚)输出PWM,因此除了像上章实验配置定时器的基本参数外,还需要配置通用定时器的输出比较通道,具体的步骤如下:
①:配置TMR14的自动重装载值和预分频器数值等参数
②:配置输出比较通道1
③:使能TMR14
④:使能输出比较通道1输出
⑤:修改比较值以修改PWM输出的占空比
在Geehy标准库中对应的驱动函数如下:
①:配置TMR
请见第16.2.1小节中配置TMR的相关内容。
②:配置输出比较通道
该函数用于配置TMR的任意输出比较通道,其函数原型如下所示:
void TMR_ConfigOC1(TMR_T* tmr, TMR_OCConfig_T* OCConfig);
void TMR_ConfigOC2(TMR_T* tmr, TMR_OCConfig_T* OCConfig);
void TMR_ConfigOC3(TMR_T* tmr, TMR_OCConfig_T* OCConfig);
void TMR_ConfigOC4(TMR_T* tmr, TMR_OCConfig_T* OCConfig);
该函数的形参描述,如下表所示:
该函数的返回值描述,如下表所示:
该函数使用TMR_OCConfig_T类型的结构体变量传入TMR输出比较通道的配置参数,该结构体的定义如下所示:
typedef enum
{
TMR_OC_MODE_TMRING = 0x00, /* 冻结 */
TMR_OC_MODE_ACTIVE = 0x01, /* 匹配时输出置为高 */
TMR_OC_MODE_INACTIVE = 0x02, /* 匹配时输出置为低 */
TMR_OC_MODE_TOGGLE = 0x03, /* 匹配时输出翻转 */
TMR_OC_MODE_LOWLEVEL = 0x04, /* 强制输出为低 */
TMR_OC_MODE_HIGHLEVEL = 0x05, /* 强制输出为高 */
TMR_OC_MODE_PWM1 = 0x06, /* PWM模式1 */
TMR_OC_MODE_PWM2 = 0x07 /* PWM模式2 */
} TMR_OC_MODE_T;
typedef enum
{
TMR_OC_STATE_DISABLE, /* 禁止输出 */
TMR_OC_STATE_ENABLE /* 开启输出 */
} TMR_OC_STATE_T;
typedef enum
{
TMR_OC_NSTATE_DISABLE, /* 禁止互补输出 */
TMR_OC_NSTATE_ENABLE /* 开启互补输出 */
} TMR_OC_NSTATE_T;
typedef enum
{
TMR_OC_POLARITY_HIGH, /* 高电平有效 */
TMR_OC_POLARITY_LOW /* 低电平有效 */
} TMR_OC_POLARITY_T;
typedef enum
{
TMR_OC_NPOLARITY_HIGH, /* 互补高电平有效 */
TMR_OC_NPOLARITY_LOW /* 互补低电平有效 */
} TMR_OC_NPOLARITY_T;
typedef enum
{
TMR_OC_IDLE_STATE_RESET, /* 空闲时为低电平 */
TMR_OC_IDLE_STATE_SET /* 空闲时为高电平 */
} TMR_OC_IDLE_STATE_T;
typedef enum
{
TMR_OC_NIDLE_STATE_RESET, /* 互补空闲时为低电平 */
TMR_OC_NIDLE_STATE_SET /* 互补空闲时为高电平 */
} TMR_OC_NIDLE_STATE_T;
typedef struct
{
TMR_OC_MODE_T mode; /* 模式 */
TMR_OC_STATE_T outputState; /* 输出状态 */
TMR_OC_NSTATE_T outputNState; /* 互补通道输出状态 */
TMR_OC_POLARITY_T polarity; /* 极性 */
TMR_OC_NPOLARITY_T nPolarity; /* 互补通道极性 */
TMR_OC_IDLE_STATE_T idleState; /* 空闲状态 */
TMR_OC_NIDLE_STATE_T nIdleState; /* 互补通道空闲状态 */
uint16_t pulse; /* 比较值 */
} TMR_OCConfig_T;
该函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "apm32f4xx_tmr.h"
void example_fun(void)
{
TMR_OCConfig_T tmr_oc_init_struct;
/* 配置TMR1输出比较通道1及其互补通道 */
tmr_oc_init_struct.mode = TMR_OC_MODE_PWM1;
tmr_oc_init_struct.outputState = TMR_OC_STATE_ENABLE;
tmr_oc_init_struct.outputNState = TMR_OC_NSTATE_ENABLE;
tmr_oc_init_struct.polarity = TMR_OC_POLARITY_LOW;
tmr_oc_init_struct.nPolarity = TMR_OC_NPOLARITY_HIGH;
tmr_oc_init_struct.idleState = TMR_OC_IDLE_STATE_RESET;
tmr_oc_init_struct.nIdleState = TMR_OC_NIDLE_STATE_RESET;
tmr_oc_init_struct.pulse = 255;
TMR_ConfigOC1(TMR1, &tmr_oc_init_struct);
}
③:使能TMR
请见第16.2.1小节中使能TMR的相关内容。
④:使能捕获比较通道
该函数用于使能捕获比较通道,其函数原型如下所示:
void TMR_EnableCCxChannel(TMR_T* tmr, TMR_CHANNEL_T channel);
该函数的形参描述,如下表所示:
表18.2.1.3 函数TMR_EnableCCxChannel()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表18.2.1.4 函数TMR_EnableCCxChannel()返回值描述
该函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "apm32f4xx_tmr.h"
void example_fun(void)
{
/* 使能TMR1捕获比较通道1 */
TMR_EnableCCxChannel(TMR1, TMR_CHANNEL_1);
}
⑤:配置捕获比较值
该函数用于配置TMR指定通道的捕获比较值,其函数原型如下所示:
void TMR_ConfigCompare1(TMR_T* tmr, uint32_t compare1);
void TMR_ConfigCompare2(TMR_T* tmr, uint32_t compare2);
void TMR_ConfigCompare3(TMR_T* tmr, uint32_t compare3);
void TMR_ConfigCompare4(TMR_T* tmr, uint32_t compare4);
该函数的形参描述,如下表所示:
形参 描述
tmr 指向TMR外设结构体的指针
例如:TMR1、TMR2等(在apm32f4xx.h文件中有定义)
comparen 捕获比较值
表18.2.1.5 函数TMR_ConfigComparen()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表18.2.1.6 函数TMR_ConfigComparen()返回值描述
该函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "apm32f4xx_tmr.h"
void example_fun(void)
{
/* 配置TMR1捕获比较寄存器1的值 */
TMR_ConfigCompare1(TMR1, 200);
}
18.2.2 通用定时器驱动
本章实验的通用定时器驱动主要负责向应用层提供通用定时器的初始化函数。本章实验中,通用定时器的驱动代码包括gtmr.c和gtmr.h两个文件。
通用定时器驱动中,对TMR、GPIO的相关宏定义,如下所示:
#define GTMR_TMRX_PWM TMR14
#define GTMR_TMRX_PWM_CHY TMR_CHANNEL_1
#define GTMR_TMRX_PWM_CLK_ENABLE() \
do { \
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR14); \
} while (0)
#define GTMR_TMRX_PWM_CHY_GPIO_PORT GPIOF
#define GTMR_TMRX_PWM_CHY_GPIO_PIN GPIO_PIN_9
#define GTMR_TMRX_PWM_CHY_GPIO_PIN_SOURCE GPIO_PIN_SOURCE_9
#define GTMR_TMRX_PWM_CHY_GPIO_AF GPIO_AF_TMR14
#define GTMR_TMRX_PWM_CHY_GPIO_CLK_ENABLE() \
do { \
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOF); \
} while (0)
通用定时器驱动中TMR14的初始化函数,如下所示:
/**
* @brief 初始化通用定时器PWM输出
* @note 当APB1PSC!=1时,定时器的时钟频率为APB1时钟的2倍
* 因此定时器的时钟频率为84MHz
* 定时器溢出时间计算方法:Tout = ((arr + 1) * (psc + 1)) / TMRxCLK
* TMRxCLK=定时器时钟频率,单位MHz
* @param arr: 自动重装载值
* @param psc: 预分频器数值
* @retval 无
*/
void gtmr_tmrx_pwm_chy_init(uint16_t arr, uint16_t psc)
{
GPIO_Config_T gpio_init_struct;
TMR_BaseConfig_T tmr_init_struct;
TMR_OCConfig_T tmr_oc_init_struct;
/* 使能时钟 */
GTMR_TMRX_PWM_CLK_ENABLE(); /* 使能通用定时器时钟 */
GTMR_TMRX_PWM_CHY_GPIO_CLK_ENABLE(); /* 使能PWM输出引脚端口时钟 */
/* 配置PWM输出引脚 */
gpio_init_struct.pin = GTMR_TMRX_PWM_CHY_GPIO_PIN; /* PWM输出引脚 */
gpio_init_struct.mode = GPIO_MODE_AF; /* 复用功能模式 */
gpio_init_struct.speed = GPIO_SPEED_100MHz; /* 高速 */
gpio_init_struct.otype = GPIO_OTYPE_PP; /* 推挽输出 */
gpio_init_struct.pupd = GPIO_PUPD_DOWN; /* 下拉 */
/* 配置PWM输出引脚 */
GPIO_Config(GTMR_TMRX_PWM_CHY_GPIO_PORT, &gpio_init_struct);
/* 配置引脚复用功能 */
GPIO_ConfigPinAF( GTMR_TMRX_PWM_CHY_GPIO_PORT,
GTMR_TMRX_PWM_CHY_GPIO_PIN_SOURCE,
GTMR_TMRX_PWM_CHY_GPIO_AF);
/* 配置通用定时器 */
tmr_init_struct.countMode = TMR_COUNTER_MODE_UP; /* 向上计数 */
tmr_init_struct.clockDivision = TMR_CLOCK_DIV_1; /* 时钟分频系数 */
tmr_init_struct.period = arr; /* 自动重装载值 */
tmr_init_struct.division = psc; /* 预分频器数值 */
TMR_ConfigTimeBase(GTMR_TMRX_PWM, &tmr_init_struct); /* 配置通用定时器 */
/* 配置输出比较通道 */
tmr_oc_init_struct.mode = TMR_OC_MODE_PWM1; /* PWM模式1 */
tmr_oc_init_struct.outputState = TMR_OC_STATE_DISABLE; /* 使能PWM输出 */
tmr_oc_init_struct.polarity = TMR_OC_POLARITY_LOW; /* 低电平有效 */
tmr_oc_init_struct.pulse = (arr + 1) >> 1; /* PWM有效电平脉宽 */
TMR_ConfigOC1(GTMR_TMRX_PWM, &tmr_oc_init_struct); /* 配置输出比较通道1 */
/* 使能通用定时器和PWM输出 */
TMR_Enable(GTMR_TMRX_PWM); /* 使能通用定时器 */
TMR_EnableCCxChannel(GTMR_TMRX_PWM, GTMR_TMRX_PWM_CHY);/* 使能输出比较通道输出 */
}
从TMR14的初始化代码中可以看到,不仅配置了TMR14的自动重装载值和预分频器数值等基本参数,还配置了TMR14的输出比较通道1,由于需要使用GPIO引脚输出PWM,因此对应的GPIO引脚也配置了复用功能。
18.2.3 实验应用代码
本章实验的应用代码,如下所示:
int main(void)
{
uint32_t ledpwmval = 0;
uint8_t dir = 1;
NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_3); /* 设置中断优先级分组为组3 */
sys_apm32_clock_init(336, 8, 2, 7); /* 配置系统时钟 */
delay_init(168); /* 初始化延时功能 */
usart_init(115200); /* 初始化串口 */
gtmr_tmrx_pwm_chy_init(500 - 1, 84 - 1); /* 初始化通用定时器PWM输出 */
while (1)
{
delay_ms(10);
/* 根据方向修改ledpwmval */
if (dir == 1)
{
ledpwmval++;
}
else
{
ledpwmval--;
}
/* 当ledpwmval大于300时,方向改为递减 */
if (ledpwmval > 300)
{
dir = 0;
}
/* 当ledpwmval等于0时,方向改为递增 */
if (ledpwmval == 0)
{
dir = 1;
}
/* 修改通用定时器输出占空比 */
TMR_ConfigCompare1(GTMR_TMRX_PWM, ledpwmval);
}
}
从上面的代码中可以看到,在初始化完TMR14输出PWM后,就不断地改变TMR14通道1的比较值,以达到改变PWM占空比,又因为PWM由PF9引脚输出,PF9引脚连接至LED0,因此LED0的亮度也将随之改变,实现呼吸灯的效果。
18.3 下载验证
在完成编译和烧录操作后,可以看到板子上的LED0先由暗慢慢变亮,再由亮慢慢变暗,依次循环,实现了呼吸灯的效果。