我目前正在将我的DCF77 library(您可能会发现source code at GitHub)从Arduino(基于AVR)移植到Arduino Due(ARM Cortex M3)。
图书馆需要精确的1ms时间。一个明显的候选人是使用systicks。相应地,Arduino Due已经设置为1 kHz的喷杆。
然而,我的(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信息中心,我发现了以下documentation.
配置SysTick
...
要配置SysTick,您需要加载SysTick重新加载值 使用SysTick事件之间所需的间隔进行注册。计时器 中断或COUNTFLAG位(在SysTick控制和状态中) 寄存器)在从1到0的转换时被激活,因此它 激活每n + 1个时钟周期。如果需要100的期限99 应写入SysTick重载值寄存器。 SysTick 重载值寄存器支持1到0x00FFFFFF之间的值。
如果您想使用SysTick在定时生成事件 间隔,例如1ms,您可以使用SysTick校准值 注册以扩展Reload寄存器的值。 SysTick 校准值寄存器是一个只读寄存器,包含 TENMS字段中的脉冲数,持续10ms的时间段(位0到 23)。该寄存器还有一个SKEW位(30),用于指示 TENMS部分中10ms的校准不完全是10ms 由于时钟频率的微小变化。位31用于指示 如果提供了参考时钟。
...
不幸的是,我没有找到关于如何连接SysTick-&gt; LOAD和SysTick-&gt; CALIB的任何内容。那就是:如果我想限制或加速喷杆,我是否需要操纵LOAD或CALIB值?我需要将哪些值放入这些寄存器中?
搜索互联网没有提出任何更好的提示。也许我在错误的地方寻找。 有没有更详细的参考这些问题?或者甚至可能是一些很好的例子?
答案 0 :(得分:2)
将AtMega328 datasheet与Cortex-M3 TRM进行比较,突出的一点是定时器的工作方式相反:在AVR上,您正在将值加载到OCR2A
并等待TCNT2
中的计时器计数到达,而在M3上,您将延迟值加载到SYST_RVR
,然后系统将 down 从此值计算为{{} 1}}。
校准的最大区别在于因为比较值固定为0并且您只能调整重载值,与直接调整比较值相比,您可能会有更多延迟(假设计数器重新加载发生在同一位置)生成中断的时间。)
SYST_CVR
中的只读值(如果确实存在,实现定义和可选),仅用于将SYSTICK刻度与实际挂钟时间相关联 - 首次初始化定时器时,您需要知道勾选频率,以便为您所需的时间段选择适当的重载值,因此具有一个寄存器字段,表示“这个很多参考时钟滴答发生在10ms(可能)”提供了一些在运行时以便携方式计算的可能性,而不是而不必硬编码可能需要更改不同设备的值。
然而,在这种情况下,不仅具有更准确的外部时钟来进行同步,这使得这一点变得不那么重要,但至关重要的是,固件已经为您配置了计时器。因此,您可以假设SYST_CALIB
中的任何值代表足够接近1KHz,并从那里开始工作 - 实际上只需微调1KHz周期,您甚至不需要知道实际值是多少如果错误在任何一个方向上都太大,那么只需SYST_RVR
或SysTick->LOAD++
。
进一步深入,SAM3X datasheet表明对于该SoC中的特定M3实现,SYSTICK具有10.5 MHz参考时钟,因此SysTick->LOAD--
寄存器应该给出值为105000个刻度,持续10ms。除非它没有,因为显然Atmel认为使明确命名的TENMS字段给出 1ms ,10500的滴答计数是非常聪明的。精彩。
答案 1 :(得分: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
的{{1}}中有
init
由于SystemCoreClock的计算结果为84000000,因此它的编译类似于 // Set Systick to 1ms interval, common to all SAM3 variants
if (SysTick_Config(SystemCoreClock / 1000))
{
// Capture error
while (true);
}
。我针对DCF77模块进行了验证,SysTick_Config(84000)
会降低SysTicks的速度,SysTick_Config(84001)
会加快速度。{1}}