STM32F7普通定时器的使用(定时+中断+PWM)

发布者:楼高峰最新更新时间:2022-07-08 来源: csdn关键字:STM32F7  定时器  中断  PWM 手机看文章 扫描二维码
随时随地手机看文章

基本计时功能

最简单的,定时器嘛,基本的定时器就是定时功能,简单来说就是TIMx->CNT会跟随着输入时钟的脉冲而计数。


初始化定时器的参数,大家都好理解,因为TIM2的输入时钟是108Mhz,这里进行10800分频,输入频率为10K,重装载值设置为20K,每2秒溢出一次。


在HAL_TIM_Base_Init的执行过程中,会先调用HAL_TIM_Base_MspInit再进行其他参数的配置,即先开时钟。


TIM_HandleTypeDef TIM2_Handler;

static void MX_TIM2_Init(void)

{

    TIM2_Handler.Instance = TIM2;

    TIM2_Handler.Init.Prescaler = 10800;

    TIM2_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;

    TIM2_Handler.Init.Period = 20000;

    TIM2_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;


    HAL_TIM_Base_Init(&TIM2_Handler);

HAL_TIM_Base_Start(&TIM2_Handler);

}


void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)

{

__HAL_RCC_TIM2_CLK_ENABLE();            //使能TIM3时钟  

}


主函数中每秒打印一次定时器的值:


   while (1)

    {

printf("cnt:%drn",TIM2->CNT);

        delay_ms(1000);

    }


显示效果如下:

在这里插入图片描述

定时器中断

通过HAL_TIM_Base_Start可以开启基本计时功能,但要实现定时器中断功能,就需要开启相应的标志位,即使用HAL_TIM_Base_Start_IT进行定时的开启;

在配置定时器之前,除了要开启时钟,还需要先设置中断优先级,和使能中断向量;

在定时器中断TIM2_IRQHandler服务函数中调用HAL库提供的定时器中断处理函数HAL_TIM_IRQHandler,解析到的定时器超时中断会自动跳转到HAL_TIM_PeriodElapsedCallback;


TIM_HandleTypeDef TIM2_Handler;

static void MX_TIM2_Init(void)

{

    TIM2_Handler.Instance = TIM2;

    TIM2_Handler.Init.Prescaler = 10800;

    TIM2_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;

    TIM2_Handler.Init.Period = 20000;

    TIM2_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;


    HAL_TIM_Base_Init(&TIM2_Handler);

    //HAL_TIM_Base_Start(&TIM2_Handler);

HAL_TIM_Base_Start_IT(&TIM2_Handler);

}


void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)

{

    __HAL_RCC_TIM2_CLK_ENABLE();            //使能TIM2时钟

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

    HAL_NVIC_EnableIRQ(TIM2_IRQn);          //开启ITM2中断

}


void TIM2_IRQHandler(void)

{

    HAL_TIM_IRQHandler(&TIM2_Handler);

}


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

{

    if (htim == (&TIM2_Handler))

    {

        printf("enter irqrn");

    }

}


显示效果如下:

在这里插入图片描述

PWM输出

硬件PWM输出是不需要使用定时器中断的,但同样需要基本的定时参数配置,初始化也不再是使用HAL_TIM_Base_Init而是使用HAL_TIM_PWM_Init进行初始化了;

配置完定时器为PWM模式,那么相应的输出通道也需要通过HAL_TIM_PWM_ConfigChannel进行配置;

同样的启动也不是HAL_TIM_Base_Start或者HAL_TIM_Base_Start_IT了,而是HAL_TIM_PWM_Start了;


TIM_HandleTypeDef TIM2_Handler;

TIM_OC_InitTypeDef TIM2_CH2Handler;

static void MX_TIM2_Init(void)

{

    TIM2_Handler.Instance = TIM2;

    TIM2_Handler.Init.Prescaler = 10800;

    TIM2_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;

    TIM2_Handler.Init.Period = 20000;

    TIM2_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

    HAL_TIM_PWM_Init(&TIM2_Handler);


    TIM2_CH2Handler.OCMode = TIM_OCMODE_PWM1;

    TIM2_CH2Handler.Pulse = 10000;

    TIM2_CH2Handler.OCPolarity = TIM_OCPOLARITY_HIGH;

    TIM2_CH2Handler.OCFastMode = TIM_OCFAST_DISABLE;

    HAL_TIM_PWM_ConfigChannel(&TIM2_Handler, &TIM2_CH2Handler, TIM_CHANNEL_2) ;


    HAL_TIM_PWM_Start(&TIM2_Handler, TIM_CHANNEL_2); //开启PWM通道2

}


void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)

{

    GPIO_InitTypeDef GPIO_InitStruct = {0};

__HAL_RCC_TIM2_CLK_ENABLE();           

    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_1;

    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

    GPIO_InitStruct.Pull = GPIO_NOPULL;

    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

    GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;

    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

}


通过万用表可以发现PA1口的电压在0和3.3V中大概每秒变动一次(疫情期间在家学习,没有示波器真是太惨了……为了确定确实有PWM波形,只能出此下策了T_T),串口打印数据如下:

在这里插入图片描述

PWM + 定时器溢出中断

我们都知道,PWM模式是在比较值处翻转,在溢出的时候再次翻转,而溢出的时候,我们也是可以产生中断的;所以同一个定时器,在这种定时时长和周期相同的时候,是可以即做硬件PWM输出,又做溢出中断的。

这里我们观察一下两种初始化函数:


/**

  * @brief  Initializes the TIM Time base Unit according to the specified

  *         parameters in the TIM_HandleTypeDef and create the associated handle.

  * @param  htim: pointer to a TIM_HandleTypeDef structure that contains

  *                the configuration information for TIM module.

  * @retval HAL status

  */

HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim)

  /* Check the TIM handle allocation */

  if(htim == NULL)

  {

    return HAL_ERROR;

  }

  

  /* Check the parameters */

  assert_param(IS_TIM_INSTANCE(htim->Instance)); 

  assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));

  assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));

  

  if(htim->State == HAL_TIM_STATE_RESET)

  {  

    /* Init the low level hardware : GPIO, CLOCK, NVIC */

    HAL_TIM_Base_MspInit(htim);

  }

  

  /* Set the TIM state */

  htim->State= HAL_TIM_STATE_BUSY;

  

  /* Set the Time Base configuration */

  TIM_Base_SetConfig(htim->Instance, &htim->Init); 

  

  /* Initialize the TIM state*/

  htim->State= HAL_TIM_STATE_READY;

  

  return HAL_OK;

}



HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim)

{

  /* Check the TIM handle allocation */

  if(htim == NULL)

  {

    return HAL_ERROR;

  }


  /* Check the parameters */

  assert_param(IS_TIM_INSTANCE(htim->Instance));

  assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));

  assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));


  if(htim->State == HAL_TIM_STATE_RESET)

  {

    /* Allocate lock resource and initialize it */

    htim->Lock = HAL_UNLOCKED;  

    /* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */

    HAL_TIM_PWM_MspInit(htim);

  }


  /* Set the TIM state */

  htim->State= HAL_TIM_STATE_BUSY;  

  

  /* Init the base time for the PWM */  

  TIM_Base_SetConfig(htim->Instance, &htim->Init); 

   

  /* Initialize the TIM state*/

  htim->State= HAL_TIM_STATE_READY;

  

  return HAL_OK;

}  


两者除了底层的HAL_TIM_Base_MspInit和HAL_TIM_PWM_MspInit,其他地方一模一样,所以我们随便调用一个初始化就可以了,但是要注意,我们在HAL_TIM_Base_MspInit里配置了NVIC,在HAL_TIM_PWM_MspInit里配置了GPIO,所以只调用一个的话,必须手动把底层的粘贴到另一个函数里去,如果是想两个都调用一次,这里就需要考虑htim->State这个变量了,因为在调用了一次之后,其值就从HAL_TIM_STATE_RESET变成了HAL_TIM_STATE_READY,后面的一个就得不到执行了。

再来看启动代码:


/**

  * @brief  Starts the TIM Base generation.

  * @param  htim: pointer to a TIM_HandleTypeDef structure that contains

  *                the configuration information for TIM module.

  * @retval HAL status

  */

HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim)

{

  /* Check the parameters */

  assert_param(IS_TIM_INSTANCE(htim->Instance));

  

  /* Set the TIM state */

  htim->State= HAL_TIM_STATE_BUSY;

  

  /* Enable the Peripheral */

  __HAL_TIM_ENABLE(htim);

  

  /* Change the TIM state*/

  htim->State= HAL_TIM_STATE_READY;

  

  /* Return function status */

  return HAL_OK;

}


/**

  * @brief  Starts the TIM Base generation in interrupt mode.

  * @param  htim: pointer to a TIM_HandleTypeDef structure that contains

  *                the configuration information for TIM module.

  * @retval HAL status

  */

HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)

{

  /* Check the parameters */

  assert_param(IS_TIM_INSTANCE(htim->Instance));

  

  /* Enable the TIM Update interrupt */

  __HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);

      

  /* Enable the Peripheral */

  __HAL_TIM_ENABLE(htim);

      

  /* Return function status */

  return HAL_OK;

}


/**

  * @brief  Starts the PWM signal generation.

  * @param  htim: pointer to a TIM_HandleTypeDef structure that contains

  *                the configuration information for TIM module.

  * @param  Channel: TIM Channels to be enabled.

  *          This parameter can be one of the following values:

  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected

  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected

  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected

  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected

  * @retval HAL status

  */

HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)

{

  /* Check the parameters */

  assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));


  /* Enable the Capture compare channel */

  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);

  

  if(IS_TIM_ADVANCED_INSTANCE(htim->Instance) != RESET)  

  {

    /* Enable the main output */

    __HAL_TIM_MOE_ENABLE(htim);

  }

    

  /* Enable the Peripheral */

  __HAL_TIM_ENABLE(htim);

  

  /* Return function status */

  return HAL_OK;


其中的核心代码分别是:


 __HAL_TIM_ENABLE(htim);


  __HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);

  __HAL_TIM_ENABLE(htim);


  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);

  __HAL_TIM_ENABLE(htim);


那么我们可以不用hal库提供给我们的启动函数,直接写三句话,就实现了定时器中断+PWM功能了。


  __HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);

[1] [2]
关键字:STM32F7  定时器  中断  PWM 引用地址:STM32F7普通定时器的使用(定时+中断+PWM)

上一篇:STM32F7实现ADC等周期采集(定时器触发+DMA传输)采集完成后的中断设置
下一篇:CubeMX 5.5 修改HAL库库函数版本

推荐阅读最新更新时间:2024-11-20 11:48

pwm控制的基本原理 pwm是怎么驱动电机的
pwm控制的基本原理 PWM(脉宽调制)是一种常用的控制方法,通过调节信号的脉冲宽度来控制电路中的设备或器件的工作状态。其基本原理如下: 1. 脉冲周期:PWM信号由固定的脉冲周期组成,表示一个完整的周期的时间长度。周期通常是固定的,并由控制系统确定。 2. 脉冲宽度:信号中每个脉冲的宽度代表了设备或器件的工作时间,通常通过改变脉冲宽度来控制设备的输出。 3. 调制比例:PWM信号的调制比例表示脉冲宽度与脉冲周期之间的比例关系,通常以百分比来表示。调制比例决定了设备工作的平均功率或输出量。 4. 控制电路:PWM信号的生成是通过控制电路实现的,控制电路可以根据需要调整脉冲宽度,以控制设备的输出。 工作原理示意图可以描述为:通过控制
[嵌入式]
pic16f877a的电平变化中断(interrupt on change)使用误区
在PORTB的RB4:RB7是电平变化中断(interrupt on change),要使用它,将它们引脚对应于TRISB的位设定为输入是必须的,然后配置中断。 工作原理:RB4:RB7是电平变化中断的引脚的输入状态和上一次读取PORTB的锁存器的值进行比较,当两组数据相异时,发生中断同,当这四个引脚的一部分发生电平变化时 ,将会最后取或,只要有一个引脚的输入状态发生变化,就会发生中断改变。 注意:在进入中断服务子程序时,一定要读取或写入PORTB锁存器的值,这样才会结束两组数据的比较,然后再去清除中断标志位。如果没有读取PORTB的值,两组数据的比较一直存在,先清除中断标志(RBIF)是没有用的,RBIF也
[单片机]
STM32CubeMx配置定时器的编码器模式
上次文章写了编码器是如何工作的,今天就来用STM32F103C8T6的TIM3的通道1跟通道2编写一个编码器识别程序。 编程思路: A相:TIM3_CH1 B相:TIM3_CH2 SWITCH:PB5(外部中断的方式) 实现效果: 编码器顺时针旋转编码器计数值 +1 编码器逆时针旋转编码器计数值 -1 按下SWITCH编码器计数值值清 0 话不多说,上教程! 1.配置时钟 选择外部高速时钟源HSE 2.配置SWITCH管脚为外部中断模式(默认上拉) 选择中断模式触发下降沿有效 默认是上拉输入 添加用户标签为SWITCH 使能NVIC配置 3.配置定时器管脚为编码器模式 选择编码器模式 分频值
[单片机]
STM32CubeMx配置<font color='red'>定时器</font>的编码器模式
嵌入式学习笔记10——51单片机之中断定时器中断
1. CPU时序的有关知识 (1)振荡周期:为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期)。 (2)状态周期:2个振荡周期为1个状态周期,用S表示。振荡周期又称S周期或时钟周期。 (3)机器周期:1个机器周期含6个状态周期,12个振荡周期(使用计时器时,每隔一个机器周期计时器加1)。 (4)指令周期:完成1条指令所占用的全部时间,它以机器周期为单位。 2. 学习定时器前须明白: (1)51单片机有2组定时器/计数器(52单片机有三组),可定时或计数。 (2)定时器/计数器和单片机的CPU是相互独立的,定时器/计数器工作的过程是自动完成的,不需要CPU的参与。 (3)51单片机中的定时器/计
[单片机]
嵌入式学习笔记10——51单片机之<font color='red'>中断</font><font color='red'>定时器</font><font color='red'>中断</font>
基于s3c2410软中断服务的uC/OS-II任务切换
1.关于软中断指令 软件中断指令(SWI)可以产生一个软件中断异常,这为应用程序调用系统例程提供了一种机制。 语法: SWI { cond } SWI_number SWI执行后的寄存器变化: lr_svc = SWI指令后面的指令地址 spsr_svc = cpsr pc = vectors + 0x08 cpsr模式 = SVC cpsr I = 1(屏蔽IRQ中断) 处理器执行SWI指令时,设置程序计数器pc为向量表的0x08偏移处,同事强制切换处理器模式到SVC模式,以便操作系统例程可以在特权模式下被调用。 每个SWI指令有一个关联的SWI号(number)
[单片机]
STM32定时器触发ADC的时序话题
在STM32芯片的ADC应用中,我们往往会利用定时器来触发ADC的启动转换,而能够触发ADC转换的定时器事件往往有多个,有时我们可能很关注这些定时器事件在触发ADC时有哪些时序上的差别。下面以STM32G4芯片为例,来大致聊聊该话题。 这里选择TIM1来触发ADC。我们从手册或CubeMx配置界面不难看到可用来触发ADC的定时器事件可以是定时器TRGO信号和通道CC事件/信号,而TRGO可能来自定时器的使能动作、定时器的更新事件、通道输出比较事件、通道比较输出参考信号以及编码时钟。 这里先重点就Update事件、通道OCx信号和OCxREF信号作为TRGO来触发ADC,看看相应的触发时间点在哪里。 另外,作为ADC的外部触
[单片机]
STM32<font color='red'>定时器</font>触发ADC的时序话题
stm32f407之高级定时器 死区互补PWM(操作寄存器)
高级定时器的功能有很多,这里只介绍死区互补PWM输出功能。其实死区互补PWM跟通用定时器的PWM配置差异不大,只是需要设置多CCER寄存器中的几个位以及刹车与死区寄存器BDTR。对寄存器的具体描述请参考数据手册。 配置步骤: 1. 使能相关时钟。 2. 配置相关的引脚模式、速度、以及复用功能。 3. 使能TIM时钟 4. 设置分频。 5. 设置周期。(ARR的值)周期=(PSC+1)* ARR / TIMx时钟 6. 产生一次更新事件,更新影子寄存器的值。 7. 在CCMR中设置PWM模式。 8. 设置各通道占空比。占空比= CCRx / ARR。 9. 使能比较输出。 10. 启动预装载。
[单片机]
基于PWM开关变压器的动态电压恢复器研究
1 引言     随着科学技术的进步及工业水平的提高,出现了越来越多的敏感负载如半导体生产、银行系统、自动化生产线、精密数控机床、铁路交通系统等,它们对电能质量及供电可靠性提出了更高的要求。电压跌落对敏感负载的影响最为严重。为解决这些电能质量问题,人们提出了DVR。     目前大多DVR都是基于逆变器加串联变压器拓扑,这种拓扑控制复杂,带有直流储能环节,增加了系统成本,其补偿能力受到储能装置容量的限制。文献首次提出了基于交流斩波器的DVR拓扑,进行了原理分析和实验验证,但其仅能补偿电压凹陷而不能补偿电压凸起,且补偿速度也不够。文献提出了双交流Buck的拓扑实现倒相,但其要利用8个IGBT,增加了系统的成本且控制复杂。在此提出了基于
[电源管理]
基于<font color='red'>PWM</font>开关变压器的动态电压恢复器研究
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved