Edit online

设计说明

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 方式或配合自定义的设备驱动框架进行使用。


sw_system12

1. CAP 驱动的软件架构图

关键流程设计

初始化流程

CAP 驱动的初始化接口通过 INIT_DEVICE_EXPORT(drv_inputcap_init) 完成,主要是通过调用 INPUT CAPTURE 子系统的接口 rt_device_inputcapture_register() 注册一个 CAP 设备。

CAP 控制器的配置过程,主要步骤有:

  1. 初始化 CAP 模块的 clk

  2. 使能 CAP 指定通道的 clk

  3. 设置 CAP 捕捉事件

  4. 使能 CAP 的中断或 DMA

  5. 启动 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 模式。

1. aic_inputcap_init
函数原型 static rt_err_t aic_inputcap_init(struct rt_inputcapture_device *inputcapture)
功能说明
参数定义
inputcapture - 指向 rt_inputcapture_device 设备的指针
返回值 0,成功;<0,失败
注意事项 -
2. aic_inputcap_open
函数原型 static rt_err_t aic_inputcap_open(struct rt_inputcapture_device *inputcapture)
功能说明 并配置使能 CAP
参数定义
inputcapture - 指向 rt_inputcapture_device 设备的指针
返回值 0,成功;<0,失败
注意事项 -
3. aic_inputcap_close
函数原型 static rt_err_t aic_inputcap_close(struct rt_inputcapture_device *inputcapture)
功能说明 关闭 CAP
参数定义
inputcapture - 指向 rt_inputcapture_device 设备的指针
返回值 0,成功;<0,失败
注意事项 -
4. aic_inputcap_get_pulsewidth
函数原型 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,失败
注意事项 -
5. aic_inputcap_setbuf
函数原型 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);