⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 i2c.cxx

📁 开放源码实时操作系统源码.
💻 CXX
📖 第 1 页 / 共 2 页
字号:
//             acknowledgement bit has been received and processed.
//             None of the devices should be driving the bus.
//     SDA     output, indeterminate value.
// Postconditions:
//     SCL     output low, the acknowledgement has been received.
//     SDA     may be left as an input or an output, depending on the
//             last argument
//
// The return value is the acknowledge bit, i.e. 0 for ack, 1 for
// nak.

static cyg_bool
cyg_i2c_bitbang_send_byte(cyg_i2c_bus* mash, const cyg_i2c_device* dev, cyg_uint32 delay, cyg_uint8 data, cyg_bool leave_as_input)
{
    cyg_i2c_bitbang_fn      banger  = (cyg_i2c_bitbang_fn)(mash->i2c_extra);
    cyg_uint8               mask;
    cyg_bool                result;

    DEBUG("i2c bitbang send_byte %02x, leave_as_input %d\n", data, leave_as_input);
    
    for (mask = 0x0080; 0x00 != mask; ) {
        // Transfer the next bit of data
        (*banger)(mash, (0 != (data & mask)) ? CYG_I2C_BITBANG_SDA_HIGH : CYG_I2C_BITBANG_SDA_LOW);
        mask = mask >> 1;
        // We should now be able to just set the clock high and wait
        // for the delay period. However if the device is having
        // difficulty keeping up then it may perform clock stretching,
        // basically keeping the clock low for a period of time.
        // SCL_CLOCKSTRETCH means set the clock high, then wait
        // until it really has gone high.
        (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH_CLOCKSTRETCH);
        // The device is now busy sampling the bit.
        HAL_DELAY_US(delay);
        // Unless this was the last bit, just drop the clock. If it is
        // the last bit switch SDA to an input as well.
        (*banger)(mash, (0 != mask) ? CYG_I2C_BITBANG_SCL_LOW : CYG_I2C_BITBANG_SCL_LOW_SDA_INPUT);
        HAL_DELAY_US(delay);
    }
    // The last bit has been sent, SCL is low, and SDA has been turned
    // into an input. The device should be placing the acknowledge bit
    // onto SDA. Reading the acknowledge bit still needs a clock cycle.
    (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
    HAL_DELAY_US(delay);
    result  = (*banger)(mash, CYG_I2C_BITBANG_SDA_READ);
    DEBUG("i2c bitbang send_byte, got ack %d\n", result);
    
    // Drop SCL again, and leave SDA as either an input or an output
    // depending on the calling code's requirements and whether or not
    // the device ack'd.
    (*banger)(mash, CYG_I2C_BITBANG_SCL_LOW);
    HAL_DELAY_US(delay);
    if ((1 == result) || !leave_as_input) {
        (*banger)(mash, CYG_I2C_BITBANG_SDA_OUTPUT);
    }

    return result;
}

// Read a single byte from the bus and generate an ack/nak.
// Preconditions:
//    SCL       output low. The previous operation has completed.
//              The device should have placed the data on the bus.
//    SDA       input
// Postconditions:
//    SCL       output low. The acknowledgement has been sent.
//    SDA       may be left as an input or an output, depending on
//              whether or not this is the last byte in a transfer.
//              For the last byte we want to send a nak.
//
// The result is the byte read.

static cyg_uint8
cyg_i2c_bitbang_read_byte(cyg_i2c_bus* mash, const cyg_i2c_device* dev, cyg_uint32 delay, cyg_bool nak)
{
    cyg_i2c_bitbang_fn  banger  = (cyg_i2c_bitbang_fn)(mash->i2c_extra);
    cyg_uint32          i;
    cyg_uint8           result  = 0;

    DEBUG("i2c bitbang read_byte, nak %d\n", nak);
    
    for (i = 0; i < 8; i++) {
        (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
        HAL_DELAY_US(delay);
        result = result << 1;
        if (0 != (*banger)(mash, CYG_I2C_BITBANG_SDA_READ)) {
            result |= 0x01;
        }
        (*banger)(mash, CYG_I2C_BITBANG_SCL_LOW);
        HAL_DELAY_US(delay);
    }
    // We have read the last bit. SDA is still an input, SCL is low.
    // We need to switch SDA to an output and send the ack/nak
    (*banger)(mash, CYG_I2C_BITBANG_SDA_OUTPUT);
    (*banger)(mash, nak ? CYG_I2C_BITBANG_SDA_HIGH : CYG_I2C_BITBANG_SDA_LOW);
    (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
    HAL_DELAY_US(delay);
    (*banger)(mash, nak ? CYG_I2C_BITBANG_SCL_LOW : CYG_I2C_BITBANG_SCL_LOW_SDA_INPUT);
    HAL_DELAY_US(delay);

    DEBUG("i2c bitbang read_byte, result %02x\n", result);
    return result;
}

// Generate a start condition.
//
// Preconditions:
//    SCL       output, indeterminate
//    SDA       output, indeterminate
// Postconditions:
//    SCL       output low
//    SDA       output low
//
// At the start of a transaction we know that both SCL and SDA will be
// high, but for a repeated start
static void
cyg_i2c_bitbang_send_start(cyg_i2c_bus* mash, cyg_uint32 delay)
{
    cyg_i2c_bitbang_fn      banger  = (cyg_i2c_bitbang_fn)(mash->i2c_extra);

    DEBUG("i2c bitbang, generating start\n");
    // First get both SCL and SDA back into a known state
    (*banger)(mash, CYG_I2C_BITBANG_SDA_HIGH);
    (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
    HAL_DELAY_US(delay);
    // A start condition involves dropping SDA while SCL stays high.
    (*banger)(mash, CYG_I2C_BITBANG_SDA_LOW);
    // Make sure that all devices have seen the start condition and
    // reacted
    HAL_DELAY_US(delay);
    // Drop the clock, so that we can send the address/direction byte
    (*banger)(mash, CYG_I2C_BITBANG_SCL_LOW);
    HAL_DELAY_US(delay);
}

// Generate a stop condition
// Preconditions:
//    SCL       output, low
//    SDA       output, indeterminate
// Postconditions:
//    SCL       output, high
//    SDA       output, high
static void
cyg_i2c_bitbang_send_stop(cyg_i2c_bus* mash, cyg_uint32 delay)
{
    cyg_i2c_bitbang_fn      banger  = (cyg_i2c_bitbang_fn)(mash->i2c_extra);
    
    DEBUG("i2c bitbang, generating stop\n");
    // We need SDA low, then we can generate the stop signal
    (*banger)(mash, CYG_I2C_BITBANG_SDA_LOW);
    (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
    HAL_DELAY_US(delay);
    (*banger)(mash, CYG_I2C_BITBANG_SDA_HIGH);
    // Ensure that the minimum delay between stop and start is observed
    HAL_DELAY_US(delay);
}

// A full transfer to a given device, within in a transaction.
extern "C" cyg_uint32
cyg_i2c_bitbang_tx(const cyg_i2c_device* dev, cyg_bool send_start, const cyg_uint8* buf, cyg_uint32 count, cyg_bool send_stop)
{
    cyg_uint32      result  = 0;
    // The bit-bang code works in terms of us delays rather than ns
    cyg_uint32      delay = (0 == dev->i2c_delay) ? 1 : (dev->i2c_delay + 1999) / 2000;
    
    CYG_CHECK_DATA_PTR(buf, "i2c tx operation requested but no data supplied");
    CYG_PRECONDITION(count > 0, "at least one byte should be sent");

    DEBUG("i2c bitbang tx, dev %08x, addr %02x, delay %d, send_start %d, buf %08x, count %d, send_stop %d\n",
          dev, dev->i2c_address, delay, send_start, buf, count, send_stop);
    
    if (send_start) {
        cyg_i2c_bitbang_send_start(dev->i2c_bus, delay);

        // Now send a single byte, holding the address shifted left and a
        // 0 to indicate a write. A nak indicates that the device has not
        // responded. SDA should stay as an output.
        if (0 != cyg_i2c_bitbang_send_byte(dev->i2c_bus, dev, delay, (dev->i2c_address << 1) | 0x00, false) ) {
            // Get the bus back in a consistent state
            DEBUG("i2 bitbang tx, no device has responded to address %02x\n", dev->i2c_address);
            cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
            return 0;
        }
        // The device has ack'd so we can continue sending
    }
    // Send all bytes, unless we receive a nak. SDA should remain an output
    do {
        result += 1;
        if (0 != cyg_i2c_bitbang_send_byte(dev->i2c_bus, dev, delay, buf[result - 1], false)) {
            break;
        }
    } while (result < count);

    // At this point we have SCL low and SDA an indeterminate output
    if (send_stop) {
        cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
    }
    DEBUG("i2c bitbang tx, %d bytes sent out of %d\n", result, count);
    
    return result;
}

// A full transfer from a given device. The bus has already been locked
// by higher-level code. On entry both SDA and SCL should be outputs
// and both should be high. The same conditions should hold on exit.
extern "C" cyg_uint32
cyg_i2c_bitbang_rx(const cyg_i2c_device* dev, cyg_bool send_start, cyg_uint8* buf, cyg_uint32 count, cyg_bool send_nak, cyg_bool send_stop)
{
    cyg_uint32      result  = 0;
    // The bit-bang code works in terms of us delays rather than ns
    cyg_uint32      delay = (0 == dev->i2c_delay) ? 1 : (dev->i2c_delay + 1999) / 2000;
    
    DEBUG("i2c bitbang rx, dev %08x, addr %02x, delay %d, send_start %d, buf %08x, count %d, send_nak %d, send_stop %d\n",
          dev, dev->i2c_address, delay, send_start, buf, count, send_nak, send_stop);
    
    CYG_CHECK_DATA_PTR(buf, "i2c rx operation requested but no data supplied");
    CYG_PRECONDITION(count > 0, "at least one byte should be sent");
    CYG_PRECONDITION(send_nak || !send_stop, "a stop can only be generated after the receive has been nak'd");

    if (send_start) {
        cyg_i2c_bitbang_send_start(dev->i2c_bus, delay);

        // Now send a single byte, holding the address shifted left and a
        // 1 to indicate a read. A nak indicates that the device has not
        // responded. SDA should become an output.
        if (0 != cyg_i2c_bitbang_send_byte(dev->i2c_bus, dev, delay, (dev->i2c_address << 1) | 0x01, true) ) {
            // Get the bus back in a consistent state
            DEBUG("i2c bitbang rx, no device has responded to address %02x\n", dev->i2c_address); 
            cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
            return 0;
        }
        // The device has ack'd so we can continue sending
    }

    // The device has ack'd. Read the number of bytes specified. The
    // device cannot stop sending, although it may clock-stretch. For
    // the last byte we usually we want to NAK and SDA should become
    // an input, but if the data stream is variable length with e.g.
    // the first byte indicating the length then we want to support
    // partial reads.
    for (result = 0; result < count; result++) {
        buf[result] = cyg_i2c_bitbang_read_byte(dev->i2c_bus, dev, delay, (send_nak && (result == (count - 1))) ? true : false);
    }

    // At this point we have SCL low and SDA an indeterminate output
    if (send_stop) {
        cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
    }

    DEBUG("i2c bitbang rx, read %d bytes\n", result);
    return result;
}

extern "C" void
cyg_i2c_bitbang_stop(const cyg_i2c_device* dev)
{
    // The bit-bang code works in terms of us delays rather than ns
    cyg_uint32      delay = (0 == dev->i2c_delay) ? 1 : (dev->i2c_delay + 1999) / 2000;
    cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -