.. _LPWM使用:

LPWM使用
======================================

硬件架构
---------

.. image:: media/lpwmarch.png

软件说明
---------

软件架构
~~~~~~~~~~~~~~~~~~

.. image:: media/softarch.png

整体软件架构分为硬件层，驱动层以及用户层。

- 硬件层：即LPWM IP。

- 驱动层：实现LPWM设备的注册与操作，包括设置LPWM属性值、动态配置、开启关闭等。

- 用户层：用户层app调用VPF框架接口，依次调用到VPF驱动接口，VIN NODE驱动接口最终调用到LPWM驱动接口。

代码路径
~~~~~~~~~~~~~~~~~~

LPWM只有驱动层代码，无用户层代码，由VIN接口统一调度。

.. code-block:: bash

    hobot-drivers/camsys/lpwm_j6/hobot_lpwm_dev.c   #LPWM驱动框架
    hobot-drivers/camsys/lpwm_j6/hobot_lpwm_ops.c   #LPWM驱动接口具体实现
    hobot-drivers/camsys/lpwm_j6/hobot_lpwm_reg.c   #LPWM寄存器操作接口

DTS相关
~~~~~~~~~~~~~~~~~~

LPWM dts路径
^^^^^^^^^^^^^

.. code-block:: bash

    hobot-drivers/kernel-dts/hobot-j6e-camsys.dtsi

该设备树应用于不同类型板端，板端实际配置由hobot-j6e-evm-camsys.dtsi、hobot-j6e-matrix-camsys.dtsi等子设备树区分。

LPWM dts配置
^^^^^^^^^^^^^

.. code-block:: bash

    lpwm0: lpwm0@370f0000 {
        compatible = "hobot,hobot-lpwm";
        reg = <0 0x370f0000 0 0x1000>;
        pinctrl-names = "default";
        pinctrl-0 = <&cam_lpwm0>;
        // clocks = <&peri_cmm0 4>;
        // clock-names = "lpwm_clk";
        // resets = <&rst_peri 0x8 8>;
        // reset-names = "rst_lpwm0";
        interrupt-parent = <&gic>;
        interrupts = <GIC_SPI CAMERASYS_LPWM_INTR_0 CAMERASYS_LPWM_INTR_0_TRIG_TYPE>;
        offset = <1 1 1 1>;
        trigger-source = <4>;
        channel = <1 1 1 1>;
        // fusa-cfg = <&peri_sys>;
        // fusa-erm = <&peri_erm>;
        status = "okay";
    };

    lpwm1: lpwm1@370f1000 {
        compatible = "hobot,hobot-lpwm";
        reg = <0 0x370f1000 0 0x1000>;
        pinctrl-names = "default";
        pinctrl-0 = <&cam_lpwm1>;
        // clocks = <&peri_cmm0 4>;
        // clock-names = "lpwm_clk";
        // resets = <&rst_peri 0x8 8>;
        // reset-names = "rst_lpwm0";
        interrupt-parent = <&gic>;
        interrupts = <GIC_SPI CAMERASYS_LPWM_INTR_1 CAMERASYS_LPWM_INTR_1_TRIG_TYPE>;
        offset = <1 1 1 1>;
        trigger-source = <4>;
        channel = <1 1 1 1>;
        // fusa-cfg = <&peri_sys>;
        // fusa-erm = <&peri_erm>;
        status = "okay";
    };

    lpwm2: lpwm2@370f2000 {
        compatible = "hobot,hobot-lpwm";
        reg = <0 0x370f2000 0 0x1000>;
        pinctrl-names = "default";
        pinctrl-0 = <&video_lpwm2_dout0>;
        interrupt-parent = <&gic>;
        interrupts = <GIC_SPI CAMERASYS_LPWM_INTR_2 CAMERASYS_LPWM_INTR_2_TRIG_TYPE>;
        offset = <1 1 1 1>;
        trigger-source = <4>;
        channel = <1 1 1 1>;
        status = "okay";
    };

LPWM dts配置说明
^^^^^^^^^^^^^^^^^

channel：表明该LPWM的channel连接情况，默认4个channel属性都为1，表明4个channel有效，实际上不同类型板端情况有别，该区别由板端camsys dtsi进行区分。

.. code-block:: bash

    J6E-MATRIX
    &lpwm0 {
            channel = <1 1 1 1>;
    };

    &lpwm1 {
            channel = <1 1 0 0>;
    };

    &lpwm2 {
            channel = <1 0 0 0>;
    };

LPWM用户层JSON配置
~~~~~~~~~~~~~~~~~~

用户层LPWM相关软件内容体现在lpwm.json，该配置文件由各个case的vpm_config.json所引用，该配置文件内容如下：

.. code-block:: bash

    {
        "lpwm_chn0": {
            "trigger_source": 10,
            "trigger_mode": 1,
            "period": 40000,
            "offset": 0,
            "duty_time": 100,
            "threshold": 0,
            "adjust_step": 0
        },
        "lpwm_chn1": {
            "trigger_source": 10,
            "trigger_mode": 1,
            "period": 80000,
            "offset": 0,
            "duty_time": 100,
            "threshold": 0,
            "adjust_step": 0
        },
        "lpwm_chn2": {
            "trigger_source": 10,
            "trigger_mode": 1,
            "period": 80000,
            "offset": 0,
            "duty_time": 100,
            "threshold": 0,
            "adjust_step": 0
        },
        "lpwm_chn3": {
            "trigger_source": 10,
            "trigger_mode": 1,
            "period": 80000,
            "offset": 0,
            "duty_time": 100,
            "threshold": 0,
            "adjust_step": 0
        }
    }

JSON参数说明
^^^^^^^^^^^^^^^^^

- trigger_mode [0, 1]：LPWM触发方式，0为内部触发，1为外部触发。
- trigger_source [0, 10]：LPWM触发源，使用外部触发源需将trigger_mode设置为1。目前主要使用0和10，trigger周期分别为1s以及400ms。

============================== =======================================
trigger_source的值              对应的触发源                                        
============================== =======================================
 0                              aon_rtc_pps           
 1                              reserve     
 2                              pps0                     
 3                              pps1
 4                              pps2       
 5                              reserve
 6                              pcie0_ptm_pps
 7                              pcie1_ptm_pps
 8                              acore_eth0_pps
 9                              acore_eth1_pps
 10                             mcu_eth_pps
============================== =======================================

- period [0, 1000000 | period)us：LPWM方波周期。
- offset [2, 1000000)us：LPWM在每个trigger周期内第一个波形的偏移时间。
- duty_time [0, 4096 | period)us：LPWM触发波形的有效电平时间。
- threshold [0, 65535]us：缓慢同步功能阈值。
- adjust_step [0, 15]：每次的调整时间adjust_time = 2^adjust_step。

配置计算说明
~~~~~~~~~~~~~~~~~~

period/offset/duty_time
^^^^^^^^^^^^^^^^^^^^^^^^^

- LPWM的trigger源为PPS，常用周期为1s，在收到trigger信号后，首先进行一个offset的时间偏移，接着会输出连续方波，周期以及有效点平时间由配置所得，当下一个trigger信号到来，会重复偏移以及出波。

- offset设置依赖于sensor fps，如果fps不能被1s整除，则需要设置offset，反之offset设置为0，例如

- 常见场景如接入30fps sensor，period应设置为 1s/fps = 33333us。sensor在跑完30帧经过999,990us，与下次PPS trigger会有10us的间隙，因此offset应设为10us（至少10us，至多period - duty_time us，否则lpwm会在1s内发出31个方波）。

- 当offset设置10时测试发现，PPS会落在高电平区域，若关闭缓慢同步功能或者缓慢同步成功后需要走完高电平区域才能进入下一个trigger周期，即重新计算offset，此时可能存在trigger周期内LPWM波形未达到预期数量，导致曝光同步下sensor帧率降低，可将offset适当增加保证PPS每次一定落在低电平区域，输出预期的波形，经测试验证此时offset设置为11，可解决此问题。

.. code-block:: bash

    #快速公式计算：
    Period = 1000000 / fps
    Offset = 1000000 - Period * fps

.. image:: media/case.png

threshold/adjust_step
^^^^^^^^^^^^^^^^^^^^^^^^^

可能存在的情况：长时间未收到脉冲信号，对于需要offset校准的场景，lpwm输出方波无法校准，此时重新收到一个trigger信号时，ip如果强制按照新的trigger信号输出，
会与之前的lpwm输出波存在无法预计的相位差，一些sensor（sony）会对这种情况做出反应出现hang住的现象，导致出图异常。
解决方法：新增threshold寄存器与sync机制。
J6X新增sync机制：
当一个新的PPS信号到来时，会计算与上一个脉冲信号间的时间差diff，并进行以下计算：

.. code-block:: bash

    val = |diff+offset-period|
    val > threshold          ——>         PPS SYNC失败
    val <= threshold         ——>         PPS SYNC成功

.. image:: media/diff.png

pps sync失败之后会根据计算得出的jitter将period调整为 period ± adjust_time（2^adjust_step），并计算出需要n个周期后能够同步成功，在同步成功后将周期重新调整为period。
对可能出现的情况进行以下罗列：

.. image:: media/slowsync_1.png

.. image:: media/slowsync_2.png

.. image:: media/slowsync_3.png

可以对缓慢同步功能进行关闭，此时sync机制与J5相同，即任何情况下都会sync成功。

推荐使用配置
~~~~~~~~~~~~~~~~~~

LPWM本身无精度误差，造成同步条件下帧率未达到期望值的原因为pps的误差，LPWM在Acore，时钟来自26M晶振，MCU_PHC来自40M晶振，RTC来自32.768K晶振，三个晶振
频率都不一样，会引起较大的频率误差，因此最好使用相同晶振。实际使用需要确保pps稳定落在低电平区域，因此需要适当调大offset，有以下注意事项并给出推荐配置。

- acore eth pps误差最小。
- 全场景30fps/25fps，优先使用acore eth pps。
- 混合场景12.5/25 fps，需要使用mcu_eth_pps，只有400ms的pps周期才能满足使用要求。
- 其余混合场景例如10/30 fps，非小数数值，优先使用acore eth pps。

============ ================== ============== =========== ======== ==============
使用场景      trigger_source     trigger_mode   duty_time   offset   period                           
============ ================== ============== =========== ======== ==============
 全30fps      8(eth0)/9(eth1)    1              100         11       33333
 全25fps      8(eth0)/9(eth1)    1              100         11       40000
 12.5/25fps   10                 1              100         11       80000/40000            
 30/10fps     8(eth0)/9(eth1)    1              100         11       33333/100000       
============ ================== ============== =========== ======== ==============

LPWM debug
~~~~~~~~~~~~~~~~~~

J6X LPWM会被用于camsys与背光调节，这两个在驱动内部以instance为单位区分，即某个instance被其中一个申请后，剩余的channel也不能被另一个申请使用。camsys的调用通过
VIN NODE模块调用，sysfs属性节点主要打印一些信息debug使用。本章旨在介绍曝光同步相关内容，因此本小节只介绍camsys相关的LPWM sys节点信息。

LPWM sys节点如图所示，debug信息包括lpwm_config_info、lpwm_interrupt、trigger_source。

.. code-block:: bash

    root@hobot:~# ls /sys/devices/platform/soc/370f0000.lpwm0/
    driver           lpwm_config_info  modalias  power  subsystem                           trigger_source
    driver_override  lpwm_interrupt    of_node   pwm    supplier:platform:370f3000.pinctrl  uevent

- lpwm_config_info：通过cat lpwm_config_info可查看当前所用LPWM的4个channel的基本信息。

.. image:: media/info.png

- lpwm_interrupt：通过cat lpwm_interrupt 可查看当前LPWM的中断是否使能，同时可以通过如下方式打开或关闭LPWM中断。

.. code-block:: bash

    echo 1 > /sys/devices/platform/soc/370f0000.lpwm0/lpwm_interrupt #打开LPWM0的中断
    echo 0 > /sys/devices/platform/soc/370f0000.lpwm0/lpwm_interrupt #关闭LPWM0的中断
    cat /proc/interrupts | grep lpwm                                 #查看LPWM的中断次数

.. image:: media/inter.png

.. image:: media/inter_show.png

- trigger_source：通过cat trigger_source可查看LPWM支持的trigger_source，同时可以通过如下方式改变LPWM的trigger_source。

.. code-block:: bash

    echo x > /sys/devices/platform/soc/370f0000.lpwm0/trigger_source #x表示trigger_source，范围为[0 - 10]

.. image:: media/source.png
