cpu/esp*: move periph/i2c_sw to esp/common
This commit is contained in:
parent
0292f8b6a3
commit
05faec7cf8
@ -39,7 +39,6 @@ TARGET_ARCH ?= xtensa-esp32-elf
|
|||||||
PSEUDOMODULES += esp_eth_hw
|
PSEUDOMODULES += esp_eth_hw
|
||||||
PSEUDOMODULES += esp_gdbstub
|
PSEUDOMODULES += esp_gdbstub
|
||||||
PSEUDOMODULES += esp_hw_counter
|
PSEUDOMODULES += esp_hw_counter
|
||||||
PSEUDOMODULES += esp_i2c_sw
|
|
||||||
PSEUDOMODULES += esp_i2c_hw
|
PSEUDOMODULES += esp_i2c_hw
|
||||||
PSEUDOMODULES += esp_idf_newlib
|
PSEUDOMODULES += esp_idf_newlib
|
||||||
PSEUDOMODULES += esp_rtc_timer
|
PSEUDOMODULES += esp_rtc_timer
|
||||||
|
|||||||
@ -1,717 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 Gunar Schorcht
|
|
||||||
*
|
|
||||||
* This file is subject to the terms and conditions of the GNU Lesser
|
|
||||||
* General Public License v2.1. See the file LICENSE in the top level
|
|
||||||
* directory for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ingroup cpu_esp32
|
|
||||||
* @ingroup drivers_periph_i2c
|
|
||||||
* @{
|
|
||||||
*
|
|
||||||
* @file
|
|
||||||
* @brief Low-level I2C driver implementation for ESP32 SDK
|
|
||||||
*
|
|
||||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
|
||||||
*
|
|
||||||
* @}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
PLEASE NOTE:
|
|
||||||
|
|
||||||
Some parts of the implementation bases on the bit-banging implementation as
|
|
||||||
described in [wikipedia](https://en.wikipedia.org/wiki/I%C2%B2C) as well as
|
|
||||||
its implementation in [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos.git).
|
|
||||||
These parts are under the copyright of their respective owners.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if defined(MODULE_ESP_I2C_SW) /* software implementation used */
|
|
||||||
|
|
||||||
#define ENABLE_DEBUG (0)
|
|
||||||
#include "debug.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#include "cpu.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "mutex.h"
|
|
||||||
#include "periph_conf.h"
|
|
||||||
#include "periph/gpio.h"
|
|
||||||
#include "periph/i2c.h"
|
|
||||||
|
|
||||||
#include "esp_common.h"
|
|
||||||
#include "gpio_arch.h"
|
|
||||||
#include "rom/ets_sys.h"
|
|
||||||
#include "soc/gpio_reg.h"
|
|
||||||
#include "soc/gpio_struct.h"
|
|
||||||
|
|
||||||
/* only include the code if one of the IC2 interface bus speeds are defined */
|
|
||||||
#if defined(I2C0_SPEED) || defined(I2C1_SPEED)
|
|
||||||
|
|
||||||
/* max clock stretching counter */
|
|
||||||
#define I2C_CLOCK_STRETCH 200
|
|
||||||
|
|
||||||
/* gpio access macros */
|
|
||||||
#define GPIO_SET(l,h,b) if (b < 32) GPIO.l = BIT(b); else GPIO.h.val = BIT(32-b)
|
|
||||||
#define GPIO_GET(l,h,b) ((b < 32) ? GPIO.l & BIT(b) : GPIO.h.val & BIT(32-b))
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
i2c_speed_t speed;
|
|
||||||
i2c_t dev;
|
|
||||||
|
|
||||||
bool started;
|
|
||||||
|
|
||||||
gpio_t scl;
|
|
||||||
gpio_t sda;
|
|
||||||
|
|
||||||
uint32_t scl_bit; /* gpio bit mask for faster access */
|
|
||||||
uint32_t sda_bit; /* gpio bit mask for faster access */
|
|
||||||
|
|
||||||
uint32_t delay;
|
|
||||||
mutex_t lock;
|
|
||||||
|
|
||||||
} _i2c_bus_t;
|
|
||||||
|
|
||||||
static _i2c_bus_t _i2c_bus[I2C_NUMOF] = {};
|
|
||||||
|
|
||||||
/* to ensure that I2C is always optimized with -O2 to use the defined delays */
|
|
||||||
#pragma GCC optimize ("O2")
|
|
||||||
|
|
||||||
static const uint32_t _i2c_delays[][3] =
|
|
||||||
{
|
|
||||||
/* values specify one half-period and are only valid for -O2 option */
|
|
||||||
/* value = [period - 0.25 us (240 MHz) / 0.5us(160MHz) / 1.0us(80MHz)] */
|
|
||||||
/* * cycles per second / 2 */
|
|
||||||
/* 1 us = 48 cycles (240) / 32 cycles (160 MHz) / 16 cycles (80 MHz) */
|
|
||||||
/* values for 240, 160, 80 MHz */
|
|
||||||
[I2C_SPEED_LOW] = {2390, 1590, 790}, /* 10 kbps (period 100 us) */
|
|
||||||
[I2C_SPEED_NORMAL] = { 230, 150, 70}, /* 100 kbps (period 10 us) */
|
|
||||||
[I2C_SPEED_FAST] = { 51, 31, 11}, /* 400 kbps (period 2.5 us) */
|
|
||||||
[I2C_SPEED_FAST_PLUS] = { 15, 7, 0}, /* 1 Mbps (period 1 us) */
|
|
||||||
[I2C_SPEED_HIGH] = { 0, 0, 0} /* 3.4 Mbps (period 0.3 us) not working */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* forward declaration of internal functions */
|
|
||||||
|
|
||||||
static inline void _i2c_delay (_i2c_bus_t* bus);
|
|
||||||
static inline bool _i2c_scl_read (_i2c_bus_t* bus);
|
|
||||||
static inline bool _i2c_sda_read (_i2c_bus_t* bus);
|
|
||||||
static inline void _i2c_scl_high (_i2c_bus_t* bus);
|
|
||||||
static inline void _i2c_scl_low (_i2c_bus_t* bus);
|
|
||||||
static inline void _i2c_sda_high (_i2c_bus_t* bus);
|
|
||||||
static inline void _i2c_sda_low (_i2c_bus_t* bus);
|
|
||||||
static int _i2c_start_cond (_i2c_bus_t* bus);
|
|
||||||
static int _i2c_stop_cond (_i2c_bus_t* bus);
|
|
||||||
static int _i2c_write_bit (_i2c_bus_t* bus, bool bit);
|
|
||||||
static int _i2c_read_bit (_i2c_bus_t* bus, bool* bit);
|
|
||||||
static int _i2c_write_byte (_i2c_bus_t* bus, uint8_t byte);
|
|
||||||
static int _i2c_read_byte (_i2c_bus_t* bus, uint8_t* byte, bool ack);
|
|
||||||
static int _i2c_arbitration_lost (_i2c_bus_t* bus, const char* func);
|
|
||||||
static void _i2c_abort (_i2c_bus_t* bus, const char* func);
|
|
||||||
static void _i2c_clear (_i2c_bus_t* bus);
|
|
||||||
|
|
||||||
/* implementation of i2c interface */
|
|
||||||
|
|
||||||
void i2c_init(i2c_t dev)
|
|
||||||
{
|
|
||||||
CHECK_PARAM (dev < I2C_NUMOF)
|
|
||||||
|
|
||||||
if (i2c_config[dev].speed == I2C_SPEED_HIGH) {
|
|
||||||
LOG_TAG_INFO("i2c", "I2C_SPEED_HIGH is not supported\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_init(&_i2c_bus[dev].lock);
|
|
||||||
|
|
||||||
_i2c_bus[dev].scl = i2c_config[dev].scl;
|
|
||||||
_i2c_bus[dev].sda = i2c_config[dev].sda;
|
|
||||||
_i2c_bus[dev].speed = i2c_config[dev].speed;
|
|
||||||
|
|
||||||
_i2c_bus[dev].dev = dev;
|
|
||||||
_i2c_bus[dev].scl_bit = BIT(_i2c_bus[dev].scl); /* store bit mask for faster access */
|
|
||||||
_i2c_bus[dev].sda_bit = BIT(_i2c_bus[dev].sda); /* store bit mask for faster access */
|
|
||||||
_i2c_bus[dev].started = false; /* for handling of repeated start condition */
|
|
||||||
|
|
||||||
switch (ets_get_cpu_frequency()) {
|
|
||||||
case 240: _i2c_bus[dev].delay = _i2c_delays[_i2c_bus[dev].speed][0]; break;
|
|
||||||
case 160: _i2c_bus[dev].delay = _i2c_delays[_i2c_bus[dev].speed][1]; break;
|
|
||||||
case 80: _i2c_bus[dev].delay = _i2c_delays[_i2c_bus[dev].speed][2]; break;
|
|
||||||
default : LOG_TAG_INFO("i2c", "I2C software implementation is not "
|
|
||||||
"supported for this CPU frequency: %d MHz\n",
|
|
||||||
ets_get_cpu_frequency());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG ("%s scl=%d sda=%d speed=%d\n", __func__,
|
|
||||||
_i2c_bus[dev].scl, _i2c_bus[dev].sda, _i2c_bus[dev].speed);
|
|
||||||
|
|
||||||
/* reset the GPIO usage if the pins were used for I2C before */
|
|
||||||
if (gpio_get_pin_usage(_i2c_bus[dev].scl) == _I2C) {
|
|
||||||
gpio_set_pin_usage(_i2c_bus[dev].scl, _GPIO);
|
|
||||||
}
|
|
||||||
if (gpio_get_pin_usage(_i2c_bus[dev].sda) == _I2C) {
|
|
||||||
gpio_set_pin_usage(_i2c_bus[dev].sda, _GPIO);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* try to configure SDA and SCL pin as GPIO in open-drain mode with enabled pull-ups */
|
|
||||||
if (gpio_init (_i2c_bus[dev].scl, GPIO_IN_OD_PU) ||
|
|
||||||
gpio_init (_i2c_bus[dev].sda, GPIO_IN_OD_PU)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* store the usage type in GPIO table */
|
|
||||||
gpio_set_pin_usage(_i2c_bus[dev].scl, _I2C);
|
|
||||||
gpio_set_pin_usage(_i2c_bus[dev].sda, _I2C);
|
|
||||||
|
|
||||||
/* set SDA and SCL to be floating and pulled-up to high */
|
|
||||||
_i2c_sda_high (&_i2c_bus[dev]);
|
|
||||||
_i2c_scl_high (&_i2c_bus[dev]);
|
|
||||||
|
|
||||||
/* clear the bus if necessary (SDA is driven permanently low) */
|
|
||||||
_i2c_clear (&_i2c_bus[dev]);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i2c_acquire(i2c_t dev)
|
|
||||||
{
|
|
||||||
CHECK_PARAM_RET (dev < I2C_NUMOF, -1)
|
|
||||||
|
|
||||||
mutex_lock(&_i2c_bus[dev].lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void i2c_release(i2c_t dev)
|
|
||||||
{
|
|
||||||
assert(dev < I2C_NUMOF);
|
|
||||||
|
|
||||||
mutex_unlock(&_i2c_bus[dev].lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
int /* IRAM */ i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, uint8_t flags)
|
|
||||||
{
|
|
||||||
DEBUG ("%s: dev=%u addr=%02x data=%p len=%d flags=%01x\n",
|
|
||||||
__func__, dev, addr, data, len, flags);
|
|
||||||
|
|
||||||
CHECK_PARAM_RET (dev < I2C_NUMOF, -EINVAL);
|
|
||||||
CHECK_PARAM_RET (len > 0, -EINVAL);
|
|
||||||
CHECK_PARAM_RET (data != NULL, -EINVAL);
|
|
||||||
|
|
||||||
_i2c_bus_t* bus = &_i2c_bus[dev];
|
|
||||||
|
|
||||||
int res = 0;
|
|
||||||
|
|
||||||
/* send START condition and address if I2C_NOSTART is not set */
|
|
||||||
if (!(flags & I2C_NOSTART)) {
|
|
||||||
|
|
||||||
/* START condition */
|
|
||||||
if ((res = _i2c_start_cond (bus)) != 0) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* send 10 bit or 7 bit address */
|
|
||||||
if (flags & I2C_ADDR10) {
|
|
||||||
/* prepare 10 bit address bytes */
|
|
||||||
uint8_t addr1 = 0xf0 | (addr & 0x0300) >> 7 | I2C_READ;
|
|
||||||
uint8_t addr2 = addr & 0xff;
|
|
||||||
/* send address bytes with read flag */
|
|
||||||
if ((res = _i2c_write_byte (bus, addr1)) != 0 ||
|
|
||||||
(res = _i2c_write_byte (bus, addr2)) != 0) {
|
|
||||||
/* abort transfer */
|
|
||||||
_i2c_abort (bus, __func__);
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* send address byte with read flag */
|
|
||||||
if ((res = _i2c_write_byte (bus, (addr << 1 | I2C_READ))) != 0) {
|
|
||||||
/* abort transfer */
|
|
||||||
_i2c_abort (bus, __func__);
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* receive bytes if send address was successful */
|
|
||||||
for (unsigned int i = 0; i < len; i++) {
|
|
||||||
if ((res = _i2c_read_byte (bus, &(((uint8_t*)data)[i]), i < len-1)) != 0) {
|
|
||||||
/* abort transfer */
|
|
||||||
_i2c_abort (bus, __func__);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* send STOP condition if I2C_NOSTOP flag is not set */
|
|
||||||
if (!(flags & I2C_NOSTOP)) {
|
|
||||||
_i2c_stop_cond (bus);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int /* IRAM */ i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_t len, uint8_t flags)
|
|
||||||
{
|
|
||||||
DEBUG ("%s: dev=%u addr=%02x data=%p len=%d flags=%01x\n",
|
|
||||||
__func__, dev, addr, data, len, flags);
|
|
||||||
|
|
||||||
CHECK_PARAM_RET (dev < I2C_NUMOF, -EINVAL);
|
|
||||||
CHECK_PARAM_RET (len > 0, -EINVAL);
|
|
||||||
CHECK_PARAM_RET (data != NULL, -EINVAL);
|
|
||||||
|
|
||||||
_i2c_bus_t* bus = &_i2c_bus[dev];
|
|
||||||
|
|
||||||
int res = 0;
|
|
||||||
|
|
||||||
/* if I2C_NOSTART is not set, send START condition and ADDR */
|
|
||||||
if (!(flags & I2C_NOSTART)) {
|
|
||||||
|
|
||||||
/* START condition */
|
|
||||||
if ((res = _i2c_start_cond (bus)) != 0) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* send 10 bit or 7 bit address */
|
|
||||||
if (flags & I2C_ADDR10) {
|
|
||||||
/* prepare 10 bit address bytes */
|
|
||||||
uint8_t addr1 = 0xf0 | (addr & 0x0300) >> 7;
|
|
||||||
uint8_t addr2 = addr & 0xff;
|
|
||||||
/* send address bytes without read flag */
|
|
||||||
if ((res = _i2c_write_byte (bus, addr1)) != 0 ||
|
|
||||||
(res = _i2c_write_byte (bus, addr2)) != 0) {
|
|
||||||
/* abort transfer */
|
|
||||||
_i2c_abort (bus, __func__);
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* send address byte without read flag */
|
|
||||||
if ((res = _i2c_write_byte (bus, addr << 1)) != 0) {
|
|
||||||
/* abort transfer */
|
|
||||||
_i2c_abort (bus, __func__);
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* send bytes if send address was successful */
|
|
||||||
for (unsigned int i = 0; i < len; i++) {
|
|
||||||
if ((res = _i2c_write_byte (bus, ((uint8_t*)data)[i])) != 0) {
|
|
||||||
/* abort transfer */
|
|
||||||
_i2c_abort (bus, __func__);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* send STOP condition if I2C_NOSTOP flag is not set */
|
|
||||||
if (!(flags & I2C_NOSTOP)) {
|
|
||||||
return _i2c_stop_cond (bus);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void i2c_poweron(i2c_t dev)
|
|
||||||
{
|
|
||||||
/* since I2C is realized in software there is no device to power on */
|
|
||||||
/* just return */
|
|
||||||
}
|
|
||||||
|
|
||||||
void i2c_poweroff(i2c_t dev)
|
|
||||||
{
|
|
||||||
/* since I2C is realized in software there is no device to power off */
|
|
||||||
/* just return */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- internal functions --- */
|
|
||||||
|
|
||||||
static inline void _i2c_delay (_i2c_bus_t* bus)
|
|
||||||
{
|
|
||||||
/* produces a delay */
|
|
||||||
/* ca. 16 cycles = 1 us (80 MHz) or ca. 32 cycles = 1 us (160 MHz) */
|
|
||||||
|
|
||||||
uint32_t cycles = bus->delay;
|
|
||||||
if (cycles) {
|
|
||||||
__asm__ volatile ("1: _addi.n %0, %0, -1 \n"
|
|
||||||
" bnez %0, 1b \n" : "=r" (cycles) : "0" (cycles));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Please note: SDA and SDL pins are used in GPIO_OD_PU mode
|
|
||||||
* (open-drain with pull-ups).
|
|
||||||
*
|
|
||||||
* Setting a pin which is in open-drain mode leaves the pin floating and
|
|
||||||
* the signal is pulled up to high. The signal can then be actively driven
|
|
||||||
* to low by a slave. A read operation returns the current signal at the pin.
|
|
||||||
*
|
|
||||||
* Clearing a pin which is in open-drain mode actively drives the signal to
|
|
||||||
* low.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static inline bool _i2c_scl_read(_i2c_bus_t* bus)
|
|
||||||
{
|
|
||||||
/* read SCL status (pin is in open-drain mode and set) */
|
|
||||||
return GPIO_GET(in, in1, bus->scl);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _i2c_sda_read(_i2c_bus_t* bus)
|
|
||||||
{
|
|
||||||
/* read SDA status (pin is in open-drain mode and set) */
|
|
||||||
return GPIO_GET(in, in1, bus->sda);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _i2c_scl_high(_i2c_bus_t* bus)
|
|
||||||
{
|
|
||||||
/* set SCL signal high (pin is in open-drain mode and pulled-up) */
|
|
||||||
GPIO_SET(out_w1ts, out1_w1ts, bus->scl);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _i2c_scl_low(_i2c_bus_t* bus)
|
|
||||||
{
|
|
||||||
/* set SCL signal low (actively driven to low) */
|
|
||||||
GPIO_SET(out_w1tc, out1_w1tc, bus->scl);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _i2c_sda_high(_i2c_bus_t* bus)
|
|
||||||
{
|
|
||||||
/* set SDA signal high (pin is in open-drain mode and pulled-up) */
|
|
||||||
GPIO_SET(out_w1ts, out1_w1ts, bus->sda);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _i2c_sda_low(_i2c_bus_t* bus)
|
|
||||||
{
|
|
||||||
/* set SDA signal low (actively driven to low) */
|
|
||||||
GPIO_SET(out_w1tc, out1_w1tc, bus->sda);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _i2c_clear(_i2c_bus_t* bus)
|
|
||||||
{
|
|
||||||
DEBUG("%s: dev=%u\n", __func__, bus->dev);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sometimes a slave blocks and drives the SDA line permanently low.
|
|
||||||
* Send some clock pulses in that case (10 at maximum)
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If SDA is low while SCL is high for 10 half cycles, it is not an
|
|
||||||
* arbitration lost but a bus lock.
|
|
||||||
*/
|
|
||||||
int count = 10;
|
|
||||||
while (!_i2c_sda_read (bus) && _i2c_scl_read (bus) && count) {
|
|
||||||
count--;
|
|
||||||
_i2c_delay (bus);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count) {
|
|
||||||
/* was not a bus lock */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* send 10 clock pulses in case of bus lock */
|
|
||||||
count = 10;
|
|
||||||
while (!_i2c_sda_read (bus) && count--) {
|
|
||||||
_i2c_scl_low (bus);
|
|
||||||
_i2c_delay (bus);
|
|
||||||
_i2c_scl_high (bus);
|
|
||||||
_i2c_delay (bus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _i2c_abort(_i2c_bus_t* bus, const char* func)
|
|
||||||
{
|
|
||||||
DEBUG("%s: dev=%u\n", func, bus->dev);
|
|
||||||
|
|
||||||
/* reset SCL and SDA to passive HIGH (floating and pulled-up) */
|
|
||||||
_i2c_sda_high (bus);
|
|
||||||
_i2c_scl_high (bus);
|
|
||||||
|
|
||||||
/* reset repeated start indicator */
|
|
||||||
bus->started = false;
|
|
||||||
|
|
||||||
/* clear the bus if necessary (SDA is driven permanently low) */
|
|
||||||
_i2c_clear(bus);
|
|
||||||
}
|
|
||||||
|
|
||||||
static /* IRAM */ int _i2c_arbitration_lost (_i2c_bus_t* bus, const char* func)
|
|
||||||
{
|
|
||||||
DEBUG("%s: arbitration lost dev=%u\n", func, bus->dev);
|
|
||||||
|
|
||||||
/* reset SCL and SDA to passive HIGH (floating and pulled-up) */
|
|
||||||
_i2c_sda_high (bus);
|
|
||||||
_i2c_scl_high (bus);
|
|
||||||
|
|
||||||
/* reset repeated start indicator */
|
|
||||||
bus->started = false;
|
|
||||||
|
|
||||||
/* clear the bus if necessary (SDA is driven permanently low) */
|
|
||||||
_i2c_clear(bus);
|
|
||||||
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
static /* IRAM */ int _i2c_start_cond(_i2c_bus_t* bus)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* send start condition
|
|
||||||
* on entry: SDA and SCL are set to be floating and pulled-up to high
|
|
||||||
* on exit : SDA and SCL are actively driven to low
|
|
||||||
*/
|
|
||||||
|
|
||||||
int res = 0;
|
|
||||||
|
|
||||||
if (bus->started) {
|
|
||||||
/* prepare the repeated start condition */
|
|
||||||
|
|
||||||
/* SDA = passive HIGH (floating and pulled-up) */
|
|
||||||
_i2c_sda_high (bus);
|
|
||||||
|
|
||||||
/* t_VD;DAT not necessary */
|
|
||||||
/* _i2c_delay (bus); */
|
|
||||||
|
|
||||||
/* SCL = passive HIGH (floating and pulled-up) */
|
|
||||||
_i2c_scl_high (bus);
|
|
||||||
|
|
||||||
/* clock stretching, wait as long as clock is driven to low by the slave */
|
|
||||||
uint32_t stretch = I2C_CLOCK_STRETCH;
|
|
||||||
while (!_i2c_scl_read (bus) && stretch--) {}
|
|
||||||
if (stretch == 0) {
|
|
||||||
DEBUG("%s: clock stretching timeout dev=%u\n", __func__, bus->dev);
|
|
||||||
res = -ETIMEDOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* wait t_SU;STA - set-up time for a repeated START condition */
|
|
||||||
/* min. in us: 4.7 (SM), 0.6 (FM), 0.26 (FPM), 0.16 (HSM); no max. */
|
|
||||||
_i2c_delay (bus);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if SDA is low, arbitration is lost and someone else is driving the bus */
|
|
||||||
if (!_i2c_sda_read (bus)) {
|
|
||||||
return _i2c_arbitration_lost (bus, __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* begin the START condition: SDA = active LOW */
|
|
||||||
_i2c_sda_low (bus);
|
|
||||||
|
|
||||||
/* wait t_HD;STA - hold time (repeated) START condition, */
|
|
||||||
/* max none */
|
|
||||||
/* min 4.0 us (SM), 0.6 us (FM), 0.26 us (FPM), 0.16 us (HSM) */
|
|
||||||
_i2c_delay (bus);
|
|
||||||
|
|
||||||
/* complete the START condition: SCL = active LOW */
|
|
||||||
_i2c_scl_low (bus);
|
|
||||||
|
|
||||||
/* needed for repeated start condition */
|
|
||||||
bus->started = true;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static /* IRAM */ int _i2c_stop_cond(_i2c_bus_t* bus)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* send stop condition
|
|
||||||
* on entry: SCL is active low and SDA can be changed
|
|
||||||
* on exit : SCL and SDA are set to be floating and pulled-up to high
|
|
||||||
*/
|
|
||||||
|
|
||||||
int res = 0;
|
|
||||||
|
|
||||||
/* begin the STOP condition: SDA = active LOW */
|
|
||||||
_i2c_sda_low (bus);
|
|
||||||
|
|
||||||
/* wait t_LOW - LOW period of SCL clock */
|
|
||||||
/* min. in us: 4.7 (SM), 1.3 (FM), 0.5 (FPM), 0.16 (HSM); no max. */
|
|
||||||
_i2c_delay (bus);
|
|
||||||
|
|
||||||
/* SCL = passive HIGH (floating and pulled up) while SDA = active LOW */
|
|
||||||
_i2c_scl_high (bus);
|
|
||||||
|
|
||||||
/* clock stretching, wait as long as clock is driven to low by the slave */
|
|
||||||
uint32_t stretch = I2C_CLOCK_STRETCH;
|
|
||||||
while (!_i2c_scl_read (bus) && stretch--) {}
|
|
||||||
if (stretch == 0) {
|
|
||||||
DEBUG("%s: clock stretching timeout dev=%u\n", __func__, bus->dev);
|
|
||||||
res = -ETIMEDOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* wait t_SU;STO - hold time START condition, */
|
|
||||||
/* min. in us: 4.0 (SM), 0.6 (FM), 0.26 (FPM), 0.16 (HSM); no max. */
|
|
||||||
_i2c_delay (bus);
|
|
||||||
|
|
||||||
/* complete the STOP condition: SDA = passive HIGH (floating and pulled up) */
|
|
||||||
_i2c_sda_high (bus);
|
|
||||||
|
|
||||||
/* reset repeated start indicator */
|
|
||||||
bus->started = false;
|
|
||||||
|
|
||||||
/* wait t_BUF - bus free time between a STOP and a START condition */
|
|
||||||
/* min. in us: 4.7 (SM), 1.3 (FM), 0.5 (FPM), 0.16 (HSM); no max. */
|
|
||||||
_i2c_delay (bus);
|
|
||||||
/* one additional delay */
|
|
||||||
_i2c_delay (bus);
|
|
||||||
|
|
||||||
/* if SDA is low, arbitration is lost and someone else is driving the bus */
|
|
||||||
if (_i2c_sda_read (bus) == 0) {
|
|
||||||
return _i2c_arbitration_lost (bus, __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static /* IRAM */ int _i2c_write_bit (_i2c_bus_t* bus, bool bit)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* send one bit
|
|
||||||
* on entry: SCL is active low, SDA can be changed
|
|
||||||
* on exit : SCL is active low, SDA can be changed
|
|
||||||
*/
|
|
||||||
|
|
||||||
int res = 0;
|
|
||||||
|
|
||||||
/* SDA = bit */
|
|
||||||
if (bit) {
|
|
||||||
_i2c_sda_high (bus);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_i2c_sda_low (bus);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* wait t_VD;DAT - data valid time (time until data are valid) */
|
|
||||||
/* max. in us: 3.45 (SM), 0.9 (FM), 0.45 (FPM); no min */
|
|
||||||
_i2c_delay (bus);
|
|
||||||
|
|
||||||
/* SCL = passive HIGH (floating and pulled-up), SDA value is available */
|
|
||||||
_i2c_scl_high (bus);
|
|
||||||
|
|
||||||
/* wait t_HIGH - time for the slave to read SDA */
|
|
||||||
/* min. in us: 4 (SM), 0.6 (FM), 0.26 (FPM), 0.09 (HSM); no max. */
|
|
||||||
_i2c_delay (bus);
|
|
||||||
|
|
||||||
/* clock stretching, wait as long as clock is driven low by the slave */
|
|
||||||
uint32_t stretch = I2C_CLOCK_STRETCH;
|
|
||||||
while (!_i2c_scl_read (bus) && stretch--) {}
|
|
||||||
if (stretch == 0) {
|
|
||||||
DEBUG("%s: clock stretching timeout dev=%u\n", __func__, bus->dev);
|
|
||||||
res = -ETIMEDOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if SCL is high, now data is valid */
|
|
||||||
/* if SDA is high, check that nobody else is driving SDA low */
|
|
||||||
if (bit && !_i2c_sda_read(bus)) {
|
|
||||||
return _i2c_arbitration_lost (bus, __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SCL = active LOW to allow next SDA change */
|
|
||||||
_i2c_scl_low(bus);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static /* IRAM */ int _i2c_read_bit (_i2c_bus_t* bus, bool* bit)
|
|
||||||
{
|
|
||||||
/* read one bit
|
|
||||||
* on entry: SCL is active low, SDA can be changed
|
|
||||||
* on exit : SCL is active low, SDA can be changed
|
|
||||||
*/
|
|
||||||
|
|
||||||
int res = 0;
|
|
||||||
|
|
||||||
/* SDA = passive HIGH (floating and pulled-up) to let the slave drive data */
|
|
||||||
_i2c_sda_high (bus);
|
|
||||||
|
|
||||||
/* wait t_VD;DAT - data valid time (time until data are valid) */
|
|
||||||
/* max. in us: 3.45 (SM), 0.9 (FM), 0.45 (FPM); no min */
|
|
||||||
_i2c_delay (bus);
|
|
||||||
|
|
||||||
/* SCL = passive HIGH (floating and pulled-up), SDA value is available */
|
|
||||||
_i2c_scl_high (bus);
|
|
||||||
|
|
||||||
/* clock stretching, wait as long as clock is driven to low by the slave */
|
|
||||||
uint32_t stretch = I2C_CLOCK_STRETCH;
|
|
||||||
while (!_i2c_scl_read (bus) && stretch--) {}
|
|
||||||
if (stretch == 0) {
|
|
||||||
DEBUG("%s: clock stretching timeout dev=%u\n", __func__, bus->dev);
|
|
||||||
res = -ETIMEDOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* wait t_HIGH - time for the slave to read SDA */
|
|
||||||
/* min. in us: 4 (SM), 0.6 (FM), 0.26 (FPM), 0.09 (HSM); no max. */
|
|
||||||
_i2c_delay (bus);
|
|
||||||
|
|
||||||
/* SCL is high, read out bit */
|
|
||||||
*bit = _i2c_sda_read (bus);
|
|
||||||
|
|
||||||
/* SCL = active LOW to allow next SDA change */
|
|
||||||
_i2c_scl_low(bus);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static /* IRAM */ int _i2c_write_byte (_i2c_bus_t* bus, uint8_t byte)
|
|
||||||
{
|
|
||||||
/* send one byte and returns 0 in case of ACK from slave */
|
|
||||||
|
|
||||||
/* send the byte from MSB to LSB */
|
|
||||||
for (unsigned i = 0; i < 8; i++) {
|
|
||||||
int res = _i2c_write_bit(bus, (byte & 0x80) != 0);
|
|
||||||
if (res != 0) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
byte = byte << 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read acknowledge bit (low) from slave */
|
|
||||||
bool bit;
|
|
||||||
int res = _i2c_read_bit (bus, &bit);
|
|
||||||
if (res != 0) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !bit ? 0 : -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static /* IRAM */ int _i2c_read_byte(_i2c_bus_t* bus, uint8_t *byte, bool ack)
|
|
||||||
{
|
|
||||||
bool bit;
|
|
||||||
|
|
||||||
/* read the byte */
|
|
||||||
for (unsigned i = 0; i < 8; i++) {
|
|
||||||
int res = _i2c_read_bit (bus, &bit);
|
|
||||||
if (res != 0) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
*byte = (*byte << 1) | (bit ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* write acknowledgement flag */
|
|
||||||
_i2c_write_bit(bus, !ack);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void i2c_print_config(void)
|
|
||||||
{
|
|
||||||
for (unsigned dev = 0; dev < I2C_NUMOF; dev++) {
|
|
||||||
printf("\tI2C_DEV(%u)\tscl=%d sda=%d\n",
|
|
||||||
dev, i2c_config[dev].scl, i2c_config[dev].sda);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#else /* defined(I2C0_SPEED) || defined(I2C1_SPEED) */
|
|
||||||
|
|
||||||
void i2c_print_config(void)
|
|
||||||
{
|
|
||||||
LOG_TAG_INFO("i2c", "no I2C devices\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* defined(I2C0_SPEED) || defined(I2C1_SPEED) */
|
|
||||||
|
|
||||||
#endif /* MODULE_ESP_I2C_SW */
|
|
||||||
@ -1,3 +1,8 @@
|
|||||||
# additional modules dependencies
|
# additional modules dependencies
|
||||||
|
|
||||||
include $(RIOTCPU)/esp_common/Makefile.dep
|
include $(RIOTCPU)/esp_common/Makefile.dep
|
||||||
|
|
||||||
|
ifneq (,$(filter periph_i2c,$(USEMODULE)))
|
||||||
|
USEMODULE += esp_i2c_sw
|
||||||
|
USEMODULE += periph_i2c_sw
|
||||||
|
endif
|
||||||
|
|||||||
@ -29,6 +29,7 @@ endif
|
|||||||
# ESP* pseudomodules
|
# ESP* pseudomodules
|
||||||
|
|
||||||
PSEUDOMODULES += esp_gdb
|
PSEUDOMODULES += esp_gdb
|
||||||
|
PSEUDOMODULES += esp_i2c_sw
|
||||||
PSEUDOMODULES += esp_log_colored
|
PSEUDOMODULES += esp_log_colored
|
||||||
PSEUDOMODULES += esp_log_tagged
|
PSEUDOMODULES += esp_log_tagged
|
||||||
PSEUDOMODULES += esp_log_startup
|
PSEUDOMODULES += esp_log_startup
|
||||||
|
|||||||
@ -7,12 +7,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ingroup cpu_esp8266
|
* @ingroup cpu_esp_common
|
||||||
* @ingroup drivers_periph_i2c
|
* @ingroup drivers_periph_i2c
|
||||||
* @{
|
* @{
|
||||||
*
|
*
|
||||||
* @file
|
* @file
|
||||||
* @brief Low-level I2C driver implementation using ESP8266 SDK
|
* @brief Low-level I2C driver software implementation using for ESP SoCs
|
||||||
*
|
*
|
||||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||||
*
|
*
|
||||||
@ -40,6 +40,7 @@
|
|||||||
#include "periph/gpio.h"
|
#include "periph/gpio.h"
|
||||||
#include "periph/i2c.h"
|
#include "periph/i2c.h"
|
||||||
|
|
||||||
|
#include "esp_attr.h"
|
||||||
#include "esp_common.h"
|
#include "esp_common.h"
|
||||||
#include "gpio_arch.h"
|
#include "gpio_arch.h"
|
||||||
#include "rom/ets_sys.h"
|
#include "rom/ets_sys.h"
|
||||||
@ -116,7 +117,7 @@ static const uint32_t _i2c_delays[][2] =
|
|||||||
/* 1 us = 20 cycles (80 MHz) / 40 cycles (160 MHz) */
|
/* 1 us = 20 cycles (80 MHz) / 40 cycles (160 MHz) */
|
||||||
[I2C_SPEED_LOW] = {989, 1990}, /* 10 kbps (period 100 us) */
|
[I2C_SPEED_LOW] = {989, 1990}, /* 10 kbps (period 100 us) */
|
||||||
[I2C_SPEED_NORMAL] = { 89, 190}, /* 100 kbps (period 10 us) */
|
[I2C_SPEED_NORMAL] = { 89, 190}, /* 100 kbps (period 10 us) */
|
||||||
[I2C_SPEED_FAST] = { 16, 40}, /* 400 kbps (period 2.5 us) */
|
[I2C_SPEED_FAST] = { 15, 40}, /* 400 kbps (period 2.5 us) */
|
||||||
[I2C_SPEED_FAST_PLUS] = { 0, 13}, /* 1 Mbps (period 1 us) */
|
[I2C_SPEED_FAST_PLUS] = { 0, 13}, /* 1 Mbps (period 1 us) */
|
||||||
[I2C_SPEED_HIGH] = { 0, 0} /* 3.4 Mbps (period 0.3 us) is not working */
|
[I2C_SPEED_HIGH] = { 0, 0} /* 3.4 Mbps (period 0.3 us) is not working */
|
||||||
};
|
};
|
||||||
@ -124,22 +125,22 @@ static const uint32_t _i2c_delays[][2] =
|
|||||||
|
|
||||||
/* forward declaration of internal functions */
|
/* forward declaration of internal functions */
|
||||||
|
|
||||||
static inline void _i2c_delay (_i2c_bus_t* bus);
|
static inline void _i2c_delay(_i2c_bus_t* bus);
|
||||||
static inline bool _i2c_scl_read (_i2c_bus_t* bus);
|
static inline bool _i2c_scl_read(_i2c_bus_t* bus);
|
||||||
static inline bool _i2c_sda_read (_i2c_bus_t* bus);
|
static inline bool _i2c_sda_read(_i2c_bus_t* bus);
|
||||||
static inline void _i2c_scl_high (_i2c_bus_t* bus);
|
static inline void _i2c_scl_high(_i2c_bus_t* bus);
|
||||||
static inline void _i2c_scl_low (_i2c_bus_t* bus);
|
static inline void _i2c_scl_low(_i2c_bus_t* bus);
|
||||||
static inline void _i2c_sda_high (_i2c_bus_t* bus);
|
static inline void _i2c_sda_high(_i2c_bus_t* bus);
|
||||||
static inline void _i2c_sda_low (_i2c_bus_t* bus);
|
static inline void _i2c_sda_low(_i2c_bus_t* bus);
|
||||||
static int _i2c_start_cond (_i2c_bus_t* bus);
|
static int _i2c_start_cond(_i2c_bus_t* bus);
|
||||||
static int _i2c_stop_cond (_i2c_bus_t* bus);
|
static int _i2c_stop_cond(_i2c_bus_t* bus);
|
||||||
static int _i2c_write_bit (_i2c_bus_t* bus, bool bit);
|
static int _i2c_write_bit(_i2c_bus_t* bus, bool bit);
|
||||||
static int _i2c_read_bit (_i2c_bus_t* bus, bool* bit);
|
static int _i2c_read_bit(_i2c_bus_t* bus, bool* bit);
|
||||||
static int _i2c_write_byte (_i2c_bus_t* bus, uint8_t byte);
|
static int _i2c_write_byte(_i2c_bus_t* bus, uint8_t byte);
|
||||||
static int _i2c_read_byte (_i2c_bus_t* bus, uint8_t* byte, bool ack);
|
static int _i2c_read_byte(_i2c_bus_t* bus, uint8_t* byte, bool ack);
|
||||||
static int _i2c_arbitration_lost (_i2c_bus_t* bus, const char* func);
|
static int _i2c_arbitration_lost(_i2c_bus_t* bus, const char* func);
|
||||||
static void _i2c_abort (_i2c_bus_t* bus, const char* func);
|
static void _i2c_abort(_i2c_bus_t* bus, const char* func);
|
||||||
static void _i2c_clear (_i2c_bus_t* bus);
|
static void _i2c_clear(_i2c_bus_t* bus);
|
||||||
|
|
||||||
/* implementation of i2c interface */
|
/* implementation of i2c interface */
|
||||||
|
|
||||||
@ -224,11 +225,11 @@ void i2c_init(i2c_t dev)
|
|||||||
gpio_set_pin_usage(_i2c_bus[dev].sda, _I2C);
|
gpio_set_pin_usage(_i2c_bus[dev].sda, _I2C);
|
||||||
|
|
||||||
/* set SDA and SCL to be floating and pulled-up to high */
|
/* set SDA and SCL to be floating and pulled-up to high */
|
||||||
_i2c_sda_high (&_i2c_bus[dev]);
|
_i2c_sda_high(&_i2c_bus[dev]);
|
||||||
_i2c_scl_high (&_i2c_bus[dev]);
|
_i2c_scl_high(&_i2c_bus[dev]);
|
||||||
|
|
||||||
/* clear the bus if necessary (SDA is driven permanently low) */
|
/* clear the bus if necessary (SDA is driven permanently low) */
|
||||||
_i2c_clear (&_i2c_bus[dev]);
|
_i2c_clear(&_i2c_bus[dev]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -248,15 +249,15 @@ void i2c_release(i2c_t dev)
|
|||||||
mutex_unlock(&_i2c_bus[dev].lock);
|
mutex_unlock(&_i2c_bus[dev].lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
int /* IRAM */ i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, uint8_t flags)
|
int IRAM_ATTR i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, uint8_t flags)
|
||||||
{
|
{
|
||||||
DEBUG ("%s: dev=%u addr=%02x data=%p len=%d flags=%01x\n",
|
DEBUG("%s: dev=%u addr=%02x data=%p len=%d flags=%01x\n",
|
||||||
__func__, dev, addr, data, len, flags);
|
__func__, dev, addr, data, len, flags);
|
||||||
|
|
||||||
assert(dev < I2C_NUMOF);
|
assert(dev < I2C_NUMOF);
|
||||||
|
|
||||||
CHECK_PARAM_RET (len > 0, -EINVAL);
|
CHECK_PARAM_RET(len > 0, -EINVAL);
|
||||||
CHECK_PARAM_RET (data != NULL, -EINVAL);
|
CHECK_PARAM_RET(data != NULL, -EINVAL);
|
||||||
|
|
||||||
_i2c_bus_t* bus = &_i2c_bus[dev];
|
_i2c_bus_t* bus = &_i2c_bus[dev];
|
||||||
|
|
||||||
@ -266,7 +267,7 @@ int /* IRAM */ i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len,
|
|||||||
if (!(flags & I2C_NOSTART)) {
|
if (!(flags & I2C_NOSTART)) {
|
||||||
|
|
||||||
/* START condition */
|
/* START condition */
|
||||||
if ((res = _i2c_start_cond (bus)) != 0) {
|
if ((res = _i2c_start_cond(bus)) != 0) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,18 +277,18 @@ int /* IRAM */ i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len,
|
|||||||
uint8_t addr1 = 0xf0 | (addr & 0x0300) >> 7 | I2C_READ;
|
uint8_t addr1 = 0xf0 | (addr & 0x0300) >> 7 | I2C_READ;
|
||||||
uint8_t addr2 = addr & 0xff;
|
uint8_t addr2 = addr & 0xff;
|
||||||
/* send address bytes with read flag */
|
/* send address bytes with read flag */
|
||||||
if ((res = _i2c_write_byte (bus, addr1)) != 0 ||
|
if ((res = _i2c_write_byte(bus, addr1)) != 0 ||
|
||||||
(res = _i2c_write_byte (bus, addr2)) != 0) {
|
(res = _i2c_write_byte(bus, addr2)) != 0) {
|
||||||
/* abort transfer */
|
/* abort transfer */
|
||||||
_i2c_abort (bus, __func__);
|
_i2c_abort(bus, __func__);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* send address byte with read flag */
|
/* send address byte with read flag */
|
||||||
if ((res = _i2c_write_byte (bus, (addr << 1 | I2C_READ))) != 0) {
|
if ((res = _i2c_write_byte(bus, (addr << 1 | I2C_READ))) != 0) {
|
||||||
/* abort transfer */
|
/* abort transfer */
|
||||||
_i2c_abort (bus, __func__);
|
_i2c_abort(bus, __func__);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -295,30 +296,30 @@ int /* IRAM */ i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len,
|
|||||||
|
|
||||||
/* receive bytes if send address was successful */
|
/* receive bytes if send address was successful */
|
||||||
for (unsigned int i = 0; i < len; i++) {
|
for (unsigned int i = 0; i < len; i++) {
|
||||||
if ((res = _i2c_read_byte (bus, &(((uint8_t*)data)[i]), i < len-1)) != 0) {
|
if ((res = _i2c_read_byte(bus, &(((uint8_t*)data)[i]), i < len-1)) != 0) {
|
||||||
/* abort transfer */
|
/* abort transfer */
|
||||||
_i2c_abort (bus, __func__);
|
_i2c_abort(bus, __func__);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send STOP condition if I2C_NOSTOP flag is not set */
|
/* send STOP condition if I2C_NOSTOP flag is not set */
|
||||||
if (!(flags & I2C_NOSTOP)) {
|
if (!(flags & I2C_NOSTOP)) {
|
||||||
res = _i2c_stop_cond (bus);
|
res = _i2c_stop_cond(bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int /* IRAM */ i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_t len, uint8_t flags)
|
int IRAM_ATTR i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_t len, uint8_t flags)
|
||||||
{
|
{
|
||||||
DEBUG ("%s: dev=%u addr=%02x data=%p len=%d flags=%01x\n",
|
DEBUG("%s: dev=%u addr=%02x data=%p len=%d flags=%01x\n",
|
||||||
__func__, dev, addr, data, len, flags);
|
__func__, dev, addr, data, len, flags);
|
||||||
|
|
||||||
assert(dev < I2C_NUMOF);
|
assert(dev < I2C_NUMOF);
|
||||||
|
|
||||||
CHECK_PARAM_RET (len > 0, -EINVAL);
|
CHECK_PARAM_RET(len > 0, -EINVAL);
|
||||||
CHECK_PARAM_RET (data != NULL, -EINVAL);
|
CHECK_PARAM_RET(data != NULL, -EINVAL);
|
||||||
|
|
||||||
_i2c_bus_t* bus = &_i2c_bus[dev];
|
_i2c_bus_t* bus = &_i2c_bus[dev];
|
||||||
|
|
||||||
@ -328,7 +329,7 @@ int /* IRAM */ i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_
|
|||||||
if (!(flags & I2C_NOSTART)) {
|
if (!(flags & I2C_NOSTART)) {
|
||||||
|
|
||||||
/* START condition */
|
/* START condition */
|
||||||
if ((res = _i2c_start_cond (bus)) != 0) {
|
if ((res = _i2c_start_cond(bus)) != 0) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,18 +339,18 @@ int /* IRAM */ i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_
|
|||||||
uint8_t addr1 = 0xf0 | (addr & 0x0300) >> 7;
|
uint8_t addr1 = 0xf0 | (addr & 0x0300) >> 7;
|
||||||
uint8_t addr2 = addr & 0xff;
|
uint8_t addr2 = addr & 0xff;
|
||||||
/* send address bytes without read flag */
|
/* send address bytes without read flag */
|
||||||
if ((res = _i2c_write_byte (bus, addr1)) != 0 ||
|
if ((res = _i2c_write_byte(bus, addr1)) != 0 ||
|
||||||
(res = _i2c_write_byte (bus, addr2)) != 0) {
|
(res = _i2c_write_byte(bus, addr2)) != 0) {
|
||||||
/* abort transfer */
|
/* abort transfer */
|
||||||
_i2c_abort (bus, __func__);
|
_i2c_abort(bus, __func__);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* send address byte without read flag */
|
/* send address byte without read flag */
|
||||||
if ((res = _i2c_write_byte (bus, addr << 1)) != 0) {
|
if ((res = _i2c_write_byte(bus, addr << 1)) != 0) {
|
||||||
/* abort transfer */
|
/* abort transfer */
|
||||||
_i2c_abort (bus, __func__);
|
_i2c_abort(bus, __func__);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -357,16 +358,16 @@ int /* IRAM */ i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_
|
|||||||
|
|
||||||
/* send bytes if send address was successful */
|
/* send bytes if send address was successful */
|
||||||
for (unsigned int i = 0; i < len; i++) {
|
for (unsigned int i = 0; i < len; i++) {
|
||||||
if ((res = _i2c_write_byte (bus, ((uint8_t*)data)[i])) != 0) {
|
if ((res = _i2c_write_byte(bus, ((uint8_t*)data)[i])) != 0) {
|
||||||
/* abort transfer */
|
/* abort transfer */
|
||||||
_i2c_abort (bus, __func__);
|
_i2c_abort(bus, __func__);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send STOP condition if I2C_NOSTOP flag is not set */
|
/* send STOP condition if I2C_NOSTOP flag is not set */
|
||||||
if (!(flags & I2C_NOSTOP)) {
|
if (!(flags & I2C_NOSTOP)) {
|
||||||
res = _i2c_stop_cond (bus);
|
res = _i2c_stop_cond(bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@ -386,7 +387,7 @@ void i2c_poweroff(i2c_t dev)
|
|||||||
|
|
||||||
/* --- internal functions --- */
|
/* --- internal functions --- */
|
||||||
|
|
||||||
static inline void _i2c_delay (_i2c_bus_t* bus)
|
static inline void _i2c_delay(_i2c_bus_t* bus)
|
||||||
{
|
{
|
||||||
/* produces a delay */
|
/* produces a delay */
|
||||||
uint32_t cycles = bus->delay;
|
uint32_t cycles = bus->delay;
|
||||||
@ -498,9 +499,9 @@ static void _i2c_clear(_i2c_bus_t* bus)
|
|||||||
* arbitration lost but a bus lock.
|
* arbitration lost but a bus lock.
|
||||||
*/
|
*/
|
||||||
int count = 10;
|
int count = 10;
|
||||||
while (!_i2c_sda_read (bus) && _i2c_scl_read (bus) && count) {
|
while (!_i2c_sda_read(bus) && _i2c_scl_read(bus) && count) {
|
||||||
count--;
|
count--;
|
||||||
_i2c_delay (bus);
|
_i2c_delay(bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count) {
|
if (count) {
|
||||||
@ -510,11 +511,11 @@ static void _i2c_clear(_i2c_bus_t* bus)
|
|||||||
|
|
||||||
/* send 10 clock pulses in case of bus lock */
|
/* send 10 clock pulses in case of bus lock */
|
||||||
count = 10;
|
count = 10;
|
||||||
while (!_i2c_sda_read (bus) && count--) {
|
while (!_i2c_sda_read(bus) && count--) {
|
||||||
_i2c_scl_low (bus);
|
_i2c_scl_low(bus);
|
||||||
_i2c_delay (bus);
|
_i2c_delay(bus);
|
||||||
_i2c_scl_high (bus);
|
_i2c_scl_high(bus);
|
||||||
_i2c_delay (bus);
|
_i2c_delay(bus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -523,8 +524,8 @@ static void _i2c_abort(_i2c_bus_t* bus, const char* func)
|
|||||||
DEBUG("%s: dev=%u\n", func, bus->dev);
|
DEBUG("%s: dev=%u\n", func, bus->dev);
|
||||||
|
|
||||||
/* reset SCL and SDA to passive HIGH (floating and pulled-up) */
|
/* reset SCL and SDA to passive HIGH (floating and pulled-up) */
|
||||||
_i2c_sda_high (bus);
|
_i2c_sda_high(bus);
|
||||||
_i2c_scl_high (bus);
|
_i2c_scl_high(bus);
|
||||||
|
|
||||||
/* reset repeated start indicator */
|
/* reset repeated start indicator */
|
||||||
bus->started = false;
|
bus->started = false;
|
||||||
@ -533,13 +534,13 @@ static void _i2c_abort(_i2c_bus_t* bus, const char* func)
|
|||||||
_i2c_clear(bus);
|
_i2c_clear(bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
static /* IRAM */ int _i2c_arbitration_lost (_i2c_bus_t* bus, const char* func)
|
static IRAM_ATTR int _i2c_arbitration_lost(_i2c_bus_t* bus, const char* func)
|
||||||
{
|
{
|
||||||
DEBUG("%s: arbitration lost dev=%u\n", func, bus->dev);
|
DEBUG("%s: arbitration lost dev=%u\n", func, bus->dev);
|
||||||
|
|
||||||
/* reset SCL and SDA to passive HIGH (floating and pulled-up) */
|
/* reset SCL and SDA to passive HIGH (floating and pulled-up) */
|
||||||
_i2c_sda_high (bus);
|
_i2c_sda_high(bus);
|
||||||
_i2c_scl_high (bus);
|
_i2c_scl_high(bus);
|
||||||
|
|
||||||
/* reset repeated start indicator */
|
/* reset repeated start indicator */
|
||||||
bus->started = false;
|
bus->started = false;
|
||||||
@ -550,7 +551,7 @@ static /* IRAM */ int _i2c_arbitration_lost (_i2c_bus_t* bus, const char* func)
|
|||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
static /* IRAM */ int _i2c_start_cond(_i2c_bus_t* bus)
|
static IRAM_ATTR int _i2c_start_cond(_i2c_bus_t* bus)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* send start condition
|
* send start condition
|
||||||
@ -564,17 +565,17 @@ static /* IRAM */ int _i2c_start_cond(_i2c_bus_t* bus)
|
|||||||
/* prepare the repeated start condition */
|
/* prepare the repeated start condition */
|
||||||
|
|
||||||
/* SDA = passive HIGH (floating and pulled-up) */
|
/* SDA = passive HIGH (floating and pulled-up) */
|
||||||
_i2c_sda_high (bus);
|
_i2c_sda_high(bus);
|
||||||
|
|
||||||
/* t_VD;DAT not necessary */
|
/* t_VD;DAT not necessary */
|
||||||
/* _i2c_delay (bus); */
|
/* _i2c_delay(bus); */
|
||||||
|
|
||||||
/* SCL = passive HIGH (floating and pulled-up) */
|
/* SCL = passive HIGH (floating and pulled-up) */
|
||||||
_i2c_scl_high (bus);
|
_i2c_scl_high(bus);
|
||||||
|
|
||||||
/* clock stretching, wait as long as clock is driven to low by the slave */
|
/* clock stretching, wait as long as clock is driven to low by the slave */
|
||||||
uint32_t stretch = I2C_CLOCK_STRETCH;
|
uint32_t stretch = I2C_CLOCK_STRETCH;
|
||||||
while (stretch && !_i2c_scl_read (bus)) {
|
while (stretch && !_i2c_scl_read(bus)) {
|
||||||
stretch--;
|
stretch--;
|
||||||
}
|
}
|
||||||
if (stretch == 0) {
|
if (stretch == 0) {
|
||||||
@ -584,24 +585,24 @@ static /* IRAM */ int _i2c_start_cond(_i2c_bus_t* bus)
|
|||||||
|
|
||||||
/* wait t_SU;STA - set-up time for a repeated START condition */
|
/* wait t_SU;STA - set-up time for a repeated START condition */
|
||||||
/* min. in us: 4.7 (SM), 0.6 (FM), 0.26 (FPM), 0.16 (HSM); no max. */
|
/* min. in us: 4.7 (SM), 0.6 (FM), 0.26 (FPM), 0.16 (HSM); no max. */
|
||||||
_i2c_delay (bus);
|
_i2c_delay(bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if SDA is low, arbitration is lost and someone else is driving the bus */
|
/* if SDA is low, arbitration is lost and someone else is driving the bus */
|
||||||
if (!_i2c_sda_read (bus)) {
|
if (!_i2c_sda_read(bus)) {
|
||||||
return _i2c_arbitration_lost (bus, __func__);
|
return _i2c_arbitration_lost(bus, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* begin the START condition: SDA = active LOW */
|
/* begin the START condition: SDA = active LOW */
|
||||||
_i2c_sda_low (bus);
|
_i2c_sda_low(bus);
|
||||||
|
|
||||||
/* wait t_HD;STA - hold time (repeated) START condition, */
|
/* wait t_HD;STA - hold time (repeated) START condition, */
|
||||||
/* max none */
|
/* max none */
|
||||||
/* min 4.0 us (SM), 0.6 us (FM), 0.26 us (FPM), 0.16 us (HSM) */
|
/* min 4.0 us (SM), 0.6 us (FM), 0.26 us (FPM), 0.16 us (HSM) */
|
||||||
_i2c_delay (bus);
|
_i2c_delay(bus);
|
||||||
|
|
||||||
/* complete the START condition: SCL = active LOW */
|
/* complete the START condition: SCL = active LOW */
|
||||||
_i2c_scl_low (bus);
|
_i2c_scl_low(bus);
|
||||||
|
|
||||||
/* needed for repeated start condition */
|
/* needed for repeated start condition */
|
||||||
bus->started = true;
|
bus->started = true;
|
||||||
@ -609,7 +610,7 @@ static /* IRAM */ int _i2c_start_cond(_i2c_bus_t* bus)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static /* IRAM */ int _i2c_stop_cond(_i2c_bus_t* bus)
|
static IRAM_ATTR int _i2c_stop_cond(_i2c_bus_t* bus)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* send stop condition
|
* send stop condition
|
||||||
@ -620,18 +621,18 @@ static /* IRAM */ int _i2c_stop_cond(_i2c_bus_t* bus)
|
|||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
/* begin the STOP condition: SDA = active LOW */
|
/* begin the STOP condition: SDA = active LOW */
|
||||||
_i2c_sda_low (bus);
|
_i2c_sda_low(bus);
|
||||||
|
|
||||||
/* wait t_LOW - LOW period of SCL clock */
|
/* wait t_LOW - LOW period of SCL clock */
|
||||||
/* min. in us: 4.7 (SM), 1.3 (FM), 0.5 (FPM), 0.16 (HSM); no max. */
|
/* min. in us: 4.7 (SM), 1.3 (FM), 0.5 (FPM), 0.16 (HSM); no max. */
|
||||||
_i2c_delay (bus);
|
_i2c_delay(bus);
|
||||||
|
|
||||||
/* SCL = passive HIGH (floating and pulled up) while SDA = active LOW */
|
/* SCL = passive HIGH (floating and pulled up) while SDA = active LOW */
|
||||||
_i2c_scl_high (bus);
|
_i2c_scl_high(bus);
|
||||||
|
|
||||||
/* clock stretching, wait as long as clock is driven to low by the slave */
|
/* clock stretching, wait as long as clock is driven to low by the slave */
|
||||||
uint32_t stretch = I2C_CLOCK_STRETCH;
|
uint32_t stretch = I2C_CLOCK_STRETCH;
|
||||||
while (stretch && !_i2c_scl_read (bus)) {
|
while (stretch && !_i2c_scl_read(bus)) {
|
||||||
stretch--;
|
stretch--;
|
||||||
}
|
}
|
||||||
if (stretch == 0) {
|
if (stretch == 0) {
|
||||||
@ -641,29 +642,29 @@ static /* IRAM */ int _i2c_stop_cond(_i2c_bus_t* bus)
|
|||||||
|
|
||||||
/* wait t_SU;STO - hold time STOP condition, */
|
/* wait t_SU;STO - hold time STOP condition, */
|
||||||
/* min. in us: 4.0 (SM), 0.6 (FM), 0.26 (FPM), 0.16 (HSM); no max. */
|
/* min. in us: 4.0 (SM), 0.6 (FM), 0.26 (FPM), 0.16 (HSM); no max. */
|
||||||
_i2c_delay (bus);
|
_i2c_delay(bus);
|
||||||
|
|
||||||
/* complete the STOP condition: SDA = passive HIGH (floating and pulled up) */
|
/* complete the STOP condition: SDA = passive HIGH (floating and pulled up) */
|
||||||
_i2c_sda_high (bus);
|
_i2c_sda_high(bus);
|
||||||
|
|
||||||
/* reset repeated start indicator */
|
/* reset repeated start indicator */
|
||||||
bus->started = false;
|
bus->started = false;
|
||||||
|
|
||||||
/* wait t_BUF - bus free time between a STOP and a START condition */
|
/* wait t_BUF - bus free time between a STOP and a START condition */
|
||||||
/* min. in us: 4.7 (SM), 1.3 (FM), 0.5 (FPM), 0.16 (HSM); no max. */
|
/* min. in us: 4.7 (SM), 1.3 (FM), 0.5 (FPM), 0.16 (HSM); no max. */
|
||||||
_i2c_delay (bus);
|
_i2c_delay(bus);
|
||||||
/* one additional delay */
|
/* one additional delay */
|
||||||
_i2c_delay (bus);
|
_i2c_delay(bus);
|
||||||
|
|
||||||
/* if SDA is low, arbitration is lost and someone else is driving the bus */
|
/* if SDA is low, arbitration is lost and someone else is driving the bus */
|
||||||
if (_i2c_sda_read (bus) == 0) {
|
if (_i2c_sda_read(bus) == 0) {
|
||||||
return _i2c_arbitration_lost (bus, __func__);
|
return _i2c_arbitration_lost(bus, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static /* IRAM */ int _i2c_write_bit (_i2c_bus_t* bus, bool bit)
|
static IRAM_ATTR int _i2c_write_bit(_i2c_bus_t* bus, bool bit)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* send one bit
|
* send one bit
|
||||||
@ -675,26 +676,26 @@ static /* IRAM */ int _i2c_write_bit (_i2c_bus_t* bus, bool bit)
|
|||||||
|
|
||||||
/* SDA = bit */
|
/* SDA = bit */
|
||||||
if (bit) {
|
if (bit) {
|
||||||
_i2c_sda_high (bus);
|
_i2c_sda_high(bus);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
_i2c_sda_low (bus);
|
_i2c_sda_low(bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* wait t_VD;DAT - data valid time (time until data are valid) */
|
/* wait t_VD;DAT - data valid time (time until data are valid) */
|
||||||
/* max. in us: 3.45 (SM), 0.9 (FM), 0.45 (FPM); no min */
|
/* max. in us: 3.45 (SM), 0.9 (FM), 0.45 (FPM); no min */
|
||||||
_i2c_delay (bus);
|
_i2c_delay(bus);
|
||||||
|
|
||||||
/* SCL = passive HIGH (floating and pulled-up), SDA value is available */
|
/* SCL = passive HIGH (floating and pulled-up), SDA value is available */
|
||||||
_i2c_scl_high (bus);
|
_i2c_scl_high(bus);
|
||||||
|
|
||||||
/* wait t_HIGH - time for the slave to read SDA */
|
/* wait t_HIGH - time for the slave to read SDA */
|
||||||
/* min. in us: 4 (SM), 0.6 (FM), 0.26 (FPM), 0.09 (HSM); no max. */
|
/* min. in us: 4 (SM), 0.6 (FM), 0.26 (FPM), 0.09 (HSM); no max. */
|
||||||
_i2c_delay (bus);
|
_i2c_delay(bus);
|
||||||
|
|
||||||
/* clock stretching, wait as long as clock is driven low by the slave */
|
/* clock stretching, wait as long as clock is driven low by the slave */
|
||||||
uint32_t stretch = I2C_CLOCK_STRETCH;
|
uint32_t stretch = I2C_CLOCK_STRETCH;
|
||||||
while (stretch && !_i2c_scl_read (bus)) {
|
while (stretch && !_i2c_scl_read(bus)) {
|
||||||
stretch--;
|
stretch--;
|
||||||
}
|
}
|
||||||
if (stretch == 0) {
|
if (stretch == 0) {
|
||||||
@ -705,7 +706,7 @@ static /* IRAM */ int _i2c_write_bit (_i2c_bus_t* bus, bool bit)
|
|||||||
/* if SCL is high, now data is valid */
|
/* if SCL is high, now data is valid */
|
||||||
/* if SDA is high, check that nobody else is driving SDA low */
|
/* if SDA is high, check that nobody else is driving SDA low */
|
||||||
if (bit && !_i2c_sda_read(bus)) {
|
if (bit && !_i2c_sda_read(bus)) {
|
||||||
return _i2c_arbitration_lost (bus, __func__);
|
return _i2c_arbitration_lost(bus, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SCL = active LOW to allow next SDA change */
|
/* SCL = active LOW to allow next SDA change */
|
||||||
@ -714,7 +715,7 @@ static /* IRAM */ int _i2c_write_bit (_i2c_bus_t* bus, bool bit)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static /* IRAM */ int _i2c_read_bit (_i2c_bus_t* bus, bool* bit)
|
static IRAM_ATTR int _i2c_read_bit(_i2c_bus_t* bus, bool* bit)
|
||||||
{
|
{
|
||||||
/* read one bit
|
/* read one bit
|
||||||
* on entry: SCL is active low, SDA can be changed
|
* on entry: SCL is active low, SDA can be changed
|
||||||
@ -724,18 +725,18 @@ static /* IRAM */ int _i2c_read_bit (_i2c_bus_t* bus, bool* bit)
|
|||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
/* SDA = passive HIGH (floating and pulled-up) to let the slave drive data */
|
/* SDA = passive HIGH (floating and pulled-up) to let the slave drive data */
|
||||||
_i2c_sda_high (bus);
|
_i2c_sda_high(bus);
|
||||||
|
|
||||||
/* wait t_VD;DAT - data valid time (time until data are valid) */
|
/* wait t_VD;DAT - data valid time (time until data are valid) */
|
||||||
/* max. in us: 3.45 (SM), 0.9 (FM), 0.45 (FPM); no min */
|
/* max. in us: 3.45 (SM), 0.9 (FM), 0.45 (FPM); no min */
|
||||||
_i2c_delay (bus);
|
_i2c_delay(bus);
|
||||||
|
|
||||||
/* SCL = passive HIGH (floating and pulled-up), SDA value is available */
|
/* SCL = passive HIGH (floating and pulled-up), SDA value is available */
|
||||||
_i2c_scl_high (bus);
|
_i2c_scl_high(bus);
|
||||||
|
|
||||||
/* clock stretching, wait as long as clock is driven to low by the slave */
|
/* clock stretching, wait as long as clock is driven to low by the slave */
|
||||||
uint32_t stretch = I2C_CLOCK_STRETCH;
|
uint32_t stretch = I2C_CLOCK_STRETCH;
|
||||||
while (stretch && !_i2c_scl_read (bus)) {
|
while (stretch && !_i2c_scl_read(bus)) {
|
||||||
stretch--;
|
stretch--;
|
||||||
}
|
}
|
||||||
if (stretch == 0) {
|
if (stretch == 0) {
|
||||||
@ -745,10 +746,10 @@ static /* IRAM */ int _i2c_read_bit (_i2c_bus_t* bus, bool* bit)
|
|||||||
|
|
||||||
/* wait t_HIGH - time for the slave to read SDA */
|
/* wait t_HIGH - time for the slave to read SDA */
|
||||||
/* min. in us: 4 (SM), 0.6 (FM), 0.26 (FPM), 0.09 (HSM); no max. */
|
/* min. in us: 4 (SM), 0.6 (FM), 0.26 (FPM), 0.09 (HSM); no max. */
|
||||||
_i2c_delay (bus);
|
_i2c_delay(bus);
|
||||||
|
|
||||||
/* SCL is high, read out bit */
|
/* SCL is high, read out bit */
|
||||||
*bit = _i2c_sda_read (bus);
|
*bit = _i2c_sda_read(bus);
|
||||||
|
|
||||||
/* SCL = active LOW to allow next SDA change */
|
/* SCL = active LOW to allow next SDA change */
|
||||||
_i2c_scl_low(bus);
|
_i2c_scl_low(bus);
|
||||||
@ -756,7 +757,7 @@ static /* IRAM */ int _i2c_read_bit (_i2c_bus_t* bus, bool* bit)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static /* IRAM */ int _i2c_write_byte (_i2c_bus_t* bus, uint8_t byte)
|
static IRAM_ATTR int _i2c_write_byte(_i2c_bus_t* bus, uint8_t byte)
|
||||||
{
|
{
|
||||||
/* send one byte and returns 0 in case of ACK from slave */
|
/* send one byte and returns 0 in case of ACK from slave */
|
||||||
|
|
||||||
@ -771,7 +772,7 @@ static /* IRAM */ int _i2c_write_byte (_i2c_bus_t* bus, uint8_t byte)
|
|||||||
|
|
||||||
/* read acknowledge bit (low) from slave */
|
/* read acknowledge bit (low) from slave */
|
||||||
bool bit;
|
bool bit;
|
||||||
int res = _i2c_read_bit (bus, &bit);
|
int res = _i2c_read_bit(bus, &bit);
|
||||||
if (res != 0) {
|
if (res != 0) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -780,13 +781,13 @@ static /* IRAM */ int _i2c_write_byte (_i2c_bus_t* bus, uint8_t byte)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static /* IRAM */ int _i2c_read_byte(_i2c_bus_t* bus, uint8_t *byte, bool ack)
|
static IRAM_ATTR int _i2c_read_byte(_i2c_bus_t* bus, uint8_t *byte, bool ack)
|
||||||
{
|
{
|
||||||
bool bit;
|
bool bit;
|
||||||
|
|
||||||
/* read the byte */
|
/* read the byte */
|
||||||
for (unsigned i = 0; i < 8; i++) {
|
for (unsigned i = 0; i < 8; i++) {
|
||||||
int res = _i2c_read_bit (bus, &bit);
|
int res = _i2c_read_bit(bus, &bit);
|
||||||
if (res != 0) {
|
if (res != 0) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user