1 /*
2  * Copyright (c) 2022-2024, Xiaohua Semiconductor Co., Ltd.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2024-12-30     CDT          first version
9  */
10 
11 /*
12  * 程序清单:这是一个 hwtimer 设备使用例程
13  * 例程导出了 hwtimer_sample 命令到控制终端
14  * 命令调用格式:hwtimer_sample  hwtimer_sample [option1] [option2] [option3]
15  *                                            option1: [tmra_1/2/3..] 定时器单元
16  *                                            option2: [oneshot/period] 定时模式
17  *                                            option3: 超时时间,单位毫秒
18  * eg:hwtimer_sample tmra_1 period 1000
19  * 程序功能:每隔一秒打印一次定时器运行时间值,在定时器超时回调函数中打印总tick值
20  * 可以使用逻辑分析进一步查看测试管脚PA0定时时间是否准确
21  */
22 
23 #include <rtthread.h>
24 #include <rtdevice.h>
25 #include <stdlib.h>
26 #include <board.h>
27 
28 #ifdef BSP_USING_HWTIMER
29 
30 /* IO用于定时时间测试 */
31 #define TIMEOUT_TEST_PIN GET_PIN(A, 0)
32 
33 static rt_uint32_t tick;
34 static rt_bool_t cb_run = RT_FALSE;
35 
_hwtimer_cmd_print_usage(void)36 static void _hwtimer_cmd_print_usage(void)
37 {
38     rt_kprintf("hwtimer_sample [option1] [option2] [option3]\n");
39     rt_kprintf("  option1: [tmra_1/2/3..] tmra uint\n");
40     rt_kprintf("  option2: [oneshot/period] timing mode set\n");
41     rt_kprintf("  option3: timeout unit:ms\n");
42     rt_kprintf("    e.g. MSH >hwtimer_sample tmra_1 period 1000\n");
43 }
44 
45 /* 定时器超时回调函数 */
timeout_cb(rt_device_t dev,rt_size_t size)46 static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
47 {
48     static rt_uint8_t pin_cnt = 0;
49     rt_pin_write(TIMEOUT_TEST_PIN, ++pin_cnt % 2);   /* 电平取反 */
50     /* 打印出的tick值由于printf原因可能有误差,可以查看测试IO来精确确认时间 */
51     rt_kprintf("callback successful! ticks = %d \n", rt_tick_get() - tick);
52     tick = rt_tick_get();
53     cb_run = RT_TRUE;
54 
55     return 0;
56 }
57 
hwtimer_sample(int argc,char * argv[])58 static int hwtimer_sample(int argc, char *argv[])
59 {
60     rt_uint8_t i;
61     rt_err_t ret = RT_EOK;
62     rt_hwtimerval_t timeout_s;                              /* 定时器超时值 */
63     rt_hwtimer_mode_t mode = HWTIMER_MODE_ONESHOT;          /* 定时器模式 */
64     rt_device_t hw_dev = RT_NULL;                           /* 定时器设备句柄 */
65     rt_hwtimer_t *hwtimer;
66     float t;
67     rt_uint8_t loop_cnt;                                    /* 循环打印次数 */
68     rt_hwtimerval_t overflow_tv;                            /* 定时器超时值 */
69     rt_uint32_t timer_out_s;
70 
71     if ((argc != 4) || (rt_strcmp("oneshot", argv[2]) && rt_strcmp("period", argv[2])))
72     {
73         _hwtimer_cmd_print_usage();
74         return -RT_ERROR;
75     }
76 
77     /* 查找定时器设备 */
78     hw_dev = rt_device_find(argv[1]);
79     if (hw_dev == RT_NULL)
80     {
81         rt_kprintf("hwtimer sample run failed! can't find %s device!\n", argv[1]);
82         return -RT_ERROR;
83     }
84     else
85     {
86         hwtimer = (rt_hwtimer_t *)hw_dev;
87     }
88 
89     /* 以读写方式打开设备 */
90     ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
91     if (ret != RT_EOK)
92     {
93         rt_kprintf("open %s device failed!\n", argv[1]);
94         return ret;
95     }
96 
97     /* 设置模式 */
98     if (0 == rt_strcmp(argv[2], "oneshot"))
99     {
100         mode = HWTIMER_MODE_ONESHOT;
101         loop_cnt = 1;
102     }
103     else if (0 == rt_strcmp(argv[2], "period"))
104     {
105         mode = HWTIMER_MODE_PERIOD;
106         loop_cnt = 5;
107     }
108     rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
109 
110     /* 设置超时回调函数 */
111     rt_device_set_rx_indicate(hw_dev, timeout_cb);
112 
113     /* 设置定时器超时并启动定时器 */
114     timeout_s.sec = atoi(argv[3]) / 1000U;            /* 秒 */
115     timeout_s.usec = (atoi(argv[3]) % 1000U) * 1000U; /* 微秒 */
116     if (rt_device_write(hw_dev, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s))
117     {
118         rt_kprintf("set timeout value failed\n");
119         return -RT_ERROR;
120     }
121     tick = rt_tick_get();
122     rt_kprintf("set timeout (%d) ms successful\n", atoi(argv[3]));
123 
124     /* 设置测试管脚为输出模式 */
125     rt_pin_mode(TIMEOUT_TEST_PIN, PIN_MODE_OUTPUT);
126 
127     /* oneshot模式cb函数执行一次,period模式cb函数执行5次,且每秒打印一次运行时间 */
128     timer_out_s = (atoi(argv[3]) / 1000U) > 1 ? (atoi(argv[3]) / 1000U) : 1;
129     for (i = 0; i < (timer_out_s * loop_cnt); i++)
130     {
131         /* 延时1000ms */
132         rt_thread_mdelay(1000);
133 
134         /* 读取定时器当前值 */
135         if (mode == HWTIMER_MODE_PERIOD)
136         {
137             rt_device_read(hw_dev, 0, &timeout_s, sizeof(timeout_s));
138         }
139         else if (mode == HWTIMER_MODE_ONESHOT)
140         {
141             rt_device_read(hw_dev, 0, &timeout_s, sizeof(timeout_s));
142 
143             t = hwtimer->overflow * hwtimer->period_sec;
144             overflow_tv.sec = (rt_int32_t)t;
145             overflow_tv.usec = (rt_int32_t)((t - overflow_tv.sec) * 1000000);
146 
147             timeout_s.sec = overflow_tv.sec + (timeout_s.usec + overflow_tv.usec) / 1000000;
148             timeout_s.usec = (timeout_s.usec + overflow_tv.usec) % 1000000;
149         }
150         rt_kprintf("Read: Sec = %d, Usec = %d\n", timeout_s.sec, timeout_s.usec);
151     }
152 
153     /* 确保oneshot模式cb函数执行一次后才关闭定时器 */
154     while (cb_run == RT_FALSE);
155     cb_run = RT_FALSE;
156 
157     /* close */
158     rt_device_close(hw_dev);
159 
160     return ret;
161 }
162 /* 导出到 msh 命令列表中 */
163 MSH_CMD_EXPORT(hwtimer_sample, hwtimer sample: devname [oneshot | period] timeout);
164 #endif
165