union和bit_field巧妙进行寄存器位操作

1 union结构区分大小端#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define read_bits(stc, field)({stc.raw = 0x12345678; stc.bits.field;})
union a{
unsigned int raw;
struct {
unsigned int bit_a : 8;
unsigned int bit_b : 8;
unsigned int bit_c : 5;
unsigned int bit_d : 3;
unsigned int bit_e : 1;
}bits;
};
int main(void){
union a num;
printf("%#x, %#x, %#x, %#x, %#x\n",
read_bits(num, bit_a),
read_bits(num, bit_b),
read_bits(num, bit_c),
read_bits(num, bit_d),
read_bits(num, bit_e));
return 0;
}

img

这样的结果,原理如下图:

img
那么从这里可以看出,低地址对应低字节, 因此我们的运行机器是Little Endian。

那么bit_a=0x78; bit_b=0x56; bit_c等于0x34取低5位,也就是0x14; bit_d 等于0x34取高3位,也就是0x1; bit_e等于0x12取最低位,也就是0。

由于这里的num是union结构,因此对.raw进行操作,那么也就等于对.bits也进行了操作,那么返回bit field是不是和寄存器的位操作很类似。下面详细介绍如何用union和bit field巧妙进行寄存器位操作。

2 寄存器的位操作#

举个例子,这是mipi-rx DPHY的寄存器的部分截取:

img

那么我们可以对该module进行结构定义如下:(当然如果觉得手动去定义麻烦,网上有专门的python工具可以对excel到寄存器定义的转换)
img

img

这里对该module的每个寄存器都定义成union。

2.1 offsetof获取结构体成员的偏移量#

1
#define offsetof(struct_t,member) ( (int)&((struct_t *)0)->member )

(struct_t )0),可以看到这里*把一个0地址转换成一个指针,它表示一个结构体指针变量,并且是值=0的指针, 那么访问它的成员,成员的地址自然就会往后递增,因此该成员的地址那么就等于该成员的偏移量。

eg:

1
2
3
4
5
6
struct student{
unsigned char name[100];
int age;
int id;
unsigned char sex;
}

那么offsetof(struct student, id)就为100 + 4=104,同理.name的offsetof为0,.age的offsetof为100,.sex的offsetof为108。

2.2 container_of根据结构体成员找到该结构体#

img

该函数实现位于include/linux/kernel.h, 源码如下:

1
2
3
#define container_of(ptr, type, member) ({            \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
  1. 定义一个结构体成员指针mptr指向该成员,
  2. 用该成员指针减去该成员在结构体中的偏移量,不就是该结构体的起始地址

img

1
2
struct student stu={.name="robin", .age=18, .id=123456, .sex='M'}, *pstu;
pstu = container_of(&stu.sex, struct student, sex);

2.3 读取寄存器#

1
2
3
4
5
#define _reg_read(addr) readl((void __iomem *)addr)
#define DPHY_BA_ADDR (0x0300b000)
#define _OFST(_BLK_T, _REG) ((uint64_t)&(((struct _BLK_T *)0)->_REG))//this is same with offsetof
#define RD_REG(_BA, _BLK_T, _REG) \
(_reg_read(_BA+_OFST(_BLK_T, _REG)))

用如下函数:

1
RD_REG(DPHY_BA_ADDR, REG_CSI_DPHY_4LANE_WRAP_T, REG_08);

这样就表示对该module的REG_08的寄存器进行了read。

2.4 写寄存器#

1
2
3
#define _reg_write(addr, data) writel(data, (void __iomem *)addr)
#define WR_REG(_BA, _BLK_T, _REG, _V) \
(_reg_write((_BA+_OFST(_BLK_T, _REG)), _V))

用如下函数:

1
WR_REG(DPHY_BA_ADDR, REG_CSI_DPHY_4LANE_WRAP_T, REG_08, 0x3333ffff);

这样就表示对该module的REG_08的寄存器进行了write, write的数据为0x3333ffff。

2.5 位读取#

1
2
3
4
5
6
#define RD_BITS(_BA, _BLK_T, _REG, _FLD) \
({\
typeof(((struct _BLK_T *)0)->_REG) _r;\
_r.raw = RD_REG(_BA, _BLK_T, _REG);\
_r.bits._FLD;\
})

用如下函数:

1
RD_BITS(DPHY_BA_ADDR, REG_CSI_DPHY_4LANE_WRAP_T, REG_08, MIPIRX_TEST_BIST1);

这里首先是定义了一个module的REG_08的寄存器结构,typeof表示对该成员取数据结构类型,然后把该寄存器里的值读出来,最后返回bit[31:16]。

2.6 位写入#

1
2
3
4
5
6
7
8
9
#define WR_BITS(_BA, _BLK_T, _REG, _FLD, _V) \
do {\
typeof(((struct _BLK_T *)0)->_REG) _r;\
_r.raw = RD_REG(_BA, _BLK_T, _REG);\
_r.bits._FLD = _V;\
_reg_write((_BA+_OFST(_BLK_T, _REG)), _r.raw);\
} while (0)

WR_BITS(DPHY_BA_ADDR, REG_CSI_DPHY_4LANE_WRAP_T, REG_08, MIPIRX_TEST_BIST1, 0x1111);

这里首先是定义了一个module的REG_08的寄存器结构,然后把该寄存器里的值读出来, 再把该寄存器的bit[31:16]写入0x1111。

卷帘快门RollingShutter与全局快门GlobalShutter

1 Global Shutter#

通过整幅场景在同一时间曝光实现的。Sensor所有像素点同时收集光线,同时曝光。即在曝光开始的时候,Sensor开始收集光线;在曝光结束的时候,光线收集电路被切断。CCD就是Global shutter工作方式。所有像元同时曝光

2 Rolling Shutter#

与Global shutter不同,它是通过Sensor逐行曝光的方式实现的。在曝光开始的时候,Sensor逐行扫描逐行进行曝光,直至所有像素点都被曝光。

对比优缺点:

Global shutter: 曝光时间更短,但会增加读出噪声;

Rolling shutter:可以达到更高的帧速,噪声小,但是运动场景会产生果冻效应。

卷帘快门与全局快门大致原理如下图:

 

卷帘快门:

适用性:

Global shutter rolling shutter
曝光时间短的应用(如<500μs****) 曝光时间长(如大于500μs)的应用可以有更低的噪声和帧速
快速移动物体拍适合 静止的物体适合

mipi-csi软件篇

1 MIPI CSI2的发送和接收#

img

上图反映了sensor 和 soc 的数据关系,soc 通过 CCI (Camera Control Interface) 控制 sensor 寄存器,配置正确,sensor 将会通过 mipi 接口输出图像数据。

控制信息是 soc 通过 CCI 发送到 sensor,CCI 接口一般的就是 I2C 接口,最大支持400KHz。

data信息是CSI DPHY发送者发送到CSI DPHY接收者,由sensor端发送到soc的mipi-rx。

2 D-PHY(DSI和CSI的物理层定义)#

D-PHY 是 MIPI 聯盟發布的高速物理層標準,規定了接口層的物理特性和傳輸協議。 DPHY 採用了 200mV 源同步的低压差分信號技術,每個 Lane 的數據綠率範圍支持到2500Mbps。 D-PHY 可以工作在低功耗 (Low Power, LP) 和高速 (High Speed, HS) 兩種模式下。

2.1 传输模式#

LP(Low-Power) 模式:用于传输控制信号,最高速率 10 MHz

HS(High-Speed)模式:用于高速传输数据,速率范围 [80 Mbps, 2.5Gbps] per Lane

传输的最小单元为 1 个字节,采用小端的方式,也就是 LSB first,MSB last。

相关缩写名词:

HS-RX:高速接收器
HS-TX:高速发送器
LP-RX:低功耗接收器
LP-TX:低功耗发送器
LPS: Low Power State, 封包之間的spacing間距。
ST: Start of Transmission (SoT), 封包的起始訊號, 一般為低速轉換為高速的暫態訊號。
ET: End of Transmission (EoT), 封包的結束訊號, 一般為高速轉換為低速的暫態訊號。
PH: Packet Header, 32 bit表示, 為封包的標頭。
PF: Packet Footer, 16 bit表示, 為封包的結尾。

2.2 lane states#

* LP mode 有 4 种状态: LP00、LP01(0)、LP10(1)、LP11 (Dp、Dn)

* HS mode 有 2 种状态: HS-0、HS-1

HS 发送器发送的数据 LP 接收器看到的都是 LP00。

2.3 Lane Levels#

* LP: 0 ~ 1.2V

* HS: 100 ~ 300mV,HS common level = 200mV,swing = 200 mv

2.4 操作模式#

• 数据Lane的3种操作模式

  • Escape mode,

  • High-Speed(Burst) mode

  • Control mode

    ①Escape mode request
    LP-11→LP-10→LP-00→LP-01→LP-00
    exit:LP-10 -> LP-11

当进入 Escape mode 需要发送 8-bit entry command 表明请求的动作,比如要进行低速数据传输则需要发送 cmd: 0x87,进入超低功耗模式则发送 cmd: 0x78。

②High-Speed mode request :
LP-11→LP-01→LP-00->SOT(0001_1101)
exit: EOT -> LP-11

img

③Turnaround request
LP-11→LP-10→LP-00→LP-10→LP-00

这是开启 BTA 的时序,一般用于从 slave 返回数据如 ACK: 0x84.
exit:LP00→LP10→LP11

最常用的就是“低功耗进入高速模式”如下图:

img

我们的D0-D4都是一个差分信号,它从lowpower state进入到HS后,从hi speed mode 中sync出我们的data。

sensor控制的信号由绿色框圈出来,主要是以下三个讯号:

1
2
3
HS-Prepare:表示从low power mode进入到hi speed mode需要prepare一段时间
Hs-zero: 表示从low power mode进入到hi speed mode需要keep LP-00状态一段时间
Hs-trail:表示送完1 st data后需要keep一段时间后才允许进入low power mode,进行下一轮数据传输。

soc端的控制的信号由黄色框圈出来:

1
Hs-settle:表示soc要等一段时间才去开始去parse “sync code”, 当抓到sync code后表示sensor已经进入了hi speed mode, 这个时候就可以sync data了。

注意:

1.每次EOT(end of trans)结束讯号后,都会进入low power mode,而不是sensor 工作后就一直处于hi speed mode。也就是每传一个package,都会走一遍上述的过程。

2.hs-settle为mclk/8 *n(这个n表示配置几个clk,对应code的话配置这个mipi_dev_attr_s->dphy)

当hs-settle的时间太长会压到data中的“sync code”,那么就会出现sync code parse不到,出现ecc err. 又或者从data中parse到一个假的“sync code”,那么最后就会出现数据不太对,出现ecc err.

Ecc还有一种出现可能就是lane mapping 出错,当我们传输数据出现ecc err, 有可能就是传输short pack时,4 byte的short pack拼接的不对(详见CSI的数据包结构),导致出现ecc error.

3.如果hs-trail持续的太短(拉高的太快),有可能会压到最后面的data,所以会出现crc/wc(word count ) err.另外,如果hs -settle太大,也有可能hs-trail也会是错误的数据,所以出现wc, crc err,因此不一定是hs-trail的问题,得先确保前面的ecc/decode无误后再来调整hs-trail。

一般排查流程如下:

img

2.5 时钟模式#

  • 连续时钟模式:数据包传输间隔,clk lane 保持在高速模式;
  • 非连续时钟模式:数据包传输间隔,clk lane 进入 LP-11 状态,退出hi speed mode;

2.6 时序要求#

在调试 DSI 或者 CSI 的时候, HS mode 下的几个时序非常重要:T_LPX,T_HS-SETTLE ≈ T_HS-PREPARE + T_HS-ZERO,T_HS-TRAIL,一般遵循的原则为:Host 端的 T_HS-SETTLE > Slave 端的 T_HS-SETTLE。

img

2.7 Start-of-Transmission ( SoT )过程#

img

当要进行数据传输的时候,data lane 将会通过信号(SoT)退出停止状态,进入高速模式。过程如下:

TX Side RX Side
退出停止状态 ( LP-11 ) 检测停止状态
进入 HS-Rqst 状态 ( LP-01 ),并处于该状态的时间间隔为 TLPX 检测到 lane 从 LP-11 转变为 LP-01
进入 Bridge 状态 ( LP-00 ),并处于该状态的时间间隔为 THS-PREPARE 检测到 lane 从 LP-01 转变为 LP-00,间隔 TD-TERM-EN 时间之后将使能传输
同时退出低功耗模式,进入高速模式
处于 HS-0 状态,时长为 THS-ZERO 使能 HS-RX 并等待 THS-SETTLE,以忽略转换状态
开始从数据流中等待同步序列
时钟上升边缘插入 HS 同步序列 ‘00011101’
识别到同步序列 ‘00011101’
高速模式开始传输有效数据
接收到有效数据

2.8 End-of-Transmission ( EoT )过程#

在数据完成传输时,通过结束传输(EoT)过程,数据通道退出高速模式并进入停止状态,这个过程状态变化如下:

TX Side RX Side
传输数据 接收到数据
在完成最后一个字节数据的传输之后,保持该状态的时间间隔为 THS-TRAIL
关闭 HS-TX,启用 LP-TX,并在 THS-EXIT一段时间内处于停止状态 ( LP-11 ) 检测到 lane 状态进入停止状态 ( LP-11 ),关闭传输功能
忽略 THS-SKIP一段时间内的 lane 变化,以忽略转换状态
检测有效数据的最后一次转换,确定最后一次有效数据并忽略包尾序列

接收器是如何判断数据将要开始传输了呢?

当出现LP11→LP01→LP00时,接收器将会判断,将会有数据达到,同时,使用示波器查看mipi波形,将会发现在PL00(THS-PREPARE)时会有一个小脉冲(峰刺),一般的,在这个小脉冲之后,接收器将会打开比较器(由于在THS-PREPARE会有这个小脉冲的存在,所以在接收器中,会通过设置接收器的settle time,避开这个小脉冲,在这个脉冲之后再打开比较器),准备接收数据。而HS-00011101则表示有效数据开始,同时数据的开头,将会有数据表明将要数据的数据量,所以mipi接收器将会按其数据量接收,直到接收完成。
每根 lane(data lane/clk lane)从 LP 模式切换到 HS 模式都会有 LP11→LP01→LP00 这样的一个时序,同时还要检查 HS-00011101 ,HS-00011101 主要是用于同步,只有前面正确采集到 00011101 ,才能保证 clk 和 data 相位一一对应。

mipi csi调试助手:
测量 sensor 有相应的 mipi 信号输出,但是主控并没有接收到数据,通过查看主控的 mipi 寄存器发现,mipi接收器还处于 LP 模式,这种情况一般是mipi没有检测到sensor发送的从 LP 进入 HS 的时序。此时可测量sensor 开始输出图像数据时,clk lane 是否有 LP11→LP01→LP00 这样的一个时序。同时,应该先开 mipi,sensor 再开始 mipi 数据传输;
由于THS-PREPARE会有一个小脉冲的存在,所以,主控在接收mipi数据的时候,需要通过设置主控的settle time,这个时间需要在这个小脉冲之后,这样接收才不会有问题;
当出现 sensor 有数据输出,但是主控没有接收成功,这个情况一般是 mipi 的时序问题,sensor 端的时序没有和主控端的配合好,这个时候,可以尝试的减小sensor端的THS-PREPARE,增大THS-ZERO和THS-TRAIL;
由于一些主控的需求,在一帧数据完成之后,需要一定的时间才可以进行相应的ISP处理,当一帧传输完毕之后的LP11时间达不到主控ISP的时间要求导致ISP报错,可通过调节THS-TRAIL时间,以此得到ISP对帧间的时序长度要求 。

3 CSI-2数据包协议#

CSI-2 是針對攝像頭的數據協議, 規定了主機與外設通信的數據包格式。CSI-2 可以支持不同像素格式的圖像應用, 數據傳輸的最小粒度是字節。 為增加 CSI-2 的性能,可以選擇數據 Lane 的數量, CSI-2 協議規訂了發送端將像素數據打包成字節的機制, 並指明多個數據 Lane 分配和管理的方式。字節數據以數據包的形式組織,數據包在SoT 與 EoT 之間傳輸。 接收端根據協議解析相應的數據包, 恢復出原始的像素數據。

CSI-2 的數據包分為長包和短包兩種,包含有校驗碼,能進行誤碼糾正和錯誤檢測。長包和短包都是在 SoT 和 EoT 之間傳輸,在數據傳送的間隙, D-PHY 處於 LP 模式。 CSI-2數據包的傳輸機制如圖所示。 PH 和 PF 分別表示 Packet Header 和 Packet Footer。

3.1 一个数据包结构#

img

3.2 包类型#

• 短包:4 bytes (固定)
• 长包:6~65541 bytes (可变)

3.2.1 短包结构#

一个short packet(也叫做pack head(PH))

img

包结构(4个字节):
• 数据标识(DI) 1个字节
• WC (长度固定为2个字节)
• 错误检测(ECC) 1个字节

包大小:
• 长度固定为4个字节

3.2.2 pack footer(PF)的结构#

img

3.2.3 长包结构#

img

包头部(4个字节)(PH):
•   数据标识(DI) 1个字节
•   数据计数WC (2个字节 )(PH和PF之間的資料個數)
•   错误检测(ECC) 1个字节
•  数据填充(0~65535 字节)
• 长度=WC*字节

包尾:校验和(2个字节)(PF)

长包 = 短包(包头) + 数据 + 包尾

包大小:
4 + (0~65535) + 2 = 6 ~ 65541 字节

3.2.4 H-blanking & V-blanking#

传输多个pack和传输一个pack时对应的图像如下, VVALID/HVALID/DVALID可以先把它想成是影像的同步訊號VSync/HSync/DE,而Data就是影像資料,以方便理解。

从图中可以看到,当水平同步讯号HVALID为Low的这段区间,剛好就是每行的Blanking间隔, 也就是H-blanking。

img

从下图可以看到上一FE到下一FS之间的间隔为V-blanking.

img

frame的封包示意图:

img

3.2.5 MIPI帧数据类型DI#

img

Data Identifier (DI) 为虚拟通道(VC, 2 bit)和资料类型(DT, 6 bit)组成。

3.2.5.1 VC(virtual channel)#

可以看出MIPI最多可以輸入4组影像来源,其ID为0~3,且內容可以是任意的內容,下图就表示用virtual chn来传输不同格式的数据。比如一般Sony, OV的HDR 模式基本都是VC mode,包括2帧HDR, 3帧HDR。

img

img

3.2.5.2 DT(data type)#

Data Type目前定义多种资料形态,范围从0x000x3F,其中0x000x0F为短封包类型,0x10~0x3F为長封包类型,如下表:

img

用于同步的短包Data Type:

img

soc到外设发送的包类型:

img

外设到soc的数据包类型:

img

4 mipi支持的图像格式#

MIPI CSI 共支持五種pixel資料格式的傳輸, 包含 YUV422-8bit、 YUV422-10bit、 RAW8、RAW10 和 RAW12。

4.1 RGB格式#

传统的红绿蓝格式,比如RGB565,RGB888,其16-bit数据格式为5-bit R + 6-bit G + 5-bit B。G多一位,原因是人眼对绿色比较敏感。

格式 描述
RGB565 1. 每个像素用16位表示,RGB分量分别使用5位、6位、5位。2. 内存中排列(高字节->低字节):R R R R R G G G G G G B B B B B
RGB555 1. 每个像素用16位表示,RGB分量都使用5位(剩下1位不用)2. 内存中排列(高字节->低字节):X R R R R G G G G G B B B B B(X表示不用,可以忽略)
RGB24(RGB888) 1. 每个像素用24位表示,RGB分量各使用8位。在内存中RGB各分量的排列顺序为:BGR BGR BGR ……2. 内存中排列(高字节->低字节):B B B B B B B B G G G G G G G G R R R R R R R R
ARGB32(ARGB8888) 1. 每个像素用32位表示,RGB分量各使用8位(剩下的8位用于表示Alpha通道值)2. 内存中排列(高字节->低字节):B B B B B B B B G G G G G G G G R R R R R R R R A A A A A A A A

4.2 YUV格式#

YUV是一种色彩编码方法,是一种彩色编码系统,相对于RGB色彩空间,YUV传输带宽占用更低,传输数据不易出错。

Y’UV、YUV、YCbCr、YPbPr 几个概念其实是一回事儿。由于历史关系,Y’UV、YUV 主要是用在彩色电视中,用于模拟信号表示。YCbCr 是用在数字视频、图像的压缩和传输,如 MPEG、JPEG。今天大家所讲的 YUV 其实就是指 YCbCr。Y 表示亮度(luma),CbCr 表示色度(chroma)。

人眼的视觉特点是对亮度更敏感,对位置、色彩相对来说不敏感。所以在视频编码系统中为了降低带宽,可以保存更多的亮度信息(luma),保存较少的色差信息(chroma)。

luminance 亮度,luma 是在视频编码系统中指亮度值;

chrominance 色度,chroma 是在视频编码系统中指色度值。

Y’UV 设计的初衷是为了使彩色电视能够兼容黑白电视。对于黑白电视信号,没有色度信息也就是(UV),那么在彩色电视显示的时候只显示亮度信息。

YUV是一个比较笼统地说法,针对它的具体排列方式,可以分为很多种具体的格式。色度(UV)定义了颜色的两个方面─色调与饱和度,分别用CB和CR表示。其中,Cr反映了RGB输入信号红色部分与RGB信号亮度值之间的差异。而Cb反映的是RGB输入信号蓝色部分与RGB信号亮度值之间的差异。

4.2.1 YUV采样模式(subsamping)#

原则:在数字图像中
1) 每一个图形像素都要包含 luma(亮度)值;
2)几个图形像素共用一个 Cb + Cr 值,一般是 2、4、8 个像素。

主要的采样格式有YCbCr 4:2:0、YCbCr 4:2:2、YCbCr 4:1:1和 YCbCr 4:4:4。

4.2.1.1 YUV444采样#

全采样,对每个像素点的的YUV分量都进行采样,这样的三个分量信息量完整。
假设4*4像素,采样格式如下:

1
2
3
4
[y u v] [y u v] [y u v] [y u v]
[y u v] [y u v] [y u v] [y u v]
[y u v] [y u v] [y u v] [y u v]
[y u v] [y u v] [y u v] [y u v]

那么19201080文件的大小:19201080*3(B),那么一个像素对应3个字节。

4.2.1.2 YUV422采样#

部分采样,可节省1/3存储空间和1/3的数据传输量。UV分量是Y分量采样的一半,Y分量和UV 分量按照2 : 1的比例采样。如果水平方向有10个像素点,那么采样了10个Y分量,而只采样了5个UV分量。其中,每采样过一个像素点,都会采样其Y分量,而U、V分量就会间隔一个采集一个。
假设4*4像素,采样格式如下:

1
2
3
4
[y u] [y v] [y u] [y v]
[y v] [y u] [y v] [y u]
[y u] [y v] [y u] [y v]
[y v] [y u] [y v] [y u]

19201080文件的大小:19201080+192010800.5+192010800.5(B),那么UV的数量减少了一半,相对于YUV444空间节省了1/3。

4.2.1.3 YUV420采样#

部分采样,可节省1/2存储空间和1/2的数据传输量。YUV 420采样,并不是指只采样U分量而不采样V分量。而是指,在每一行扫描时,只扫描一种色度分量(U或者V)和Y分量按照2 : 1的方式采样。比如,第一行扫描时,YU 按照 2 : 1的方式采样,那么第二行扫描时,YV分量按照 2:1的方式采样。对于每个色度分量来说,它的水平方向和竖直方向的采样和Y分量相比都是2:1 。其实yuv420的取名方式不是很高明,更确切的命名为yuv420yuv402,也就是第一行只有U,而第二行只有V。
假设4*4像素,采样格式如下:

1
2
3
4
[y u] [y] [y u] [y]
[y v] [y] [y v] [y]
[y u] [y] [y u] [y]
[y v] [y] [y v] [y]

19201080文件的大小:19201080+192010800.25+192010800.25(B)相对于YUV444空间节省1/2,因此也是比较主流的采样方式。

4.2.2 YUV存储方式#

YUV的格式有两大类:planar(平面格式)和packed(打包格式)

4.2.2.1 planner存储#

对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。

一般默认是3个平面,即y平面,u平面,v平面。但还有一种semi-planar是两个平面。也就是说uv为同一个平面,即一个y平面,一个uv平面。

4.2.2.1.1 yuv420 planner#

img

img

例如:

img

可以看到第一行的Y1Y2和第二行的Y7Y8共同使用一组U1V1。

4.2.2.1.2 yuv420sp#

two-planer双平面,Y一个平面,UV在同一个平面交叉存储。也叫做semi-planar的YUV格式。

  1. nv12

    先存储全部的Y分量,然后UV分量交叉存储。

    img

  2. nv21

    先UV分量交叉存储, 然后存储全部的Y分量。

img

4.2.2.1.3 yuv422 planner#

3平面,数据量:u=v=y/2,不画图展示

4.2.2.1.4 yuv422sp#

img

可以看到y0y82个像素公用一组u0v0。

4.2.2.2 packed存储#

packed的YUV格式,每个像素点的Y、U、V都是连续交叉存储的。

4.2.2.2.1 yuyv#

该格式属于4:2:2类型,且是用packed形式存储的,相邻的2个像素共用一个Cb(U)和Cr(V),以16个像素为例如下图:

img

4.2.2.2.2 yvyu#

与YUYV相似,只是存储时UV分量顺序不同而已。

4.2.2.2.2 uyvy

与YUYV相似,只是存储时UV分量顺序不同而已。

img

4.3 RAW 格式#

RAW图像就是CMOS或者CCD图像感应器将捕捉到的光源信号转化为数字信号的原始数据。RAW文件是一种记录了数码相机传感器的原始信息,同时记录了由相机拍摄所产生的一些元数据(Metadata,如ISO的设置、快门速度、光圈值、白平衡等)的文件。RAW是未经处理、也未经压缩的格式,可以把RAW概念化为“原始图像编码数据”或更形象的称为“数字底片”。Raw data(Raw RGB)经过彩色插值就变成RGB。

img

sensor上每个像素只采集特定颜色的光的强度,因此sensor每个像素只能为R或G或B 。

4.3.1 bayer阵列#

人眼对绿色比较敏感,所以一般bayer格式的图片绿色格式的像素是是r和g像素的和,那么bayer格式一般有下面4种:

img

5 mipi csi数据包传输#

raw8格式传输:

img

raw10格式传输:

img

raw12格式传输:

img

从图像看,RAW8是一传输个字节对应一个pixel, 而raw10是5个byte去对应4个pixel,第5个byte用来存储pixel 0~3的bit[0:1]。同理raw12用3个byte存储2个pixel。

img

6 色彩深度#

8位彩色,有256种深度。
16位彩色:65,536种颜色。
24位彩色:每种原色都有256个层次,它们的组合便有256256256种颜色。
32位彩色:除了24位彩色的颜色外,额外的8位是储存重叠图层的图形资料(alpha透明度)。

7 图像解析度/分辨率#

Resolution:

1280 * 720 = 921600 1M 100万像素 720P H65 sensor
1920 * 1080 = 2073600 2M 200万像素 1080P Imx307/imx327 sensor
2560 * 1440 = 36864002560 * 1600 = 40960002592 * 1944 = 5038848 4M/5M 400万像素500万像素 2K Imx335/sc4210 sensor
3840 * 2160 = 8294400 8M 800万像素 4K Imx334 sensor

mipi-csi硬件篇

# 1 mipi-csi硬件

1.1 mipi-csi概念#

DSI (Display serial interface)定义了一个位于处理器和显示模组之间的高速串行接口,对应MIPI-TX.
CSI(Camera Serial Interface)定义了一个位于处理器和摄像模组之间的高速串行接口,也就是接下来要讲的MIPI-RX。

MIPI Rx (Mobile Industry Processor Interface Receiver) 模塊主要功能為接收由 CMOS sensor 所傳送的視頻數據, 支持 MIPI D-PHY、 sub-LVDS (Low-Voltage Differential Signal)、 HiSPi (High-Speed Serial Pixel Interface) 等不同的串行視頻信號輸入, 並將其處理轉化為內部視頻時序,傳遞給下一級的視頻處理模塊 (ISP)。MIPI Rx 模塊中可細分為 PHY 和 Controller 兩部分,其中 PHY 模塊集成了模擬和數字兩個部分,主要將串行信號轉換為並行信號,而 Controller 模塊則負責解碼不同的視頻數據格式,傳送給後端的視頻處理模塊 (ISP)。功能框圖及在系統中的位置如下图所示:

img

1.2 MIPI vs DVP#

DVP:

并口传输数据需要帧同步信号(Vsync)、行同步信号(Hsync)和八条数据线,共十根数据线, DVP 接口在信号完整性方面受限制,速率也受限制。

img

MIPI:

而 MIPI 传输只需要clk lane和data lane. 对比MIPI 接口比 DVP 的接口信号线少,由于是低压差分信号,产生的干扰小,抗干扰能力也强。

img

1.3 CSI规格#

可同時支持 2 路 sensor 輸入(2组D-PHY, 每组5对差分线(1C4D))

• sensor 0 最大支持 4K2K @60fps HDR or @30fps 線性輸入

• sensor 1 最大支持 3M(2304x1296) @60fps HDR or linear 輸入

• 單路最多支持 4-Lane MIPI D-PHY 接口,最大支持 2.5Gbps/Lane

• 單路最多支持 4-Lane sub-LVDS/ HiSPi 接口,最大支持 1.5Gbps/Lane

• 支持 RAW8/ RAW10/ RAW12 數據類型的解析

• 支持 YUV422 8-bit/ YUV422 10-bit 數據類型的解析

• 最多支持 2 幀 WDR,支持多種 WDR 時序

• 支持 sub-LVDS/ HiSPi 模式像素/同步碼大小端配置

• 支持 Lane 數和 Lane 順序可配置

MIPI Rx 的帶寬有兩部分限制: PHY 的接口數據率和內部處理速度。

輸入接口最大支持 2.5Gbps/Lane,內部處理速度最大為 600M*1pixels/s(MAC clk)

1.4 CSI接口類型#

Common modevoltage Differential modevoltage Maximum clockfrequency Maximum datarate per lane
MIPI DPHY 200mV 200mV 1.25GHz 2.5Gbps
Sub-LVDS 900mV 150mV 750MHz 1.5Gbps
HiSPi(HiVCM) 900mV 280mV 750MHz 1.5Gbps
HiSPi(SLVDS) 200mV 200mV 750MHz 1.5Gbps

1.5 CSI 硬件引脚及接线#

常用的电脑摄像头是USB接口, 主流的智能手机摄像头是MIPI接口, 下面讲解常用的智能手机 camera MIPI接口。

MIPI CSI一般会有1对I2C通信引脚,1对MIPI差分时钟引脚和1~4对MIPI差分数据信号引脚, 也就是1CD4(1 clk lane & 4 data lane)。

1.5.1 mipi sensor引脚描述:#

信号名 引脚类型 描述
DOVDD 电源 1.8V IO 电源
DVDD 电源 1.2V 数字电源
AVDD 电源 2.8V 模拟电源
SCL 输入 I2C 时钟线
SDA 输入/输出 I2C 数据线(open drain)
SID0 输入 I2C Device ID 的选择 0 (内置下拉电阻,默认Device ID 是 7’h30)
SID1 输入 I2C Device ID 的选择 1 (内置下拉电阻,默认Device ID 是 7’h30)
XSHUTDN (RST) 输入 复位信号输入(内置上拉电阻,低电位有效)
EXTCLK 输入 时钟输入
PWDNB 输入 Power Down 信号输入(内置上拉电阻, 低电位有效)
D<3>(MD3P) 输出 DVP 输出 bit[3]/MIPI 数据 3 正极信号
D<5>(MD1P) 输出 DVP 输出 bit[5]/MIPI 数据 1 正极信号
D<7>(MCP) 输出 DVP 输出 bit[7]/MIPI 时钟正极信号
D<8>(MD0P) 输出 DVP 输出 bit[8]/MIPI 数据 0 正极信号
D<10>(MD2P) 输出 DVP 输出 bit[10]/MIPI 数据 2 正极信号
D<4>(MD3N) 输出 DVP 输出 bit[4]/MIPI 数据 3 负极信号
D<6>(MD1N) 输出 DVP 输出 bit[6]/MIPI 数据 1 负极信号
PCLK(MCN) 输出 DVP 输出时钟/MIPI 时钟负极信号
D<9>(MD0N) 输出 DVP 输出 bit[9]/MIPI 数据 0 负极信号
D<11>(MD2N) 输出 DVP 输出 bit[11]/MIPI 数据 2 负极信号

1.5.2 电路图线路结构#

img

一般mipi接口的sensor支持4 lane,2 lane,1 lane等数据传输方式。上图硬件上的连接了1组clk lane, 4组data lane。这幅图只连接了i2c和lane总线,还有EXTCLK ,PWDN, RST, VDD等引脚的连线需要外部soc去提供。这里就需要驱动人员会看原理图,知道lane id和pn swap的接线。clk lane, data lane等。

1.6 差分信号介绍#

我们用一个方法对差分信号做一下比喻,差分信号就好比是跷跷板上的两个人,当一个人被跷上去的时候,另一个人被跷下来了 - 但是他们的平均位置是不变的。继续跷跷板的类推,正值可以表示左边的人比右边的人高,而负值表示右边的人比左边的人高。0 表示两个人都是同一水平。

img

下图,应用到电学上,这两个跷跷板用一对标识为V+和V-的导线来表示。当V+ > V-时,信号定义成正极信号,V+ < V-时,信号定义成负极信号。 差分对的平均电压设置成 2.5V。

img

1.7 MIPI sensor的 power on时序#

Sony imx334:

img

img

SC4210:

img

这里列举了sony imx334和格科微gc2093, sc4210的上电时序,现在市面上大部分的mipi接口sensor都可以让VDD,PWDN, RST,EXTCLK讯号同时发出,然后过一段时间后就可以进行I2c通信了。

在HW交接到SW后,要确保最基本的power on时序是ok的,最好是用示波器对VDD,PWDN, RST,EXTCLK,I2c等波形进行测量无误后再porting到SW手上。

Vim配置成类似source-insight的IDE

前言#

基本安装#

sudo apt-get install vim vim-scripts vim-doc

vim-scripts是vim的一些基本插件,包括语法高亮的支持、缩进等等。
整体配置好后效果如下:
image

1 ctags + taglist安装配置#

1.1 ctag作用#

ctags 最先是用来生成C代码的tags文件,后来扩展成可以生成各类语言的tags, 有些语言也有专有的tags生成工具(比如java的jtags, python的 ptags),因此ctag用来进行vim阅读源码时进行函数,变量的快速定位跳转。

1.2 安装ctags#

sudo apt-get install ctags

1.3 安装taglist#

taglist_46.zip压缩包:解压到~/.vim目录下即可。
https://www.vim.org/scripts/script.php?script_id=273

mkdir ~/.vim
cd ~/.vim
unzip taglist_46.zip

1.4 创建tags文件#

$ctags -R --c++-kinds=+p+l+x+c+d+e+f+g+m+n+s+t+u+v --fields=+liaS --extra=+q
如果为了简单,也可以只进行:
$ctags -R *
不过,这种成员变量没有包含在里面。所以可能有些对象的成员时无法跳转。

1.4.1配置选项#

  $ ctags -R               #递归的为当前目录及子目录下的所有代码文件生成tags文件 (推荐使用此命令)
  $ ctags filename.c filename1.c file.h  #为当前目录某些源码生成tags文件
  $ ctags *.c *.h              #为当前目录所有.c, .h源码生成tags文件

  为了使得字段补全有效,在生成tags时需要一些额外的参数,推荐的c++参数主要是:ctags -R --c++-kinds=+px --fields=+iaS --extra=+q
  其中:
    选项c++-kinds 用于指定C++语言的 tags记录类型,  --c-kinds用于指定c语言的,  通用格式是  --{language}-kinds
    选项 fileds 用于指定每条标记的扩展字段域
    extra 选项用于增加额外的条目:   f表示为每个文件增加一个条目,  q为每个类增加一个条目

1.5 配置.vimrc#

"--------------display-----------------
set nu                          "行号
syntax on                       "语法高亮
set ruler                       "显示当前行和列
set showcmd                     "显示部分命令
set showmode            "最后一行显示当前模式
"set match                      "显示括号匹配
"--------------display-----------------

"---------------input------------------
set smartindent         "自动缩进
set expandtab           "将tab转化为空格
set smarttab            "插入tab使用shiftwidth
set shiftwidth=4        "缩进列数为4
"---------------input------------------

"---------------search-----------------
set hlsearch            "搜索结果高亮显示
"set inccase                    "预览搜索结果
set ignorecase          "不区分大小写
set smartcase           "当有大写字母时区分大小写
"---------------search-----------------

"---------------encoding----------------
set encoding=utf-8              "设置编码格式
set fileencodings=utf-8,gb18030,gbk,gb2312,big5
set termencoding=utf-8


"--------------ctags----------------
set tags=tags;
set autochdir        "通用方式,如果没有找到tags文件,或者没有找到对应的目标,就到父目录中查找,一直向上递归。
"--------------ctags----------------


"--------------taglist----------------
let Tlist_Use_Left_Window=1             "vim左侧窗口显示Taglist
let Tlist_Exit_OnlyWindow=1             "Taglist是最后一个窗口是退出vim
let Tlist_WinWidth=60           "设置Taglist窗口宽度为60
nmap <F2> :Tlist<CR>                    "设置快捷键<F2>打开Taglist
"--------------taglist----------------

2 ctags + taglist使用#

2.1 ctags使用#

book@100ask:~/ftp/openedv/uboot-2016$ vi drivers/usb/gadget/f_fastboot.c

image

2.1.1 ctrl + ]可以进行函数跳转#

image

2.1.2 ctrl + T可以返回跳转之前位置#

image

2.1.3 加载tags#

默认从创建tags的目录去启动vim打开文件会自动加载当前目录的tags文件,因此可以直接跳转:
如我是从uboot根目录建立的tags文件,然后:

book@100ask:~/ftp/openedv/uboot-2016$ vi drivers/usb/gadget/f_fastboot.c

这样自动加载tags文件,能够进行跳转。
如果进入其他目录:可以看到就无法打开tags文件。

book@100ask:~/ftp/openedv/uboot-2016$ cd  drivers/usb/gadget/
book@100ask:~/ftp/openedv/uboot-2016/drivers/usb/gadget$ vi f_fastboot.c

image

vim进入命令模式输入:

:set tags+='/home/book/ftp/openedv/uboot-2016/tags

image
此时就可以正常跳转了。

2.1.4 自动加载tags#

在~/.vimrc文件中添加下面两行:

set tags=tags;
set autochdir

通用方式,如果没有找到tags文件,或者没有找到对应的目标,就到父目录中查找,一直向上递归。
这样就可以在不用在tags根路径去使用vim了。如:

book@100ask:~/ftp/openedv/uboot-2016/drivers/usb/gadget$ vi f_fastboot.c

image

2.1.5 ts 列出所有匹配的标签#

image
可以看到有2处地方匹配,一个是cmd/bootm.c,一个是include/command.h
image

2.1.5 Ctrl + W + ]分割当前窗口#

image
输入:q退出分割窗口。

2.1.6 vi –t tag 找到名为 tag 的变量的定义处#

例如stitch_event_handler_th函数位于当前tags目录中的stitch/common/stitch.c的第1212行。只需确保在tags中的子目录中任意位置输入:

robin.lee@WORKSTATION5:/robin.lee/zip/A2/osdrv/interdrv/v2/dwa$ vi -t stitch_event_handler_th
image

2.2 taglist使用#

2.2.1 F2键打开关闭Taglist#

image

2.2.2 ctrl w w键taglist和vim窗口光标切换#

image

例如光标位置处于vim窗口的第1211行,按下ctrl w w键(w按2次),光标会跳到左边taglist窗口。按上下左右方向键可以查找宏定义,符号,函数等。再次按ctrl w w又会跳到vim的窗口。

2.2.3 回车键taglist跳转到具体位置#

image
比如现在光标位于taglist界面的stitch_src_qbuf位置,按下回车会跳转到函数定义的具体位置:
image

3 NERDTree插件#

NERDTree是Vim界面显示树形目录的文件管理器插件,可在vim操作界面进行文件打开、目录浏览操作。

3.1 安装配置NERDTree#

3.1.1 安装#

https://www.vim.org/scripts/script.php?script_id=1658
下载后放到~/.vim目录,解压即可完成安装。

3.1.2 配置#

vi ~/.vimrc
添加如下配置:

"--------------NERDTree---------------
let NERDTreeWinPos='right'		"设置窗口在右侧
nmap <F8> :NERDTree<CR>			"设置快捷键F8打开
let NERDTreeWinSize=40		 	"窗口大小为40
"--------------NERDTree---------------

3.2 使用NERDTree#

3.2.1 F8键开启关闭右图文件列表#

image

3.2.2 ctrl w w键NERDTree和vim窗口光标切换#

image
光标原本再右边窗口,输入ctrl w w(w按2次)切换到左边vim窗口。

3.2.3 回车键NERDTree跳转到具体位置#

同理,和taglist一样,也是光标移动到具体文件后回车即可跳转到具体文件。方向键上下移动选择具体文件。跳转到stitch_ctx.h:
image

3.2.3 返回上一层目录#

选择‘up a dir’,按回车:
image
返回上一层目录的结果如下:
image

4 SrcExpl插件(功能鸡肋先不介绍)#

SrcExpl(源资源管理器)是一个源代码资源管理器,通过显示函数或类型定义来显示当前选定的关键字或在单独的窗口中声明。该插件旨在重新创建上下文 IDE 中可用的窗口。

结合“Taglist”和“NERD tree”效果更佳。

4.1 SrcExp安装配置#

安装:
https://github.com/wenlongche/SrcExpl
https://www.vim.org/scripts/script.php?script_id=2179
下载后放入/.vim进行解压即可。
配置:
打开
/.vimrc,添加如下配置:

" // The switch of the Source Explorer
nmap <F12> :SrcExplToggle<CR>

" // Set the height of Source Explorer window
let g:SrcExpl_winHeight = 16

" // Set 100 ms for refreshing the Source Explorer
let g:SrcExpl_refreshTime = 500

" // Set "Enter" key to jump into the exact definition context
let g:SrcExpl_jumpKey = "<ENTER>"

" // Set "Space" key for back from the definition context
let g:SrcExpl_gobackKey = "<SPACE>"

" // In order to avoid conflicts, the Source Explorer should know what plugins except
" // itself are using buffers. And you need add their buffer names into below list
" // according to the command ":buffers!"
let g:SrcExpl_pluginList = [
        \ "__Tag_List__",
        \ "_NERD_tree_",
        \ "Source_Explorer"
    \ ]

" // The color schemes used by Source Explorer. There are five color schemes
" // supported for now - Red, Cyan, Green, Yellow and Magenta. Source Explorer
" // will pick up one of them randomly when initialization.
let g:SrcExpl_colorSchemeList = [
        \ "Red",
        \ "Cyan",
        \ "Green",
        \ "Yellow",
        \ "Magenta"
    \ ]

" // Enable/Disable the local definition searching, and note that this is not
" // guaranteed to work, the Source Explorer doesn't check the syntax for now.
" // It only searches for a match with the keyword according to command 'gd'
let g:SrcExpl_searchLocalDef = 1

" // Workaround for Vim bug @https://goo.gl/TLPK4K as any plugins using autocmd for
" // BufReadPre might have conflicts with Source Explorer. e.g. YCM, Syntastic etc.
let g:SrcExpl_nestedAutoCmd = 1

" // Do not let the Source Explorer update the tags file when opening
let g:SrcExpl_isUpdateTags = 0

" // Use 'Exuberant Ctags' with '--sort=foldcase -R .' or '-L cscope.files' to
" // create/update the tags file
let g:SrcExpl_updateTagsCmd = "ctags --sort=foldcase -R ."

" // Set "<F12>" key for updating the tags file artificially
let g:SrcExpl_updateTagsKey = "<F12>"

" // Set "<F3>" key for displaying the previous definition in the jump list
let g:SrcExpl_prevDefKey = "<F3>"

" // Set "<F4>" key for displaying the next definition in the jump list
let g:SrcExpl_nextDefKey = "<F4>"

4.2 SrcExpl使用#

4.2.1 F12启动和禁用SrcExpl#

5 YouCompleteMe代码补齐插件#

5.1 安装配置#

先安装编译依赖工具:

sudo apt install build-essential cmake python-dev python3-dev clang

下载YouCompleteMe源码:

git clone https://github.com/Valloric/YouCompleteMe.git ~/.vim/bundle/
cd .vim/bundle/YouCompleteMe
git submodule update --init --recursive

编译安装 YouCompleteMe:

./install.py --clang-completer

安装好后打印如下:
image

5.2 YouCompleteMe使用#

.vimrc添加配置:

let g:ycm_global_ycm_extra_conf = '~/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/ycm/.ycm_extra_conf.py'
set runtimepath+=~/.vim/bundle/YouCompleteMe
autocmd InsertLeave * if pumvisible() == 0|pclose|endif
inoremap <expr> <CR> pumvisible() ? "\<C-y>" : "\<CR>"
imap <silent><leader><TAB> <Plug>(coc-complete)
vmap <silent><leader><TAB> <Plug>(coc-complete)
nmap <silent><leader><TAB> <Plug>(coc-complete)

vim版本比较低达不到插件YCM的要求从而报错【YouCompleteMe unavailable: requires Vim 8.1.2269+.】,这样也好解决,从github上重新下最新版本的vim即可。命令如下所示:

cd /usr/local/share
sudo git clone https://github.com/vim/vim.git
cd vim/src
sudo ./configure --with-features=huge \
--enable-multibyte \
--enable-rubyinterp=yes \
--enable-pythoninterp=yes \
--enable-python3interp=yes \
--prefix=/usr/local/vim82

之后make和make install一下,命令如下所示:

sudo make
sudo make install

在/usr/bin目录下建立符号链接,命令如下所示:

sudo ln -s /usr/local/vim82/bin/vim /usr/bin/vim82
sudo ln -s /usr/local/vim82/bin/vim /usr/bin/vim
sudo ln -s /usr/local/vim82/bin/vim /usr/bin/vi

补齐效果如下:
image
输入vim –version信息可以看vim版本号,现已经v9.1,默认应该是8.1的。
image

vim配色方案:
https://github.com/flazz/vim-colorschemes
设置函数高亮:

vi /usr/share/vim/vim91/syntax/c.vim

"highlight Functions
syn match cFunctions "\<[a-zA-Z_][a-zA-Z_0-9]*\>[^()]*)("me=e-2
syn match cFunctions "\<[a-zA-Z_][a-zA-Z_0-9]*\>\s*("me=e-1
hi cFunctions gui=NONE cterm=bold  ctermfg=40

6 cscope插件#

6.1 安装#

sudo apt-get install cscope

6.2 配置产生cscope.out#

cscope -Rbqk

-R: 在生成索引文件时,搜索子目录树中的代码
-b: 只生成索引文件,不进入cscope的界面
-q: 生成cscope.in.out和cscope.po.out文件,加快cscope的索引速度
-k: 在生成索引文件时,不搜索/usr/include目录
-i: 如果保存文件列表的文件名不是cscope.files时,需要加此选项告诉cscope到哪儿去找源文件列表。可以使用”–“,表示由标准输入获得文件列表。

image

6.3 配置.vimrc#

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" cscope setting
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
if has("cscope")
  set csprg=/usr/bin/cscope
  set csto=1
  set cst
  set nocsverb
  " add any database in current directory
  if filereadable("cscope.out")
      cs add cscope.out
  endif
  set csverb
endif

nmap <C-@>s :cs find s <C-R>=expand("<cword>")<CR><CR>
nmap <C-@>g :cs find g <C-R>=expand("<cword>")<CR><CR>
nmap <C-@>c :cs find c <C-R>=expand("<cword>")<CR><CR>
nmap <C-@>t :cs find t <C-R>=expand("<cword>")<CR><CR>
nmap <C-@>e :cs find e <C-R>=expand("<cword>")<CR><CR>
nmap <C-@>f :cs find f <C-R>=expand("<cfile>")<CR><CR>
nmap <C-@>i :cs find i ^<C-R>=expand("<cfile>")<CR>$<CR>
nmap <C-@>d :cs find d <C-R>=expand("<cword>")<CR><CR>

set cscopequickfix=s-,c-,d-,i-,t-,e-

6.5 使用#

6.5.1 建立数据库连接#

:cs add ./cscope.out

6.5.2 查询数据库连接#

:cs show

image

6.5.3 查看引用调用位置#

:cs f s symbol #查看symbol和引用的地方, 如:cs find s IDLE_TIMEOUT_MS

image

7 source insight扩展#

7.1 将kernel工程精确快速导入到sourceinsight#

https://github.com/tonyho/Generate_Kernel_Uboot_Project_forIDE

windows10搭建Linux子系统WSL2及winnfsd服务

1 安装WSL2子系统#

1.1 下载安装#

进入windows Store,搜索Ubuntu,下载:
image

1.2 开启依赖#

右键windows左下角->应用和功能->相关设置-程序和功能->启用或关闭windows功能。勾选适用于Linux的windows子系统和虚拟机平台(WSL2需要)选项:
image

也可以直接在powershell中输入如下两条指令开启:

1
2
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

1.3 启动WSL Ubuntu#

wsl是windows控制Ubuntu镜像的工具。

1.3.1 查看版本为WSL2#

安装完成后,启动Ubuntu,会提示创建用户。在powerShell中输入:

1
wsl -l --all -v

image

可以看到版本为wsl2。如果不是wsl2,请按照下面方式升级:

1
wsl --set-version Ubuntu-20.04 2

提示需要更新内核组件,我们这里直接下载linux内核包进行安装:

https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi

使WSL 2成为你的默认体系结构:

1
wsl --set-default-version 2

1.4 导出Ubuntu到D盘#

默认会安装到c盘:C:\Users\robin.lee\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu_79rhkp1fndgsc

image

1
2
3
4
wsl --shutdown //关闭虚拟机
wsl --export Ubuntu-20.04 D:\wsl-ubuntu20.04.tar //导出打包到D:\wsl-ubuntu20.04.tar
wsl --unregister Ubuntu-20.04 //注销当前分发版
wsl --import Ubuntu-20.04 D:\wsl2-ubuntu20.04 D:\wsl-ubuntu20.04.tar --version 2 //重新导入并安装WSL2到指定目录

导出完成后,在相应磁盘路径下即可看到文件夹:
image

可以删除tar文件,也可保留,方便损坏后重新导入tar文件。

1.5 wsl2与windows相互访问#

1.5.1 wsl2访问windows#

直接在ubuntu中访问/mnt/下磁盘挂载目录即可:
image

1.5.2 windows访问wsl2#

直接在资源管理器中输入\\wsl$ 即可:

1
\\wsl.localhost\Ubuntu

image

为了方便,固定到快速访问。注意这是不能再透过/mnt目录访问自己,如c,d盘:
image

1.6 vhdx虚拟磁盘瘦身#

由于WSL2使用虚拟硬盘(VHD)存储linux下的文件,随着Linux下文件越来越多,占用空间也会不断增长。但是,在Linux中减少文件占用,WSL却没有相应的自动减少硬盘空间的占用。如下可以看到vhdx虚拟磁盘文件很大。
image

1.6.1 diskpart#

执行diskpart,会弹出一个cmd窗口:输入如下命令即可完成瘦身。
image

1
2
3
4
select vdisk file="D:\WSL\Ubuntu2004\ext4.vhdx"
attach vdisk readonly
compact vdisk
detach vdisk

image

1.7 连接USB设备#

https://learn.microsoft.com/zh-cn/windows/wsl/connect-usb

1.8 美化终端#

1.8.1 windows terminal#

原生的Ubuntu窗口还是low了点,字体和字符编码支持得都不是很好。可以进入windows的应用商店下载windows terminal:
image

启动后界面如下,可自定义配色和语言字体等个性化设置。
image

2 winnfsd-windows搭建nfs服务#

2.1 下载运行winnfsd#

下载windows nfs服务Release 2.4.0 · winnfsd/winnfsd · GitHub,直接运行,选择要导出的目录:

1
2
.\WinNFSd.exe D:\code\
Usage: WinNFSd.exe [-id <uid> <gid>] [-log on | off] [-pathFile <file>] [-addr <ip>] [export path] [alias path]

wsl子系统默认是通过虚拟网卡NAT模式转接到笔记本的网卡上的,所以wsl是可以上网的:
image

目前wsl只支持一张虚拟网卡,也就是说wsl系统要上网和要和板子通信两者只能取其一,但是wsl系统中可以随意访问windows文件系统,所以可以在windows上搭建NFS服务,然后直接在wsl系统中访问windows导出的共享文件夹,这样就能同步更改到板子上了。

比如我们把D:\code作为开发目录,windows和wls都能访问,又能挂载到板子上。

2.2 配置Windows网卡和开发板互通#

windows电脑的WIFI网卡上网, 以太网卡作为局域网和板端互通。

配置网卡ip;(网络适配器中找到对应网口的以太网卡,配置静态ip和网关)

2.3 挂载windows目录到板子#

1
mount -t nfs -o nolock 192.168.1.100:/D/code /mnt/nfs

这里挂载d盘的code文件夹,注意D一定要大写。

这样D/code就挂载到了板端。同样D/code下可以在wsl下进行代码编辑编译浏览,编译好之后直接可以切到板端去运行测试。

Windows10搭建NFS服务

1 下载haneWIN NFS Server for Windows#

链接如下:
https://www.hanewin.net/nfs-e.htm
image

2 安装并且执行 haneWIN NFS Server#

安装完后,打开hanWin如下:进入Edit->Preferences
image

进入Exports->Edit exports file
image

2.1 修改配置文件#

image
添加一行配置如下:
D:\ldc_res -name:nfs *(rw,sync,no_root_squash)
表示把D盘ldc_res目录当成nfs共享目录,-name:nfs表示客户端挂载时使用nfs表示D:\ldc_res路径
mount -t nfs -o nolock 192.168.0.100:/nfs /mnt/sd

2.2 保存配置#

勾选如下2个选项:仅对授权用户可见,通过nfs将导出添加到文件夹的上下文菜单
image

3 让NFS服务通过防火墙#

方法1:打开电脑防火墙设置,将专用网络和公用网络的防火墙禁用。
image
image

方法2:设置防火墙入栈连接规则
查看haneWIN NFS server的端口映射:
image
可以看到haneWIN使用的端口如下:

TCP:111, 1058, 2049
UDP:111, 1058, 2049

进入windows防火墙高级设置,设置入栈规则->新建规则:

规则类型选端口,
协议选TCP, 规则应用于特定本地端口,输入刚才查看的映射端口号:111,1058, 2049
操作选允许连接
配置文件将域,专用,公用网络全部勾选
最后设置好名称和描述

image
image
image
image
image
image

4 开发板客服端挂载测试#

image

设置板端IP地址:
image
先确保windows和板端能ping通
image

4.1 挂载共享目录到开发板#

image

mount -t nfs -o nolock 192.168.0.100:/nfs /mnt/sd
可以看到已经挂载上了,并且能访问挂载目录的文件

4.2 添加可写可执行权限#

挂载上后发现没有可写权限,
image

修改配置exports选项,添加-maproot:0 -public
D:\ldc_res -name:nfs -maproot:0 -public *(rw,sync,no_root_squash)
image

可以可写可执行了:
image

5 nfs配置文件#

#
# exports example
#
# please read doc for a list of all options
# drive letters should be in upper case, because file-id returns upper case
#   by default (option setting) they are mapped to lower case for clients
# Option -range restricts access to specified address range
#   a list of addresses restricts to these clients only
# Option -readonly prohibits create/write/delete
# Option -name:<x> makes folder for clients avalailable as /<x>
# Option -maproot:<uid> maps unix root to specified <uid>
#   without it uid root -> uid NOBODY
# Option -alldirs allows clients to mount folder or any subfolder
# Use UNC path specification for access to remote drive
# Hidden volumes without a drive letter can be mounted by volume GUID
#
C:\ftp -range 192.168.1.1 192.168.1.10
C:\video -readonly 192.168.1.1 192.168.1.4 192.18.1.23
C:\server -alldirs -name:server -maproot:0 -range 192.168.1.1 192.168.1.30
\\router\FRITZ.NAS\SanDisk-U3CruzerMicro-00 -name:fritz
\\?\Volume{6afa3aa3-1b38-11e6-a140-0000fbaa0005}\ -name:drive1
D:\ldc_res -name:nfs -public -maproot:0 *(rw,sync,no_root_squash)
E:\ -name:nfs_udisk -public -maproot:0 *(rw,sync,no_root_squash)

挂载:
mount -t nfs -o nolock 192.168.0.100:/nfs_udisk /mnt/

virtualbox配置实现PC-虚拟机-开发板互ping

1 设置virtualbox网卡#

1.1 关闭虚拟机#

1.2 设置添加网卡#

进入设置-网络

1.2.1 添加网卡1(NAT网络)#

这个网络是主机通过网络地址转换到虚拟机,比如主机用无线网卡WLAN上网,用NAT转换比较合适,这样虚拟机也可以访问外网。

1.2.2 添加网卡2(host only网络)#

这个网络是主机通过无线网卡WLAN上外网,但是虚拟机想通过有线网卡和主机、甚至与开发板互相ping通,传输文件。

当然开启host only模式前先确保windows下的virtual host only网卡有开启,如下图:

如果没有请按照下图操作:(这里是添加host only网卡)

2 配置windows和虚拟机#

2.1 配置window以太网卡#

刚添加了虚拟机虚拟virtual host only网卡, 那么我们反正用的WLAN上的外网,为了方便,设置windows下的以太网卡网段和virtual host only网卡保持一致。

2.2 配置虚拟机网络#

一般默认情况下,虚拟机是通过NAT共享网络给虚拟机用的,那么此时虚拟机也是可以上网的,只是不能和外界开发板进行互相ping, 同时windows主机也不能ping虚拟机。 如下图:



图中可以看到两块网卡,enp0s3是Ubuntu默认的网卡(NAT),enp0s8是新增的网卡(host only),可以看到enp0s8还没有分配IP地址,我们需要手工指定一下。sudo vim /etc/network/interfaces将文件内容修改为如下所示:

1
2
3
4
5
6
7
8
# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback

auto enp0s8
iface enp0s8 inet static
address 192.168.56.101
netmask 255.255.255.0

再来看下网卡ip信息:

3 测试上网状态#


从测试结果上可以看到虚拟机上外网和ping windows主机都是ok的。

  10.80.0.75时无线网卡的ip,
  192.168.56.100是以太网卡的ip,
  192.168.56.101是host only网卡的ip。 

同理用Windows ping虚拟机也是ok的。

用开发板ping主机和虚拟机发现,只能ping通windows主机,无法ping通虚拟机,如下图。这是为什么呢?

这是由于我们windows主机和开发板用的网线直连,两个以太网卡在同一网段下,当然能够ping通。但是虚拟机和windows主机用的确是host only网卡(NAT方式用来上外网),这样只能保证虚拟机和Windows主机是互通的,但是却无法被开发板访问,我们的开发板是没有无线网卡的,所以不能用WLAN,因此我们要想让开发板访问到虚拟机还得经过windows的以太网卡。

那么我们虚拟机和windows主机是host only方式,所以我们无法用到以太网卡进行虚拟机和windows主机的交互。所以我们可以采取将host only网卡桥接到以太网卡,进入 网络-更改适配器-同时选中host only网卡和以太网卡,点击右键-选择桥接,如下图所示:

这样我们的虚拟机的host only网卡就可以透过以太网卡和开发板交互了,效果如下:

开发板能够ping windows主机和虚拟机。

4 改进和优化#

如果不想在windows下进行host only网卡和以太网卡桥接,也可直接把virtualbox的第2路网卡由原来的host only修改成以太网卡桥接模式,如下:

这样需要配置虚拟机的enp0s8网卡和windows主机的以太网卡网段保持一致的。(和前面2.2操作一样)

4.1 windows ping 虚拟机#

4.2 虚拟机 ping windows#

4.3 开发板 ping windows和虚拟机#

vmware双网卡_NAT_桥接

1 配置虚拟机NET模式

vmware安装后默认就会用NAT方式和pc主机共享网络,虚拟机是通过pc主机的网卡数据转换进行上网的,只要windows主机有网,虚拟机就会对应有网络。
NAT模式在这里主要目的就是提供给虚拟机进行上网。
如果没有可以进行如下配置,打开虚拟机->编辑虚拟机设置->网络适配器,网络连接选择NAT模式如下图:

img

Read More

gdb移植到arm开发板

1.下载gdb#

https://ftp.gnu.org/gnu/gdb/

http://ftp.gnu.org/gnu/ncurses/

我这里选择的是gdb-7.12.tar.gz和ncurses-5.6.tar.gz

2.编译安装#

2.1 设置工具链#

export PATH=$PATH:/home/robin/share/cv183x/host-tools/gcc/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/

2.2 编译ncurses#

1
2
3
./configure --host=aarch64-linux-gnu-gcc --prefix=/home/robin/share/ncurses --without-ada --enable-termcap --with-shared CFLAGS="-O3 -fPIC"
make
make install

执行./configure时可能会失败,报错如下:

这是由于64bit machine配置引起的,进行如下操作后继续尝试。

wget -O config.guess 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD'
wget -O config.sub 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD'

编译出的ncurses库如下:

2.3 编译gdb#

1
2
3
4
5
tar xvf gdb-7.12.tar.gz;
cd gdb-7.12
./configure --host=aarch64-linux-gnu-gcc(--host=arm-linux-gnueabihf) --enable-shared --prefix=/home/robin/share/gdb --without-x --disable-gdbtk --disable-tui --without-included-regex --without-included-gettext --disable-werror CFLAGS="-O0 -fPIC"
make
make install

./configure后结果如下:

make install后结果如下:

3.运行测试#

1.将gdb和ncureses库cp到板子运行,结果如下: