源码说明
源代码位于 bsp/artinchip/
:
bsp/artinchip/drv/hrtimer/drv_hrtimer.c,HRTimer Driver 层实现
bsp/artinchip/include/drv/drv_hrtimer.h,HRTimer Driver 层接口
bsp/artinchip/hal/pwmcs/hal_cap.c,PWMCS CAP模块的 HAL 层实现
bsp/artinchip/include/hal/hal_cap.h,PWMCS CAP模块的 HAL 层接口头文件
模块架构
HRTimer 驱动 Driver 层采用 RT-Thread 的 hwtimer设备驱动框架,如果只使用HAL层也可以支持 baremetal 方式的应用场景。
关键流程设计
初始化流程
HRTimer 驱动的初始化接口通过 INIT_DEVICE_EXPORT(drv_hwtimer_init)
完成,主要是通过调用 hwtimer子系统的接口 rt_device_hwtimer_register() 注册一个 hwtimer设备。
CAP 控制器的配置过程,主要步骤有:
初始化PWMCS模块的clk
使能CAP指定通道的clk,并设置为PWM计数模式
设置CAP的cnt值
使能CAP的中断
启动CAP计数
数据结构设计
struct aic_cap_arg
属于 HAL 层接口,记录每一个CAP通道的配置信息:
struct aic_cap_arg {
u16 available;
u16 id;
u32 freq;
u32 prd;
u32 irq_cnt;
};
1
2
3
4
5
6
7
2
3
4
5
6
7
struct hrtimer_info
属于 Driver 层接口,记录一个HRTimer设备的配置信息:
struct hrtimer_info {
char name[12];
u32 id;
rt_hwtimer_t hrtimer;
};
1
2
3
4
5
2
3
4
5
Driver 层接口设计
以下接口是 hwtimer 设备驱动框架的标准接口。
struct rt_hwtimer_ops
{
void (*init)(struct rt_hwtimer_device *timer, rt_uint32_t state);
rt_err_t (*start)(struct rt_hwtimer_device *timer, rt_uint32_t cnt, rt_hwtimer_mode_t mode);
void (*stop)(struct rt_hwtimer_device *timer);
rt_uint32_t (*count_get)(struct rt_hwtimer_device *timer);
rt_err_t (*control)(struct rt_hwtimer_device *timer, rt_uint32_t cmd, void *args);
};
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
drv_hrtimer_init
函数原型 | void drv_hrtimer_init(rt_hwtimer_t* timer, rt_uint32_t state) |
---|---|
功能说明 | 初始化配置一路Timer |
参数定义 | timer - 指向rt_hwtimer_t设备的指针 state - 1, 表示打开; 0, 表示关闭 |
返回值 | 无 |
注意事项 |
drv_hrtimer_start
函数原型 | rt_err_t drv_hrtimer_start(rt_hwtimer_t* timer, rt_uint32_t cnt, rt_hwtimer_mode_t mode) |
---|---|
功能说明 | 启动Timer |
参数定义 | timer - 指向rt_hwtimer_t设备的指针 cnt - Timer的超时计数, 单位是: 1/Freq秒 mode - Oneshot、或Period类型 |
返回值 | 0, 成功; <0, 失败 |
注意事项 |
drv_hrtimer_stop
函数原型 | void drv_hrtimer_stop(rt_hwtimer_t *timer) |
---|---|
功能说明 | 停止 Timer |
参数定义 | timer - 指向 rt_hwtimer_t 设备的指针 |
返回值 | 无 |
注意事项 |
drv_hrtimer_ctrl
函数原型 | rt_err_t drv_hrtimer_ctrl(rt_hwtimer_t* timer, rt_uint32_t cmd, void* args) |
---|---|
功能说明 | HRTimer驱动的ioctl接口 |
参数定义 | timer - 指向rt_hwtimer_t设备的指针 cmd - ioctl命令码 args - ioctl命令相应的参数 |
返回值 | 0, 成功; <0, 失败 |
注意事项 | 目前仅支持设置Timer的Freq值 |
HAL 层接口设计
HAL 层的函数接口声明存放在 hal_cap.h 中,主要接口有:
void hal_cap_ch_init(u32 ch);
void hal_cap_ch_deinit(u32 ch);
void hal_cap_int_enable(u32 ch, int enable);
u32 hal_cap_is_pending(u32 ch);
int hal_cap_set_freq(u32 ch, u32 freq);
int hal_cap_set_cnt(u32 ch, u32 cnt);
int hal_cap_get(u32 ch);
int hal_cap_enable(u32 ch);
int hal_cap_disable(u32 ch);
void hal_cap_cnt_start(u32 ch);
void hal_cap_cnt_stop(u32 ch);
int hal_cap_init(void);
int hal_cap_deinit(void);
void hal_cap_status_show(void);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Demo
本Demo是 test_hrtimer 的部分源码(bsp/examples/test-hrtimer/test_hrtimer.c):
/* Timer timeout callback function */
static rt_err_t hrtimer_cb(rt_device_t dev, rt_size_t size)
{
struct hrtimer_info *info = (struct hrtimer_info *)dev->user_data;
if (g_debug)
printf("%d/%d hrtimer%d timeout callback! Elapsed %ld us\n",
g_loop_cnt, g_loop_max,
info->id, aic_timer_get_us() - g_start_us);
g_start_us = aic_timer_get_us();
g_loop_cnt++;
if ((g_loop_max > 1) && (g_loop_cnt > g_loop_max))
rt_device_control(g_hrtimer_dev[info->id], HWTIMER_CTRL_STOP, NULL);
return RT_EOK;
}
static void cmd_test_hrtimer(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
u32 c, ch = 0;
rt_hwtimerval_t tm = {0};
rt_hwtimer_mode_t mode = HWTIMER_MODE_ONESHOT;
optind = 0;
while ((c = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
switch (c) {
case 'm':
if (strncasecmp("period", optarg, strlen(optarg)) == 0)
mode = HWTIMER_MODE_PERIOD;
continue;
case 'c':
ch = atoi(optarg);
if (ch > AIC_HRTIMER_CH_NUM) {
pr_err("Channel number %s is invalid\n", optarg);
return;
}
continue;
case 's':
tm.sec = atoi(optarg);
continue;
case 'u':
tm.usec = atoi(optarg);
continue;
case 'd':
g_debug = 1;
continue;
case 'h':
usage(argv[0]);
return;
default:
pr_err("Invalid argument\n");
usage(argv[0]);
return;
}
}
if ((tm.sec == 0) && (tm.usec == 0)) {
pr_err("Invalid argument\n");
usage(argv[0]);
return;
}
if (!g_hrtimer_dev[ch]) {
char name[10] = "";
snprintf(name, 10, "hrtimer%d", ch);
/* find timer device */
g_hrtimer_dev[ch] = rt_device_find(name);
if (g_hrtimer_dev[ch] == RT_NULL) {
pr_err("Can't find %s device!\n", name);
return;
}
/* Open the device in read-write mode */
ret = rt_device_open(g_hrtimer_dev[ch], RT_DEVICE_OFLAG_RDWR);
if (ret != RT_EOK) {
pr_err("Failed to open %s device!\n", name);
return;
}
}
/* set timeout callback function */
rt_device_set_rx_indicate(g_hrtimer_dev[ch], hrtimer_cb);
ret = rt_device_control(g_hrtimer_dev[ch], HWTIMER_CTRL_MODE_SET, &mode);
if (ret != RT_EOK) {
pr_err("Failed to set mode! ret is %d\n", ret);
return;
}
printf("hrtimer%d: Create a timer of %d.%06d sec, %s mode\n",
ch, tm.sec, tm.usec,
mode == HWTIMER_MODE_ONESHOT ? "Oneshot" : "Period");
if (mode != HWTIMER_MODE_ONESHOT) {
g_loop_max = HRTIMER_MAX_ELAPSE / (tm.sec * USEC_PER_SEC + tm.usec);
printf("\tWill loop %d times\n", g_loop_max);
}
g_loop_cnt = 0;
g_start_us = aic_timer_get_us();
if (!rt_device_write(g_hrtimer_dev[ch], 0, &tm, sizeof(tm))) {
pr_err("set timeout value failed\n");
return;
}
}
MSH_CMD_EXPORT_ALIAS(cmd_test_hrtimer, test_hrtimer, test hrtimer);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114