相机作为视觉控制器最重要的传感器之一,并且作为一种可拔插的设备,判断其功能是否正常工作是非常重要的。相机的诊断我认为可以分为以下三种类型:

  • 电源诊断,包括相机开路、短路、过流过压等电源故障的判断
  • 图像信号诊断,包括视频流信号是否正常、控制信号是否正常、帧率、分辨率等图像信号故障的判断
  • 图像质量诊断,包括相机是否被遮挡、安装位置是否正常等故障的判断

电源诊断

我们的控制器使用了Maxim的max20087作为相机PoC的PMIC,按美信的说法是该款芯片是“业界唯一的ASIL级摄像机保护器”,最多支持4通道输出,支持过流过压保护。最重要的是可以通过I2C将诊断信息的读出,可读的信息包括输入输出通道的电压、电流,输出通道的过流过压欠压等信息。相机电源诊断就是通过这些信息判断是否存在电源故障。

max20087官网 https://www.maximintegrated.com/cn/products/analog/analog-switches-multiplexers/MAX20087.html

android检查相机是否被关闭 检查相机出了什么问题_自动驾驶

图1.max20087特性

根据max20087的特性,实现下表中的故障类型。故障码定义不符合ISO14229标准,仅作为例子展示

故障码(hex)

故障描述

判断标准

备注

相机工作正常

输入电压、输出电压、输出电流均正常,各异常标志位未置位

400400

相机0未连接

输出电压=输入电压,电流为0

400401

相机1未连接

400402

相机2未连接

400403

相机3未连接

400404

通道0短路

输出电压为0,过流或欠压或过热保护标志位置位

400405

通道1短路

400406

通道2短路

400407

通道3短路

400408

相机0已连接但未开始工作

输出电压正常,但电流小于正常工作电流,相机I2C访问正常

需要提前采样工作正常时的电流,需要主控去访问摄像头

400409

相机1已连接但未开始工作

40040a

相机2已连接但未开始工作

40040b

相机3已连接但未开始工作

40040c

相机0已连接但损坏了

输出电压远小于输入电压,电流远小于或远大于正常工作电流,相机I2C无法访问,但过流和欠压和过热保护标志位均未置位

需要提前采样工作正常时的电流,需要主控去访问摄像头

40040d

相机1已连接但损坏了

40040e

相机2已连接但损坏了

40040f

相机3已连接但损坏了

400410

通道0输入电压过压

过压标志位置位

400411

通道1输入电压过压

400412

通道2输入电压过压

400413

通道3输入电压过压

400414

PMIC无法访问

max20087 i2c无法访问

状态读出

要实现故障的判断,需要先把max20087的寄存器通过i2c读出,再通过一系列的逻辑判断出故障类型。需要注意的是,读取该芯片寄存器的时候,需要先把芯片EN引脚拉高。

电压和电流

max20087的电压电流是通过8位的ADC采样实现的,需要读出的值有 4个通道的电压+4个通道的电流+输入电压+芯片供电电压,一共10个值,但ADC寄存器只有4个。因此max20087通过设置mux寄存器,将ADC分时复用实现11个值的转换(有一个值没有用到)。

android检查相机是否被关闭 检查相机出了什么问题_自动驾驶_02

图2. max20087 ADC描述

实现起来也很简单,直接上代码。输入参数index指哪片max20087,因为板子上放置了3颗max20087;mode指ADC对哪部分进行采样,即mux setting;result是输出结果,传入长度为4的uint16_t类型的数组的指针。

int max20087_read_adc(uint8_t index, uint8_t mode, uint16_t* result) {
    uint8_t val = 0;
    int ret = 0;
    uint8_t max20087_addr = max20087_addr_group[index];
    uint8_t adc[4];
    uint8_t wait_adc_complete_retry = 2;

    /*set ADC mux to 01 = ADC1–ADC4 registers contain the output-voltage readings of each output*/
    ret |= i2c_read_8_8(I2C_BUS, max20087_addr, 0x01, &val, 0);
    val &= 0x3f;
    if(mode == VOLTAGE_OUT_MODE) {
        val |= (1 << 6);
    } else if(mode == VOLTAGE_CHIP_MODE) {
        val |= (1 << 7);
    }
    ret |= i2c_write_8_8(I2C_BUS, max20087_addr, 0x01, val, 0);

//    waiting for adc conversion complete
    while(--wait_adc_complete_retry) {
        ret |= i2c_read_8_8(I2C_BUS, max20087_addr, 0x03, &val, 1);
        if((val >> 4) & 1) {
            break;
        }
    }

    if(!wait_adc_complete_retry) {
        return -2;
    }

    ret |= i2c_read_8_8(I2C_BUS, max20087_addr, 0x06, &adc[0], 0);
    ret |= i2c_read_8_8(I2C_BUS, max20087_addr, 0x07, &adc[1], 0);
    ret |= i2c_read_8_8(I2C_BUS, max20087_addr, 0x08, &adc[2], 0);
    ret |= i2c_read_8_8(I2C_BUS, max20087_addr, 0x09, &adc[3], 0);

    if(ret) {
        return ret;
    }

    for(val=0; val<4; ++val) {
        if(mode == CURRENT_OUT_MODE) {
            /*
             * ADC1–ADC4 readings contain the output current through OUT1–OUT4, respectively. The current can be
             * calculated as:
             * I OUT1 –I OUT4 = ADC1–ADC4 x 3mA
            */
            *(result+val) = adc[val] * 3;
        } else if (mode == VOLTAGE_OUT_MODE) {
            /*
             * ADC1–ADC4 readings contain the voltage at the output of the switches of OUT1–OUT4, respectively. The
             * output voltage can be calculated as:
             * V OUT1 –V OUT4 = ADC1–ADC4 x 70mV
             */
            *(result+val) = adc[val] * 70;
        }
    }

    if (mode == VOLTAGE_CHIP_MODE) {
        /*
         * ADC1–ADC4 contain the following voltage readings:
         * ADC1 = V IN (70mV/count)
         * ADC2 = V DD (25mV/count)
         * ADC3 = V ISET (5mV/count)
         * ADC4 = Unused
        */
        *(result) = adc[0] * 70;
        *(result+1) = adc[1] * 25;
        *(result+2) = adc[2] * 5;
    }

    return ret;
}

诊断标志位读出

芯片已经实现了输出通道的过流、过压、欠压、过热关闭,以及输入的过压,欠压检测功能,检测结果直接存储在寄存器中,只需要简单的读出即可。

直接上代码,result是输出结果,使用一个长度为3的uint8_t类型数组

int max20087_read_stat(uint8_t index, uint8_t* result) {
    int ret = 0;
    uint8_t max20087_addr = max20087_addr_group[index];

    ret |= i2c_read_8_8(I2C_BUS, max20087_addr, 0x03, result, 0);
    ret |= i2c_read_8_8(I2C_BUS, max20087_addr, 0x04, result+1, 0);
    ret |= i2c_read_8_8(I2C_BUS, max20087_addr, 0x05, result+2, 0);

    return ret;
}

故障诊断

首先实现的判断相机是否连接,当输出电压等于输入电压,并且各异常标志位没有置位时认为相机没有连接。考虑到ADC可能会有误差,因此代码上会做一个容错。

代码中的SET_BIT.....语句是与UDS协议中DTC有关的一个状态,后面会单独写文章描述。

int camera_is_connected(uint16_t voltage_out, uint16_t current_out, uint8_t ts, uint8_t oc, uint8_t ov, uint8_t uv, uint32_t dtc) {
    //当输出电压=输入电压(考虑到采样误差以及内部压降,当两者差的绝对值小于140mv认为两者相等,即ADC的5个采样误差,2x70=140mV),
    //ts,oc,ov,uv标志位均未置位,
    //输出电流=0(考虑到采样误差,当电流小于5mv,即1个ADC采样误差,1x3=3mA,认为相机未连接
    struct uds_dtc* dtc_handler = get_dtc_handler(dtc);
    if(!dtc_handler) {
        return -DTC_NOT_FOUND;
    }
    if(abs(voltage_out - DIA_CAMERA_POWER_STATUS->voltage_vin) < 140 && current_out < 3 && !ts && !oc && !ov && !uv) {
        SET_BIT(dtc_handler->status, DTC_STATS_BIT_TEST_FAILED);
        SET_BIT(dtc_handler->status, DTC_STATS_BIT_PENDING_DTC);
        if(dtc_handler->cycle > dtc_handler->threshold) {
            SET_BIT(dtc_handler->status, DTC_STATS_BIT_CONFIRMED_DTC);
            CLR_BIT(dtc_handler->status, DTC_STATS_BIT_PENDING_DTC);
//                TODO 根据实际需求决定是否实现将该dtc写入非易失内存
        }
        ++dtc_handler->cycle;
    } else if (abs(voltage_out - DIA_CAMERA_POWER_STATUS->voltage_vin) < 140 && current_out > 27 && !ts && !ov && !uv && !oc) {
        CLR_BIT(dtc_handler->status, DTC_STATS_BIT_TEST_FAILED);
        if(dtc_handler->cycle > 0)
            --dtc_handler->cycle;
    }
    return UDS_SUCCESS;
}

然后是判断相机通道是否短路,当输出电压等于0,并且欠压和过流标志位置位时认为该通道已经短路到地。

int channel_is_short2gnd(uint16_t voltage_out, uint8_t uv, uint8_t oc, uint32_t dtc) {
//    当输出电压=0,欠压标志位和过流标志位置位时认为通道处于短路状态
    struct uds_dtc* dtc_handler = get_dtc_handler(dtc);
    if(!dtc_handler) {
        return -DTC_NOT_FOUND;
    }
    if(voltage_out < 140 && uv && oc) {
        SET_BIT(dtc_handler->status, DTC_STATS_BIT_TEST_FAILED);
        SET_BIT(dtc_handler->status, DTC_STATS_BIT_PENDING_DTC);
        if(dtc_handler->cycle > dtc_handler->threshold) {
            SET_BIT(dtc_handler->status, DTC_STATS_BIT_CONFIRMED_DTC);
            CLR_BIT(dtc_handler->status, DTC_STATS_BIT_PENDING_DTC);
//                TODO 根据实际需求决定是否实现将该dtc写入非易失内存
        }
        ++dtc_handler->cycle;
    } else if (abs(voltage_out - DIA_CAMERA_POWER_STATUS->voltage_vin) < 140 && !uv && !oc) {
//        当输出电压=输入电压,欠压标志位和过流标志位清除的时候认为短路情况已恢复
        CLR_BIT(dtc_handler->status, DTC_STATS_BIT_TEST_FAILED);
        if(dtc_handler->cycle > 0)
            --dtc_handler->cycle;
    }
    return UDS_SUCCESS;
}

判断通道是否过压(短路到电源),当ov标志位置位时即认该通道短路到电源。

int channel_is_over_voltage(uint8_t ov, uint32_t dtc) {
//    当过压标志位置位时认为是过压(短路到电源)
    struct uds_dtc* dtc_handler = get_dtc_handler(dtc);
    if(!dtc_handler) {
        return -DTC_NOT_FOUND;
    }
    if(ov) {
        SET_BIT(dtc_handler->status, DTC_STATS_BIT_TEST_FAILED);
        SET_BIT(dtc_handler->status, DTC_STATS_BIT_CONFIRMED_DTC);
        ++dtc_handler->cycle;
    } else {
        CLR_BIT(dtc_handler->status, DTC_STATS_BIT_TEST_FAILED);
        --dtc_handler->cycle;
    }
    return UDS_SUCCESS;
}

至于相机连接了但工作不正常的故障码,需要与相机的诊断相配合,只有电源诊断是无法实现的,后面会再用几篇文章专门介绍。