4.4.5. 设计说明¶
4.4.5.1. 源码说明¶
源代码位于 bsp/artinchip/
:
bsp/artinchip/drv/gpt/drv_gpt.c,GPTimer Driver 层实现
bsp/artinchip/include/drv/drv_gptimer.h,GPTimer Driver 层接口
bsp/artinchip/hal/gpt/hal_gpt.c,GTPimer 模块的 HAL 层实现
bsp/artinchip/include/hal/hal_gpt.h,GTPimer 模块的 HAL 层接口头文件
4.4.5.2. 模块架构¶
GPTimer 驱动 Driver 层采用 RT-Thread 的 hwtimer 设备驱动框架。HAL 层也可以支持 Baremetal 方式或配合自定义的设备驱动框架进行使用。

图 4.25 HRTimer驱动的软件架构图¶
4.4.5.3. 关键流程设计¶
4.4.5.3.1. 初始化流程¶
GPTimer 驱动的初始化接口通过 INIT_DEVICE_EXPORT(drv_hwtimer_init)
完成,主要是通过调用 hwtimer 子系统的接口 rt_device_hwtimer_register() 注册一个 hwtimer 设备。
GPTimer 控制器的配置过程,主要步骤有:
初始化 GPTimer 模块的 clk
设置 GPTimer 的cnt值
设置 GPTimer 的工作模式
使能 GPTimer 的中断
启动 GPTimer 计数
4.4.5.4. 数据结构设计¶
4.4.5.4.1. struct gptimer_info¶
属于 Driver 层接口,记录一个 GPTimer 设备的配置信息:
struct gptimer_info {
char name[12];
u32 id;
struct irq_flag gptirq_flag;
rt_hwtimer_t gptimer;
};
4.4.5.4.2. struct gptimer_para¶
属于 Driver 层接口,用于设置 GPTimer 的工作模式:
struct gptimer_match_out
{
u8 is_en;
enum gpt_out_init out_init;
enum gpt_cmp_out cmpa_out;
enum gpt_cmp_out cmpb_out;
};
struct gptimer_match
{
enum gpt_cmp_act cmpa_act;
enum gpt_cmp_act cmpb_act;
struct gptimer_match_out outval[GPT_OUT_NUMS];
};
struct gptimer_para
{
enum gptimer_mode gptimer_mode;
enum gpt_trg_mode gptimer_trgmode;
struct gptimer_match matchval;
};
4.4.5.5. Driver 层接口设计¶
以下接口是 hwtimer 设备驱动框架的标准接口。
struct rt_hwtimer_ops
{
void (*init)(struct rt_hwtimer_device *timer, rt_uint32_t state);
#ifdef AIC_GPTIMER_DRV
rt_err_t (*start)(struct rt_hwtimer_device *timer, rt_uint32_t cnt, rt_hwtimer_mode_t mode, void *args);
#else
rt_err_t (*start)(struct rt_hwtimer_device *timer, rt_uint32_t cnt, rt_hwtimer_mode_t mode);
#endif
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);
};
为了拓展 GPTimer 的功能,(*start)
接口中新增了 args,用于设置 GPTimer 的工作模式。
4.4.5.5.1. drv_gptimer_init¶
函数原型 |
static void drv_gptimer_init(rt_hwtimer_t *timer, rt_uint32_t state) |
---|---|
功能说明 |
初始化配置一路 Timer |
参数定义 |
timer - 指向 rt_hwtimer_t 设备的指针
state - 1,表示打开;0,表示关闭
|
返回值 |
无 |
注意事项 |
4.4.5.5.2. drv_hrtimer_start¶
函数原型 |
static rt_err_t drv_gptimer_start(rt_hwtimer_t *timer, rt_uint32_t cnt, rt_hwtimer_mode_t mode, void *args) |
---|---|
功能说明 |
启动 Timer |
参数定义 |
timer - 指向 rt_hwtimer_t 设备的指针
cnt - Timer的超时计数,单位是:1/Freq秒
mode - Oneshot、或Period类型
args - 指向 GPtimer 工作模式参数指针
|
返回值 |
0,成功;<0,失败 |
注意事项 |
输出模式下 CMPA 值默认为 cnt 值的四分之一,CMPB 值默认为 cnt 值的二分之一 |
4.4.5.5.3. drv_hrtimer_stop¶
函数原型 |
static void drv_gptimer_stop(rt_hwtimer_t *timer) |
---|---|
功能说明 |
停止 Timer |
参数定义 |
timer - 指向 rt_hwtimer_t 设备的指针
|
返回值 |
无 |
注意事项 |
4.4.5.5.4. drv_hrtimer_ctrl¶
函数原型 |
static rt_err_t drv_gptimer_ctrl(rt_hwtimer_t *timer, rt_uint32_t cmd, void *args) |
---|---|
功能说明 |
GPTimer 驱动的 ioctl 接口 |
参数定义 |
timer - 指向 rt_hwtimer_t 设备的指针
cmd - ioctl 命令码
args - ioctl 命令相应的参数
|
返回值 |
0,成功;<0,失败 |
注意事项 |
目前仅支持设置 Timer 的 Freq 值 |
4.4.5.6. HAL 层接口设计¶
HAL 层的函数接口声明存放在 hal_gpt.h 中,主要接口有:
u32 hal_gpt_int_stat(u32 i);
void hal_gpt_int_clr(u32 i, u32 mask);
void hal_gpt_clk_div_set(u32 i, u32 div);
void hal_gpt_ctl_set(u32 i, u32 trg_db, enum gpt_trg_mode trg_mode, enum gpt_run_mode run_mode);
void hal_gpt_en(u32 i, u32 enable);
void hal_gpt_clr(u32 i);
void hal_gpt_irq_en(u32 i, enum gpt_irq_mode irq_mode, u32 enable);
void hal_gpt_irq_disable(u32 i);
void hal_gpt_set_max(u32 i, u32 val);
void hal_gpt_out_init(u32 i, u32 out_num, struct gptimer_match cfg, u32 cmpa_val, u32 cmpb_val);
void hal_gpt_out_en(u32 i, u32 out_num, u32 enable);
4.4.5.7. Demo¶
本 Demo 是 test_gptimer 的部分源码(bsp/examples/test-gptimer/test_gptimer.c):
struct test_gptimer_para {
rt_hwtimerval_t tm;
struct gptimer_para gpt_para;
};
struct gptimer_match_out g_outval[GPT_OUT_NUMS] = {
{1, OUT_INIT_LOW, CMP_OUT_HIGH, CMP_OUT_LOW},
{1, OUT_INIT_HIGH, CMP_OUT_LOW, CMP_OUT_HIGH},
{0, OUT_INIT_LOW, CMP_OUT_HIGH, CMP_OUT_LOW},
{0, OUT_INIT_LOW, CMP_OUT_HIGH, CMP_OUT_LOW},
};
enum gpt_cmp_act g_cmpa_act = GPTIMER_CNT_CONTINUE;
enum gpt_cmp_act g_cmpb_act = GPTIMER_CNT_CONTINUE;
/* Timer timeout callback function */
static rt_err_t gptimer_cb(rt_device_t dev, rt_size_t size)
{
struct gptimer_info *info = (struct gptimer_info *)dev->user_data;
#ifdef ULOG_USING_ISR_LOG
if (g_debug)
printf("%d/%d gptimer%d timeout callback! Elapsed %ld us\n",
g_loop_cnt, g_loop_max,
info->id, aic_timer_get_us() - g_start_us);
#endif
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_gptimer_dev[info->id], HWTIMER_CTRL_STOP, NULL);
return RT_EOK;
}
static void cmd_test_gptimer(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
u32 c, ch = 0;
struct test_gptimer_para para = {0};
rt_hwtimer_mode_t mode = HWTIMER_MODE_ONESHOT;
enum gptimer_mode gpt_mode = GPTIMER_MODE_COUNT;
enum gpt_trg_mode trg_mode = GPT_TRG_MODE_AUTO;
u32 freq= 1000000;
optind = 0;
g_debug = 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 > TIMER_NUM) {
pr_err("Channel number %s is invalid\n", optarg);
return;
}
continue;
case 's':
para.tm.sec = atoi(optarg);
continue;
case 'u':
para.tm.usec = atoi(optarg);
continue;
case 'd':
g_debug = 1;
continue;
case 'g':
if (strncasecmp("count", optarg, strlen(optarg)) == 0)
gpt_mode = GPTIMER_MODE_COUNT;
else if (strncasecmp("match", optarg, strlen(optarg)) == 0)
gpt_mode = GPTIMER_MODE_MATCH;
continue;
case 'a':
if (strncasecmp("auto", optarg, strlen(optarg)) == 0)
trg_mode = GPT_TRG_MODE_AUTO;
else if (strncasecmp("rsi", optarg, strlen(optarg)) == 0)
trg_mode = GPT_TRG_MODE_RSI;
else if (strncasecmp("fall", optarg, strlen(optarg)) == 0)
trg_mode = GPT_TRG_MODE_FALL;
else if (strncasecmp("bil", optarg, strlen(optarg)) == 0)
trg_mode = GPT_TRG_MODE_BILATERAL;
continue;
case 'f':
freq = atoi(optarg);
continue;
case 'h':
usage(argv[0]);
return;
default:
pr_err("Invalid argument\n");
usage(argv[0]);
return;
}
}
if ((para.tm.sec == 0) && (para.tm.usec == 0)) {
pr_err("Invalid argument\n");
usage(argv[0]);
return;
}
if (!g_gptimer_dev[ch]) {
char name[10] = "";
snprintf(name, 10, "gptimer%d", ch);
/* find timer device */
g_gptimer_dev[ch] = rt_device_find(name);
if (g_gptimer_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_gptimer_dev[ch], RT_DEVICE_OFLAG_RDWR);
if (ret != RT_EOK) {
pr_err("Failed to open %s device!\n", name);
return;
}
}
para.gpt_para.gptimer_mode = gpt_mode;
para.gpt_para.gptimer_trgmode = trg_mode;
if (gpt_mode == GPTIMER_MODE_MATCH) {
para.gpt_para.matchval.cmpa_act = g_cmpa_act;
para.gpt_para.matchval.cmpb_act = g_cmpb_act;
memcpy(¶.gpt_para.matchval.outval[0], g_outval, sizeof(struct gptimer_match_out) * 4);
}
/* set timeout callback function */
rt_device_set_rx_indicate(g_gptimer_dev[ch], gptimer_cb);
/* set the timer mode, oneshot or period */
ret = rt_device_control(g_gptimer_dev[ch], HWTIMER_CTRL_MODE_SET, &mode);
if (ret != RT_EOK) {
pr_err("Failed to set mode! ret is %d\n", ret);
return;
}
/* set the timer frequency to freqHz */
ret = rt_device_control(g_gptimer_dev[ch], HWTIMER_CTRL_FREQ_SET, &freq);
if (ret != RT_EOK) {
pr_err("Failed to set the freq! ret is %d\n", ret);
return;
}
printf("gptimer%d: Create a timer of %d.%06d sec, %s mode\n",
ch, (u32)para.tm.sec, (u32)para.tm.usec,
mode == HWTIMER_MODE_ONESHOT ? "Oneshot" : "Period");
if (mode != HWTIMER_MODE_ONESHOT) {
g_loop_max = GPTIMER_MAX_ELAPSE / (para.tm.sec * USEC_PER_SEC + para.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_gptimer_dev[ch], 0, ¶, sizeof(struct test_gptimer_para))) {
pr_err("set timeout value failed\n");
return;
}
// rt_device_close(g_gptimer_dev[ch]);
}
MSH_CMD_EXPORT_ALIAS(cmd_test_gptimer, test_gptimer, test gptimer);