首页 > 其他分享 >6.APM32-TMR-通用定时器呼吸灯

6.APM32-TMR-通用定时器呼吸灯

时间:2024-11-16 10:16:20浏览次数:3  
标签:TMR 定时器 void OC GENERAL GPIO APM32

效果展示

<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="WqBqWyTq-1731723487163" src="https://live.csdn.net/v/embed/432501"></iframe>

TMR-通用定时器呼吸灯

硬件原理图

LED模块

发光二极管的亮度主要由流过它的电流决定,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

相关文章

  • 5.APM32-TMR-基本定时器定时
    效果展示TMR-基本定时器定时使用基本定时器进行定时,使LED按1Hz的频率闪烁硬件原理图我们这次只用到PA8引脚,其他几个引脚不看源代码关于LED的代码就不贴出了,LED相关代码可以在流水灯文章中找到。定时器部分#ifndef__BSP_BASE_TMR_H__#define__BSP_BASE_......
  • 4.APM32-USART-串口接发
    效果展示USART-串口接发硬件原理图我们使用的开发板上没有USB转串口的芯片,如果要连接到电脑上还需要使用USB转串口的模块或者jlink自带的虚拟串口。开发板的PA9(TX)引脚接USB转串口模块的RX引脚,开发板的PA10(RX)引脚接USB转串口模块的TX引脚,同时双方的GND还要连起......
  • APM32实现printf串口打印
    Keil环境在Keil环境中使用printf,首先需要打开UseMicroLib,这个库是keil专门为嵌入式设备定制的,比C语言自带的库如stdio、string等占用空间更小,效率更高。首先要点击Keil的魔术棒,如下图把UseMicroLib打上勾,如下图还要包含头文件#include<stdio.h>,在Keil中串口重定向......
  • Flutter进阶(4):定时器使用(Timer)
    一、Timer简介Flutter的Timer类是Dart语言中的一个内置类,用于创建定时器。定时器可以用于在一段时间后执行代码,或者以固定的时间间隔重复执行代码。Timer类提供了一种简单的方式来管理这些时间相关的任务。二、Timer类的详细介绍2.1导入dart:async包要使用Timer类,首......
  • 关于定时器周期、频率等相关计算
    1、定时器作为计数器,时钟频率计算如下图: 2、定时器产生一次更新中断时间计算,如下:当定时器设置为边沿对齐模式和向上计数模式时,定时器计数到重装载值(arr)产生一次中断,产生一次中断的时间为: 其中:T为定时器周期,也为此处产生一次中断的时间;      arr为重装载值,即定......
  • qt定时器
    首先测试以下qt定时器的精确度运行结果:timer:5999*10mselapsed:59996结论:1min的延迟后,误差在6ms内如果你在Timeout槽函数中执行耗时操作,这将会影响定时器的精确性和响应性。具体来说,Qt的事件循环是基于单线程模型的,所有事件处理(包括定时器超时事件)都在主线程中进行。......
  • 分享一个select+定时器的一个代码
    问题1:下面代码输出什么packagemainimport( "fmt" "time")funcmain(){ //创建两个定时器,一个间隔为1秒,另一个间隔为2秒 ticker1:=time.NewTicker(1*time.Second) ticker2:=time.NewTicker(2*time.Second) //在一个新的goroutine中运行监听逻辑 gofun......
  • STM32CubeMX:使用DAC输出正弦波的三种方法(while,定时器中断,DMA)
    1.DAC概念简介:DAC的工作原理是根据数字输入信号的数值,生成相应的模拟输出电压或电流。它通常接收一个二进制数字输入,该数字代表了一个特定的数值范围。DAC通过将这个数字值转换为模拟信号的电压或电流水平来输出。(功能与ADC相反)2.正弦波输出方式1:简单粗暴while循环输出Cub......
  • 用Javafx开发定时器
    选中小时分钟秒代码附上:packagecom.example.javafx03;importjavafx.application.Application;importjavafx.fxml.FXMLLoader;importjavafx.scene.Parent;importjavafx.scene.Scene;importjavafx.scene.image.Image;importjavafx.stage.Stage;importjav......
  • 如何实现Delay_us和Delay_ms延时(SysTick定时器)
    SysTick定时器(SystemTickTimer)是ARMCortex-M内核中自带的一个24位递减计数器,通常用于产生系统节拍中断,为操作系统提供时基或用于一般性定时功能。它具有以下特点和用途:一、SysTick的介绍1.SysTick的主要用途(计时)操作系统心跳时基:在实时操作系统(RTOS)中,SysTick通常用于产......