效果展示
<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="WqBqWyTq-1731723487163" src="https://live.csdn.net/v/embed/432501"></iframe>TMR-通用定时器呼吸灯
硬件原理图
发光二极管的亮度主要由流过它的电流决定,VCC的电压是固定的,当我们不停的改变PC8端口的电压就可以实现呼吸灯的效果。
源代码
定时器部分
#ifndef __BSP_GENERAL_TMR_H__
#define __BSP_GENERAL_TMR_H__
#include "apm32f10x.h"
#include "apm32f10x_misc.h"
#include "apm32f10x_rcm.h"
#include "apm32f10x_tmr.h"
#include "apm32f10x_gpio.h"
// 定时器2 3 4 5
#define GENERAL_TMRX TMR3
#define GENERAL_TMRX_CLOCK RCM_APB1_PERIPH_TMR3
#define GENERAL_TMRX_IRQn TMR3_IRQn
// 通道3 重映射PC8
#define GENERAL_TMR_GPIO_CLOCK RCM_APB2_PERIPH_GPIOC
#define GENERAL_TMR_CH3_GPIO_PIN GPIO_PIN_8
#define GENERAL_TMR_CH3_GPIO_PORT GPIOC
/* 函数声明 ------------------------------------------------------------------*/
void General_TMR_PWM_Config(void);
#endif // __BSP_GENERAL_TMR_H
#include "bsp_general_tmr.h"
/* 私有宏定义 ----------------------------------------------------------------*/
uint8_t IndexWave[] = {1, 1, 2, 2, 3, 4, 6, 8, 10, 14, 19, 25, 33, 44, 59, 80, 107, 143, 191, 255,
255, 191, 143, 107, 80, 59, 44, 33, 25, 19, 14, 10, 8, 6, 4, 3, 2, 2, 1, 1};
/**
* @brief 配置PC8引脚,该引脚完全重映射到TIM3_CH3
* @param
*/
static void General_TMR_GPIO_Config(void)
{
GPIO_Config_T GPIO_ConfigStruct;
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_AFIO);
RCM_EnableAPB2PeriphClock(GENERAL_TMR_GPIO_CLOCK);
GPIO_ConfigPinRemap(GPIO_FULL_REMAP_TMR3); // PC8是完全重映射
GPIO_ConfigStruct.mode = GPIO_MODE_AF_PP;
GPIO_ConfigStruct.pin = GENERAL_TMR_CH3_GPIO_PIN;
GPIO_ConfigStruct.speed = GPIO_SPEED_10MHz;
GPIO_Config(GENERAL_TMR_CH3_GPIO_PORT, &GPIO_ConfigStruct);
}
/**
* @brief 配置定时器为PWM模式1,开启定时器更新中断
* @param 无
*/
void General_TMR_PWM_Config(void)
{
TMR_BaseConfig_T TMR_BaseConfigStruct;
TMR_OCConfig_T OCCongigStruct;
General_TMR_GPIO_Config();
// 开始定时器时钟,基本定时器只有内部时钟
RCM_EnableAPB1PeriphClock(GENERAL_TMRX_CLOCK);
// 配置优先级分组
NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_4);
// 预分配系数
TMR_BaseConfigStruct.division = 1999;
// 当定时器从0计数到255,即为256次,为一个定时周期
TMR_BaseConfigStruct.period = 255;
// 设置时钟分频系数:不分频(这里用不到)
TMR_BaseConfigStruct.clockDivision = TMR_CLOCK_DIV_1;
// 计数器计数模式,向上计数
TMR_BaseConfigStruct.countMode = TMR_COUNTER_MODE_UP;
// 重复计数器的值,通用定时器没有,不用管
// TMR_BaseConfigStruct.repetitionCounter = 0;
TMR_ConfigTimeBase(GENERAL_TMRX, &TMR_BaseConfigStruct);
// 模式配置:PWM模式1, 在向上计数时,一旦TMRx_CNT<TMRx_CCx时,通道x为有效电平,否则为无效电平;
OCCongigStruct.mode = TMR_OC_MODE_PWM1;
// 输出状态设置:使能输出
OCCongigStruct.outputState = TMR_OC_STATE_ENABLE;
// 有效电平为高电平
OCCongigStruct.polarity = TMR_OC_POLARITY_HIGH;
// CCx寄存器值为0
OCCongigStruct.pulse = 0;
TMR_ConfigOC3(GENERAL_TMRX, &OCCongigStruct);
// 启用预装载功能,通过程序写入 TMRx_CC3 寄存器的数值,会在产生更新事件后起作用。
TMR_ConfigOC3Preload(GENERAL_TMRX, TMR_OC_PRELOAD_ENABLE);
// 使能缓存区时,程序修改 TMRx_AUTORLD 会在下一个更新事件修改装入计数器的数值
TMR_EnableAutoReload(GENERAL_TMRX);
// 清除计数器中断标志位
TMR_ClearIntFlag(GENERAL_TMRX, TMR_INT_UPDATE);
// 开启定时器更新中断
TMR_EnableInterrupt(GENERAL_TMRX, TMR_INT_UPDATE);
// 配置定时器中断优先级
NVIC_EnableIRQRequest(GENERAL_TMRX_IRQn, 3, 0);
// 使能定时器
TMR_Enable(GENERAL_TMRX);
}
主程序部分
/**
* @file main.c
* @brief PC8输出PWM,灯接PC8
*/
/* 包含头文件 ----------------------------------------------------------------*/
#include "bsp_general_tmr.h"
/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
/* 私有变量 ------------------------------------------------------------------*/
__IO uint16_t timer_count = 0;
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
/**
* @brief 主函数
* @return 无
*/
int main(void)
{
General_TMR_PWM_Config();
while (1)
{
}
}
中断部分
/*!
* @file apm32f10x_it.c
*
* @brief Main Interrupt Service Routines
*
* @version V1.0.0
*
* @date 2019-9-30
*
*/
#include "apm32f10x_it.h"
#include "bsp_general_tmr.h"
/*!
* @brief This function handles NMI exception
*
* @param None
*
* @retval None
*
*/
void NMI_Handler(void)
{
}
/*!
* @brief This function handles Hard Fault exception
*
* @param None
*
* @retval None
*
*/
void HardFault_Handler(void)
{
/* Go to infinite loop when Hard Fault exception occurs */
while (1)
{
}
}
/*!
* @brief This function handles Memory Manage exception
*
* @param None
*
* @retval None
*
*/
void MemManage_Handler(void)
{
/* Go to infinite loop when Memory Manage exception occurs */
while (1)
{
}
}
/*!
* @brief This function handles Bus Fault exception
*
* @param None
*
* @retval None
*
*/
void BusFault_Handler(void)
{
/* Go to infinite loop when Bus Fault exception occurs */
while (1)
{
}
}
/*!
* @brief This function handles Usage Fault exception
*
* @param None
*
* @retval None
*
*/
void UsageFault_Handler(void)
{
/* Go to infinite loop when Usage Fault exception occurs */
while (1)
{
}
}
/*!
* @brief This function handles SVCall exception
*
* @param None
*
* @retval None
*
*/
void SVC_Handler(void)
{
}
/*!
* @brief This function handles Debug Monitor exception
*
* @param None
*
* @retval None
*
*/
void DebugMon_Handler(void)
{
}
/*!
* @brief This function handles PendSV_Handler exception
*
* @param None
*
* @retval None
*
*/
void PendSV_Handler(void)
{
}
/*!
* @brief This function handles SysTick Handler
*
* @param None
*
* @retval None
*
*/
void SysTick_Handler(void)
{
}
void TMR3_IRQHandler(void)
{
static uint8_t pwm_index = 0; /* 用于PWM查表 */
static uint8_t period_cnt = 0; /* 用于计算周期数 */
extern uint8_t IndexWave[];
if (TMR_ReadIntFlag(GENERAL_TMRX, TMR_INT_UPDATE) != RESET)
{
period_cnt++;
if (period_cnt >= 20)
{
TMR_ConfigCompare3(GENERAL_TMRX, IndexWave[pwm_index]); // 设置比较值
pwm_index++;
if (pwm_index >= 40)
{
pwm_index = 0;
}
period_cnt = 0;
}
TMR_ClearIntFlag(GENERAL_TMRX, TMR_INT_UPDATE); // 清除更新中断标志位
}
}
代码分析
我们主要来看定时器部分,定时器该怎么配置,才能产生PWM。
bsp_general_tmr.c
先来看定时部分
TMR_BaseConfig_T TMR_BaseConfigStruct;
// 配置优先级分组
NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_4);
// 预分配系数
TMR_BaseConfigStruct.division = 1999;
// 当定时器从0计数到255,即为256次,为一个定时周期
TMR_BaseConfigStruct.period = 255;
// 设置时钟分频系数:不分频(这里用不到)
TMR_BaseConfigStruct.clockDivision = TMR_CLOCK_DIV_1;
// 计数器计数模式,向上计数
TMR_BaseConfigStruct.countMode = TMR_COUNTER_MODE_UP;
// 重复计数器的值,通用定时器没有,不用管
// TMR_BaseConfigStruct.repetitionCounter = 0;
TMR_ConfigTimeBase(GENERAL_TMRX, &TMR_BaseConfigStruct);
这个部分的配置基本和基本定时器的配置一样,不过通用定时器可选的参数更多,通用定时器可以向上、向下和中央对齐计数而基本定时器只能向上计数;通用定时器的时钟源除了内部时钟还有外部输入、外部触发、内部触发。
主要来看下输出比较功能的PWM输出
TMR_OCConfig_T OCCongigStruct;
// 模式配置:PWM模式1, 在向上计数时,一旦TMRx_CNT<TMRx_CCx时,通道x为有效电平,否则为无效电平;
OCCongigStruct.mode = TMR_OC_MODE_PWM1;
// 输出状态设置:使能输出
OCCongigStruct.outputState = TMR_OC_STATE_ENABLE;
// 有效电平为高电平
OCCongigStruct.polarity = TMR_OC_POLARITY_HIGH;
// CCx寄存器值为0
OCCongigStruct.pulse = 0;
TMR_ConfigOC3(GENERAL_TMRX, &OCCongigStruct);
// 启用预装载功能,通过程序写入 TMRx_CC3 寄存器的数值,会在产生更新事件后起作用。
TMR_ConfigOC3Preload(GENERAL_TMRX, TMR_OC_PRELOAD_ENABLE);
// 使能缓存区时,程序修改 TMRx_AUTORLD 会在下一个更新事件修改装入计数器的数值
TMR_EnableAutoReload(GENERAL_TMRX);
OC
是OutputCompare(输出比较)的缩写,基本定时器的输出比较除了PWM输出模式还有强制输出模式、单脉冲模式。
来看下TMR_OCConfig_T
这个结构体定义。
/**
* @brief TMR Output Compare Configure structure definition
*/
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; /*!< This must between 0x0000 and 0xFFFF */
} TMR_OCConfig_T;
mode
可选参数如下
/**
* @brief TMR Output Compare and PWM modes
*/
typedef enum
{
TMR_OC_MODE_TMRING = 0x00,
TMR_OC_MODE_ACTIVE = 0x01,
TMR_OC_MODE_INACTIVE = 0x02,
TMR_OC_MODE_TOGGEL = 0x03,
TMR_OC_MODE_LOWLEVEL = 0x04,
TMR_OC_MODE_HIGHLEVEL = 0x05,
TMR_OC_MODE_PWM1 = 0x06,
TMR_OC_MODE_PWM2 = 0x07
} TMR_OC_MODE_T;
看到这些参数肯定一头雾水,都是干啥用的?怎么找到相关参数是干什么的?我们看下装填该结构体的函数TMR_ConfigOC3
,如图
在参考手册中找到OC3MOD这个寄存器,搜索一圈发现没有写这个寄存器,但是有OC1MOD,厂商为了省事只举了一个例子,我们就来看这个,打开APM32参考手册第190页,如下图
我们这次主要用到PWM模式1和PWM模式2,但奈何APM32这里的手册解释不好,我们还是来看下stm32的参考手册如何解释的,如下图
我们常用PWM模式1,向上计数模式,所以当计数值小于比较值时输出有效电平,大于则输出无效电平,那问题来了什么是有效电平?什么是无效电平?我们来看下参数polarity
,有两个参数可选TMR_OC_POLARITY_HIGH
,TMR_OC_POLARITY_LOW
,这次APM32参考手册写的和stm32差不多,就看apm32的参考手册如下图
当选择TMR_OC_POLARITY_HIGH
时,有效电平为高电平,TMR_OC_POLARITY_LOW
有效电平为低电平,这样上面的PWM模式1和PWM模式2就很容易解释通了。
outputState
,可选参数TMR_OC_STATE_DISABLE
关闭输出,TMR_OC_STATE_ENABLE
开启输出。
pulse
这个参数就是我们常说的比较值,这个值时初始化时赋值给比较值寄存器。
其他的参数就不介绍了,其他的参数都是高级定时器中的,平时也用不到。
TMR_ConfigOC3Preload(GENERAL_TMRX, TMR_OC_PRELOAD_ENABLE);
这个函数是启用通道3的预装载功能,若要用到其他通道,把3换成1 2 4就可以设置其他通道了。我们来解释下预装载功能,启用预装载功能后若要改变比较值,新的比较值会在定时器更新事件后起作用,若不启用预装载功能,更改比较值后会立马启作用。
TMR_EnableAutoReload(GENERAL_TMRX);
这个函数是使能自动重装载缓冲功能,和上面函数差不多,只不过这个函数是对寄存器TMRx_AUTORLD
起作用,也就是我们配置TMR_BaseConfigStruct
中的period
。使能后只有在产生定时器溢出事件后才更新自动重装载数值,否则直接起作用。
apm32f10x_it.c
void TMR3_IRQHandler(void)
{
static uint8_t pwm_index = 0; /* 用于PWM查表 */
static uint8_t period_cnt = 0; /* 用于计算周期数 */
extern uint8_t IndexWave[];
if (TMR_ReadIntFlag(GENERAL_TMRX, TMR_INT_UPDATE) != RESET)
{
period_cnt++;
if (period_cnt >= 20)
{
TMR_ConfigCompare3(GENERAL_TMRX, IndexWave[pwm_index]); // 设置比较值
pwm_index++;
if (pwm_index >= 40)
{
pwm_index = 0;
}
period_cnt = 0;
}
TMR_ClearIntFlag(GENERAL_TMRX, TMR_INT_UPDATE); // 清除更新中断标志位
}
}
这段代码是定时器产生更新中断对通道3比较值的更改来实现输出不同电压。我们主要来看下TMR_ConfigCompare3(GENERAL_TMRX, IndexWave[pwm_index]);
这个函数。
/*!
* @brief Configures the Capture Compare3 Register value
*
* @param tmr: The TMRx can be 1 to 8 except 6 and 7
*
* @param compare3: specifies the Capture Compare1 value.
*
* @retval None
*/
void TMR_ConfigCompare3(TMR_T* tmr, uint16_t compare3)
{
tmr->CC3 = compare3;
}
可以看到这个函数就是更改CC3寄存器的值,若要更改其他通道的比较值把3换成其他数字即可。
标签:TMR,定时器,void,OC,GENERAL,GPIO,APM32 From: https://blog.csdn.net/showgu/article/details/143813089