基本定时器一启动就直接进中断的两种解决方式

发布时间:2024年01月19日

关于基本定时器的影子寄存器的解释

  • 预分频器和自动重装载寄存器都是有影子寄存器的(预加载寄存器和影子寄存器)。
    • 重装载的寄存器中的预加载寄存器是:只要确定了预加载寄存器,那么后续的值会先到预加载寄存器中,等待counter溢出了之后,才会加载到影子寄存器中直接给counter。如果没有勾选预加载寄存器的话,后续的值会直接到影子寄存器中,影子寄存器会实时同步到counter中去。有没有预加载寄存器就是有没有缓存,有的话,需要等待到溢出的时候,其中的值才会实时同步到。没有话,只要改变了重装载中的值,那么就会实时同步到counter。——我们默认是没有的,如果寄存器ARPE是0的话(默认为0),那么就是没有缓存,同步更新;如果这个寄存器是1的话,那么就是有缓存,就不能同步更新。
    • 而预分频器中也是有影子寄存器: 预分频器值寄存器TIMx_PSC存在影子寄存器,所以在定时器启动后更改TIMx_PSC的值并不会立即影响当前定时器的时钟频率。要等到下一个更新事件(UEV)发生时才会生效。 总之需要记住,预分频器自动有缓存的功能,只有经过一次更新之后,其频率才能进入到影子寄存器中,然后输出出去,其和上述的重装载寄存器类似,也有一个预分频的寄存器当做是缓存,而这个缓存是一定存在的,是没有控制寄存器的位数来控制的。

关于基本定时器中两个控制寄存器位的用法以及基本定时器一启动就直接进中断的两种解决方式

  • 在认识这两种寄存器之前呢,我们不得不有一个背景知识的了解:当我们的counter溢出的时候,会产生一次更新,这个更新是万能的,不仅可以触发中断,触发DMA输出(没学到),还可以对寄存器进行一次更新,因为这个更新的本质是溢出,溢出代表一个新的轮回的开始。不管开了中断与否,中断标志位会被置位为1,触发中断,自动重装载寄存器中的预装载寄存器中的值会自动更新到影子寄存器,同步更新到counter中(前提是开启了预装载缓存),预分频器中的缓存也会发送到影子寄存器中进行一个输出(这里的缓存是不可以更更改的。)
  • 接下来介绍的这两个寄存器和上述的更新是有关系的
    • 事件产生寄存器(EGR)中的控制位UG——一般由软件产生的叫做事件。事件是什么呢?事件可以理解为是一种另类的更新,这个更新和上述将的溢出的更新是一样的,这个是一种人为的更新。那这个有什么用呢?我们知道在自动重装载寄存器中,如果不开缓存的话,其写入寄存器中的数据会直接同步给counter的,但是预分频器可没有这样的功能,其中的缓存是固定的,不可以开启和取消,也就是说当你配置预分频器的时候,你配置的值会自动进入到缓存之中,而不会输出出去,这就导致了什么呢?这就导致输出给counter的频率还是原来的频率即72Mhz,而不是设置的10khz,这样的话counter里面的数值会以比以前7200倍的速度运行,那么其中的延时就不再准确了,counter里面的数据就是很快溢出,进而很快标志中断位,进而很快执行中断服务函数,这就是为什么基本定时器一启动就给人一种直接进入中断的感觉。而其中的UG就可以解决这个问题,这个问题根源在于分频的频率没有顺利输出,而存在于缓存当中,在上述解释(上述的解释均来自于手册)中我们可以知道,设置了UG之后,会自动产生一个更新,有了这次更新之后,预分频器中的缓存就能同步进影子寄存器然后直接输出了。这样的话,预分频器和重装载器中的数值就是我们所需要的了。不过等到执行的时候,又发现,一启动就直接进入中断了,这是为什么呢?原来啊,你软件完成了一次更新之后,你的中断标志位变成1了,这样的话,你一开启中断又马上进入中断了,根本来不及counter溢出了,就执行了一次中断,这就是原因。怎么解决呢?在执行外UG之后,在手动将中断标志位置为0就好了,就关闭中断了。
    • CR1控制寄存器中控制位URS(更新请求源),这个有什么用呢?在上述的解决办法中,设置UG(update generation)控制的位是必须的,但随之而来的副作用是其中的中断标志位会自动的置为1,这个是手动更新导致的。而这个URS刚好可以改变更新触发的其他的变化,在手册中,当URS为1的时候,只有溢出才能触发中断,也就是说当你UG为1的时候,就不会触发中断了。
    • 额外的感悟:原来将UG控制位激活之后,产生了一次更新,而这个更新之所以能够控制中断并不是由其本身自己这个寄存器位来决定的,而是由其他寄存器决定的。有些寄存器的位数在没有启用的时候,其默认值一般都是0,但即便是默认值也是有相应的功能。比如上述两个寄存器中控制位的关系。
    • 其中的UG的功能是产生一个更新,而这个更新只能是更新寄存器,而不能产生中断。产生中断的功能从哪里来呢?就从URS这个控制位上来,如果我们不管这个控制位的话,那么其默认就是0,那么其中就已经说明了UG是可以产生中断的,如果将这个URS变成1的话,那么就不可以产生中断了。

两种解决办法的相关代码实现

void Dri_TIM6_Init()
{
    /*打开定时器的时钟*/
    RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
    /*配置定时器的重装载*/
    TIM6->ARR = AUTO - 1;

    /*配置定时器的预分频*/
    TIM6->PSC = 7200 - 1;

    printf("begin!\r\n");

    /*手动产生一次更新*/
    TIM6->EGR |= TIM_EGR_UG;

    /*关闭由于更新产生的中断位*/
    TIM6->SR &= ~TIM_SR_UIF;

    /*配置计数器的使能*/
    TIM6->CR1 |= TIM_CR1_CEN;

    /*NVIC的部分*/
    /*配置中断优先级组*/
    NVIC_SetPriorityGrouping(3);

    /*配置的中断优先级*/
    NVIC_SetPriority(TIM6_IRQn, 1);

    /*配置所有的中断*/
    NVIC_EnableIRQ(TIM6_IRQn);

    /*配置中断反馈使能*/
    TIM6->DIER |= TIM_DIER_UIE;
}

void TIM6_IRQHandler(void)
{
    TIM6->SR &= ~TIM_SR_UIF;
    Int_LED_Toggle(LED2);
    printf("1s\r\n");
}
void Dri_TIM6_Init()
{
    /*打开定时器的时钟*/
    RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
    /*配置定时器的重装载*/
    TIM6->ARR = AUTO - 1;

    /*配置定时器的预分频*/
    TIM6->PSC = 7200 - 1;

    printf("begin!\r\n");

    /*首先关闭UG会更新中断的功能*/
    TIM6->CR1 |= TIM_CR1_URS;

    /*手动产生一次更新*/
    TIM6->EGR |= TIM_EGR_UG;

    /*配置计数器的使能*/
    TIM6->CR1 |= TIM_CR1_CEN;

    /*NVIC的部分*/
    /*配置中断优先级组*/
    NVIC_SetPriorityGrouping(3);

    /*配置的中断优先级*/
    NVIC_SetPriority(TIM6_IRQn, 1);

    /*配置所有的中断*/
    NVIC_EnableIRQ(TIM6_IRQn);

    /*配置中断反馈使能*/
    TIM6->DIER |= TIM_DIER_UIE;
}

void TIM6_IRQHandler(void)
{
    TIM6->SR &= ~TIM_SR_UIF;
    Int_LED_Toggle(LED2);
    printf("1s\r\n");
}

文章来源:https://blog.csdn.net/Caesar2334/article/details/135678651
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。