4、Sensor初始化流程:上电复位序列、PLL配置与时钟树、输出格式与分辨率设置
Sensor上电到正常输出图像,中间那几步,说白了就是「唤醒」它。我见过不少新手,拿着datasheet上来就配寄存器,结果图像出不来,折腾半天发现是上电时序没走对。这一节,咱们就把初始化流程拆开揉碎了讲。
4.1 上电复位序列:Sensor的「开机自检」
每个Sensor都有自己严格的电源域上电顺序。一般分三路:模拟电压(VDD_ANA,通常3.3V或2.8V)、数字核心电压(VDD_DIG,1.2V或1.8V)、IO电压(VDD_IO,1.8V或3.3V)。
我习惯先看datasheet里的「Power-Up Sequence」时序图。它规定了各路电压上升的先后顺序和延迟时间。举个例子,某款IMX系列Sensor要求:
- 先上VDD_ANA,稳定后等待至少100μs。
- 再上VDD_DIG,稳定后等待至少100μs。
- 最后上VDD_IO。
为什么这么讲究?因为如果数字域先于模拟域上电,内部逻辑可能进入不确定状态,复位都救不回来。我曾经在一个项目里,为了省一个电源管理芯片的时序控制,直接用电阻电容硬搭延迟,结果量产时发现部分模组上电后图像有竖条纹。查了三天,最后定位到是VDD_DIG比VDD_ANA早到了几十微秒。嗯,从那以后我再也不敢省这个时序了。
4.2 硬件复位与软件复位
上电完成后,第一步是拉低复位引脚(XCLR或RESET_B),保持至少1ms,再拉高。这叫硬件复位。有些Sensor还支持软件复位——写一个寄存器位,让内部逻辑重新初始化。
我个人习惯:硬件复位后,再发一次软件复位。双重保险。代码大概长这样:
// 硬件复位:拉低XCLR引脚1ms
gpio_set_level(GPIO_SENSOR_XCLR, 0);
vTaskDelay(pdMS_TO_TICKS(1));
gpio_set_level(GPIO_SENSOR_XCLR, 1);
// 等待内部PLL稳定
vTaskDelay(pdMS_TO_TICKS(5));
// 软件复位:写寄存器0x0103为0x01
sensor_write_reg(0x0103, 0x01);
vTaskDelay(pdMS_TO_TICKS(10));
// 再写0x0103为0x00,完成复位
sensor_write_reg(0x0103, 0x00);
vTaskDelay(pdMS_TO_TICKS(10));
4.3 PLL配置与时钟树:给Sensor一个稳定的心跳
Sensor内部所有操作——像素读出、ADC转换、数据输出——都依赖时钟。外部晶振通常给一个固定频率(比如24MHz或27MHz),然后通过内部PLL倍频到所需的工作频率。
时钟树说白了就是一条链路:外部晶振 → PLL倍频 → 分频给各个模块。我画了一张图,你看一眼就明白了:
配置PLL时,核心是算好倍频系数(PLL_MULT)和分频系数(PLL_DIV)。公式很简单:
PLL输出频率 = 外部晶振频率 × (PLL_MULT / PLL_DIV)
举个例子,外部晶振24MHz,目标PLL输出720MHz,那倍频系数就是30,分频系数是1。但要注意,PLL输出不能超过Sensor允许的最大值,否则内部逻辑会跑飞。我见过有人把PLL配到900MHz,Sensor直接不干活了——内部时序全乱了。
4.4 输出格式与分辨率设置
时钟配好了,接下来告诉Sensor:你要输出什么格式的图像。这一步通常包括:
- 输出接口:MIPI CSI-2、LVDS、并行DVP。选哪个取决于你的硬件设计。
- 数据位宽:8bit、10bit、12bit。注意,位宽越高,数据量越大,MIPI lane数要够。
- 分辨率:全分辨率(比如1920×1080)还是binning/subsampling后的分辨率。
- 帧率:通过调整行时间(HTS)和帧时间(VTS)来控制。
我整理了一个常见配置项的表格,方便你对照:
| 配置项 | 典型寄存器 | 说明 |
|---|---|---|
| 输出接口模式 | 0x0100 | 0x00=待机,0x01=流模式 |
| 数据格式 | 0x0340 | RAW8/RAW10/RAW12/YUV等 |
| 水平起始/宽度 | 0x0342/0x0344 | 定义有效像素窗口 |
| 垂直起始/高度 | 0x0346/0x0348 | 定义有效行窗口 |
| 行长度(HTS) | 0x034C | 每行总像素数(含消隐) |
| 帧长度(VTS) | 0x034E | 每帧总行数(含消隐) |
配置分辨率时,我习惯先配窗口起始和大小,再配HTS和VTS。顺序反了,有些Sensor会报错。举个例子,配一个1080p的窗口:
// 设置窗口起始 (0,0)
sensor_write_reg(0x0342, 0x00); // 水平起始高字节
sensor_write_reg(0x0343, 0x00); // 水平起始低字节
sensor_write_reg(0x0346, 0x00); // 垂直起始高字节
sensor_write_reg(0x0347, 0x00); // 垂直起始低字节
// 设置窗口大小 1920x1080
sensor_write_reg(0x0344, 0x07); // 水平宽度高字节 (1920=0x0780)
sensor_write_reg(0x0345, 0x80);
sensor_write_reg(0x0348, 0x04); // 垂直高度高字节 (1080=0x0438)
sensor_write_reg(0x0349, 0x38);
// 设置行长度和帧长度
sensor_write_reg(0x034C, 0x08); // HTS = 2200 (0x0898)
sensor_write_reg(0x034D, 0x98);
sensor_write_reg(0x034E, 0x04); // VTS = 1125 (0x0465)
sensor_write_reg(0x034F, 0x65);
4.5 启动输出流
所有配置写完后,最后一步是让Sensor开始输出图像。通常写寄存器0x0100为0x01(Streaming ON)。有些Sensor需要先写0x0100为0x00(Standby),再写0x01,中间加个延迟。
启动后,用示波器量一下MIPI clock lane和数据lane,应该能看到差分信号跳动了。如果没信号,回头检查PLL Lock标志和输出接口配置。嗯,这一步急不来。