2

我目前正在将我的DCF77 库(您可以在 GitHub 上找到源代码)从 Arduino(基于 AVR)移植到 Arduino Due(ARM Cortex M3)。

该库需要精确的 1ms 时序。一个明显的候选者是使用 systicks。方便的是,Arduino Due 已经为 1 kHz 的 systicks 设置好了。

但是,我的(AVR)DCF77 库一旦锁定到 DCF77 就能够调整时序。这是通过像这样操作计时器重新加载值来完成的

void isr_handler() {
    cumulated_phase_deviation += adjust_pp16m;
    // 1 / 250 / 64000 = 1 / 16 000 000
    if (cumulated_phase_deviation >= 64000) {
        cumulated_phase_deviation -= 64000;
        // cumulated drift exceeds 1 timer step (4 microseconds)
        // drop one timer step to realign
        OCR2A = 248;
    } else if (cumulated_phase_deviation <= -64000) {
        // cumulated drift exceeds 1 timer step (4 microseconds)
        // insert one timer step to realign
        cumulated_phase_deviation += 64000;
        OCR2A = 250;
    } else {
        // 249 + 1 == 250 == 250 000 / 1000 =  (16 000 000 / 64) / 1000
        OCR2A = 249;
    }

    DCF77_Clock_Controller::process_1_kHz_tick_data(the_input_provider());
}

我想把它移植到 ARM 处理器上。在 ARM 信息中心,我找到了以下文档。

配置 SysTick

...

要配置 SysTick,您需要使用 SysTick 事件之间所需的间隔加载 SysTick Reload Value 寄存器。定时器中断或 COUNTFLAG 位(在 SysTick 控制和状态寄存器中)在从 1 到 0 的转换时被激活,因此它每 n+1 个时钟周期激活一次。如果需要 100 的周期,则应将 99 写入 SysTick 重载值寄存器。SysTick 重载值寄存器支持 1 到 0x00FFFFFF 之间的值。

如果您想使用 SysTick 以定时间隔(例如 1 毫秒)生成事件,您可以使用 SysTick 校准值寄存器来缩放您的重载寄存器值。SysTick 校准值寄存器是一个只读寄存器,在 TENMS 字段(位 0 到 23)中包含 10ms 周期内的脉冲数。该寄存器还有一个 SKEW 位 (30),用于指示由于时钟频率的微小变化,TENMS 部分中的 10ms 校准不完全是 10ms。第 31 位用于指示是否提供参考时钟。

...

不幸的是,我没有找到任何关于 SysTick->LOAD 和 SysTick->CALIB 是如何连接的。也就是说:如果我想限制或加速 systicks,我是否需要操纵 LOAD 或 CALIB 值?我需要将哪些值放入这些寄存器中?

搜索互联网并没有带来任何更好的提示。也许我在错误的地方搜索。这些问题有更详细的参考吗?或者甚至是一些很好的例子?

4

2 回答 2

2

AtMega328 数据表Cortex-M3 TRM进行比较,突出的一点是定时器的工作方式相反:在 AVR 上,您正在加载一个值OCR2A并等待定时器TCNT2计数到它,而在 M3 上您将延迟值加载到SYST_RVR中,然后系统将从该值倒计时0 中SYST_CVR

校准的最大区别是因为比较值固定为0并且您只能调整重新加载值,与直接调整比较值相比,您可能会有更多延迟(假设计数器重新加载与中断同时发生)生成)。

中的只读值SYST_CALIB(如果它确实存在,则由实现定义和可选)仅用于将 SYSTICK 滴答声与实际挂钟时间相关联 - 首次初始化计时器时,您需要知道滴答声频率才能选择为您所需的周期提供适当的重新加载值,因此有一个寄存器字段说“这么多参考时钟滴答发生在 10 毫秒(可能)”提供了一些在运行时以可移植方式计算它的可能性,而不必硬编码一个值这可能需要针对不同的设备进行更改。

然而,在这种情况下,不仅有一个更精确的外部时钟进行同步会使这变得不那么重要,而且至关重要的是,固件已经为您配置了计时器。因此,您可以假设其中的任何值都SYST_RVR表示足够接近 1KHz,并从那里开始工作 - 事实上,只需微调 1KHz 周期,您甚至不需要知道实际值是什么,只需执行SysTick->LOAD++SysTick->LOAD--如果错误在任一方向上变得太大。


再深入一点,SAM3X 数据表显示,对于该 SoC 中的特定 M3 实现,SYSTICK 有一个 10.5 MHz 的参考时钟,因此SYST_CALIB寄存器应该在 10 毫秒内给出 105000 个滴答的值。除非它没有,因为显然 Atmel 认为让明确命名的 TENMS 字段给出1ms的滴答计数,而不是 10500 是非常聪明的。精彩的。

于 2015-01-11T13:00:14.640 回答
1

只是因为其他人不必像必须做的那样四处挖掘 - 这是我另外发现的。

在 arduino-1.5.8/hardware/arduino/sam/system/CMSIS/CMSIS/Include/core_cm*.h 中有操作 SysTick 的代码。特别是在 core_cm3.h 中有一个函数

static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */

  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Cortex-M0 System Interrupts */
  SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
  return (0);                                                  /* Function successful */
}

然后arduino-1.5.8/hardware/arduino/sam/variants/arduino_due_x/variant.cpp在函数init中有

  // Set Systick to 1ms interval, common to all SAM3 variants
  if (SysTick_Config(SystemCoreClock / 1000))
  {
    // Capture error
    while (true);
  }

由于 SystemCoreClock 的计算结果为 84000000,因此它编译为SysTick_Config(84000). 我针对 DCF77 模块进行了验证,该模块SysTick_Config(84001)会减慢 SysTicks,同时SysTick_Config(83999)会加快它的速度。

于 2015-01-13T20:09:31.087 回答