在下面使用stopwatch_delay(4)
来实现约24纳秒的延迟。它使用了STM32的DWT_CYCCNT寄存器,该寄存器专门设计用于计算实际的时钟周期,位于地址0xE0001004。
为了验证延迟的准确性(参见main
),您可以调用STOPWATCH_START
,运行stopwatch_delay(ticks)
,然后调用STOPWATCH_STOP
并使用CalcNanosecondsFromStopwatch(m_nStart, m_nStop)
进行验证。根据需要调整ticks
。
uint32_t m_nStart; //DEBUG Stopwatch start cycle counter value
uint32_t m_nStop; //DEBUG Stopwatch stop cycle counter value
#define DEMCR_TRCENA 0x01000000
/* Core Debug registers */
#define DEMCR (*((volatile uint32_t *)0xE000EDFC))
#define DWT_CTRL (*(volatile uint32_t *)0xe0001000)
#define CYCCNTENA (1<<0)
#define DWT_CYCCNT ((volatile uint32_t *)0xE0001004)
#define CPU_CYCLES *DWT_CYCCNT
#define CLK_SPEED 168000000 // EXAMPLE for CortexM4, EDIT as needed
#define STOPWATCH_START { m_nStart = CPU_CYCLES;}
#define STOPWATCH_STOP { m_nStop = CPU_CYCLES;}
static inline void stopwatch_reset(void)
{
/* Enable DWT */
DEMCR |= DEMCR_TRCENA;
*DWT_CYCCNT = 0;
/* Enable CPU cycle counter */
DWT_CTRL |= CYCCNTENA;
}
static inline void stopwatch_delay(uint32_t ticks)
{
uint32_t end_ticks = ticks + CPU_CYCLES;
while(1)
{
if (CPU_CYCLES >= end_ticks)
break;
}
}
// WARNING: ONLY VALID FOR <25ms measurements due to scaling by 1000!
uint32_t CalcNanosecondsFromStopwatch(uint32_t nStart, uint32_t nStop)
{
uint32_t nDiffTicks;
uint32_t nSystemCoreTicksPerMicrosec;
// Convert (clk speed per sec) to (clk speed per microsec)
nSystemCoreTicksPerMicrosec = CLK_SPEED / 1000000;
// Elapsed ticks
nDiffTicks = nStop - nStart;
// Elapsed nanosec = 1000 * (ticks-elapsed / clock-ticks in a microsec)
return 1000 * nDiffTicks / nSystemCoreTicksPerMicrosec;
}
void main(void)
{
int timeDiff = 0;
stopwatch_reset();
// =============================================
// Example: use a delay, and measure how long it took
STOPWATCH_START;
stopwatch_delay(168000); // 168k ticks is 1ms for 168MHz core
STOPWATCH_STOP;
timeDiff = CalcNanosecondsFromStopwatch(m_nStart, m_nStop);
printf("My delay measured to be %d nanoseconds\n", timeDiff);
// =============================================
// Example: measure function duration in nanosec
STOPWATCH_START;
// run_my_function() => do something here
STOPWATCH_STOP;
timeDiff = CalcNanosecondsFromStopwatch(m_nStart, m_nStop);
printf("My function took %d nanoseconds\n", timeDiff);
}
main()
内部的循环中运行__no_operation()
一千次(例如,在run_my_function()
处),以获取1000次运行的纳秒秒表,然后只需将该数字除以1000即可查看在所讨论的系统上单个流水线__no_operation()
调用需要多长时间...然后按需使用。 - bunkerdiveCPU_CYCLES
是一个自由运行的计时器,当它接近0xFFFFFFFF
时,这将无法工作,因为end_ticks
会溢出,然后stopwatch_getticks() >= end_ticks
将立即退出。2)如果你让秒表运行超过了26ms
(如果我计算正确),那么1000 * nDiffTicks
将会溢出。3)不清楚为什么你的STOPWATCH_START
/STOPWATCH_STOP
宏没有使用DWT_CYCCNT
,因为它已经被定义了。4)对于一个通用的解决方案,stopwatch_reset
是一个坏主意,因为它会阻止你从多个中断中使用秒表。 - vgrua++;
可能可以实现(如果a位于堆栈中)。stopwatch_delay()
函数完美地为我完成了这个任务,可以验证或用于不同的延迟长度。 - bunkerdive你应该研究一下你的芯片中可用的FSMC外设。虽然配置可能会比较复杂,特别是如果你没有使用它设计用于的内存部件,但你可能会发现你的并行接口设备在某种程度上很适合与其中一种内存接口模式相匹配。
这类外部内存控制器必须具备一系列可配置的时序选项,以支持各种不同的内存芯片,因此你将能够确保数据手册所要求的时序。
能够这样做的一个好处是,你的LCD将会像任何普通的内存映射外设一样,将底层的接口细节抽象化。
nanosleep()
函数吗?注意:需要包含<time.h>
头文件才能使用它。 - user740316