From: batt Date: Wed, 1 Oct 2008 21:05:33 +0000 (+0000) Subject: Add skel of i2c bitbang driver. X-Git-Tag: 2.0.0~69 X-Git-Url: https://codewiz.org/gitweb?a=commitdiff_plain;h=4f641b0adb067c5b233abc37b81787936a3bcacf;p=bertos.git Add skel of i2c bitbang driver. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@1865 38d2e660-2303-0410-9eaa-f027e97ec537 --- diff --git a/bertos/drv/i2c_bitbang.c b/bertos/drv/i2c_bitbang.c new file mode 100644 index 00000000..71243898 --- /dev/null +++ b/bertos/drv/i2c_bitbang.c @@ -0,0 +1,197 @@ +/** + * \file + * + * + * \brief I2C bitbang driver (implementation) + * + * \version $Id: adc.c 1604 2008-08-10 17:19:51Z bernie $ + * \author Francesco Sacchi + */ + +#include "i2c.h" +#include "timer.h" +#include "hw/hw_i2c_bitbang.h" + +#define I2C_PERIOD DIV_ROUND(500000UL / CONFIG_I2C_FREQ) + +INLINE bool i2c_start(void) +{ + SDA_HI; + SCL_HI; + timer_udelay(I2C_PERIOD); + SDA_LOW; + timer_udelay(I2C_PERIOD); + return !SDA; +} + +INLINE void i2c_stop(void) +{ + SCL_HI; + timer_udelay(I2C_PERIOD); + SDA_HI; +} + +bool i2c_put(uint8_t _data) +{ + uint16_t data = (_data << 1) | 1; + for (uint16_t i = 0x100; i >= 0; i >>= 1) + { + SCL_LO; + timer_udelay(I2C_PERIOD); + if (data & i) + SDA_HI; + else + SDA_LO; + SCL_HI; + timer_udelay(I2C_PERIOD); + ASSERT(SDA == (data & i)); + } + bool ack = !SDA; + ASSERT(ack); + SCL_LO; + timer_udelay(I2C_PERIOD); + return ack; +} + +bool i2c_start_w(uint8_t id) +{ + id &= ~I2C_READBIT; + /* + * Loop on the select write sequence: when the device is busy + * writing previously sent data it will reply to the SLA_W + * control byte with a NACK. In this case, we must + * keep trying until the deveice responds with an ACK. + */ + ticks_t start = timer_clock(); + while (i2c_start()) + { + if (i2c_put(id)) + return true; + else if (timer_clock() - start > ms_to_ticks(CONFIG_TWI_START_TIMEOUT)) + { + LOG_ERR("Timeout on I2C start write\n"); + break; + } + } + + return false; +} + +bool i2c_start_r(uint8_t id) +{ + id |= I2C_READBIT; + if (i2c_start()) + { + if (i2c_put(id)) + return true; + + LOG_ERR("NACK on I2c start read\n"); + } + + return false; +} + +int i2c_get(bool ack) +{ + uint8_t data = 0; + for (uint8_t i = 0x80; i >= 0; i >>= 1) + { + SCL_LO; + timer_udelay(I2C_PERIOD); + SCL_HI; + timer_udelay(I2C_PERIOD); + if (SDA) + data |= i; + } + SCL_LO; + timer_udelay(I2C_PERIOD); + + if (ack) + SDA_LO; + else + SDA_HI; + + SCL_HI; + /* avoid sign extension */ + return (int)(uint8_t)data; +} + +/** + * Send a sequence of bytes in master transmitter mode + * to the selected slave device through the I2C bus. + * + * \return true on success, false on error. + */ +bool i2c_send(const void *_buf, size_t count) +{ + const uint8_t *buf = (const uint8_t *)_buf; + + while (count--) + { + if (!i2c_put(*buf++)) + return false; + } + return true; +} + +/** + * Receive a sequence of one or more bytes from the + * selected slave device in master receive mode through + * the I2C bus. + * + * Received data is placed in \c buf. + * + * \note a NACK is automatically given on the last received + * byte. + * + * \return true on success, false on error + */ +bool i2c_recv(void *_buf, size_t count) +{ + uint8_t *buf = (uint8_t *)_buf; + + while (count--) + { + /* + * The last byte read does not has an ACK + * to stop communication. + */ + int c = i2c_get(count); + + if (c == EOF) + return false; + else + *buf++ = c; + } + + return true; +} +