mipi驱动研究

一,MIPI了解

MIPI 接口广泛用于摄像头,和显示部分;PHY 属于 MIPI 接口的最底层部分,也就是物理层,直接涉及到物理连线,信号传输等等,要搞清楚 MIPI 的数据传送,那么从 PHY 入手了解,是一个不错的选择;

既然是 PHY,那么肯定在 PHY 层上,还搭建了其他的硬件层次结构,这里暂时只关注 PHY 层,后面会结合更加上层的逻辑来分析;在 PHY 上,只负责定义数据物理层的收发标准;

MIPI 的 PHY 分为了 3 种:

D-PHY
C-PHY
M-PHY

他们之前有什么区别和联系呢?

D-PHY 最先出来,现在用得很多,传输速度快;M-PHY 再出来,速度最快,但是与 D-PHY 不兼容,而且受限于其他器件的发展,如此高的带宽,暂时用不上;最后出来的是 C-PHY,没有时钟信号,靠 pin 脚的编码来区分数据信号;

也就是说,M-PHY 现在基本没有应用场景,D-PHY 和 C-PHY 用得居多;

注意:C-PHY 和 D-PHY 的 C 和 D,不是 Camera 和 Display 的简写,只是 PHY 的命名;D-PHY 可以接 CSI 成为摄像头端的 PHY,也可以用在 DSI,作为 Display 的 PHY;C-PHY 也一样;

C-PHY是电压驱动型,由于是两两相差,信号幅度绝对值分别是0,100,200mv,信号较弱,不利于传输。D-PHY是电流驱动型,单信号幅度一般是200mv,线对差分的幅度在400mv左右,布线要求是等长且成双成对。

C-PHY和D-PHY最直观的差异是DPHY是源同步系统,有专门的同步时钟通道,但是CPHY没有同步时钟,时钟是嵌入到数据中的。显然的,实现嵌入时钟的目的是为了增加带宽,肯定会涉及到编码,物理层的结构必然是完全不同,单从线路上看,CPHY是一个A/B/C三线系统

      MIPI DPHY的物理层,咱们大家都很清楚,一对时钟,几对数据,接收端根据时钟边沿采样数据,找到0xB8的同步头,物理层实现就算是齐活了,但MIPI CPHY不同,因为它不传输时钟,那么要接收CPHY的数据,必须先恢复时钟,然后再用恢复的时钟采样数据并寻找同步头,最后还需要进行数据解码恢复出最初的发送的内容(发送端的过程相反)

MIPI D-PHY是差分串口传输,速度快,抗干扰。主流手机模组现在都是用MIPI传输,传输时使用4对差分信号传输图像数据和一对差分时钟信号;

ov13850 MIPI摄像头采用D-PHY,有三个电源:VDDIO(IO电源),AVDD(模拟电源),DVDD(内核数字电源),不同sensor模组的摄像头供电不同

MIPI传输模式
LP(Low-Power) 模式:用于传输控制信号,最高速率 10 MHz
HS(High-Speed)模式:用于高速传输数据,速率范围 [80 Mbps, 1Gbps] per Lane
传输的最小单元为 1 个字节,采用小端的方式及 LSB first,MSB last。

clk lane:
mipi csi clk 存在两种工作模式,一种是连续时钟模式,传输过程不会切换 LP 状态;另一种是非连续时钟信号模式,每传输完一帧图像数据,帧 blanking 时将会切换为 LP 状态。
从时序图可以看到,clk lane 也会有一个 LP11→LP01→LP00 的时序,从而进入 HS 模式。
如果是连续时钟信号模式,在 sensor 传输的图像数据的过程,帧间隔时,clk lane 不会切换到 LP 状态,即 LP11→LP01→LP00 时序只有一次;
如果是非连续时钟信号模式,每传输完一帧图像数据,都将会从 HS 模式切换回 LP 模式,在传输下一帧图像数据时,再从 LP 模式进入 HS 模式;
如果 camera sensor mipi clk lane 支持非连续时钟模式,建议配置为非连续时钟模式。

data lane:
在数据线上有 3 种可能的操作模式:Escape mode, High-Speed (Burst) mode and Control mode,下面是从停止状态进入相应模式需要的时序:
Escape mode
进入时序:LP11→LP10→LP00→LP01→LP00
退出时序:LP10→LP11
High-Speed mode
进入时序:LP11→LP01→LP00→SoT(0001_1101)
退出时序:EoT→LP11
时序图如下:
Turnaround
进入时序:LP11→LP10→LP00→LP10→LP00
退出时序:LP00→LP10→LP11

D-PHY规范的2.0版的标准

二,关键数据

MIPI接口在系统的实现如上图所示,MIPI DPHY提供了4 Lane的Rx接口,由Sensor提供Clock,并通过四条数据Lane输入图像数据。DPHY与CSI-2 Host Contrller之间通过PPI(PHY-Protocol Interface)相连,该接口包括了控制,数据,时钟等多条信号。CSI-2 Host Contrller通过PPI接口收到数据后进行解析,完成后通过IDI(Image Data Interface)或者IPI(Image Pixel Interface)输出到SoC的其他模块(ISP);同时SoC通过APB Slave总线控制CSI-2 Host Contrller的相关寄存器。

PPI(Phy Protocal Interface)接口:

PPI接口主要分成四部分,分别为

(1)    Data: 用于传输HS/LP模式下数据;

(2)    Clock: 用于传输数据时钟控制;

(3)    Interface Control: 接口控制,如Shutdown信号;

(4)    Error Signal: 用于传输错误信号;

(5)几个用于实现Synopsys专用命令控制的TEST信号;

IDI(Image Data Interface )接口:

-       输出CSI-2 规格书中推荐的32位或者64位的图像格式;

-       提供水平或竖直方向的精确视频同步信号;

-       提供CSI-2 Data Type (DT) 和 Virtual Channel (VC)的相关信息;

 IPI(Image Pixel Interface)接口:

-       48 位并行输出,提供像素时钟同步信号;

-       提供水平或竖直方向的精确视频同步信号;

MIPI DPHY采用1对源同步的差分时钟和1~4对差分数据链路来进行数据传输,数据传输采用DDR方式,即在时钟的上下边沿都有数据传输;根据Sensor不同的输出链路数量,接收端可以配置1到4条输入链路;数据链路越多,图像数据传输速度也就越快,多通道发送实现如上图所示,接收端与其类似,从不同通道接收的数据加以合并最后形成完整的数据流。

Interface Control:

SHUTDOWNZ:

用于关闭DPHY的信号;

ENABLE_N:

用于使能第N条Lane的信号;

RSTZ:

用于Reset DPHY的信号;

Clock

STOPSTATEDATA_N:

用于指示第N条Lane处于停止状态;

RXBYTECLKHS

High-Speed数据接收时钟(Byte为单位),即用于PPI接口图像数据的同步时钟;

STOPSTATECLK

用于指示时钟通道处于停止状态;

RXULPSCLKNOT

用于指示时钟通道处于ULP(Ultra Low Power)模式;

RXCLKACTIVEHS

用于指示时钟通道处于高速数据接收状态;

Data

RXDATAHS_N[7:0]

高速模式下数据传输线,在RXBYTECLKHS的上升沿被传输;

RXACTIVEHS_N

用于指示当前处于高速数据接收状态;

RXVALIDHS_N

用于指示当前正在接收有效的数据(从Phy层到协议层);

ERRSOTHS_N

用于指示高速数据传输时发生SoT(Start of Transmission)错误,但是信号仍然可以被同步;每个RXBYTECLKHS周期检测一次;

ERRSOTSYNCHS_N

用于指示高速数据传输时发生SoT(Start of Transmission)错误,信号也无法被同步;每个RXBYTECLKHS周期检测一次;

ERRESC_N  

用于指示Escape模式进入错误,会一直保持错误状态直至返回停止状态;

TEST信号

TESTDIN[7:0]

Vendor专用数据输入信号

TESTDOUT[7:0]

Vendor专用数据输出信号

TESTCLK

Vendor专用信号时钟

TESTEN

Vendor专用信号使能

TESTCLR

Vendor专用信号清除

(1)数据Lane序号对应PPI接口中的序号N,例如数据Lane0对应的输出数据线为RXDATAHS_0[7:0];

  (2)  MIPI DPHY使用DDR(Double Date Rate)时钟,即上升沿和下降沿都能传输数据,所以一个时钟周期传输2bit;

  (3) 假设clk lane频率为200Mhz, 那么数据lane的bit clk = 200*2 = 400Mbps; 对应的RXBYTECLKHS字节时钟为400/8=50MBps;

  (4) 每个数据Lane对应的PPI都有8根数据线(RXDATAHS_N[7:0],如果配置为1条Lane,则每个RXBYTECLKHS时钟周期传输1个Byte; 如果配置为2条Lane,则每个RXBYTECLKHS时钟周期传输2个Byte;如果配置为4条Lane,则每个RXBYTECLKHS时钟周期传输4个Byte;

DPHY的状态切换

DPHY支持HS(High Speed)和LP(Low Power)两种工作模式。HS模式下采用低压差分信号,功耗较大,但是可以传输很高的数据速率(数据速率为80M~1.5Gbps); LP模式下采用单端信号,数据速率很低(<10Mbps),但是相应的功耗也很低。两种模式的结合保证了MIPI总线在需要传输大量数据(如图像)时可以高速传输,而在不需要大数据量传输时又能够减少功耗。

三,mipi驱动研究

1,mipi寄存器写入方法

用于实现Synopsys专用命令控制的TEST信号
GRF_DPHY_RX0_TESTDIN[7:0]:Vendor专用数据输入信号
GRF_DPHY_RX0_TESTCLK:Vendor专用信号时钟
GRF_DPHY_RX0_TESTEN:Vendor专用信号使能
GRF_DPHY_RX0_TESTDOUT[7:0]:Vendor专用数据输出信号

https://rockchip.fr/RK3288%20TRM/rk3288-chapter-35-mipi-csi-phy.pdf

中描述,设置D-PHY为RX模式的方法:

If you want the D-PHY work as for RX, you must set grf_dphy_tx1rx1_masterslavez = 1’b1,
and set grf_dphy_tx1rx1_basedir = 1’b1, then you must select the data from D-PHY RX1 to
CSI Host or ISP by setting grf_con_isp_dphy_sel (bit[1] of GRF_SOC_CON6)

2,mipi链路速率计算方法

数据总线频率。数据总线频率与媒体总线像素代码、总线类型(每个采样的时钟周期)一起定义像素阵列中的像素速率(V4L2_CID_PIXEL_RATE),(如果设备不是图像sensor,则可能在其他地方)。帧速率可以通过像素时钟、图像宽度和高度以及水平和垂直消隐来计算。虽然像素速率控制可以在包含像素阵列的子设备之外的其他地方定义,但无法从该信息获得帧速率。这是因为只有在像素阵列上,才能假定垂直和水平消隐信息是准确的:像素阵列中不允许其他消隐。帧速率的选择通过选择所需的水平和垂直消隐来执行。这种控制的单位是赫兹

链路的速率,即是MIPI-CSI 的clock
在摄像头驱动中,link_freq_menu_items是一个参数或变量,用于表示可选的链接频率列表。链接频率指的是摄像头与主机之间的通信速度。
确定link_freq_menu_items的值需要考虑以下几个因素:
摄像头硬件支持的链接频率范围。
主机系统支持的链接频率范围。
应用场景对链接频率的需求和性能要求。
举个例子,假设某款摄像头硬件支持以下三种链接频率:100 MHz、200 MHz、300 MHz。而主机系统对这些频率都提供了相应的接口和支持。
那么,在设置link_freq_menu_items时可以将其定义为一个包含这三种链接频率的数组
关于pixelclock 有如下关系:
pclk = (HTS*VTS)*fps = (width + HBLANK)*(height + VBLANK)*fps
关于曝光时间,计算如下:
exposure_line_time = HTS/pclk;
exposure_time = exposure_line_time * exposure_line
曝光时间就是开启一次快门,能曝光多少行。一帧图像需要多次曝光来完成。
exposure_line 是曝光的行数,一般是一个寄存器,用来控制曝光时间是多少倍的行曝光时间(exposure_line_time)。曝光行数最大就是VTS。
MIPI-CSI 链路速率计算公式如下:
link_freq = (pclk * bits_per_sample) /(2*nr_of_lanes)
变量                   描述
nr_of_lanes    Number of data lanes used on the CSI-2 link. This can be obtained from the OF endpoint configuration.CSI-2链路上使用的数据通道数。这可以从OF端点配置中获得
2                 Two bits are transferred per clock cycle per lane.每个通道每个时钟周期传输两个比特
bits_per_sample      Number of bits per sample
当描述单一像素的时候,位深的概念可以被定义为 BPP(bit per pixel,每个像素的位深)或者是 BPS(bit per sample,每个样本的位深),
这指明了用于描述一个像素的比特总数。当描述单一像素的单一色彩分量时候,位深的概念可以被定义为 BPC(bit per channel,每个通道的位深)或者是 BPC(bit per color,每个色彩的位深)

3,mipi txrx stream on方法

/*
     *Config rk3288:
     *step1:rk3288 isp connected to phy1-rx
     *step2:rk3288 phy1-rx test bus connected to csi host
     *step3:rk3288 phy1-rx source selected as: isp = 1'b1,csi-host = 1'b0
If you want the D-PHY work as for RX, you must set grf_dphy_tx1rx1_masterslavez = 1’b1,
and set grf_dphy_tx1rx1_basedir = 1’b1, then you must select the data from D-PHY RX1 to
CSI Host or ISP by setting grf_con_isp_dphy_sel (bit[1] of GRF_SOC_CON6)

**/
    write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);/**PHY_REG(0x25c, 1, 1)**/
    write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);/**PHY_REG(0x25c, 1, 14)**/
    if (is_linked_isp)
        write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);/**PHY_REG(0x27c, 1, 13)**/
    else
        write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 0);


    /* Belowed is the sequence of mipi configuration */
    /* Step1: set RSTZ = 1'b0, phy1-rx controlled by isp */
    /* Step2: set SHUTDOWNZ = 1'b0, phy1-rx controlled by isp */
    /**RSTZ用于Reset DPHY的信号
     * SHUTDOWNZ用于关闭DPHY的信号**/
     /**
#define CIF_MIPI_CTRL_OUTPUT_ENA        BIT(0)
#define CIF_MIPI_CTRL_FLUSH_FIFO        BIT(1)
#define CIF_MIPI_CTRL_SHUTDOWNLANES(a)        (((a) & 0xF) << 8)
#define CIF_MIPI_CTRL_NUM_LANES(a)        (((a) & 0x3) << 12)
#define CIF_MIPI_CTRL_ERR_SOT_HS_SKIP        BIT(16)
#define CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP    BIT(17)
#define CIF_MIPI_CTRL_CLOCKLANE_ENA        BIT(18)
     **/
    if (!is_linked_isp) {
        write_txrx_reg(priv, TXRX_PHY_RSTZ, 0);
        write_txrx_reg(priv, TXRX_PHY_SHUTDOWNZ, 0);
    } else {
        mipi_ctrl = readl(isp_mipi_ctrl);
        /**shutdown模式是最低电源消耗模式,RSTZ和SHUTDOWNZ被拉低,此时模拟电路禁止,数字部分被复位,主要消耗来自模拟电路的待机电流
        和数字部分的漏电流。在此模式下,DATAN和CLKN/CLKP都处于高阻态,但可以通过TESTCLR/TESTDIN信号进行hsfreqrange频率的配置**/
        mipi_ctrl &= 0xfffff0ff;/**CIF_MIPI_CTRL_SHUTDOWNLANES**/
        writel(mipi_ctrl, isp_mipi_ctrl);
    }

    /* Step3: set TESTCLR= 1'b1,TESTCLK=1'b1 */
    /***
    TESTCLKVendor专用信号时钟,这个操作触发testclk(bit2),并将testddatain的数据采样生成一个新的test code
    TESTCLRVendor专用信号清除,这个操作激活接口初始化
    **/
    write_txrx_reg(priv, TXRX_PHY_TEST_CTRL0, PHY_TESTCLR | PHY_TESTCLK);
    usleep_range(100, 150);

    /* Step4: apply REFCLK signal with the appropriate frequency */

    /* Step5: apply CFG_CLK signal with the appropriate frequency */

    /*
     * Step6: set MASTERSLAVEZ = 1'b0 (for SLAVE),
     *        phy1 is set as slave,controlled by isp
     */
    write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);

    /*
     * Step7: set BASEDIR_N = 1’b1 (for SLAVE),
     *        phy1 is set as slave,controlled by isp
     */
    write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);

    /* Step8: set all REQUEST inputs to zero, need to wait to take effective */
    /**它的PHY层将TurnRequest作为协议层的输入**/
    /* Disable lan turn around, which is ignored in receive mode */
    write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);/**PHY_REG(0x268, 4, 4)**/
    write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);/**PHY_REG(0x268, 4, 8)**/
    write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST,0);/**PHY_REG(RK3288_GRF_SOC_CON15, 4, 4)**/
    write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);/**PHY_REG(0x268, 4, 0)**/
    /* Step9: Wait for taking effective */
    usleep_range(100, 150);

    /* Step10: set TESTCLR=1'b0,TESTCLK=1'b1 need to wait to take effective */
    write_txrx_reg(priv, TXRX_PHY_TEST_CTRL0, PHY_TESTCLK);

    /* Step11: Wait for taking effective */
    usleep_range(100, 150);

    /*
     * Step12: configure Test Code 0x44 hsfreqrange according to values
     * step12.1:set clock lane
     * step12.2:set hsfreqrange by lane0(test code 0x44)
     */
    /***clock lane: continue mode: non-continue mode?
    continue mode: 连续时钟模式,即无论data lane上是否有数据在传输,clock lane总是在HS状态,从不进入LP状态
    non-continue mode:非连续时钟模式, 即当data lane 进入LP状态时,clock lane也会进入LP状态,在下次data lane进入HS状
    态时 clock lane会先于 data lane进入提前进入HS状态
    **/

    hsfreq <<= 1;
    /* HS hsfreq_range & lane 0  settle bypass */
    mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0, true);
    /* HS RX Control of lane0 */
    mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq, true);
    mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0, true);
    mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0, true);
    mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0, true);

    /* Step13: Configure analog references: of Test Code 0x22 */

    /*
     * Step14: Set ENABLE_N=1'b1, need to wait 5ns
     * Set lane num:
     * for 3288,controlled by isp,enable lanes actually
     * is set by grf_soc_con9[12:15];
     * for 3399,controlled by isp1,enable lanes actually
     * is set by isp1,
     * if run 3399 here operates grf_soc_con23[0:3]
     */
    if (is_linked_isp) {
        mipi_ctrl = readl(isp_mipi_ctrl);

        /*
         * Step15.1: Set ENABLE_N=1'b1, controlled by isp
         *             with isp_mipi_ctrl[18]
         */
        mipi_ctrl &= 0xfffbffff;
        mipi_ctrl |= 0x00040000;
/**CIF_MIPI_CTRL_CLOCKLANE_ENA**/

        /*
         * Step15.2: set lane num, controlled by isp
         *            with isp_mipi_ctrl[13:12]
         */
        if (sensor->lanes == 4)
            mipi_ctrl |= 0x00003000;
        else if (sensor->lanes == 3)
            mipi_ctrl |= 0x00002000;
        else if (sensor->lanes == 2)
            mipi_ctrl |= 0x00001000;
        else if (sensor->lanes == 1)
            mipi_ctrl &= 0xffff0fff;
/**CIF_MIPI_CTRL_NUM_LANES(a) (((a) & 0x3) << 12)**/

        writel(mipi_ctrl, isp_mipi_ctrl);

        write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
                  GENMASK(sensor->lanes - 1, 0));
/*PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),0x0268*/
    } else {
        write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLECLK, 1);
        write_txrx_reg(priv, TXRX_PHY_N_LANES, sensor->lanes - 1);
    }

    /* Step15: Wait for taking effective */
    usleep_range(100, 150);

    /*
     * Step16:Set SHUTDOWNZ=1'b1, phy1-rx controlled by isp,
     *        need to wait to take effective
     */

    /* Step17: Wait for taking effective */
    /* Step18:Set RSTZ=1'b1, phy1-rx controlled by isp*/
    if (!is_linked_isp) {
        write_txrx_reg(priv, TXRX_PHY_SHUTDOWNZ, 1);
        usleep_range(100, 150);
        write_txrx_reg(priv, TXRX_PHY_RSTZ, 1);
        write_txrx_reg(priv, TXRX_PHY_RESETN, 1);
    } else {
        mipi_ctrl = readl(isp_mipi_ctrl);
        mipi_ctrl |= 0x00000f00;
/**CIF_MIPI_CTRL_SHUTDOWNLANES(a)        (((a) & 0xF) << 8)**/
        writel(mipi_ctrl, isp_mipi_ctrl);
        dev_info(priv->dev, "enable mipi phy mipi_ctrl:0x%x\n", mipi_ctrl);

    }
    /**
    mipi DPHY 的规格书里对 data rate 有定义上下限值,最低 data rate为 80M bps, 最高为 2500M bps。
    由于mipi 传输时是双采样, 这样的话, 实际的差分时钟的上下限就是 40MHz - 1250 MHz
    mipi csi 的工作时钟是 10MHz, 这意味着 DPHY 的 data rate 不能超过 80M
    (这是DPHY data rate 的下限), 即实际的差分时钟不能超过40M**/

    /* Step19: d-phy calibration */
    //if (priv->data_rate_mbps >= 875)
    //    mipidphy1_calibration(priv);

    /*
     * Step20:Wait until STOPSTATEDATA_N & STOPSTATECLK
     *        outputs are asserted
     */
    usleep_range(100, 150);

4,isp控制mipi

Configure MIPI controller 

rkisp1_config_mipi

        数据ID(数据标识符)字段,长度为1个字节,包含了虚拟通道VC和数据类型DT字段。VC字段在DI字节里的最高的两位中体现。数据类型DT字段是DI字节里的低6位

        虚拟通道ID(虚拟通道标识符)字段,是一个4-bit或5-bit的ID,用来区分数据流中不同的逻辑数据通道

{
        .mbus_code    = MEDIA_BUS_FMT_SBGGR10_1X10,
        .fmt_type    = FMT_BAYER,
        .mipi_dt    = CIF_CSI2_DT_RAW10,
        .bayer_pat    = RAW_BGGR,
        .bus_width    = 10,
    }        

emd_vc = 0xFF;
        emd_dt = 0;        

        mipi_ctrl = CIF_MIPI_CTRL_NUM_LANES(lanes - 1) |
                CIF_MIPI_CTRL_SHUTDOWNLANES(0xf) |
                CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP |
                CIF_MIPI_CTRL_CLOCKLANE_ENA;

        writel(mipi_ctrl, base + CIF_MIPI_CTRL);

        /* Configure Data Type and Virtual Channel */
        writel(CIF_MIPI_DATA_SEL_DT(in_fmt->mipi_dt) | CIF_MIPI_DATA_SEL_VC(0),
                   base + CIF_MIPI_IMG_DATA_SEL);

        writel(CIF_MIPI_DATA_SEL_DT(emd_dt) | CIF_MIPI_DATA_SEL_VC(emd_vc),
               base + CIF_MIPI_ADD_DATA_SEL_1);
        writel(CIF_MIPI_DATA_SEL_DT(emd_dt) | CIF_MIPI_DATA_SEL_VC(emd_vc),
               base + CIF_MIPI_ADD_DATA_SEL_2);
        writel(CIF_MIPI_DATA_SEL_DT(emd_dt) | CIF_MIPI_DATA_SEL_VC(emd_vc),
               base + CIF_MIPI_ADD_DATA_SEL_3);
        writel(CIF_MIPI_DATA_SEL_DT(emd_dt) | CIF_MIPI_DATA_SEL_VC(emd_vc),
               base + CIF_MIPI_ADD_DATA_SEL_4);

        /* Clear MIPI interrupts */
        writel(~0, base + CIF_MIPI_ICR);
        /*
         * Disable CIF_MIPI_ERR_DPHY interrupt here temporary for
         * isp bus may be dead when switch isp.
         */
        writel(CIF_MIPI_FRAME_END | CIF_MIPI_ERR_CSI | CIF_MIPI_ERR_DPHY |
               CIF_MIPI_SYNC_FIFO_OVFLW(0x0F) | CIF_MIPI_ADD_DATA_OVFLW,
               base + CIF_MIPI_IMSC);

Configure MUX

    u32 dpcl = readl(dev->base_addr + CIF_VI_DPCL);

    if (sensor && sensor->mbus.type == V4L2_MBUS_CSI2) {
        ret = rkisp1_config_mipi(dev);
        dpcl |= CIF_VI_DPCL_IF_SEL_MIPI;
        dev->isp_inp = INP_CSI;
    } 

    writel(dpcl, dev->base_addr + CIF_VI_DPCL);

Activate MIPI

    if (sensor && sensor->mbus.type == V4L2_MBUS_CSI2) {
        {
            val = readl(base + CIF_MIPI_CTRL);
            writel(val | CIF_MIPI_CTRL_OUTPUT_ENA,
                   base + CIF_MIPI_CTRL);
        }
    }

Configure MIPI ISR

    writel(~0, base + CIF_MIPI_ICR);

    /*
     * Disable DPHY errctrl interrupt, because this dphy
     * erctrl signal is asserted until the next changes
     * of line state. This time is may be too long and cpu
     * is hold in this interrupt.
     */
    if (mis & CIF_MIPI_ERR_DPHY) {
        val = readl(base + CIF_MIPI_IMSC);
        writel(val & ~CIF_MIPI_ERR_DPHY, base + CIF_MIPI_IMSC);
        dev->isp_sdev.dphy_errctrl_disabled = true;
    }

    /*
     * Enable DPHY errctrl interrupt again, if mipi have receive
     * the whole frame without any error.
     */
    if (mis == CIF_MIPI_FRAME_END) {
        /*
         * Enable DPHY errctrl interrupt again, if mipi have receive
         * the whole frame without any error.
         */
        if (dev->isp_sdev.dphy_errctrl_disabled) {
            val = readl(base + CIF_MIPI_IMSC);
            val |= CIF_MIPI_ERR_DPHY;
            writel(val, base + CIF_MIPI_IMSC);
            dev->isp_sdev.dphy_errctrl_disabled = false;
        }
    } else {
        v4l2_warn(v4l2_dev, "MIPI mis error: 0x%08x\n", mis);
        val = readl(base + CIF_MIPI_CTRL);
        writel(val | CIF_MIPI_CTRL_FLUSH_FIFO, base + CIF_MIPI_CTRL);
    }

精简如下代码// 定义WiFi 11n MCS参数结构体 typedef struct { QString MCS;//MCS double datarate_20M; // 20MHz带宽数据速率(Mbps) double datarate_40M; // 40MHz带宽数据速率(Mbps) double datarate_80M; // 40MHz带宽数据速率(Mbps) double datarate_160M; // 40MHz带宽数据速率(Mbps) double datarate_320M; // 40MHz带宽数据速率(Mbps) QString modulation;//调制方式 double EVM; int sensitivity_20M; // 20MHz接收灵敏度(dBm) } Wifi11McsParams;// MCS参数对照表(MCS0-MCS7) const Wifi11McsParams wifi11n_mcs_table[] = { // MCS |datarate_20M | datarate_40M |datarate_80M|datarate_160M|datarate_320M|调制方式|EVM|sensitivity_20M {"MCS0", 7.2, 15.0, 0, 0, 0, "BPSK", -5, -82 }, // MCS0 {"MCS1", 14.2, 30.0, 0, 0, 0, "QPSK", -10, -79 }, // MCS1 {"MCS2",21.7, 45.0, 0, 0, 0, "QPSK", -13, -77 }, // MCS2 {"MCS3",28.9, 60.0, 0, 0, 0, "16-QAM", -16, -74 }, // MCS3 {"MCS4", 43.3, 90.0, 0, 0, 0, "16-QAM", -19, -70 }, // MCS4 {"MCS5", 57.8, 120.0, 0, 0, 0, "64-QAM", -22, -66 }, // MCS5 {"MCS6", 65.0, 135.0, 0, 0, 0, "64-QAM", -25, -65 }, // MCS6 {"MCS7", 72.2, 150.0, 0, 0, 0, "64-QAM", -28, -64 } // MCS7 }; if(ui->bw_2->currentText()=="20") { dataratemcs0=wifi11n_mcs_table[0].datarate_20M; dataratemcs1=wifi11n_mcs_table[1].datarate_20M; dataratemcs2=wifi11n_mcs_table[2].datarate_20M; dataratemcs3=wifi11n_mcs_table[3].datarate_20M; dataratemcs4=wifi11n_mcs_table[4].datarate_20M; dataratemcs5=wifi11n_mcs_table[5].datarate_20M; dataratemcs6=wifi11n_mcs_table[6].datarate_20M; dataratemcs7=wifi11n_mcs_table[7].datarate_20M; } else if(ui->bw_2->currentText()=="40") { dataratemcs0=wifi11n_mcs_table[0].datarate_40M; dataratemcs1=wifi11n_mcs_table[1].datarate_40M; dataratemcs2=wifi11n_mcs_table[2].datarate_40M; dataratemcs3=wifi11n_mcs_table[3].datarate_40M; dataratemcs4=wifi11n_mcs_table[4].datarate_40M; dataratemcs5=wifi11n_mcs_table[5].datarate_40M; dataratemcs6=wifi11n_mcs_table[6].datarate_40M; dataratemcs7=wifi11n_mcs_table[7].datarate_40M; } else if(ui->bw_2->currentText()=="80") { dataratemcs0=wifi11n_mcs_table[0].datarate_80M; dataratemcs1=wifi11n_mcs_table[1].datarate_80M; dataratemcs2=wifi11n_mcs_table[2].datarate_80M; dataratemcs3=wifi11n_mcs_table[3].datarate_80M; dataratemcs4=wifi11n_mcs_table[4].datarate_80M; dataratemcs5=wifi11n_mcs_table[5].datarate_80M; dataratemcs6=wifi11n_mcs_table[6].datarate_80M; dataratemcs7=wifi11n_mcs_table[7].datarate_80M; }
04-04
<think>嗯,用户让我帮忙精简一段C++代码。首先,我需要仔细看看用户提供的代码,了解它的结构和功能。 首先,用户定义了一个结构体Wifi11McsParams,里面包含了WiFi 11n MCS的各种参数,比如数据速率、调制方式、EVM和接收灵敏度等。然后创建了一个数组wifi11n_mcs_table,存储了MCS0到MCS7的参数。接下来的部分是根据带宽选择不同的数据速率,赋值给dataratemcs0到dataratemcs7这些变量。 用户想精简的是后面的条件判断部分,也就是根据ui->bw_2->currentText()的值来选择不同的带宽,然后分别赋值。现在这部分代码有很多重复,比如每个条件分支里都重复写了对dataratemcs0到dataratemcs7的赋值,只是选用的结构体字段不同(datarate_20M、datarate_40M等)。 首先,我要考虑如何减少重复代码。通常这种情况下,可以使用循环来替代逐个赋值。但在这段代码中,每个dataratemcs变量对应数组中的不同索引,比如dataratemcs0对应wifi11n_mcs_table[0],dataratemcs1对应[1],依此类推。所以可以考虑将这些变量存储在一个数组或列表中,然后通过循环来赋值。 另外,用户的条件判断是根据带宽选择不同的字段,这里可以用一个映射表(比如std::map或QHash)将带宽字符串(如"20")映射到对应的结构体成员指针,这样就可以避免多个if-else分支。 不过,C++中访问结构体成员需要通过成员指针,这可能稍微复杂一些。或者,可以将带宽字符串映射到一个枚举类型,表示不同的带宽选项,然后根据枚举值选择对应的字段。 另外,注意到代码中对于80MHz、160MHz、320MHz的处理,原来的结构体中字段名称可能有误,比如datarate_80M、datarate_160M、datarate_320M的注释都是“40MHz带宽数据速率”,这应该是笔误,但用户可能希望保持结构体定义的正确性。不过这可能不属于代码精简的范围,但需要留意。 接下来,我需要考虑如何将条件判断转换为更简洁的形式。例如,使用QHash<QString, double Wifi11McsParams::*>来映射带宽字符串到对应的成员指针。这样,根据currentText()获取到对应的成员指针后,就可以循环处理每个MCS索引,并赋值给对应的dataratemcs数组。 不过,用户原来的变量是dataratemcs0到dataratemcs7,这些可能被声明为单独的变量。如果将它们改为数组,比如datarateMCS[8],那么就可以用循环来处理,这样会大大减少代码量。例如: double datarateMCS[8]; // 替换原来的dataratemcs0到dataratemcs7 然后,根据带宽选择对应的成员指针,再循环0到7,赋值datarateMCS[i] = wifi11n_mcs_table[i].*member_ptr; 不过,用户原来的代码中,可能这些变量是类的成员变量,比如dataratemcs0到dataratemcs7是类的成员,所以可能需要保持原来的变量名。这时候,或许可以用数组或引用来处理,或者使用指针数组来引用这些变量。 但这样可能会比较复杂,尤其是如果原来的变量是分散的成员变量。如果用户无法更改变量为数组形式,可能需要另一种方法。 另一种方法是使用宏或者函数来减少重复,但可能不够直观。 或者,可以定义一个结构体成员的指针数组,然后根据带宽选择对应的成员指针数组,再循环赋值。 例如: 首先,确定带宽对应的成员。比如,"20"对应datarate_20M,"40"对应datarate_40M,等等。然后,对于每个MCS索引i,将wifi11n_mcs_table[i].对应的成员赋值给对应的dataratemcs变量。 但原来的代码中,每个带宽分支里的赋值都是按照顺序对每个dataratemcs变量进行赋值,索引从0到7。如果用户可以将这些dataratemcs变量存储在一个数组中,比如datarateMCS[8],那么可以用循环来处理: for (int i = 0; i < 8; ++i) { datarateMCS[i] = wifi11n_mcs_table[i].*member_ptr; } 但用户原来的变量是单独的,比如dataratemcs0到dataratemcs7,可能需要将它们存入一个数组中,或者在赋值时使用循环加索引的方式。例如,如果这些变量是连续的内存,可以用指针数组来引用,但这种方法可能存在风险,尤其是如果变量声明顺序改变的话。 或者,使用一个临时数组来保存这些变量的指针,然后循环处理: double* datarates[] = {&dataratemcs0, &dataratemcs1, ..., &dataratemcs7}; 然后,循环i从0到7: *datarates[i] = wifi11n_mcs_table[i].*member_ptr; 这样就能用循环替代重复的赋值语句。 综上,精简代码的步骤可能包括: 1. 确定带宽对应的结构体成员(如datarate_20M,datarate_40M等)。 2. 将dataratemcs0到dataratemcs7存储在一个指针数组中,方便循环访问。 3. 根据带宽选择对应的成员指针,然后循环赋值。 这样可以将原来的多个if-else分支和重复的赋值语句简化为一个映射查找和一个循环。 此外,检查结构体中的字段注释是否正确。例如,datarate_80M、datarate_160M、datarate_320M的注释写成了“40MHz带宽数据速率”,这显然是错误的,需要修正。但用户可能希望保持结构体不变,或者可能这是一个笔误,需要指出。 不过用户的问题主要是精简代码,所以可能注释的问题可以忽略,或者作为建议提出。 现在,具体实现的话,可以使用C++的成员指针特性。例如: // 定义成员指针类型 using DataRateMember = double Wifi11McsParams::*; // 创建映射表 QHash<QString, DataRateMember> bandwidthToMember = { {"20", &Wifi11McsParams::datarate_20M}, {"40", &Wifi11McsParams::datarate_40M}, {"80", &Wifi11McsParams::datarate_80M}, {"160", &Wifi11McsParams::datarate_160M}, {"320", &Wifi11McsParams::datarate_320M} }; 然后,获取当前带宽: QString bw = ui->bw_2->currentText(); auto it = bandwidthToMember.find(bw); if (it != bandwidthToMember.end()) { DataRateMember member = it.value(); for (int i = 0; i < 8; ++i) { // 这里需要将dataratemcs0到dataratemcs7存入数组或指针数组 // 例如,假设有一个数组dataretes[i] datarates[i] = wifi11n_mcs_table[i].*member; } } else { // 处理错误情况 } 但用户原来的变量是dataratemcs0到dataratemcs7,这些可能不是数组,所以需要将它们存入一个数组中。例如: double* datarates[] = { &dataratemcs0, &dataratemcs1, &dataratemcs2, &dataratemcs3, &dataratemcs4, &dataratemcs5, &dataratemcs6, &dataratemcs7 }; 这样,循环时就可以: for (int i = 0; i < 8; ++i) { *datarates[i] = wifi11n_mcs_table[i].*member; } 这样,整个条件判断部分就可以替换为查找映射表和循环赋值,大大减少代码量。 此外,需要处理带宽不在映射表中的情况,比如默认值或错误处理。 所以,最终的代码精简步骤是: 1. 创建带宽到结构体成员指针的映射。 2. 创建dataratemcs变量的指针数组。 3. 查找当前带宽对应的成员指针。 4. 循环赋值。 此外,检查结构体中的字段名称是否正确。例如,原结构体中的datarate_80M、datarate_160M、datarate_320M的注释都是“40MHz带宽数据速率”,这明显错误,需要修正为对应的带宽值。但用户可能希望保持结构体不变,或者在问题中没有提到这一点,所以可能作为建议提出,或者在代码精简时忽略。 最后,测试精简后的代码是否功能一致,确保每个带宽选项正确赋值对应的数据速率。</think>以下是代码的精简方案及分步说明: 一、原代码分析 问题核心在于重复的if-else结构和大量重复赋值语句,可通过以下方式优化: 二、优化步骤 1. 创建带宽与结构体成员的映射关系 ```cpp // 使用成员指针建立带宽与数据速率的映射 using DataRateMember = double Wifi11McsParams::*; QHash<QString, DataRateMember> bandwidthMap = { {"20", &Wifi11McsParams::datarate_20M}, {"40", &Wifi11McsParams::datarate_40M}, {"80", &Wifi11McsParams::datarate_80M}, {"160", &Wifi11McsParams::datarate_160M}, {"320", &Wifi11McsParams::datarate_320M} }; ``` 2. 创建目标变量指针数组 ```cpp // 将离散变量组织为数组 double* datarates[] = { &dataratemcs0, &dataratemcs1, &dataratemcs2, &dataratemcs3, &dataratemcs4, &dataratemcs5, &dataratemcs6, &dataratemcs7 }; ``` 3. 统一赋值逻辑 ```cpp QString bw = ui->bw_2->currentText(); if (bandwidthMap.contains(bw)) { DataRateMember member = bandwidthMap[bw]; for (int i = 0; i < 8; ++i) { *datarates[i] = wifi11n_mcs_table[i].*member; } } else { // 处理无效带宽值 qWarning() << "Invalid bandwidth:" << bw; } ``` 三、优化前后对比 | 优化点 | 原代码行数 | 新代码行数 | 优化效果 | |-------------------|------------|------------|----------------------------| | 条件判断结构 | 24行 | 5行 | 减少79%冗余代码 | | 数据赋值操作 | 24条 | 循环8次 | 消除重复劳动 | | 可维护性 | 低 | 高 | 新增带宽只需修改映射表 | 四、注意事项 1. 结构体字段修正建议: ```cpp // 原注释错误修正建议 double datarate_80M; // 80MHz带宽数据速率(Mbps) double datarate_160M; // 160MHz带宽数据速率(Mbps) double datarate_320M; // 320MHz带宽数据速率(Mbps) ``` 2. 扩展性增强:如需支持新带宽(如160MHz),只需在`bandwidthMap`中添加对应条目即可。 五、完整优化代码 ```cpp // 定义成员指针类型 using DataRateMember = double Wifi11McsParams::*; // 带宽与结构体成员的映射 QHash<QString, DataRateMember> bandwidthMap = { {"20", &Wifi11McsParams::datarate_20M}, {"40", &Wifi11McsParams::datarate_40M}, {"80", &Wifi11McsParams::datarate_80M}, {"160", &Wifi11McsParams::datarate_160M}, {"320", &Wifi11McsParams::datarate_320M} }; // 目标变量数组 double* datarates[] = { &dataratemcs0, &dataratemcs1, &dataratemcs2, &dataratemcs3, &dataratemcs4, &dataratemcs5, &dataratemcs6, &dataratemcs7 }; // 统一处理逻辑 QString bw = ui->bw_2->currentText(); if (auto it = bandwidthMap.find(bw); it != bandwidthMap.end()) { DataRateMember member = it.value(); for (int i = 0; i < 8; ++i) { *datarates[i] = wifi11n_mcs_table[i].*member; } } else { qWarning() << "Unsupported bandwidth:" << bw; } ``` 此方案通过数据驱动的方式,将面向过程的代码转化为面向数据的编程模式,符合软件开发中的DRY(Don't Repeat Yourself)原则,显著提升代码的可维护性和扩展性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值