高精度延时函数rt_hw_us_delay在CH32V203C8T6的实现
但其实有SysTick的单片机的用法都差不多
参考:
RT-Thread文档中心-内核-时钟管理-高精度延时
[001] [RT-Thread 学习笔记] 高精度延时函数 rt_hw_us_delay 的陷阱与优化
官方文档给了一个例程,但是这里的SysTick是递减方式的
该函数通过一个 tcnt 变量将当前计数值 tnow 与上一时刻的计数值 told 的差值进行累加(注意 SysTick->VAL
为递减计数器),当累加值 tcnt≥延时节拍 ticks 时跳出循环,而 tcnt 最大值为 0xffff
ffff,不可能出现死循环的情况。
#include <board.h>
void rt_hw_us_delay(rt_uint32_t us)
{
rt_uint32_t ticks;
rt_uint32_t told, tnow, tcnt = 0;
rt_uint32_t reload = SysTick->LOAD;
/* 获得延时经过的 tick 数 */
ticks = us * reload / (1000000 / RT_TICK_PER_SECOND);
/* 获得当前时间 */
told = SysTick->VAL;
while (1)
{
/* 循环获得当前时间,直到达到指定的时间后退出循环 */
tnow = SysTick->VAL;
if (tnow != told)
{
if (tnow < told)
{
tcnt += told - tnow;
}
else
{
tcnt += reload - tnow + told;
}
told = tnow;
if (tcnt >= ticks)
{
break;
}
}
}
}
这里我用的CH32V203C8T6,使用了WCH官方EVT内的例程,其中SysTick默认配置成为了递增计数器
extern uint32_t SystemCoreClock;
static uint32_t _SysTick_Config(rt_uint32_t ticks)
{
NVIC_SetPriority(SysTicK_IRQn, 0xf0);
NVIC_SetPriority(Software_IRQn, 0xf0);
NVIC_EnableIRQ(SysTicK_IRQn);
NVIC_EnableIRQ(Software_IRQn);
SysTick->CTLR = 0;
SysTick->SR = 0;
SysTick->CNT = 0;
SysTick->CMP = ticks - 1;
SysTick->CTLR = 0xF; // 向上计数
return 0;
}
系统计数控制寄存器(STK_CTLR)描述如下:
RTT官方文档的例程的SysTick是递减方式的,这里如果不想更改SysTick初始化的话,就修改一下rt_hw_us_delay内的计算:
void rt_hw_us_delay(rt_uint32_t us)
{
rt_uint32_t ticks;
rt_uint32_t told, tnow, tcnt = 0;
rt_uint32_t reload = SysTick->CMP;
/* 获得延时经过的 tick 数 */
ticks = us * reload / (1000000 / RT_TICK_PER_SECOND);
/* 获得当前时间 */
told = SysTick->CNT;
while (1)
{
/* 循环获得当前时间,直到达到指定的时间后退出循环 */
tnow = SysTick->CNT;
if (tnow != told)
{
if (tnow < told)
{
// 取决于 SysTick->CNT 递增或递减,这里是递增的
// 通过一个 tcnt 变量将当前计数值 tnow 与上一时刻的计数值 told 的差值进行累加(注意 SysTick->VAL 为递减还是递增计数器)
// ,当累加值 tcnt≥延时节拍 ticks 时跳出循环,而 tcnt 最大值为 0xffff ffff,不可能出现死循环的情况
// tcnt += told - tnow;
tcnt += reload - told + tnow;
}
else
{
// tcnt += reload - tnow + told;
tcnt += tnow - told;
}
told = tnow;
if (tcnt >= ticks)
{
break;
}
}
}
}
用一个IO引脚切换电平测试下修改后的rt_hw_us_delay()函数
static void test_us(void)
{
BUZZER_ON;
rt_hw_us_delay(900);
BUZZER_OFF;
}
MSH_CMD_EXPORT(test_us, test_us);
逻辑分析仪实测如下:
但其实只把SysTick初始化为递减计数器可能会更方便点,这样用RTT官方文档例程那种方式就可以了:
static uint32_t _SysTick_Config(rt_uint32_t ticks)
{
NVIC_SetPriority(SysTicK_IRQn, 0xf0);
NVIC_SetPriority(Software_IRQn, 0xf0);
NVIC_EnableIRQ(SysTicK_IRQn);
NVIC_EnableIRQ(Software_IRQn);
SysTick->CTLR = 0;
SysTick->SR = 0;
SysTick->CNT = 0;
SysTick->CMP = ticks - 1;
// SysTick->CTLR = 0xF; // 向上计数
SysTick->CTLR = 0x001F; // 向下计数 0---- 1 1111
return 0;
}