From 60946523c795436e41f41bc772967cea20e0d407 Mon Sep 17 00:00:00 2001 From: Meco Man <920369182@qq.com> Date: Wed, 4 May 2022 14:47:15 -0400 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=A1=AC=E4=BB=B6=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E5=99=A8=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../device/hwtimer/hwtimer.md | 66 ++++++++----------- .../programming-manual/thread/thread.md | 2 +- 2 files changed, 27 insertions(+), 41 deletions(-) diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/hwtimer/hwtimer.md b/rt-thread-version/rt-thread-standard/programming-manual/device/hwtimer/hwtimer.md index 68babe7..172a970 100644 --- a/rt-thread-version/rt-thread-standard/programming-manual/device/hwtimer/hwtimer.md +++ b/rt-thread-version/rt-thread-standard/programming-manual/device/hwtimer/hwtimer.md @@ -13,6 +13,8 @@ **计数频率:**定时器模式时,计数器单位时间内的计数次数,由于系统时钟频率是定值,所以可以根据计数器的计数值计算出定时时间,定时时间 = 计数值 / 计数频率。例如计数频率为 1MHz,计数器计数一次的时间则为 1 / 1000000, 也就是每经过 1 微秒计数器加一(或减一),此时 16 位计数器的最大定时能力为 65535 微秒,即 65.535 毫秒。 +本定时器设备框架内部会自动处理硬件定时器超时的问题,例如16位定时器在1MHz的频率下最大只能维持65.535ms。但是在本定时器框架下,用户可以将定时器的溢出时间设置为例如500ms,框架内部会自动处理硬件溢出问题。当时间达到500ms后,框架会调用用户预先设置好的回调函数。 + ## 访问硬件定时器设备 应用程序通过 RT-Thread 提供的 I/O 设备管理接口来访问硬件定时器设备,相关接口如下所示: @@ -114,6 +116,9 @@ static int hwtimer_sample(int argc, char *argv[]) hw_dev = rt_device_find(HWTIMER_DEV_NAME); /* 以读写方式打开设备 */ rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR); + + ... + /* 设置超时回调函数 */ rt_device_set_rx_indicate(hw_dev, timeout_cb); @@ -143,10 +148,10 @@ rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg); | **控制字** | **描述** | | ---------------------- | ------------------------ | -| HWTIMER_CTRL_FREQ_SET | 设置计数频率 | +| HWTIMER_CTRL_FREQ_SET | 设置计数频率(若未设置该项,默认为1Mhz 或 支持的最小计数频率) | | HWTIMER_CTRL_STOP | 停止定时器 | | HWTIMER_CTRL_INFO_GET | 获取定时器特征信息 | -| HWTIMER_CTRL_MODE_SET | 设置定时器模式 | +| HWTIMER_CTRL_MODE_SET | 设置定时器模式(若未设置,默认是HWTIMER_MODE_ONESHOT) | 获取定时器特征信息参数 arg 为指向结构体 struct rt_hwtimer_info 的指针,作为一个输出参数保存获取的信息。 @@ -165,17 +170,8 @@ HWTIMER_MODE_PERIOD 周期性定时 ```c #define HWTIMER_DEV_NAME "timer0" /* 定时器名称 */ rt_device_t hw_dev; /* 定时器设备句柄 */ -rt_hwtimer_mode_t mode; /* 定时器模式 */ -rt_uint32_t freq = 10000; /* 计数频率 */ - -/* 定时器超时回调函数 */ -static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size) -{ - rt_kprintf("this is hwtimer timeout callback fucntion!\n"); - rt_kprintf("tick is :%d !\n", rt_tick_get()); - - return 0; -} +rt_hwtimer_mode_t mode; /* 定时器模式 */ +rt_uint32_t freq = 10000; /* 计数频率 */ static int hwtimer_sample(int argc, char *argv[]) { @@ -183,12 +179,12 @@ static int hwtimer_sample(int argc, char *argv[]) hw_dev = rt_device_find(HWTIMER_DEV_NAME); /* 以读写方式打开设备 */ rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR); - /* 设置超时回调函数 */ - rt_device_set_rx_indicate(hw_dev, timeout_cb); - /* 设置计数频率(默认1Mhz或支持的最小计数频率) */ + ... + + /* 设置计数频率(若未设置该项,默认为1Mhz 或 支持的最小计数频率) */ rt_device_control(hw_dev, HWTIMER_CTRL_FREQ_SET, &freq); - /* 设置模式为周期性定时器 */ + /* 设置模式为周期性定时器(若未设置,默认是HWTIMER_MODE_ONESHOT)*/ mode = HWTIMER_MODE_PERIOD; rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode); return 0; @@ -197,7 +193,7 @@ static int hwtimer_sample(int argc, char *argv[]) ### 设置定时器超时值 -通过如下函数可以设置定时器的超时值: +通过如下函数可以设置定时器的超时值,在调用该函数后,定时器更新参数并旋即开启。 ```c rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size); @@ -228,29 +224,16 @@ typedef struct rt_hwtimerval ```c #define HWTIMER_DEV_NAME "timer0" /* 定时器名称 */ rt_device_t hw_dev; /* 定时器设备句柄 */ -rt_hwtimer_mode_t mode; /* 定时器模式 */ rt_hwtimerval_t timeout_s; /* 定时器超时值 */ -/* 定时器超时回调函数 */ -static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size) -{ - rt_kprintf("this is hwtimer timeout callback fucntion!\n"); - rt_kprintf("tick is :%d !\n", rt_tick_get()); - - return 0; -} - static int hwtimer_sample(int argc, char *argv[]) { /* 查找定时器设备 */ hw_dev = rt_device_find(HWTIMER_DEV_NAME); /* 以读写方式打开设备 */ rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR); - /* 设置超时回调函数 */ - rt_device_set_rx_indicate(hw_dev, timeout_cb); - /* 设置模式为周期性定时器 */ - mode = HWTIMER_MODE_PERIOD; - rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode); + + ... /* 设置定时器超时值为5s并启动定时器 */ timeout_s.sec = 5; /* 秒 */ @@ -263,7 +246,7 @@ static int hwtimer_sample(int argc, char *argv[]) ### 获取定时器当前值 -通过如下函数可以获取定时器当前值: +通过如下函数可以获取自定时器开始 (rt_device_write) 之后的运行时: ```c rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size); @@ -282,9 +265,10 @@ rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t 使用示例如下所示: ```c -rt_hwtimerval_t timeout_s; /* 用于保存定时器经过时间 */ -/* 读取定时器经过时间 */ -rt_device_read(hw_dev, 0, &timeout_s, sizeof(timeout_s)); +rt_hwtimerval_t t; +/* 读取定时器开始之后的经过时间 */ +rt_device_read(hw_dev, 0, &t, sizeof(t)); +rt_kprintf("Read: Sec = %d, Usec = %d\n", t.sec, t.usec); ``` ### 关闭定时器设备 @@ -319,7 +303,7 @@ rt_device_close(hw_dev); > [!NOTE] > 注:可能出现定时误差。假设计数器最大值 0xFFFF,计数频率 1Mhz,定时时间 1 秒又 1 微秒。由于定时器一次最多只能计时到 65535us,对于 1000001us 的定时要求。可以 50000us 定时 20 次完成,此时将会出现计算误差 1us。 -## 硬件定时器设备使用示例 +## 硬件定时器设备完整使用示例 硬件定时器设备的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下: @@ -361,6 +345,7 @@ static int hwtimer_sample(int argc, char *argv[]) rt_hwtimerval_t timeout_s; /* 定时器超时值 */ rt_device_t hw_dev = RT_NULL; /* 定时器设备句柄 */ rt_hwtimer_mode_t mode; /* 定时器模式 */ + rt_uint32_t freq = 10000; /* 计数频率 */ /* 查找定时器设备 */ hw_dev = rt_device_find(HWTIMER_DEV_NAME); @@ -381,7 +366,9 @@ static int hwtimer_sample(int argc, char *argv[]) /* 设置超时回调函数 */ rt_device_set_rx_indicate(hw_dev, timeout_cb); - /* 设置模式为周期性定时器 */ + /* 设置计数频率(若未设置该项,默认为1Mhz 或 支持的最小计数频率) */ + rt_device_control(hw_dev, HWTIMER_CTRL_FREQ_SET, &freq); + /* 设置模式为周期性定时器(若未设置,默认是HWTIMER_MODE_ONESHOT)*/ mode = HWTIMER_MODE_PERIOD; ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode); if (ret != RT_EOK) @@ -393,7 +380,6 @@ static int hwtimer_sample(int argc, char *argv[]) /* 设置定时器超时值为5s并启动定时器 */ timeout_s.sec = 5; /* 秒 */ timeout_s.usec = 0; /* 微秒 */ - if (rt_device_write(hw_dev, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s)) { rt_kprintf("set timeout value failed\n"); diff --git a/rt-thread-version/rt-thread-standard/programming-manual/thread/thread.md b/rt-thread-version/rt-thread-standard/programming-manual/thread/thread.md index d5f3c0c..74649bf 100644 --- a/rt-thread-version/rt-thread-standard/programming-manual/thread/thread.md +++ b/rt-thread-version/rt-thread-standard/programming-manual/thread/thread.md @@ -374,7 +374,7 @@ rt_err_t rt_thread_suspend (rt_thread_t thread); | \-RT_ERROR | 线程挂起失败,因为该线程的状态并不是就绪状态 | > [!NOTE] -> 注:一个线程尝试挂起另一个线程是一个非常危险的行为,因此RT-Thread对此此函数有严格的使用限制:该函数只能使用来挂起当前线程(即自己挂起自己),不可以在线程A中尝试挂起线程B。而且在挂起线程自己后,需要立刻调用 `rt_schedule()` 函数进行手动的线程上下文切换。这是因为A线程在尝试挂起B线程时,A线程并不清楚B线程正在运行什么程序,一旦B线程正在使用例如互斥量、信号量等影响、阻塞其他线程(如C线程)的内核对象,如果此时其他线程也在等待这个内核对象,那么A线程尝试挂起B线程的操作将会引发其他线程(如C线程)的饥饿,严重危及系统的实时性。 +> 注:一个线程尝试挂起另一个线程是一个非常危险的行为,因此RT-Thread对此函数有严格的使用限制:该函数只能使用来挂起当前线程(即自己挂起自己),不可以在线程A中尝试挂起线程B。而且在挂起线程自己后,需要立刻调用 `rt_schedule()` 函数进行手动的线程上下文切换。这是因为A线程在尝试挂起B线程时,A线程并不清楚B线程正在运行什么程序,一旦B线程正在使用例如互斥量、信号量等影响、阻塞其他线程(如C线程)的内核对象,如果此时其他线程也在等待这个内核对象,那么A线程尝试挂起B线程的操作将会引发其他线程(如C线程)的饥饿,严重危及系统的实时性。 恢复线程就是让挂起的线程重新进入就绪状态,并将线程放入系统的就绪队列中;如果被恢复线程在所有就绪态线程中,位于最高优先级链表的第一位,那么系统将进行线程上下文的切换。线程恢复使用下面的函数接口: -- Gitee