为啥我不能同时使用 Systick 和 Timer1

Posted

技术标签:

【中文标题】为啥我不能同时使用 Systick 和 Timer1【英文标题】:Why can`t I use Systick and Timer1 simultaneously为什么我不能同时使用 Systick 和 Timer1 【发布时间】:2020-09-13 21:47:42 【问题描述】:

我正在使用 Systick 计时器来创建延迟,systick 的处理程序每​​微秒 (1 µs) 发生一次。

此外,我使用的是 TIM1,它的处理程序每​​秒发生一次(1 秒)。 在 timer1 处理程序中,我正在切换 LED。

在 while 循环内的主函数中,我正在切换另一个 LED(与 timer1 处理程序中的不同),这里的延迟函数使用 Systick。

timer1 处理程序按预期执行,但问题是主函数中的 while 循环从未执行。

有什么帮助吗?

volatile uint32_t i=0;

void TIM1_UP_TIM10_IRQHandler(void)

    NVIC_ClearPendingIRQ(TIM1_UP_TIM10_IRQn);   
    i ^= 1;
    if(i == 1)
        LED_On(0);
    else
        LED_Off(0);
    TIM1->SR &= ~(1 << 0); 


int main(void)

    NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 32);
    NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);

    LED_Initialize();
    RCC->APB2ENR |= (1 << 0); // Enable Timer1 clock

    TIM1->CR1 &= ~(1 << 4);  // Set the direction of timer to be upcounter
    TIM1->DIER |= (1 << 0);  // Enable tim1 interrupt request

    TIM1->PSC = 15999;  // each count takes 1 msec 
    TIM1->ARR = 1000;   //each 1 sec an overflow update event occurs
    TIM1->CR1 |= (1 << 0);

    SysTick_Init(16000000/1000000);

    while(1)
           
        LED_On(1);
        Delay_MS(5000);
        LED_Off(1);
        Delay_MS(5000);
    

    return 0;

【问题讨论】:

您是否正确地从中断中返回?你当然可以使用 systick 和 tim1(如果你的系统有一个 systick(和一个 tim1)一起使用。 你的uC频率是多少?你应该试试 SysTick_Init(16000000/1000);对于 16000 兆赫。另外,使用您的计时器,PSC=16000 和 ARR=1000,我每 1 秒计数一次。 您没有提供 SYSTICK 处理程序或 Delay_MS() 函数。这有点关键。 【参考方案1】:

    如果您的时钟为 16MHz,并且您希望每 16 个时钟发生一次中断,那么它现在可以工作了。在最佳停止情况下(代码从 RAM 运行等),您需要至少 11 个时钟来进入中断处理程序,并需要 6 个时钟来退出它,这超过了中断之间的时间。

    即使你跑得更快,每个人都被打断是个坏主意。 168MHz 设备在中断之间只有 168 个时钟。假设您的处理程序将运行 20 个时钟 + 11 + 6 = ~40clcks。这意味着25%的处理器时间将用于增加变量!!!不要做。在许多其他(最大时钟 72 或 80MHz)上,情况会更糟。

    如果您希望我们延迟执行类似的操作(如果您重新加载计数器,则需要将其考虑在内)。代码只是为了展示思路

#define TICKSPER_uS 80

void delay_us(uint32_t uS)

    uint32_t endCnt = SysTick -> VAL + uS * TICKSPER_uS;
    while(SysTick -> VAL < endCNT);
   

【讨论】:

【参考方案2】:

对于 SYSTICK 中断来说,一微秒的速度过快;尤其是在仅以 16MHz 运行时。您的系统可能几乎 100% 的时间都花在了中断处理程序计数滴答上。

即使它可以维持 1MHz 的中断率,如果 Delay_MS(5000) 是 5000 个 SYSTICK 周期的延迟,那么您将切换 LED 和 100Hz,并且不会感觉到任何闪烁,只是亮度变暗。

您似乎更可能打算一毫秒,这是一个更合理的滴答间隔:

SysTick_Init( 16000000 / 1000 ) ;

虽然我实际上会建议:

SysTick_Init( SystemCoreClock / 1000 ) ;

以便您的代码能够适应时钟频率的变化 - 因为 16MHz 是运行 STM32 的相当适中的频率。

在任何情况下,您的 SYSTICK 处理程序和Delay_MS() 实现也有可能出现错误,但如果没有看到该代码,就无法猜测。如果两者都提供了库代码,则可能性较小。

【讨论】:

【参考方案3】:

这是我的 usec 延迟函数

//==============================================================================
static  uint32_t    timeMicroSecDivider = 0;
extern  uint32_t    uwTick;

//==============================================================================
//  The SysTick->LOAD match the uC Speed / 1000.
//  If the uC clock is 80MHz, the the LOAD is 80000
//  The SysTick->VAL is the decrement counter from (LOAD-1) to 0
//==============================================================================
uint64_t getTimeMicroSec()

  if ( timeMicroSecDivider == 0)
  
    //  Number of clock by micro second
    timeMicroSecDivider = SysTick->LOAD  / 1000;
  
  return( (uwTick * 1000) + ((SysTick->LOAD - SysTick->VAL) / timeMicroSecDivider));


//==============================================================================
void delayTimeMicroSec(uint32_t delay)

  uint64_t tickstart = getTimeMicroSec();

  while ((getTimeMicroSec() - tickstart) < delay)
    ;

【讨论】:

以上是关于为啥我不能同时使用 Systick 和 Timer1的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能更改 Timer.scheduledTimer 函数中变量的值?

System.Threading.Timer:它为啥讨厌我?

为啥 System.Timers.Timer 能在 GC 中存活,而 System.Threading.Timer 不能?

stm32窗口看门狗中断服务函数为啥不能用延时函数

请问为啥这个程序在用JLINK调试的时候就只能到这了,中断SysTick_Handler怎么进去?

stm32 如何产生秒脉冲中断