Electrical – Bit Bang SCCB Issue

camerai2cstm32

I am trying to bit bang an SCCB communication between an OV7670 camera module and an STM32F407 Discovery Board. In the datasheet, it is stated that the slave write and read addresses for the OV7670 are 0x42 and 0x43, respectively.

As I understand SCCB, it is essentially identical to the I2C interface with the ACK/NACK bits replaced by don't care bits. I have been using a Logic Analyzer with the PusleView decoder to verify my commands being sent are recognizable (start, stop, write byte, read bytes, etc.). As a verification my code is working, I am trying to write the value 0xF3 to the address 0x02 (default value of 0x80), then read this from the device. I've attached screen captures from the write register and read register decodings and operation in PulseView.

Regardless of how I seem to configure the addresses, I cannot read the correct 0xF3 from the address. No matter the address configuration I choose, I only read 0xFF. This tells me that either (1) the SDA pin is not configured to correctly read an open-drain input during the read function or (2) the OV7670 is not signaling at all during the read byte.

I have verified case (1) as invalid by setting the SDA pin to INPUT mode via register commands and connecting it to ground. It seems the OV7670 is not receiving or sending any data. I believe this is due to the way I am addressing the camera. I have seen many conflicting methods online on how to properly address the camera and I cannot seem to get anything to work.

I have the camera connected to 3.3V, GND, XCLK of 16 MHz, PWDN (active high) to GND, and RST to 3.3V (active low). The SDA and SCL are also connected.

Does anyone know how to properly address this camera for communication?

SCCB Write Phase:
SCCB Write Phase:

SCCB Read Phase:
SCCB Read Phase

Best Answer

Ohh, I just take a closer look on your time diagram and found that maybe your slaveAddress is not correct. The address of OV7670 is 0x42 for write and 0x43 for read. It seems that you have 0x86 and 0x87. You shouldn't make (0x42 << 1) in your code, I think. Since 0x42 already have LSB as R/-W=0 and 0x43 has R/-W=1.

I am fighting with ov7670 connected to STM32F407 (black board) from a weeks with some half-success. I will share with my experience, maybe you find some help. First attempt: I used HAL i2c library for write:

HAL_StatusTypeDef i2cPrimitives::writeReg(uint16_t RegAddr, uint8_t value)
{
    HAL_StatusTypeDef hs;
    hs = HAL_I2C_Mem_Write(_hi2c, Devaddress, RegAddr, I2C_MEMADD_SIZE_8BIT, &value, 1, timeout);
    return hs;
}

and for read

uint8_t i2cPrimitives::readReg(uint16_t RegAddr)
{
    uint8_t pValue;
    HAL_I2C_Mem_Read(_hi2c, Devaddress, RegAddr, I2C_MEMADD_SIZE_8BIT, &pValue, 1, timeout);
    return pValue;
}

Though I could write to camera, I couldn't read anything. Then I found out that the ACK bit is Don't care and after analysing the code from the forum I changed read code to

HAL_StatusTypeDef i2cPrimitives::readRegNoACK(uint16_t RegAddr, uint8_t *pValue)
{
    HAL_StatusTypeDef ret;
    uint8_t radr = (uint8_t)RegAddr;

    ret = HAL_I2C_Master_Transmit(_hi2c, Devaddress, &radr, 1, 100);
    if (ret == HAL_OK)
        ret = HAL_I2C_Master_Receive(_hi2c, Devaddress, pValue, 1, 100);

    return ret;
}

which supposed to be ACK free. Surprisingly I could get correct answer from registers 0x0A and 0x0B which is 0x7673(*). However, the communication with camera was not stable. Sometimes it read ok, sometimes not. I suspect that it is due to Don't care bit which may ACK or NACK randomly (at least we need to assume that). I read ref.manual of STM32F407 and figured out that there is no way to configure i2c port to ignore ACK. Hence I wrote i2c bitbang with option to ignore ack. I confirmed it operates properlly 1) by oscilloscope and 2) using LSM303 Acc/Mag sensor as well as 3) OLED display SSD1306. However, camera don't answer properly. Now when I read 0x0A/0x0B it always answers 0xFEFE (nonsence). Interestingly it seems that writing to the camera seems to be OK. I can configure it to operate in QCIF format/RGB565 stored as BMP to SDcard. Results are partially OK: tests patterns results (registers 0x70 and 0x71) give such output:

8-bar color bar test pattern: 8-bar color bar test pattern

Fade to gray color bar test pattern Fade to gray color bar test pattern

The 8-bar image looks quite nice (it also confirms that BMP code works ok), but the gray color bar is far away from the expectations. And so I can only obtain images like the one below :)

enter image description here

Getting back to SCCB reading problem: I found that the writing slave address 0x42 and register address (sub-address) is acknowledge by ov7670 (SDA goes low state), then after repeating start and writing slave address 0x43 is not good because when trying to write the last bit (R/-W bit set to 1 since I want to read the register) the camera keeps it low state! I am sure master releases SDA correctly since I observe that the voltage level is going slightly higher during sending R/-W bit (ca ~0.1V) - so it's rather sure the ov7670 drives it low. Why??? So far I didn't solve this issue. One of my hypothesis is that maybe it mistakenly acknowledge at 8th clock instead at 9th. If anybody could help, I am very appreciate that.

(*) be careful for OV7670 datasheet. There are two: one is from 2005, another from 2006 which differ in registers reset default values (e.g. in 2005 registers 0x0A/0x0B answers 0x7670 not 0x7673. My ov answered 0x7673 (sometimes).

2019-11-30,00:04 Update. FINALLY! Thank's to your timelines I found a problem in my code: I was lack of stop condition before repeated start in the read sequence. It's not required by i2c, but it is required by sccb protocol. Now I can properly write/read registers. For your reference: the bigbang read/write code looks like this:

void i2cbitbang::writeData(uint8_t reg, uint8_t *pData, uint16_t size)
{
    bool nack;
    uint8_t *ptr = pData;

    i2c_bus_init();

    i2c_start_cond();

    /*send slave addres*/
    nack = i2c_write_byte(slaveAddress & 0xFE);

    if ( (ackMode == ACK_CHECK) && nack)    error |= I2CBB_ERROR_NACK;


    /*send register addres*/
    nack = i2c_write_byte(reg);

    if ( (ackMode == ACK_CHECK) && nack)    error |= I2CBB_ERROR_NACK;

    /*send data addres*/
    while(size)
    {
        nack = i2c_write_byte(*ptr++);
        if (nack) break;
        size--;
    }

    i2c_stop_cond();

    if ( (ackMode == ACK_CHECK) && nack)    error |= I2CBB_ERROR_NACK;


}

void i2cbitbang::writeReg(uint8_t reg, uint8_t val)
{
    writeData(reg, &val, 1);
}

void i2cbitbang::readData(uint8_t reg, uint8_t *pData, uint16_t size)
{
    bool nack;
    uint8_t *ptr;

    ptr = pData;

    i2c_bus_init();

    i2c_start_cond();
    nack = i2c_write_byte(slaveAddress & 0xFE);
    if ( (ackMode == ACK_CHECK) && nack)
    {
        error |= I2CBB_ERROR_NACK;
    }

    nack = i2c_write_byte(reg);
    if ( (ackMode == ACK_CHECK) && nack)
    {
        error |= I2CBB_ERROR_NACK;
    }

    if (i2cMode == MODE_SCCB)
    {
        i2c_stop_cond();
    }

    i2c_start_cond();

    nack = i2c_write_byte(slaveAddress | 0x01);
    if ( (ackMode == ACK_CHECK) && nack)
    {
        error |= I2CBB_ERROR_NACK;
    }
    while(size)
    {
        *ptr++ = i2c_read_byte( (size>1) ? ACK : NACK );
        size--;
    }

    i2c_stop_cond();

}

void i2cbitbang::readReg(uint8_t reg, uint8_t *pVal)
{
    readData(reg, pVal, 1);
}

However image quality not changed. Probably register configuration wrong