parent
4ae2a6cdf0
commit
d41c0f1d72
2 changed files with 134 additions and 56 deletions
155
extras/i2c/i2c.c
155
extras/i2c/i2c.c
|
@ -25,7 +25,6 @@
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
|
|
||||||
#include <esp8266.h>
|
#include <esp8266.h>
|
||||||
#include <espressif/esp_misc.h> // sdk_os_delay_us
|
|
||||||
#include <espressif/esp_system.h>
|
#include <espressif/esp_system.h>
|
||||||
#include <FreeRTOS.h>
|
#include <FreeRTOS.h>
|
||||||
#include <task.h>
|
#include <task.h>
|
||||||
|
@ -38,25 +37,42 @@
|
||||||
#define debug(fmt, ...)
|
#define debug(fmt, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//#define CLK_STRETCH (10)
|
// The following array contains delay values for different frequencies.
|
||||||
|
// These were tuned to match the specified SCL frequency on average.
|
||||||
// Following array contain delay values for different frequencies
|
// The tuning was done using GCC 5.2.0 with -O2 optimization.
|
||||||
// Warning: 1 is minimal, that mean at 80MHz clock, frequency max is 320kHz
|
|
||||||
const static uint8_t i2c_freq_array[][2] = {
|
const static uint8_t i2c_freq_array[][2] = {
|
||||||
[I2C_FREQ_80K] = {255, 35},
|
#if I2C_USE_GPIO16 == 1
|
||||||
[I2C_FREQ_100K] = {100, 20},
|
[I2C_FREQ_80K] = {230, 107},
|
||||||
[I2C_FREQ_400K] = {10, 1},
|
[I2C_FREQ_100K] = {180, 82},
|
||||||
[I2C_FREQ_500K] = {6, 1}
|
[I2C_FREQ_400K] = {30, 7},
|
||||||
|
[I2C_FREQ_500K] = {20, 1},
|
||||||
|
[I2C_FREQ_600K] = {13, 0},
|
||||||
|
[I2C_FREQ_800K] = {5, 0},
|
||||||
|
[I2C_FREQ_1000K] = {1, 0}
|
||||||
|
#else
|
||||||
|
[I2C_FREQ_80K] = {235, 112},
|
||||||
|
[I2C_FREQ_100K] = {185, 88},
|
||||||
|
[I2C_FREQ_400K] = {36, 13},
|
||||||
|
[I2C_FREQ_500K] = {25, 8},
|
||||||
|
[I2C_FREQ_600K] = {20, 5},
|
||||||
|
[I2C_FREQ_800K] = {11, 1},
|
||||||
|
[I2C_FREQ_1000K] = {5, 0},
|
||||||
|
[I2C_FREQ_1300K] = {1, 0}
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint8_t freq; // Store CPU frequency for optimisation speed in delay function (Warning: Don't change CPU frequency during a transaction)
|
|
||||||
|
|
||||||
// Bus settings
|
// Bus settings
|
||||||
typedef struct i2c_bus_description
|
typedef struct i2c_bus_description
|
||||||
{
|
{
|
||||||
|
#if I2C_USE_GPIO16 == 1
|
||||||
uint8_t g_scl_pin; // SCL pin
|
uint8_t g_scl_pin; // SCL pin
|
||||||
uint8_t g_sda_pin; // SDA pin
|
uint8_t g_sda_pin; // SDA pin
|
||||||
|
#else
|
||||||
|
uint32_t g_scl_mask; // SCL pin mask
|
||||||
|
uint32_t g_sda_mask; // SDA pin mask
|
||||||
|
#endif
|
||||||
i2c_freq_t frequency; // Frequency
|
i2c_freq_t frequency; // Frequency
|
||||||
|
uint8_t delay;
|
||||||
bool started;
|
bool started;
|
||||||
bool flag;
|
bool flag;
|
||||||
bool force;
|
bool force;
|
||||||
|
@ -77,28 +93,47 @@ int i2c_init(uint8_t bus, uint8_t scl_pin, uint8_t sda_pin, i2c_freq_t freq)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if I2C_USE_GPIO16 == 1
|
||||||
|
const int I2C_MAX_PIN = 16;
|
||||||
|
#else
|
||||||
|
const int I2C_MAX_PIN = 15;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (scl_pin > I2C_MAX_PIN || sda_pin > I2C_MAX_PIN)
|
||||||
|
{
|
||||||
|
debug("Invalid GPIO number. All pins must be less than or equal to %d",
|
||||||
|
I2C_MAX_PIN);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
i2c_bus[bus].started = false;
|
i2c_bus[bus].started = false;
|
||||||
i2c_bus[bus].flag = false;
|
i2c_bus[bus].flag = false;
|
||||||
|
#if I2C_USE_GPIO16 == 1
|
||||||
i2c_bus[bus].g_scl_pin = scl_pin;
|
i2c_bus[bus].g_scl_pin = scl_pin;
|
||||||
i2c_bus[bus].g_sda_pin = sda_pin;
|
i2c_bus[bus].g_sda_pin = sda_pin;
|
||||||
|
#else
|
||||||
|
i2c_bus[bus].g_scl_mask = BIT(scl_pin);
|
||||||
|
i2c_bus[bus].g_sda_mask = BIT(sda_pin);
|
||||||
|
#endif
|
||||||
|
|
||||||
i2c_bus[bus].frequency = freq;
|
i2c_bus[bus].frequency = freq;
|
||||||
i2c_bus[bus].clk_stretch = I2C_DEFAULT_CLK_STRETCH;
|
i2c_bus[bus].clk_stretch = I2C_DEFAULT_CLK_STRETCH;
|
||||||
|
|
||||||
// Just to prevent these pins floating too much if not connected.
|
// Just to prevent these pins floating too much if not connected.
|
||||||
gpio_set_pullup(i2c_bus[bus].g_scl_pin, 1, 1);
|
gpio_set_pullup(scl_pin, 1, 1);
|
||||||
gpio_set_pullup(i2c_bus[bus].g_sda_pin, 1, 1);
|
gpio_set_pullup(sda_pin, 1, 1);
|
||||||
|
|
||||||
gpio_enable(i2c_bus[bus].g_scl_pin, GPIO_OUT_OPEN_DRAIN);
|
gpio_enable(scl_pin, GPIO_OUT_OPEN_DRAIN);
|
||||||
gpio_enable(i2c_bus[bus].g_sda_pin, GPIO_OUT_OPEN_DRAIN);
|
gpio_enable(sda_pin, GPIO_OUT_OPEN_DRAIN);
|
||||||
|
|
||||||
// I2C bus idle state.
|
// I2C bus idle state.
|
||||||
gpio_write(i2c_bus[bus].g_scl_pin, 1);
|
gpio_write(scl_pin, 1);
|
||||||
gpio_write(i2c_bus[bus].g_sda_pin, 1);
|
gpio_write(sda_pin, 1);
|
||||||
|
|
||||||
// Prevent user, if frequency is high
|
// Prevent user, if frequency is high
|
||||||
if (sdk_system_get_cpu_freq() == SYS_CPU_80MHZ)
|
if (sdk_system_get_cpu_freq() == SYS_CPU_80MHZ)
|
||||||
if (i2c_freq_array[i2c_bus[bus].frequency][1] == 1) {
|
if (i2c_freq_array[i2c_bus[bus].frequency][1] == 0) {
|
||||||
debug("Max frequency is 320Khz at 80MHz");
|
debug("Frequency not supported");
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,70 +152,90 @@ void i2c_set_clock_stretch(uint8_t bus, uint32_t clk_stretch)
|
||||||
|
|
||||||
static inline void i2c_delay(uint8_t bus)
|
static inline void i2c_delay(uint8_t bus)
|
||||||
{
|
{
|
||||||
uint32_t delay;
|
uint32_t delay = i2c_bus[bus].delay;
|
||||||
if (freq == SYS_CPU_160MHZ)
|
|
||||||
{
|
|
||||||
delay = i2c_freq_array[i2c_bus[bus].frequency][0];
|
|
||||||
__asm volatile (
|
__asm volatile (
|
||||||
"1: addi %0, %0, -1" "\n"
|
"1: addi %0, %0, -1" "\n"
|
||||||
"bnez %0, 1b" "\n"
|
"bnez %0, 1b" "\n"
|
||||||
:: "a" (delay));
|
: "=a" (delay) : "0" (delay));
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
delay = i2c_freq_array[i2c_bus[bus].frequency][1];
|
|
||||||
__asm volatile (
|
|
||||||
"1: addi %0, %0, -1" "\n"
|
|
||||||
"bnez %0, 1b" "\n"
|
|
||||||
:: "a" (delay));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set SCL as input, allowing it to float high, and return current
|
|
||||||
// level of line, 0 or 1
|
|
||||||
static inline bool read_scl(uint8_t bus)
|
static inline bool read_scl(uint8_t bus)
|
||||||
{
|
{
|
||||||
gpio_write(i2c_bus[bus].g_scl_pin, 1);
|
#if I2C_USE_GPIO16 == 1
|
||||||
return gpio_read(i2c_bus[bus].g_scl_pin); // Clock high, valid ACK
|
return gpio_read(i2c_bus[bus].g_scl_pin);
|
||||||
|
#else
|
||||||
|
return GPIO.IN & i2c_bus[bus].g_scl_mask;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set SDA as input, allowing it to float high, and return current
|
|
||||||
// level of line, 0 or 1
|
|
||||||
static inline bool read_sda(uint8_t bus)
|
static inline bool read_sda(uint8_t bus)
|
||||||
{
|
{
|
||||||
gpio_write(i2c_bus[bus].g_sda_pin, 1);
|
#if I2C_USE_GPIO16 == 1
|
||||||
// TODO: Without this delay we get arbitration lost in i2c_stop
|
return gpio_read(i2c_bus[bus].g_sda_pin);
|
||||||
i2c_delay(bus);
|
#else
|
||||||
return gpio_read(i2c_bus[bus].g_sda_pin); // Clock high, valid ACK
|
return GPIO.IN & i2c_bus[bus].g_sda_mask;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actively drive SCL signal low
|
// Actively drive SCL signal low
|
||||||
static inline void clear_scl(uint8_t bus)
|
static inline void clear_scl(uint8_t bus)
|
||||||
{
|
{
|
||||||
|
#if I2C_USE_GPIO16 == 1
|
||||||
gpio_write(i2c_bus[bus].g_scl_pin, 0);
|
gpio_write(i2c_bus[bus].g_scl_pin, 0);
|
||||||
|
#else
|
||||||
|
GPIO.OUT_CLEAR = i2c_bus[bus].g_scl_mask;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actively drive SDA signal low
|
// Actively drive SDA signal low
|
||||||
static inline void clear_sda(uint8_t bus)
|
static inline void clear_sda(uint8_t bus)
|
||||||
{
|
{
|
||||||
|
#if I2C_USE_GPIO16 == 1
|
||||||
gpio_write(i2c_bus[bus].g_sda_pin, 0);
|
gpio_write(i2c_bus[bus].g_sda_pin, 0);
|
||||||
|
#else
|
||||||
|
GPIO.OUT_CLEAR = i2c_bus[bus].g_sda_mask;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void set_scl(uint8_t bus)
|
||||||
|
{
|
||||||
|
#if I2C_USE_GPIO16 == 1
|
||||||
|
gpio_write(i2c_bus[bus].g_scl_pin, 1);
|
||||||
|
#else
|
||||||
|
GPIO.OUT_SET = i2c_bus[bus].g_scl_mask;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void set_sda(uint8_t bus)
|
||||||
|
{
|
||||||
|
#if I2C_USE_GPIO16 == 1
|
||||||
|
gpio_write(i2c_bus[bus].g_sda_pin, 1);
|
||||||
|
#else
|
||||||
|
GPIO.OUT_SET = i2c_bus[bus].g_sda_mask;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output start condition
|
// Output start condition
|
||||||
void i2c_start(uint8_t bus)
|
void i2c_start(uint8_t bus)
|
||||||
{
|
{
|
||||||
freq = sdk_system_get_cpu_freq();
|
if (sdk_system_get_cpu_freq() == SYS_CPU_160MHZ)
|
||||||
|
i2c_bus[bus].delay = i2c_freq_array[i2c_bus[bus].frequency][0];
|
||||||
|
else
|
||||||
|
i2c_bus[bus].delay = i2c_freq_array[i2c_bus[bus].frequency][1];
|
||||||
|
|
||||||
if (i2c_bus[bus].started) { // if started, do a restart cond
|
if (i2c_bus[bus].started) { // if started, do a restart cond
|
||||||
// Set SDA to 1
|
// Set SDA to 1
|
||||||
(void) read_sda(bus);
|
set_sda(bus);
|
||||||
i2c_delay(bus);
|
i2c_delay(bus);
|
||||||
uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
|
uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
|
||||||
|
set_scl(bus);
|
||||||
while (read_scl(bus) == 0 && clk_stretch--)
|
while (read_scl(bus) == 0 && clk_stretch--)
|
||||||
;
|
;
|
||||||
// Repeated start setup time, minimum 4.7us
|
// Repeated start setup time, minimum 4.7us
|
||||||
i2c_delay(bus);
|
i2c_delay(bus);
|
||||||
}
|
}
|
||||||
i2c_bus[bus].started = true;
|
i2c_bus[bus].started = true;
|
||||||
|
set_sda(bus);
|
||||||
if (read_sda(bus) == 0) {
|
if (read_sda(bus) == 0) {
|
||||||
debug("arbitration lost in i2c_start from bus %u", bus);
|
debug("arbitration lost in i2c_start from bus %u", bus);
|
||||||
}
|
}
|
||||||
|
@ -198,11 +253,15 @@ bool i2c_stop(uint8_t bus)
|
||||||
clear_sda(bus);
|
clear_sda(bus);
|
||||||
i2c_delay(bus);
|
i2c_delay(bus);
|
||||||
// Clock stretching
|
// Clock stretching
|
||||||
|
set_scl(bus);
|
||||||
while (read_scl(bus) == 0 && clk_stretch--)
|
while (read_scl(bus) == 0 && clk_stretch--)
|
||||||
;
|
;
|
||||||
// Stop bit setup time, minimum 4us
|
// Stop bit setup time, minimum 4us
|
||||||
i2c_delay(bus);
|
i2c_delay(bus);
|
||||||
// SCL is high, set SDA from 0 to 1
|
// SCL is high, set SDA from 0 to 1
|
||||||
|
set_sda(bus);
|
||||||
|
// additional delay before testing SDA value to avoid wrong state
|
||||||
|
i2c_delay(bus);
|
||||||
if (read_sda(bus) == 0) {
|
if (read_sda(bus) == 0) {
|
||||||
debug("arbitration lost in i2c_stop from bus %u", bus);
|
debug("arbitration lost in i2c_stop from bus %u", bus);
|
||||||
}
|
}
|
||||||
|
@ -220,12 +279,13 @@ static void i2c_write_bit(uint8_t bus, bool bit)
|
||||||
{
|
{
|
||||||
uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
|
uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
|
||||||
if (bit) {
|
if (bit) {
|
||||||
(void) read_sda(bus);
|
set_sda(bus);
|
||||||
} else {
|
} else {
|
||||||
clear_sda(bus);
|
clear_sda(bus);
|
||||||
}
|
}
|
||||||
i2c_delay(bus);
|
i2c_delay(bus);
|
||||||
// Clock stretching
|
// Clock stretching
|
||||||
|
set_scl(bus);
|
||||||
while (read_scl(bus) == 0 && clk_stretch--)
|
while (read_scl(bus) == 0 && clk_stretch--)
|
||||||
;
|
;
|
||||||
// SCL is high, now data is valid
|
// SCL is high, now data is valid
|
||||||
|
@ -243,8 +303,9 @@ static bool i2c_read_bit(uint8_t bus)
|
||||||
uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
|
uint32_t clk_stretch = i2c_bus[bus].clk_stretch;
|
||||||
bool bit;
|
bool bit;
|
||||||
// Let the slave drive data
|
// Let the slave drive data
|
||||||
(void) read_sda(bus);
|
set_sda(bus);
|
||||||
i2c_delay(bus);
|
i2c_delay(bus);
|
||||||
|
set_scl(bus);
|
||||||
// Clock stretching
|
// Clock stretching
|
||||||
while (read_scl(bus) == 0 && clk_stretch--)
|
while (read_scl(bus) == 0 && clk_stretch--)
|
||||||
;
|
;
|
||||||
|
|
|
@ -44,14 +44,31 @@ extern "C" {
|
||||||
#define I2C_MAX_BUS 2
|
#define I2C_MAX_BUS 2
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Set this to 1 if you intend to use GPIO 16 for I2C. It is not recommended
|
||||||
|
* and will result in degradation of performance and timing accuracy.
|
||||||
|
*/
|
||||||
|
#ifndef I2C_USE_GPIO16
|
||||||
|
#define I2C_USE_GPIO16 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#define I2C_DEFAULT_CLK_STRETCH (10)
|
#define I2C_DEFAULT_CLK_STRETCH (10)
|
||||||
|
|
||||||
|
/* SCL speed settings. 160 MHz sysclk frequency will result in improved
|
||||||
|
* timing accuracy. Greater bitrates will have poorer accuracy. 1000K is the
|
||||||
|
* maximum SCL speed at 80 MHz sysclk.
|
||||||
|
*/
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
I2C_FREQ_80K = 0,//!< I2C_FREQ_80K
|
I2C_FREQ_80K = 0,
|
||||||
I2C_FREQ_100K, //!< I2C_FREQ_100K
|
I2C_FREQ_100K,
|
||||||
I2C_FREQ_400K, //!< I2C_FREQ_400K
|
I2C_FREQ_400K,
|
||||||
I2C_FREQ_500K, //!< I2C_FREQ_500K
|
I2C_FREQ_500K,
|
||||||
|
I2C_FREQ_600K,
|
||||||
|
I2C_FREQ_800K,
|
||||||
|
I2C_FREQ_1000K,
|
||||||
|
#if I2C_USE_GPIO16 == 0
|
||||||
|
I2C_FREQ_1300K
|
||||||
|
#endif
|
||||||
} i2c_freq_t;
|
} i2c_freq_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue