📄 i2c.cxx
字号:
// 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 + -