/* * 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 * * @note The hardware implementation seems to be very poor and faulty. * I2C commands in the I2C command pipeline are not executed * sporadically. A number of ACK errors and timeouts caused by * protocol errors are the result. You should use the hardware * implementation only if they can be tolerated. * * @author Gunar Schorcht * * @} */ #if defined(MODULE_ESP_I2C_HW) /* hardware implementation used */ /** * PLEASE NOTE: * * Some parts of the implementation were inspired by the Espressif IoT * Development Framework [ESP-IDF](https://github.com/espressif/esp-idf.git) * implementation of I2C. These partes are marked with an according copyright * notice. */ #define ENABLE_DEBUG (0) #include "debug.h" #include #include #include #include "cpu.h" #include "log.h" #include "mutex.h" #include "periph_conf.h" #include "periph/gpio.h" #include "periph/i2c.h" #include "thread_flags.h" #include "esp_common.h" #include "gpio_arch.h" #include "driver/periph_ctrl.h" #include "irq_arch.h" #include "rom/ets_sys.h" #include "soc/gpio_reg.h" #include "soc/gpio_sig_map.h" #include "soc/gpio_struct.h" #include "soc/i2c_reg.h" #include "soc/i2c_struct.h" #include "soc/rtc.h" #include "soc/soc.h" #include "syscalls.h" #include "xtensa/xtensa_api.h" #if defined(I2C0_SPEED) || defined(I2C1_SPEED) #undef I2C_CLK_FREQ #define I2C_CLK_FREQ rtc_clk_apb_freq_get() /* APB_CLK is used */ /* operation codes used for commands */ #define I2C_CMD_RSTART 0 #define I2C_CMD_WRITE 1 #define I2C_CMD_READ 2 #define I2C_CMD_STOP 3 #define I2C_CMD_END 4 /* maximum number of data that can be written / read in one transfer */ #define I2C_MAX_DATA 30 #define I2C_FIFO_USED 1 struct i2c_hw_t { i2c_dev_t* regs; /* pointer to register data struct of the I2C device */ uint8_t mod; /* peripheral hardware module of the I2C interface */ uint8_t int_src; /* peripheral interrupt source used by the I2C device */ uint8_t pin_scl; /* SCL pin */ uint8_t pin_sda; /* SDA pin */ uint8_t signal_scl_in; /* SCL signal to the controller */ uint8_t signal_scl_out; /* SCL signal from the controller */ uint8_t signal_sda_in; /* SDA signal to the controller */ uint8_t signal_sda_out; /* SDA signal from the controller */ }; static const struct i2c_hw_t _i2c_hw[] = { #if defined(I2C0_SCL) && defined(I2C0_SDA) && defined(I2C0_SPEED) { .regs = &I2C0, .mod = PERIPH_I2C0_MODULE, .int_src = ETS_I2C_EXT0_INTR_SOURCE, .pin_scl = I2C0_SCL, .pin_sda = I2C0_SDA, .signal_scl_in = I2CEXT0_SCL_IN_IDX, .signal_scl_out = I2CEXT0_SCL_OUT_IDX, .signal_sda_in = I2CEXT0_SDA_IN_IDX, .signal_sda_out = I2CEXT0_SDA_OUT_IDX, }, #endif #if defined(I2C1_SCL) && defined(I2C1_SDA) && defined(I2C1_SPEED) { .regs = &I2C1, .mod = PERIPH_I2C1_MODULE, .int_src = ETS_I2C_EXT1_INTR_SOURCE, .pin_scl = I2C1_SCL, .pin_sda = I2C1_SDA, .signal_scl_in = I2CEXT1_SCL_IN_IDX, .signal_scl_out = I2CEXT1_SCL_OUT_IDX, .signal_sda_in = I2CEXT1_SDA_IN_IDX, .signal_sda_out = I2CEXT1_SDA_OUT_IDX, } #endif }; struct _i2c_bus_t { i2c_speed_t speed; /* bus speed */ uint8_t cmd; /* command index */ uint8_t data; /* index in RAM for data */ mutex_t lock; /* mutex lock */ kernel_pid_t pid; /* PID of thread that triggered a transfer */ uint32_t results; /* results of a transfer */ }; static struct _i2c_bus_t _i2c_bus[] = { #if defined(I2C0_SCL) && defined(I2C0_SDA) && defined(I2C0_SPEED) { .speed = I2C0_SPEED, .cmd = 0, .data = 0, .lock = MUTEX_INIT }, #endif #if defined(I2C1_SCL) && defined(I2C1_SDA) && defined(I2C1_SPEED) { .speed = I2C1_SPEED, .cmd = 0, .data = 0, .lock = MUTEX_INIT }, #endif }; /* the number of I2C bus devices used */ const unsigned i2c_bus_num = sizeof(_i2c_bus) / sizeof(_i2c_bus[0]); /* forward declaration of internal functions */ static int _i2c_init_pins (i2c_t dev); static void _i2c_start_cmd (i2c_t dev); static void _i2c_stop_cmd (i2c_t dev); static void _i2c_end_cmd (i2c_t dev); static void _i2c_write_cmd (i2c_t dev, const uint8_t* data, uint8_t len); static void _i2c_read_cmd (i2c_t dev, uint8_t* data, uint8_t len, bool last); static void _i2c_transfer (i2c_t dev); static void _i2c_reset_hw (i2c_t dev); static void _i2c_clear_bus (i2c_t dev); static void _i2c_intr_handler (void *arg); static inline void _i2c_delay (uint32_t delay); /* implementation of i2c interface */ void i2c_init(i2c_t dev) { CHECK_PARAM (dev < i2c_bus_num) if (_i2c_bus[dev].speed == I2C_SPEED_FAST_PLUS || _i2c_bus[dev].speed == I2C_SPEED_HIGH) { LOG_TAG_INFO("i2c", "I2C_SPEED_FAST_PLUS and I2C_SPEED_HIGH " "are not supported\n"); return; } i2c_acquire (dev); _i2c_bus[dev].cmd = 0; _i2c_bus[dev].data = 0; DEBUG ("%s scl=%d sda=%d speed=%d\n", __func__, _i2c_hw[dev].pin_scl, _i2c_hw[dev].pin_sda, _i2c_bus[dev].speed); /* enable (power on) the according I2C module */ periph_module_enable(_i2c_hw[dev].mod); /* initialize pins */ if (_i2c_init_pins(dev) != 0) { return; } /* set master mode */ _i2c_hw[dev].regs->ctr.ms_mode = 1; /* set bit order to MSB first */ _i2c_hw[dev].regs->ctr.tx_lsb_first = 0; _i2c_hw[dev].regs->ctr.rx_lsb_first = 0; /* determine the half period of clock in APB clock cycles */ uint32_t half_period = 0; switch (_i2c_bus[dev].speed) { case I2C_SPEED_LOW: /* 10 kbps (period 100 us) */ half_period = (I2C_CLK_FREQ / 10000) >> 1; break; case I2C_SPEED_NORMAL: /* 100 kbps (period 10 us) */ half_period = (I2C_CLK_FREQ / 100000) >> 1; half_period = half_period * 95 / 100; /* correction factor */ break; case I2C_SPEED_FAST: /* 400 kbps (period 2.5 us) */ half_period = (I2C_CLK_FREQ / 400000) >> 1; half_period = half_period * 82 / 100; /* correction factor */ break; case I2C_SPEED_FAST_PLUS: /* 1 Mbps (period 1 us) not working */ half_period = (I2C_CLK_FREQ / 1000000) >> 1; break; case I2C_SPEED_HIGH: /* 3.4 Mbps (period 0.3 us) not working */ half_period = (I2C_CLK_FREQ / 3400000) >> 1; break; default: LOG_TAG_ERROR("i2c", "Invalid speed value in %s\n", __func__); return; } /* set an timeout which is at least 16 times of half cycle */ _i2c_hw[dev].regs->timeout.tout = half_period << 4; /* timing for SCL (low and high time in APB clock cycles) */ _i2c_hw[dev].regs->scl_low_period.period = half_period; _i2c_hw[dev].regs->scl_high_period.period = half_period; /* timing for SDA (sample time after rising edge and hold time after falling edge) */ _i2c_hw[dev].regs->sda_sample.time = half_period >> 1; _i2c_hw[dev].regs->sda_hold.time = half_period >> 1; /* timing for START condition (START hold and repeated START setup time) */ _i2c_hw[dev].regs->scl_start_hold.time = half_period >> 1; _i2c_hw[dev].regs->scl_rstart_setup.time = half_period >> 1; /* timing for STOP condition (STOP hold and STOP setup time) */ _i2c_hw[dev].regs->scl_stop_hold.time = half_period >> 1; _i2c_hw[dev].regs->scl_stop_setup.time = half_period >> 1; /* configure open drain outputs */ _i2c_hw[dev].regs->ctr.scl_force_out = 1; _i2c_hw[dev].regs->ctr.sda_force_out = 1; /* sample data during high level */ _i2c_hw[dev].regs->ctr.sample_scl_level = 0; /* enable non FIFO access and disable slave FIFO address offset */ #if I2C_FIFO_USED _i2c_hw[dev].regs->fifo_conf.nonfifo_en = 0; #else _i2c_hw[dev].regs->fifo_conf.nonfifo_en = 1; _i2c_hw[dev].regs->fifo_conf.nonfifo_rx_thres = 0; _i2c_hw[dev].regs->fifo_conf.nonfifo_tx_thres = 0; _i2c_hw[dev].regs->fifo_conf.rx_fifo_full_thrhd = 0; _i2c_hw[dev].regs->fifo_conf.tx_fifo_empty_thrhd = 0; #endif _i2c_hw[dev].regs->fifo_conf.fifo_addr_cfg_en = 0; /* route all I2C interrupt sources to same the CPU interrupt */ intr_matrix_set(PRO_CPU_NUM, _i2c_hw[dev].int_src, CPU_INUM_I2C); /* set the interrupt handler and enable the interrupt */ xt_set_interrupt_handler(CPU_INUM_I2C, _i2c_intr_handler, NULL); xt_ints_on(BIT(CPU_INUM_I2C)); i2c_release (dev); return; } int i2c_acquire(i2c_t dev) { DEBUG ("%s\n", __func__); CHECK_PARAM_RET (dev < i2c_bus_num, -1) mutex_lock(&_i2c_bus[dev].lock); _i2c_reset_hw(dev); return 0; } int i2c_release(i2c_t dev) { DEBUG ("%s\n", __func__); CHECK_PARAM_RET (dev < i2c_bus_num, -1) _i2c_reset_hw (dev); mutex_unlock(&_i2c_bus[dev].lock); return 0; } #define _i2c_return_on_error(dev) \ if (_i2c_bus[dev].results & I2C_ARBITRATION_LOST_INT_ENA) { \ LOG_TAG_ERROR("i2c", "arbitration lost dev=%u\n", dev); \ _i2c_reset_hw (dev); \ __asm__ volatile ("isync"); \ return -EAGAIN; \ } \ else if (_i2c_bus[dev].results & I2C_ACK_ERR_INT_ENA) { \ LOG_TAG_ERROR("i2c", "ack error dev=%u\n", dev); \ _i2c_reset_hw (dev); \ __asm__ volatile ("isync"); \ return -EIO; \ } \ else if (_i2c_bus[dev].results & I2C_TIME_OUT_INT_ENA) { \ LOG_TAG_ERROR("i2c", "bus timeout dev=%u\n", dev); \ _i2c_reset_hw (dev); \ __asm__ volatile ("isync"); \ return -ETIMEDOUT; \ } int 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_bus_num, -EINVAL); CHECK_PARAM_RET (len > 0, -EINVAL); CHECK_PARAM_RET (data != NULL, -EINVAL); /* if I2C_NOSTART is not set, START condition and ADDR is used */ if (!(flags & I2C_NOSTART)) { /* send START condition */ _i2c_start_cmd (dev); /* address handling */ if (flags & I2C_ADDR10) { /* prepare 10 bit address bytes */ uint8_t addr10[2]; addr10[0] = 0xf0 | (addr & 0x0300) >> 7 | I2C_READ; addr10[1] = addr & 0xff; /* send ADDR with read flag */ _i2c_write_cmd (dev, addr10, 2); } else { /* send ADDR with read flag */ uint8_t addr7 = (addr << 1 | I2C_READ); _i2c_write_cmd (dev, &addr7, 1); } } /* read data bytes in blocks of I2C_MAX_DATA bytes */ uint32_t off = 0; /* if len > I2C_MAX_DATA read blocks I2C_MAX_DATA bytes at a time */ while (len > I2C_MAX_DATA) { /* read one block of data bytes command */ _i2c_read_cmd (dev, data, I2C_MAX_DATA, false); _i2c_end_cmd (dev); _i2c_transfer (dev); _i2c_return_on_error (dev); /* if transfer was successful, fetch the data from I2C RAM */ for (unsigned i = 0; i < I2C_MAX_DATA; i++) { #if I2C_FIFO_USED ((uint8_t*)data)[i + off] = _i2c_hw[dev].regs->fifo_data.data; #else ((uint8_t*)data)[i + off] = _i2c_hw[dev].regs->ram_data[i]; #endif } len -= I2C_MAX_DATA; off += I2C_MAX_DATA; } /* read remaining data bytes command with a final NAK */ _i2c_read_cmd (dev, data, len, true); /* if I2C_NOSTOP flag is not set, send STOP condition is used */ if (!(flags & I2C_NOSTOP)) { /* send STOP condition */ _i2c_stop_cmd (dev); } else { /* otherwise place end command in pipeline */ _i2c_end_cmd (dev); } /* finish operation by executing the command pipeline */ _i2c_transfer (dev); _i2c_return_on_error (dev); /* if transfer was successful, fetch data from I2C RAM */ for (unsigned i = 0; i < len; i++) { #if I2C_FIFO_USED ((uint8_t*)data)[i + off] = _i2c_hw[dev].regs->fifo_data.data; #else ((uint8_t*)data)[i + off] = _i2c_hw[dev].regs->ram_data[i]; #endif } /* return 0 on success */ return 0; } int 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_bus_num, -EINVAL); CHECK_PARAM_RET (len > 0, -EINVAL); CHECK_PARAM_RET (data != NULL, -EINVAL); /* if I2C_NOSTART is not set, START condition and ADDR is used */ if (!(flags & I2C_NOSTART)) { /* send START condition */ _i2c_start_cmd (dev); /* address handling */ if (flags & I2C_ADDR10) { /* prepare 10 bit address bytes */ uint8_t addr10[2]; addr10[0] = 0xf0 | (addr & 0x0300) >> 7; addr10[1] = addr & 0xff; /* send ADDR without read flag */ _i2c_write_cmd (dev, addr10, 2); } else { /* send ADDR without read flag */ uint8_t addr7 = addr << 1; _i2c_write_cmd (dev, &addr7, 1); } } /* send data bytes in blocks of I2C_MAX_DATA bytes */ uint32_t off = 0; /* if len > I2C_MAX_DATA write blocks I2C_MAX_DATA bytes at a time */ while (len > I2C_MAX_DATA) { /* send on block of data bytes */ _i2c_write_cmd (dev, ((uint8_t*)data) + off, I2C_MAX_DATA); _i2c_end_cmd (dev); _i2c_transfer (dev); _i2c_return_on_error (dev); len -= I2C_MAX_DATA; off += I2C_MAX_DATA; } /* write remaining data bytes command */ _i2c_write_cmd (dev, ((uint8_t*)data), len); /* if I2C_NOSTOP flag is not set, send STOP condition is used */ if (!(flags & I2C_NOSTOP)) { /* send STOP condition */ _i2c_stop_cmd (dev); } else { /* otherwise place end command in pipeline */ _i2c_end_cmd (dev); } /* finish operation by executing the command pipeline */ _i2c_transfer (dev); _i2c_return_on_error (dev); /* return 0 on success */ return 0; } /* internal functions */ static int _i2c_init_pins(i2c_t dev) { /* * reset GPIO usage type if the pins were used already for I2C before to * make it possible to reinitialize I2C */ if (gpio_get_pin_usage(_i2c_hw[dev].pin_scl) == _I2C) { gpio_set_pin_usage(_i2c_hw[dev].pin_scl, _GPIO); } if (gpio_get_pin_usage(_i2c_hw[dev].pin_sda) == _I2C) { gpio_set_pin_usage(_i2c_hw[dev].pin_sda, _GPIO); } /* try to configure SDA and SCL pin as GPIO in open-drain mode with enabled pull-ups */ if (gpio_init (_i2c_hw[dev].pin_scl, GPIO_IN_OD_PU) || gpio_init (_i2c_hw[dev].pin_sda, GPIO_IN_OD_PU)) { return -ENODEV; } /* bring signals to high */ gpio_set(_i2c_hw[dev].pin_scl); gpio_set(_i2c_hw[dev].pin_sda); /* store the usage type in GPIO table */ gpio_set_pin_usage(_i2c_hw[dev].pin_scl, _I2C); gpio_set_pin_usage(_i2c_hw[dev].pin_sda, _I2C); /* connect SCL and SDA pins to output signals through the GPIO matrix */ GPIO.func_out_sel_cfg[_i2c_hw[dev].pin_scl].func_sel = _i2c_hw[dev].signal_scl_out; GPIO.func_out_sel_cfg[_i2c_hw[dev].pin_sda].func_sel = _i2c_hw[dev].signal_sda_out; /* connect SCL and SDA input signals to pins through the GPIO matrix */ GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].sig_in_sel = 1; GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].sig_in_inv = 0; GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].func_sel = _i2c_hw[dev].pin_scl; GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].sig_in_sel = 1; GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].sig_in_inv = 0; GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].func_sel = _i2c_hw[dev].pin_sda; return 0; } static void _i2c_start_cmd(i2c_t dev) { DEBUG ("%s\n", __func__); /* place START condition command in command queue */ _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_RSTART; /* increment the command counter */ _i2c_bus[dev].cmd++; } static void _i2c_stop_cmd (i2c_t dev) { DEBUG ("%s\n", __func__); /* place STOP condition command in command queue */ _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_STOP; /* increment the command counter */ _i2c_bus[dev].cmd++; } static void _i2c_end_cmd (i2c_t dev) { DEBUG ("%s\n", __func__); /* place END command for continues data transmission in command queue */ _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_END; /* increment the command counter */ _i2c_bus[dev].cmd++; } static void _i2c_write_cmd (i2c_t dev, const uint8_t* data, uint8_t len) { DEBUG ("%s dev=%u data=%p len=%d\n", __func__, dev, data, len); if (_i2c_bus[dev].data + len > I2C_MAX_DATA) { LOG_TAG_ERROR("i2c", "Maximum number of bytes (32 bytes) that can be " "sent with on transfer reached\n"); return; } /* store the byte in RAM of I2C controller and increment the data counter */ for (int i = 0; i < len; i++) { #if I2C_FIFO_USED WRITE_PERI_REG(I2C_DATA_APB_REG(dev), data[i]); #else _i2c_hw[dev].regs->ram_data[_i2c_bus[dev].data++] = (uint32_t)data[i]; #endif } /* place WRITE command for multiple bytes in command queue */ _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].byte_num = len; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_en = 1; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_exp = 0; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_val = 0; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_WRITE; /* increment the command counter */ _i2c_bus[dev].cmd++; } static void _i2c_read_cmd (i2c_t dev, uint8_t* data, uint8_t len, bool last) { DEBUG ("%s dev=%u data=%p len=%d\n", __func__, dev, data, len); if (len < 1 || len > I2C_MAX_DATA) { /* at least one byte has to be read */ LOG_TAG_ERROR("i2c", "At least one byte has to be read\n"); return; } if (len > 1) { /* place READ command for len-1 bytes with positive ack in command queue*/ _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].byte_num = len-1; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_en = 0; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_exp = 0; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_val = 0; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_READ; /* increment the command counter */ _i2c_bus[dev].cmd++; } /* place READ command for last byte with negative ack in last segment in command queue*/ _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].byte_num = 1; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_en = 0; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_exp = 0; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_val = last ? 1 : 0; _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_READ; /* increment the command counter */ _i2c_bus[dev].cmd++; } static inline void _i2c_delay (uint32_t cycles) { /* produces a delay of 0,0625 us per cycle for -O2 compile option */ /* 1 us = ca. 16 cycles (80 MHz) / 1 us = 32 cycles (160 MHz) */ if (cycles) { __asm__ volatile ("1: _addi.n %0, %0, -1 \n" " bnez %0, 1b \n" : "=r" (cycles) : "0" (cycles)); } } /* transfer related interrupts handled by the driver */ static const uint32_t transfer_int_mask = I2C_TRANS_COMPLETE_INT_ENA | I2C_END_DETECT_INT_ENA | I2C_ACK_ERR_INT_ENA | I2C_ARBITRATION_LOST_INT_ENA | I2C_TIME_OUT_INT_ENA; /* at I2C_SPEED_NORMAL a transfer takes at most 33 byte * 9 clock cycles * 1/100000 s */ #define I2C_TRANSFER_TIMEOUT 3000 #define I2C_THREAD_FLAG BIT (0) #include "xtimer.h" void _i2c_transfer_timeout (void *arg) { i2c_t dev = (i2c_t)arg; /* reset the hardware if it I2C got stucked */ _i2c_reset_hw(dev); /* set result to timeout */ _i2c_bus[dev].results |= I2C_TIME_OUT_INT_ST; /* wake up the thread that is waiting for the results */ thread_flags_set((thread_t*)thread_get(_i2c_bus[dev].pid), I2C_THREAD_FLAG); } /* Transfer of commands in I2C controller command pipeline */ static void _i2c_transfer (i2c_t dev) { DEBUG("%s cmd=%d\n", __func__, _i2c_bus[dev].cmd); #if FIFO_USED /* reset RX FIFO queue */ _i2c_hw[dev].regs->fifo_conf.rx_fifo_rst = 1; _i2c_hw[dev].regs->fifo_conf.rx_fifo_rst = 0; #endif /* disable and enable all transmission interrupts and clear current status */ _i2c_hw[dev].regs->int_ena.val &= ~transfer_int_mask; _i2c_hw[dev].regs->int_ena.val |= transfer_int_mask; _i2c_hw[dev].regs->int_clr.val = transfer_int_mask; /* set a timer for the case the I2C hardware gets stuck */ xtimer_t i2c_timeout = {}; i2c_timeout.callback = _i2c_transfer_timeout; i2c_timeout.arg = (void*)dev; xtimer_set(&i2c_timeout, I2C_TRANSFER_TIMEOUT); /* start execution of commands in command pipeline registers */ _i2c_bus[dev].pid = thread_getpid(); _i2c_bus[dev].results = 0; _i2c_hw[dev].regs->ctr.trans_start = 0; _i2c_hw[dev].regs->ctr.trans_start = 1; /* wait for transfer results and remove timeout timer*/ thread_flags_wait_one(I2C_THREAD_FLAG); xtimer_remove(&i2c_timeout); /* returned from transmission */ DEBUG("%s results=%08x\n", __func__, _i2c_bus[dev].results); #if FIFO_USED /* reset TX FIFO queue */ _i2c_hw[dev].regs->fifo_conf.tx_fifo_rst = 1; _i2c_hw[dev].regs->fifo_conf.tx_fifo_rst = 0; #endif /* reset command and data index */ _i2c_bus[dev].cmd = 0; _i2c_bus[dev].data = 0; } static void IRAM_ATTR _i2c_intr_handler (void *arg) { /* to satisfy the compiler */ (void)arg; irq_isr_enter (); /* all I2C peripheral interrupt sources are routed to the same interrupt, so we have to use the status register to distinguish interruptees */ for (unsigned dev = 0; dev < i2c_bus_num; dev++) { /* test for transfer related interrupts */ if (_i2c_hw[dev].regs->int_status.val & transfer_int_mask) { /* set transfer result */ _i2c_bus[dev].results |= _i2c_hw[dev].regs->int_status.val; /* disable all interrupts and clear them and left them disabled */ _i2c_hw[dev].regs->int_ena.val &= ~transfer_int_mask; _i2c_hw[dev].regs->int_clr.val = transfer_int_mask; /* wake up the thread that is waiting for the results */ thread_flags_set((thread_t*)thread_get(_i2c_bus[dev].pid), I2C_THREAD_FLAG); } else if (_i2c_hw[dev].regs->int_status.val) { /* if there are any other interrupts, clear them */ _i2c_hw[dev].regs->int_clr.val = ~0x0U; } } irq_isr_exit (); } #if 1 /* TODO */ /* Some slave devices will die by accident and keep the SDA in low level, * in this case, master should send several clock to make the slave release * the bus. */ static void _i2c_clear_bus(i2c_t dev) { /* reset the usage type in GPIO table */ gpio_set_pin_usage(_i2c_hw[dev].pin_scl, _GPIO); gpio_set_pin_usage(_i2c_hw[dev].pin_sda, _GPIO); /* configure SDA and SCL pin as GPIO in open-drain mode temporarily */ gpio_init (_i2c_hw[dev].pin_scl, GPIO_IN_OD_PU); gpio_init (_i2c_hw[dev].pin_sda, GPIO_IN_OD_PU); /* master send some clock pulses to make the slave release the bus */ gpio_set (_i2c_hw[dev].pin_scl); gpio_set (_i2c_hw[dev].pin_sda); gpio_clear (_i2c_hw[dev].pin_sda); for (int i = 0; i < 20; i++) { gpio_toggle(_i2c_hw[dev].pin_scl); } gpio_set(_i2c_hw[dev].pin_sda); /* store the usage type in GPIO table */ gpio_set_pin_usage(_i2c_hw[dev].pin_scl, _I2C); gpio_set_pin_usage(_i2c_hw[dev].pin_sda, _I2C); /* connect SCL and SDA pins to output signals through the GPIO matrix */ GPIO.func_out_sel_cfg[_i2c_hw[dev].pin_scl].func_sel = _i2c_hw[dev].signal_scl_out; GPIO.func_out_sel_cfg[_i2c_hw[dev].pin_sda].func_sel = _i2c_hw[dev].signal_sda_out; /* connect SCL and SDA input signals to pins through the GPIO matrix */ GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].sig_in_sel = 1; GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].sig_in_inv = 0; GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].func_sel = _i2c_hw[dev].pin_scl; GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].sig_in_sel = 1; GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].sig_in_inv = 0; GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].func_sel = _i2c_hw[dev].pin_sda; return; } #endif /* * PLEASE NOTE: Following function is from the ESP-IDF and is licensed * under the Apache License, Version 2.0 (the "License"). * http://www.apache.org/licenses/LICENSE-2.0 * * Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD */ static void _i2c_reset_hw (i2c_t dev) { /* save current configuration */ uint32_t ctr = _i2c_hw[dev].regs->ctr.val; uint32_t fifo_conf = _i2c_hw[dev].regs->fifo_conf.val; uint32_t scl_low_period = _i2c_hw[dev].regs->scl_low_period.val; uint32_t scl_high_period = _i2c_hw[dev].regs->scl_high_period.val; uint32_t scl_start_hold = _i2c_hw[dev].regs->scl_start_hold.val; uint32_t scl_rstart_setup = _i2c_hw[dev].regs->scl_rstart_setup.val; uint32_t scl_stop_hold = _i2c_hw[dev].regs->scl_stop_hold.val; uint32_t scl_stop_setup = _i2c_hw[dev].regs->scl_stop_setup.val; uint32_t sda_hold = _i2c_hw[dev].regs->sda_hold.val; uint32_t sda_sample = _i2c_hw[dev].regs->sda_sample.val; uint32_t timeout = _i2c_hw[dev].regs->timeout.val; uint32_t scl_filter_cfg = _i2c_hw[dev].regs->scl_filter_cfg.val; uint32_t sda_filter_cfg = _i2c_hw[dev].regs->sda_filter_cfg.val; /* reset hardware mpdule */ periph_module_disable(_i2c_hw[dev].mod); _i2c_clear_bus(dev); periph_module_enable(_i2c_hw[dev].mod); /* restore configuration */ _i2c_hw[dev].regs->int_ena.val = 0; _i2c_hw[dev].regs->ctr.val = ctr & (~I2C_TRANS_START_M); _i2c_hw[dev].regs->fifo_conf.val = fifo_conf; _i2c_hw[dev].regs->scl_low_period.val = scl_low_period; _i2c_hw[dev].regs->scl_high_period.val = scl_high_period; _i2c_hw[dev].regs->scl_start_hold.val = scl_start_hold; _i2c_hw[dev].regs->scl_rstart_setup.val = scl_rstart_setup; _i2c_hw[dev].regs->scl_stop_hold.val = scl_stop_hold; _i2c_hw[dev].regs->scl_stop_setup.val = scl_stop_setup; _i2c_hw[dev].regs->sda_hold.val = sda_hold; _i2c_hw[dev].regs->sda_sample.val = sda_sample; _i2c_hw[dev].regs->timeout.val = timeout; _i2c_hw[dev].regs->scl_filter_cfg.val = scl_filter_cfg; _i2c_hw[dev].regs->sda_filter_cfg.val = sda_filter_cfg; /* disable and clear all interrupt sources */ _i2c_hw[dev].regs->int_ena.val = 0; _i2c_hw[dev].regs->int_clr.val = ~0x0U; return; } void i2c_print_config(void) { for (unsigned bus = 0; bus < i2c_bus_num; bus++) { ets_printf("\tI2C_DEV(%d)\tscl=%d sda=%d\n", bus, _i2c_hw[bus].pin_scl, _i2c_hw[bus].pin_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_HW */