设计说明
21 Nov 2024
Read time: 8 minute(s)
源码说明
源代码位于 bsp/artinchip/
:
-
bsp/artinchip/drv/inputcap/drv_inputcap.c,CAP Driver 层实现
-
bsp/artinchip/hal/inputcap/hal_inputcap.c,CAP 模块的 HAL 层实现
-
bsp/artinchip/include/hal/hal_inputcap.h,CAP 模块的 HAL 层接口头文件
模块架构
CAP 驱动 Driver 层采用 RT-Thread 的 INPUT CAPTURE 设备驱动框架。HAL 层也可以支持 Baremetal 方式或配合自定义的设备驱动框架进行使用。
关键流程设计
初始化流程
CAP 驱动的初始化接口通过 INIT_DEVICE_EXPORT(drv_inputcap_init)
完成,主要是通过调用
INPUT CAPTURE 子系统的接口 rt_device_inputcapture_register() 注册一个 CAP 设备。
CAP 控制器的配置过程,主要步骤有:
-
初始化 CAP 模块的 clk
-
使能 CAP 指定通道的 clk
-
设置 CAP 捕捉事件
-
使能 CAP 的中断或 DMA
-
启动 CAP 计数
数据结构设计
struct aic_inputcap_pdata
属于 HAL 层接口,记录每一个 CAP
通道的捕捉数据信息:
struct aic_inputcap_pdata {
u8 id;
u8 isr_event;
enum inputcap_polarity event0_pol;
enum inputcap_polarity event1_pol;
u32 cur_cnt;
u32 last_cnt;
struct aic_inputcap_transfer_info t_info0;
struct aic_inputcap_transfer_info t_info1;
};
struct aic_inputcap
属于 Driver 层接口,记录一个 CAP
设备的配置信息:
struct aic_inputcap {
struct rt_inputcapture_device rtdev;
struct aic_inputcap_pdata *data;
};
Driver 层接口设计
以下接口是 INPUT CAPTURE
设备驱动框架的标准接口。
struct rt_inputcapture_ops
{
rt_err_t (*init)(struct rt_inputcapture_device *inputcapture);
rt_err_t (*open)(struct rt_inputcapture_device *inputcapture);
rt_err_t (*close)(struct rt_inputcapture_device *inputcapture);
rt_err_t (*get_pulsewidth)(struct rt_inputcapture_device *inputcapture, rt_uint32_t *pulsewidth_us);
#if defined (AIC_INPUTCAP_DRV) && defined (AIC_DMA_DRV)
rt_err_t (*set_buf)(struct rt_inputcapture_device *inputcapture, struct rt_inputcapture_fifo_buf *ptr);
#endif
};
其中 (*set_buf)
为 CAP 拓展的接口,用于 DMA 模式。
函数原型 | static rt_err_t aic_inputcap_init(struct rt_inputcapture_device *inputcapture) |
---|---|
功能说明 | 无 |
参数定义 |
inputcapture - 指向 rt_inputcapture_device 设备的指针
|
返回值 | 0,成功;<0,失败 |
注意事项 | - |
函数原型 | static rt_err_t aic_inputcap_open(struct rt_inputcapture_device *inputcapture) |
---|---|
功能说明 | 并配置使能 CAP |
参数定义 |
inputcapture - 指向 rt_inputcapture_device 设备的指针
|
返回值 | 0,成功;<0,失败 |
注意事项 | - |
函数原型 | static rt_err_t aic_inputcap_close(struct rt_inputcapture_device *inputcapture) |
---|---|
功能说明 | 关闭 CAP |
参数定义 |
inputcapture - 指向 rt_inputcapture_device 设备的指针
|
返回值 | 0,成功;<0,失败 |
注意事项 | - |
函数原型 | static rt_err_t aic_inputcap_get_pulsewidth(struct rt_inputcapture_device *inputcapture, rt_uint32_t *pulsewidth_us) |
---|---|
功能说明 | 计算脉冲间隔时间 |
参数定义 |
inputcapture - 指向 rt_inputcapture_device 设备的指针
pulsewidth_us - RT-Thread 回调参数脉冲间隔变量的指针
|
返回值 | 0,成功;<0,失败 |
注意事项 | - |
函数原型 | static rt_err_t aic_inputcap_setbuf(struct rt_inputcapture_device *inputcapture, struct rt_inputcapture_fifo_buf *ptr) |
---|---|
功能说明 | 设置 DMA 的 BUFF 地址 |
参数定义 |
inputcapture - 指向 rt_inputcapture_device 设备的指针
ptr - 指向 rt_inputcapture_fifo_buf FIFO 地址、长度信息
|
返回值 | 0,成功;<0,失败 |
注意事项 | 当使用 DMA 模式才需要设置 |
HAL 层接口设计
HAL 层的函数接口声明存放在 hal_cap.h
中,主要接口有:
void hal_inputcap_evnt_en(u32 i, u32 event, u32 enable);
void hal_inputcap_pol_set(u32 i, u32 event, enum inputcap_polarity event_pol);
void hal_inputcap_cnt_en(u32 i, u32 enable);
u32 hal_inputcap_get_cnt(u32 i);
void hal_inputcap_set_fifo(u32 i, u32 fifo, u32 enable);
u32 hal_inputcap_int_stat(u32 i);
void hal_inputcap_int_clr(u32 i, u32 mask);
u32 hal_inputcap_get_event_fifo(u32 i, u32 fifo);
void hal_inputcap_fifo_flush(u32 i, u32 fifo);
#ifdef AIC_DMA_DRV
void hal_inputcap_dma_config(struct aic_inputcap_pdata *chan, u32 event,
dma_async_callback callback, void *callback_param);
#endif
Demo
本 Demo 是 test_inputcap
的部分源码(bsp/examples/test-inputcap/test_inputcap.c):
#define WATER_MARK 64
#ifdef AIC_DMA_DRV
#define INPUTCAP_CLK_RATE 48000000
rt_uint32_t data_buf0[WATER_MARK] __attribute__((aligned(32))) = {0};
rt_uint32_t data_buf1[WATER_MARK] __attribute__((aligned(32))) = {0};
rt_uint8_t event0_flag = 0;
rt_uint8_t event1_flag = 0;
#endif
/* callback function */
static rt_err_t inputcap_cb(rt_device_t dev, rt_size_t size)
{
#ifdef AIC_DMA_DRV
rt_uint8_t *p = dev->user_data;
if ((rt_uint32_t)*p == 0)
event0_flag = 1;
else
event1_flag = 1;
#ifdef ULOG_USING_ISR_LOG
rt_uint32_t temp_cnt;
if ((rt_uint32_t)*p == 0) {
for (int i = 0; i < WATER_MARK - 1; i++) {
if (data_buf0[i + 1] > data_buf0[i])
temp_cnt = data_buf0[i + 1] - data_buf0[i];
else
temp_cnt = data_buf0[i + 1] + ((0xFFFFFFFF - data_buf0[i]) + 1);
rt_kprintf("%s event%d: pulsewidth:%d us\n", &dev->parent.name, (rt_uint32_t)*p, temp_cnt / (INPUTCAP_CLK_RATE / 1000000));
}
} else {
for (int i = 0; i < WATER_MARK - 1; i++) {
if (data_buf1[i + 1] > data_buf1[i])
temp_cnt = data_buf1[i + 1] - data_buf1[i];
else
temp_cnt = data_buf1[i + 1] + ((0xFFFFFFFF - data_buf1[i]) + 1);
rt_kprintf("%s event%d: pulsewidth:%d us\n", &dev->parent.name, (rt_uint32_t)*p, temp_cnt / (INPUTCAP_CLK_RATE / 1000000));
}
}
#endif
#else
struct rt_inputcapture_data inputcap_data[WATER_MARK];
rt_device_read(dev, 0, (void *)inputcap_data, size);
#ifdef ULOG_USING_ISR_LOG
for (int i = 0; i < size; i++)
rt_kprintf("%s: pulsewidth:%d us\n", &dev->parent.name, inputcap_data[i].pulsewidth_us);
#endif
#endif
return RT_EOK;
}
int test_inputcap(int argc, char **argv)
{
rt_uint32_t watermark = WATER_MARK;
rt_device_t inputcap_dev = RT_NULL;
char device_name[8] = {"incap"};
int ret;
if (argc != 2) {
rt_kprintf("Usage: test_inputcap 0\n");
return -RT_EINVAL;
}
strcat(device_name, argv[1]);
inputcap_dev = rt_device_find(device_name);
if (inputcap_dev == RT_NULL) {
rt_kprintf("Can't find %s device!\n", device_name);
return -RT_EEMPTY;
}
#ifdef AIC_DMA_DRV
struct rt_inputcapture_fifo_buf buf_para;
buf_para.event0_buf = data_buf0;
buf_para.event0_buflen = sizeof(data_buf0);
buf_para.event1_buf = data_buf1;
buf_para.event1_buflen = sizeof(data_buf1);
ret = rt_device_control(inputcap_dev, INPUTCAPTURE_CMD_SET_DATA_BUF, (void *)&buf_para);
if (ret != RT_EOK) {
rt_kprintf("Failed to set %s device watermark!\n", device_name);
return ret;
}
#endif
ret = rt_device_control(inputcap_dev, INPUTCAPTURE_CMD_SET_WATERMARK, &watermark);
if (ret != RT_EOK) {
rt_kprintf("Failed to set %s device watermark!\n", device_name);
return ret;
}
/* set callback function */
rt_device_set_rx_indicate(inputcap_dev, inputcap_cb);
ret = rt_device_open(inputcap_dev, RT_DEVICE_OFLAG_RDWR);
if (ret != RT_EOK) {
rt_kprintf("Failed to open %s device!\n", device_name);
return ret;
}
rt_kprintf("inputcap%d open.\n", atoi(argv[1]));
#ifdef AIC_DMA_DRV
int wait_count = 1000;
while (1) {
if (event0_flag == 1 && event1_flag == 1) {
event0_flag = 0;
event1_flag = 0;
rt_device_close(inputcap_dev);
break;
}
rt_thread_mdelay(10);
wait_count--;
if (wait_count == 0) {
rt_kprintf("no signal input!\n");
rt_device_close(inputcap_dev);
break;
}
}
rt_kprintf("inputcap%d close.\n", atoi(argv[1]));
#endif
return 0;
}
MSH_CMD_EXPORT_ALIAS(test_inputcap, test_inputcap, Test the inputcap);