stm32 通用定时器原理及具体实现代码

STM32通用定时器学习

写于 2024/8/16 下午

1. 通用定时器简介

TIM2/TIM3 /TIM4 /TIM5为F1系列的通用定时器。

  • 16位递增、递减、中心对齐计数器(计数值:0~65535)
  • 16位预分频器(分频系数:1~65536)
  • 可用于触发DAC、ADC
  • 在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求4个独立通道,可用于:输入捕获、输出比较、输出PWM、单脉冲模式使用外部信号控制定时器且可实现多个定时器互连的同步电路支持编码器和霍尔传感器电路等

2. 基本框图

image-20240816231153144

可以看到,在通用定时器内一共由6部分组成,分别为

  1. 时钟源
  2. 控制器
  3. 时基单元
  4. 输入捕获
  5. 输入捕获和输出比较公用部分
  6. 输出比较

通用定时器包含基本定时器的所有功能

2.1 时钟源

通用定时器时钟可以选择下面四类时钟源之一:

  • 内部时钟(CK_INT)
  • 外部时钟模式 1:外部输入引脚(TIx),x=1,2(即只能来自于通道 1 或者通道 2)
  • 外部时钟模式 2:外部触发输入(ETR)
  • 内部触发输入(ITRx):使用一个定时器作为另一定时器的预分频器
2.1.1 内部时钟(CK_INT)

相似的,跟基本定时器一样,通用定时器TIM2/TIM3 /TIM4 /TIM5和基本定时器TIM6/TIM7都挂载在APB1总线上,虽然APB1的频率最高为36Mhz,但经过倍频后最大可以达到72Mhz

image-20240816232448254

2.1.2 外部时钟模式 1(TI1、TI2)

image-20240816232833608

外部时钟模式1的输入信号来自定时器的通道1与通道2,通道3与通道4不能作为外部时钟模式的输入信号。时钟源进入定时器的流程如下:外部时钟源信号→IO→TIMx_CH1(或者 TIMx_CH2)。从 IO到 TIMx_CH1(或者 TIMx_CH2),就需要我们配置 IO 的复用功能,才能使 IO 和定时器通道相连通。

信号从TIMx_CH2进入后,通过滤波器整形,由 **ICF[3:0]**位来设置滤波方式

image-20240817003402117

image-20240817003432851

,然后抵达边沿检测器。边沿检测器可以选择上边沿检测或者下边沿检测,使用TIMx_CCER中的CC2P位来设置。

image-20240817003757063

然后经过触发输入选择器,由 TIMx_SMCR的**TS[2:0]**位来选择 TRGI(触发输入信号)的来源。可以看到图 21.1.2 中框出了 TI1F_EDTI1FP1TI2FP2 三个触发输入信号(TRGI)。TI1F_ED 表示来自于 CH1,并且没有经过边沿检测器过滤的信号,所以它是 CH1 的双边沿信号,即上升沿或者下降沿都是有效的。TI1FP1 表示来自 CH1 并经过边沿检测器后的信号,可以是上升沿或者下降沿。TI2FP2 表示来自 CH2 并经过边沿检测器后的信号,可以是上升沿或者下降沿。

image-20240817004548446

最后经过从模式选择器,由TIMx_SMCRECE 位和 **SMS[2:0]**位来选择定时器的时钟源。这里我们介绍的是外部时钟模式 1,所以 ECE 位置 0,SMS[2:0] = 111 即可。CK_PSC 需要经过定时器的预分频器分频后,最终就能到达计数器进行计数了。

2.1.3 外部时钟模式2(ETR)

image-20240816234704967

外部时钟模式 2,时钟源进入定时器的流程如下:外部时钟源信号→IO→TIMx_ETR。从 IO 到 TIMx_ETR,就需要我们配置 IO 的复用功能,才能使IO 和定时器相连通。

定时器时钟信号首先从 ETR 引脚进来。接着经过外部触发极性选择器,由 ETP 位来设置上升沿有效还是下降沿有效,选择下降沿有效的话,信号会经过反相器。

image-20240817004626654

然后经过外部触发预分频器,由 **ETPS[1:0]**位来设置预分频系数,系数范围:1、2、4、8。

image-20240817004642579

紧接着经过滤波器器,由 **ETF[3:0]**位来设置滤波方式,也可以设置不使用滤波器。fDTS 由TIMx_CR1 寄存器的 CKD 位设置。

image-20240817004701697

最后经过从模式选择器,由 ECE 位和 **SMS[2:0]**位来选择定时器的时钟源。这里我们介绍的是外部时钟模式 2,直接把 ECE 位置 1 即可。CK_PSC 需要经过定时器的预分频器分频后,最终就能到达计数器进行计数了。

image-20240817004745083

2.1.4 内部触发输入(ITRx)

image-20240816235635884

内部触发输入是使用一个定时器作为另一个定时器的预分频器,即实现定时器的级联。下面以 TIM1 作为 TIM2 的预分频器为例。

上图中,TIM1 作为 TIM2 的预分频器,需要完成的配置步骤如下:

1,TIM1_CR2 寄存器的 MMS[2:0]位设置为 010,即 TIM1 的主模式选择为更新(选择更新事件作为触发输(TRGO))。

2,TIM2_SMCR 寄存器的 TS[2:0]位设置为 000,即使用 ITR0 作为内部触发。TS[2:0]位用于配置触发选择,除了 ITR0,还有其他的选择,详细描述如下图所示:

image-20240817000212524

上图中的触发选择中,我们在讲解外部时钟模式 1 的时候说过 TI1F_ED、TI1FP1 和 TI2FP2,以及外部时钟模式 2 讲的 ETRF,它们都是属于外部的,其余的都是内部触发了。

image-20240817000307876

在步骤 2 中,TS[2:0]位设置为 000,使用 ITR0作为内部触发,这个 ITR0 什么意思?由表21.1.3 可以知道,当从模式定时器为 TIM2 时,ITR0 表示主模式定时器就是 TIM1。这里只是TIM2~5 的内部触发连接情况,其他定时器请查看参考手册的相应章节。

3,TIM2_SMCR 寄存器的 SMS[2:0]位设置为 111,即从模式控制器选择外部时钟模式 1。

4,TIM1 和 TIM2 的 CEN 位都要置 1,即启动计数器。

2.2 控制器

控制器包括:从模式控制器、编码器接口和触发控制器(TRGO)。从模式控制器可以控制计数器复位、启动、递增/递减、计数。编码器接口针对编码器计数。触发控制器用来提供触发信号给别的外设,比如为其它定时器提供时钟或者为 DAC/ADC 的触发转换提供信号。

2.3 时基单元

时基单元包括:计数器寄存器(TIMx_CNT)、预分频器寄存器(TIMx_PSC)、自动重载寄存器(TIMx_ARR)。这部分内容和基本定时器基本一样的,不同点在于通用定时器的计数模式有三种:递增计数模式、递减计数模式和中心对齐模式。

2.4 输入捕获

image-20240818075306376

这里是以通道 1 输入捕获为例进行介绍,其他通道同理。待测量信号到达 TIMx_CH1 后,那么这里我们把这个待测量信号用 TI1 表示

TI1 首先经过一个滤波器,由 ICF[3:0]位来设置滤波方式,也可以设置不使用滤波器。fDTS由 TIMx_CR1 寄存器的 CKD 位设置。

image-20240818075454190

接着经过边沿检测器,由 CC1P 位来设置检测的边沿,可以上升沿或者下降沿检测。CC1NP是配置互补通道的边沿检测的,在高级定时器才有,通用定时器没有。

image-20240818075615326

然后经过输入捕获映射选择器,由 CC1S[1:0]位来选择把 IC1 映射到 TI1、TI2 还是 TRC。这里我们的待测量信号从通道 1 进来,所以选择 IC1 映射到 TI1 上即可。

image-20240818075708683

紧接着经过输入捕获 1 预分频器,由 ICPS[1:0]位来设置预分频系数,范围:1、2、4、8。

image-20240818075839070

最后需要把 CC1E 位置 1,使能输入捕获,IC1PS 就是分频后的捕获信号。这个信号将会到达输入捕获和输出比较公用部分

image-20240818080035712

2.4.1 输入捕获测量脉冲宽度

image-20240818084118037

通用定时器输入捕获实验配置步骤

  1. 配置定时器基础工作参数:HAL_TIM_IC_Init()
  2. 定时器输入捕获MSP初始化:HAL_TIM_IC_MspInit() 配置NVIC、CLOCK、GPIO等
  3. 配置输入通道映射、捕获边沿等:HAL_TIM_IC_ConfigChannel()
  4. 设置优先级,使能中断:HAL_NVIC_SetPriority() HAL_NVIC_EnableIRQ()
  5. 使能定时器更新中断:__HAL_TIM_ENABLE_IT()
  6. 使能捕获、捕获中断及计数器:HAL_TIM_IC_Start_IT()
  7. 编写中断服务函数:TIMx_IRQHandler() HAL_TIM_IRQHandler()
  8. 编写更新中断和捕获回调函数:HAL_TIM_PeriodElapsedCallback() HAL_TIM_IC_CaptureCallback()
函数主要寄存器主要功能
HAL_TIM_IC_Init()CR1、ARR、PSC初始化定时器基础参数
HAL_TIM_IC_MspInit()存放NVIC、CLOCK、GPIO初始化代码
HAL_TIM_IC_ConfigChannel()CCMRx、CCER配置通道映射、捕获边沿、分频、滤波等
__HAL_TIM_ENABLE_IT()DIER使能更新中断等
HAL_TIM_IC_Start_IT()CCER、DIER、CR1使能输入捕获、捕获中断并启动计数器
HAL_TIM_IRQHandler()SR定时器中断处理公用函数,处理各种中断
HAL_TIM_PeriodElapsedCallback()定时器更新中断回调函数,由用户重定义
HAL_TIM_IC_CaptureCallback()定时器输入捕获回调函数,由用户重定义

关键结构体介绍

typedef struct
{ 
    uint32_t ICPolarity;    	/* 输入捕获触发方式选择,比如上升、下降沿捕获 */ 
    uint32_t ICSelection; 		/* 输入捕获选择,用于设置映射关系 */ 
    uint32_t ICPrescaler; 		/* 输入捕获分频系数 */ 
    uint32_t ICFilter;          /* 输入捕获滤波器设置 */ 
} TIM_IC_InitTypeDef;

具体实现源码

timer.c

#include "./BSP/TIMER/timer.h"

TIM_HandleTypeDef g_timer_handle;
void timer_init(uint16_t psc,uint16_t arr)
{
    g_timer_handle.Instance = TIM5;
    g_timer_handle.Init.Prescaler = psc;
    g_timer_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    g_timer_handle.Init.Period = arr;
    HAL_TIM_IC_Init(&g_timer_handle);
    
    TIM_IC_InitTypeDef g_timer_channel_handle = {0};
    g_timer_channel_handle.ICPolarity = TIM_ICPOLARITY_RISING;
    g_timer_channel_handle.ICSelection = TIM_ICSELECTION_DIRECTTI;
    g_timer_channel_handle.ICPrescaler = TIM_ICPSC_DIV1;
    g_timer_channel_handle.ICFilter = 0;
    HAL_TIM_IC_ConfigChannel(&g_timer_handle,&g_timer_channel_handle,TIM_CHANNEL_1);
    __HAL_TIM_ENABLE_IT(&g_timer_handle,TIM_IT_UPDATE);
    HAL_TIM_IC_Start_IT(&g_timer_handle,TIM_CHANNEL_1);
    
    
}

void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM5)
    {
         __HAL_RCC_GPIOA_CLK_ENABLE();
        GPIO_InitTypeDef gpio_handle;
        gpio_handle.Pin = GPIO_PIN_0;                   
        gpio_handle.Mode = GPIO_MODE_AF_PP;           
        gpio_handle.Pull = GPIO_PULLDOWN;
        gpio_handle.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOA, &gpio_handle);
        
        __HAL_RCC_TIM5_CLK_ENABLE();
        HAL_NVIC_SetPriority(TIM5_IRQn,2,2);
        HAL_NVIC_EnableIRQ(TIM5_IRQn);
    }

}

void TIM5_IRQHandler()
{
    HAL_TIM_IRQHandler(&g_timer_handle);

}
uint8_t g_timxchy_cap_sta = 0;    /* 输入捕获状态 */
uint16_t g_timxchy_cap_val = 0;   /* 输入捕获值 */


void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM5)
    {
        if ((g_timxchy_cap_sta & 0X80) == 0)                /* 还未成功捕获 */
        {
            if (g_timxchy_cap_sta & 0X40)                   /* 捕获到一个下降沿 */
            {
                g_timxchy_cap_sta |= 0X80;                  /* 标记成功捕获到一次高电平脉宽 */
                g_timxchy_cap_val = HAL_TIM_ReadCapturedValue(&g_timer_handle, TIM_CHANNEL_1);  /* 获取当前的捕获值 */
                TIM_RESET_CAPTUREPOLARITY(&g_timer_handle, TIM_CHANNEL_1);                      /* 一定要先清除原来的设置 */
                TIM_SET_CAPTUREPOLARITY(&g_timer_handle, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING); /* 配置TIM5通道1上升沿捕获 */
            }
            else /* 还未开始,第一次捕获上升沿 */
            {
                g_timxchy_cap_sta = 0;                              /* 清空 */
                g_timxchy_cap_val = 0;
                g_timxchy_cap_sta |= 0X40;                          /* 标记捕获到了上升沿 */
                __HAL_TIM_SET_COUNTER(&g_timer_handle, 0);   /* 定时器5计数器清零 */
                TIM_RESET_CAPTUREPOLARITY(&g_timer_handle, TIM_CHANNEL_1);   /* 一定要先清除原来的设置!! */
                TIM_SET_CAPTUREPOLARITY(&g_timer_handle, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); /* 定时器5通道1设置为下降沿捕获 */
            }
        }
    }
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
     if (htim->Instance == TIM5)
    {
        if ((g_timxchy_cap_sta & 0X80) == 0)            /* 还未成功捕获 */
        {
            if (g_timxchy_cap_sta & 0X40)               /* 已经捕获到高电平了 */
            {
                if ((g_timxchy_cap_sta & 0X3F) == 0X3F) /* 高电平太长了 */
                {
                    TIM_RESET_CAPTUREPOLARITY(&g_timer_handle, TIM_CHANNEL_1);                     /* 一定要先清除原来的设置 */
                    TIM_SET_CAPTUREPOLARITY(&g_timer_handle, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING);/* 配置TIM5通道1上升沿捕获 */
                    g_timxchy_cap_sta |= 0X80;          /* 标记成功捕获了一次 */
                    g_timxchy_cap_val = 0XFFFF;
                }
                else      /* 累计定时器溢出次数 */
                {
                    g_timxchy_cap_sta++;
                }
            }
        }
    }



}

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/TIMER/timer.h"
extern uint8_t g_timxchy_cap_sta;    /* 输入捕获状态 */
extern uint16_t g_timxchy_cap_val;   /* 输入捕获值 */

int main(void)
{
    uint32_t temp = 0;
    uint8_t t = 0;
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟,72M */
    delay_init(72);                             /* 初始化延时函数 */
    led_init();                                 /* 初始化LED */
    usart_init(115200);
    timer_init(71,0xFFFF);
    while(1)
    {
      if (g_timxchy_cap_sta & 0X80)       /* 成功捕获到了一次高电平 */
        {
            temp = g_timxchy_cap_sta & 0X3F;
            temp *= 65536;                  /* 溢出时间总和 */
            temp += g_timxchy_cap_val;      /* 得到总的高电平时间 */
            printf("HIGH:%d us\r\n", temp); /* 打印总的高点平时间 */
            g_timxchy_cap_sta = 0;          /* 开启下一次捕获*/
        }

        t++;

        if (t > 20)         /* 200ms进入一次 */
        {
            t = 0;
            LED0_TOGGLE();  /* LED0闪烁 ,提示程序运行 */
        }
        delay_ms(10);
    }
    
}


2.4.2 通用定时器脉冲计数
  1. 配置定时器基础工作参数:HAL_TIM_IC_Init()
  2. 定时器输入捕获MSP初始化:HAL_TIM_IC_MspInit()
  3. 配置定时器从模式等:HAL_TIM_SlaveConfigSynchro()
  4. 使能输入捕获并启动计数器:HAL_TIM_IC_Start()
  5. 获取计数器的值:__HAL_TIM_GET_COUNTER()
  6. 设置计数器的值:__HAL_TIM_SET_COUNTER()
函数主要寄存器主要功能
HAL_TIM_IC_Init()CR1、ARR、PSC初始化定时器基础参数
HAL_TIM_IC_MspInit()存放NVIC、CLOCK、GPIO初始化代码
HAL_TIM_SlaveConfigSynchro()SMCR、CCMRx、CCER配置定时器从模式、触发选择、分频、滤波等
HAL_TIM_IC_Start()CCER、CR1使能输入捕获、启动计数器
__HAL_TIM_GET_COUNTER()CNT获取计数器当前值
__HAL_TIM_SET_COUNTER()CNT设置计数器的值

关键结构体

typedef struct 
{ 
    uint32_t SlaveMode;      /* 从模式选择 */ 
    uint32_t InputTrigger;   /* 输入触发源选择 */ 
    uint32_t TriggerPolarity;/* 输入触发极性 */ 
    uint32_t TriggerPrescaler; /* 输入触发预分频 */ 
    uint32_t TriggerFilter;    /* 输入滤波器设置 */ 
} TIM_SlaveConfigTypeDef;

2.5 输入捕获和输出比较公用部分

2.5.1 输入捕获部分

image-20240818080750389

首先看到捕获/比较预装载寄存器,我们以通道 1 为例,那么它就是 CCR1 寄存器,通道 2、通道 3、通道 4 就分别对应 CCR2、CCR3、CCR4。在图 21.1.1 中就可以看到 CCR1~4 是有影子寄存器的,所以这里就可以看到图 21.1.8 中有捕获/比较影子寄存器,该寄存器不可直接访问。

下面来解读一下输入捕获部分的信号流程。首先CC1E要使能捕获,并且当输入捕获部分传来信号(也就是IC1PS上有信号传来)时才会发生捕获,将计数器的值捕获到影子寄存器中。并且发生捕获条件有三个。

  • IC1PS传来有效电平,CC1E使能捕获或软件产生捕获事件

  • CC1S配置为输入

    image-20240817010721963

  • CCR1未进行读操作

满足以上三点,我们才能在捕获预装载寄存器内读取到信号到来时的计数器值。

2.5.2 输出比较部分

image-20240817005859222

首先程序员写 CCR1 寄存器,即写入比较值。这个比较值需要转移到对应的捕获/比较影子寄存器后才会真正生效。

image-20240817010501170

什么条件下才能转移?图 21.1.9 中可以看到 compare_transfer 旁边的与门,需要满足三个条件:

  • CCR1 不在写入操作期间

  • CC1S[1:0] = 0 配置为输出

    image-20240817010721963

  • OC1PE 位置0(或者 OC1PE 位置 1,并且需要发生更新事件,这个更新事件可以软件产生或者硬件产生)

    image-20240817010738245

当 CCR1 寄存器的值转移到其影子寄存器后,新的值就会和计数器的值进行比较。

2.6 输出比较

image-20240817010943649

上图中,可以看到输出模式控制器,由 OC1M[2:0]位配置输出比较模式

image-20240817011034154

oc1ref 是输出参考信号,高电平有效,为高电平时称之为有效电平,为低电平时称之为无效电平。它的高低电平受到三个方面的影响:OC1M[3:0]位配置的输出比较模式、第5部分比较器的比较结果、还有就是 OC1CE 位配置的 ETRF 信号。ETRF 信号可以将 Oc1ref 电平强制清零,该信号来自 IO 外部。

image-20240817011804526

一般来说,当计数器的值和捕获/比较寄存器的值相等时,输出参考信号 oc1ref 的极性就会根据我们选择的输出比较模式而改变。如果开启了比较中断,还会发生比较中断。CC1P 位用于选择通道输出极性。CC1E 位置 1 使能通道输出。OC1 信号就会从 TIMx_CH1 输出到 IO 端口,再到 IO 外部。

image-20240817011415090

2.6.1 通用定时器PWM模式

通用定时器PWM输出实验配置步骤

  1. 配置定时器基础工作参数:HAL_TIM_PWM_Init()
  2. 定时器PWM输出MSP初始化:HAL_TIM_PWM_MspInit() 配置NVIC、CLOCK、GPIO等
  3. 配置PWM模式/比较值等:HAL_TIM_PWM_ConfigChannel()
  4. 使能输出并启动计数器:HAL_TIM_PWM_Start()
  5. 修改比较值控制占空比(可选):__HAL_TIM_SET_COMPARE()
  6. 使能通道预装载(可选):__HAL_TIM_ENABLE_OCxPRELOAD()
函数主要寄存器主要功能
HAL_TIM_PWM_Init()CR1、ARR、PSC初始化定时器基础参数
HAL_TIM_PWM_MspInit()存放NVIC、CLOCK、GPIO初始化代码
HAL_TIM_PWM_ConfigChannel()CCMRx、CCRx、CCER配置PWM模式、比较值、输出极性等
HAL_TIM_PWM_Start()CCER、CR1使能输出比较并启动计数器
__HAL_TIM_SET_COMPARE()CCRx修改比较值
__HAL_TIM_ENABLE_OCxPRELOAD()CCER使能通道预装载

关键结构体

typedef struct 
{ 
   uint32_t OCMode; 	  /* 输出比较模式选择 */
   uint32_t Pulse; 	            /* 设置比较值 */
   uint32_t OCPolarity;       /* 设置输出比较极性 */
   uint32_t OCNPolarity;    /* 设置互补输出比较极性 */
   uint32_t OCFastMode;   /* 使能或失能输出比较快速模式 */
   uint32_t OCIdleState;     /* 空闲状态下OC1输出 */
   uint32_t OCNIdleState;  /* 空闲状态下OC1N输出 */ 
} TIM_OC_InitTypeDef;

具体代码

timer.c

#include "./BSP/TIMER/timer.h"

TIM_HandleTypeDef g_timer_handle;
TIM_OC_InitTypeDef g_oc_timer_handle;
void timer_pwm_init(uint16_t psc,uint16_t arr)
{
    g_timer_handle.Instance = TIM3;
    g_timer_handle.Init.Prescaler = psc;
    g_timer_handle.Init.Period = arr;
    g_timer_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    HAL_TIM_PWM_Init(&g_timer_handle);
    
    g_oc_timer_handle.OCMode = TIM_OCMODE_PWM1;
    g_oc_timer_handle.Pulse = arr/2;
    g_oc_timer_handle.OCPolarity = TIM_OCPOLARITY_LOW;
    HAL_TIM_PWM_ConfigChannel(&g_timer_handle,&g_oc_timer_handle,TIM_CHANNEL_2);
    
    HAL_TIM_PWM_Start(&g_timer_handle,TIM_CHANNEL_2);

}
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim ->Instance == TIM3)
    {
        __HAL_RCC_GPIOB_CLK_ENABLE();
        __HAL_RCC_TIM3_IS_CLK_ENABLED();
        GPIO_InitTypeDef gpio_init_struct;
        gpio_init_struct.Pin = GPIO_PIN_5;                   
        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(GPIOB, &gpio_init_struct);   
        
        
        __HAL_RCC_AFIO_CLK_ENABLE();
        __HAL_AFIO_REMAP_TIM3_PARTIAL();
    }

}

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/TIMER/timer.h"

int main(void)
{
    uint16_t i = 0;
    HAL_Init();                                
    sys_stm32_clock_init(RCC_PLL_MUL9);         
    delay_init(72);                            
    led_init();                               
    timer_pwm_init(71,499);
    while(1)
    {
        
        for(i = 0; i < 500; i++)
        {
            __HAL_TIM_SET_COMPARE(&g_timer_handle, TIM_CHANNEL_2, i);
            delay_ms(10); 
        }
        for(i = 500; i > 0; i--)
        {
            __HAL_TIM_SET_COMPARE(&g_timer_handle, TIM_CHANNEL_2, i);
            delay_ms(10);  
        }

    }
}

其实呼吸灯做出来效果不是很好,手上也没有示波器0.0

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值