From 66bf499e1b33751433c85b533a05ba8e4bbac60e Mon Sep 17 00:00:00 2001 From: Eduard Drusa Date: Thu, 31 Mar 2022 11:17:59 +0200 Subject: [PATCH] NRF5x: I2C EasyDMA support for NRF5x, extended API This commit adds support for NRF52 TWI Master mode and slightly extends existing I2C API. This is a breaking change, while mode selection needs to be done during enabling I2C. There is one additional breaking change done because: 1) Unicore MX API design was PITA for writes 2) It is incompatible with EasyDMA I strongly apologize to all two users who might be affected by this change. --- include/libopencm3/nrf/52/i2c.h | 64 ++++++++++++++++++++ include/libopencm3/nrf/common/i2c.h | 40 +++++++++++- lib/nrf/51/Makefile | 2 +- lib/nrf/52/Makefile | 2 +- lib/nrf/52/i2c.c | 62 +++++++++++++++++++ lib/nrf/common/{i2c.c => i2c_common.c} | 84 ++++++++++++++++---------- 6 files changed, 218 insertions(+), 36 deletions(-) create mode 100644 lib/nrf/52/i2c.c rename lib/nrf/common/{i2c.c => i2c_common.c} (57%) diff --git a/include/libopencm3/nrf/52/i2c.h b/include/libopencm3/nrf/52/i2c.h index 3341c59a..407f30d7 100644 --- a/include/libopencm3/nrf/52/i2c.h +++ b/include/libopencm3/nrf/52/i2c.h @@ -37,4 +37,68 @@ #include #include +/**@{*/ + +#define I2C_EVENT_RXSTARTED(i2c) MMIO32((i2c) + 0x14C) +#define I2C_EVENT_TXSTARTED(i2c) MMIO32((i2c) + 0x150) +#define I2C_EVENT_LASTRX(i2c) MMIO32((i2c) + 0x15C) +#define I2C_EVENT_LASTTX(i2c) MMIO32((i2c) + 0x160) + +#define I2C_RXDPTR(i2c) MMIO32((i2c) + 0x534) +#define I2C_RXDMAXCNT(i2c) MMIO32((i2c) + 0x538) +#define I2C_RXDAMOUNT(i2c) MMIO32((i2c) + 0x53C) +#define I2C_RXDLIST(i2c) MMIO32((i2c) + 0x540) +#define I2C_TXDPTR(i2c) MMIO32((i2c) + 0x544) +#define I2C_TXDMAXCNT(i2c) MMIO32((i2c) + 0x548) +#define I2C_TXDAMOUNT(i2c) MMIO32((i2c) + 0x54C) +#define I2C_TXDLIST(i2c) MMIO32((i2c) + 0x550) + +/** @addtogroup i2c_shorts + * @{ + */ + +/** On start of last byte transmission, activate start of reception task */ +#define I2C_SHORTS_LASTTX_STARTRX (1 << 7) + +/** On start of last byte transmission, activate suspend task */ +#define I2C_SHORTS_LASTTX_SUSPEND (1 << 8) + +/** On start of last byte transmission, activate stop task */ +#define I2C_SHORTS_LASTTX_STOP (1 << 9) + +/** On start of last byte reception, activate start of transmission task */ +#define I2C_SHORTS_LASTRX_STARTTX (1 << 10) + +/** On start of last byte reception, activate stop task */ +#define I2C_SHORTS_LASTRX_STOP (1 << 12) +/** @} */ + +/** @addtogroup i2c_inten I2C interrupt enable flags + * @{ */ + +#define I2C_INTEN_SUSPENDED (1 << 18) +#define I2C_INTEN_RXSTARTED (1 << 19) +#define I2C_INTEN_TXSTARTED (1 << 20) +#define I2C_INTEN_LASTRX (1 << 23) +#define I2C_INTEN_LASTTX (1 << 24) + +/** @} */ + +/** @addtogroup i2c_mode I2C peripheral mode + * @{ + */ + +/** NRF52 I2C Master mode with EasyDMA support */ +#define I2C_MODE_MASTER (6) +/**@}*/ + + +BEGIN_DECLS + +void i2c_set_tx_buffer(uint32_t i2c, const uint8_t * buffer, uint8_t len); +void i2c_set_rx_buffer(uint32_t i2c, uint8_t * buffer, uint8_t len); + +END_DECLS + +/** @} */ diff --git a/include/libopencm3/nrf/common/i2c.h b/include/libopencm3/nrf/common/i2c.h index 26c3c601..ec4ed69a 100644 --- a/include/libopencm3/nrf/common/i2c.h +++ b/include/libopencm3/nrf/common/i2c.h @@ -75,9 +75,16 @@ /* Register Contents */ /** @addtogroup i2c_shorts I2C event -> task shortcuts + * The effect of activated shortcut is, that upon I2C event + * triggering, the hardware will automatically start chosen + * task without intervention of the software. * @{ */ + +/** On byte boundary, activate suspend task. */ #define I2C_SHORTS_BB_SUSPEND (1 << 0) + +/** On byte boundary, activate stop task. */ #define I2C_SHORTS_BB_STOP (1 << 1) /**@}*/ @@ -97,11 +104,37 @@ #define I2C_ERRORSRC_ANACK (1 << 1) #define I2C_ERRORSRC_DNACK (1 << 2) -#define I2C_ENABLE_VALUE (5) +/** @addtogroup i2c_mode I2C peripheral mode + * @{ + */ +/** NRF51 legacy mode. + * On NRF51, this is the only mode available. + * On NRF52, this mode does not support EasyDMA. + */ +#define I2C_MODE_LEGACY (5) +/**@}*/ + +/** @addtogroup i2c_freq_const I2C frequency constants + * @{ + */ +/** 100kHz */ #define I2C_FREQUENCY_100K (0x01980000) +/** 250kHz */ #define I2C_FREQUENCY_250K (0x04000000) +/** 390kHz + * @note: This value is not documented in datasheet. It provides + * ~390kHz clock with correct timing. + */ +#define I2C_FREQUENCY_390K (0x06200000) +/** 400kHz + * @note: According to datasheet, there is HW bug which prevents + * MCU from generating correct timings, therefore it might be + * unusable. Use @ref I2C_FREQUENCY_390K instead, if this affects + * you. + */ #define I2C_FREQUENCY_400K (0x06680000) +/**@}*/ #define I2C_PSEL_OFF (0xffffffff) @@ -109,9 +142,9 @@ BEGIN_DECLS -void i2c_enable(uint32_t i2c); +void i2c_enable(uint32_t i2c, uint32_t mode); void i2c_disable(uint32_t i2c); -void i2c_start_tx(uint32_t i2c, uint8_t data); +void i2c_start_tx(uint32_t i2c); void i2c_start_rx(uint32_t i2c); void i2c_send_stop(uint32_t i2c); void i2c_set_fast_mode(uint32_t i2c); @@ -122,6 +155,7 @@ uint8_t i2c_get_data(uint32_t i2c); void i2c_select_pins(uint32_t i2c, uint32_t scl_pin, uint32_t sda_pin); void i2c_set_address(uint32_t i2c, uint8_t addr); void i2c_resume(uint32_t i2c); +void i2c_set_shorts(uint32_t i2c, uint32_t shorts); END_DECLS diff --git a/lib/nrf/51/Makefile b/lib/nrf/51/Makefile index 9e1688f2..612d5111 100644 --- a/lib/nrf/51/Makefile +++ b/lib/nrf/51/Makefile @@ -37,7 +37,7 @@ ARFLAGS = rcs OBJS += clock_common.o clock.o OBJS += gpio.o -OBJS += i2c.o +OBJS += i2c_common.o OBJS += ppi.o OBJS += rtc.o OBJS += radio_common.o ./radio.o diff --git a/lib/nrf/52/Makefile b/lib/nrf/52/Makefile index a0e4884c..35c2ff3f 100644 --- a/lib/nrf/52/Makefile +++ b/lib/nrf/52/Makefile @@ -37,7 +37,7 @@ ARFLAGS = rcs OBJS += clock_common.o OBJS += gpio.o -OBJS += i2c.o +OBJS += i2c.o i2c_common.o OBJS += ppi.o OBJS += radio_common.o OBJS += rtc.o diff --git a/lib/nrf/52/i2c.c b/lib/nrf/52/i2c.c new file mode 100644 index 00000000..2af32637 --- /dev/null +++ b/lib/nrf/52/i2c.c @@ -0,0 +1,62 @@ +/** @addtogroup i2c_file I2C peripheral API + */ + +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2022 Eduard Drusa + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include + +/**@{*/ + +/** Configure I2C transmit buffer properties + * + * Configures transmit buffer for EasyDMA transaction. This API + * is only available if @ref I2C_MODE_MASTER mode is activated. + * + * Configures linear TX buffer for EasyDMA transmission. + * @param[in] i2c i2c peripheral base, see @ref i2c_block + * @param[in] buffer address of buffer start + * @param[in] len length of data in the buffer + */ +void i2c_set_tx_buffer(uint32_t i2c, const uint8_t *buffer, uint8_t len) +{ + I2C_TXDPTR(i2c) = (uint32_t) buffer; + I2C_TXDMAXCNT(i2c) = len; + I2C_TXDLIST(i2c) = 0; +} + +/** Configure I2C receive buffer properties + * + * Configures receive buffer for EasyDMA transaction. This API + * is only available if @ref I2C_MODE_MASTER mode is activated. + * + * Configures linear RX buffer for EasyDMA transmission. + * @param[in] i2c i2c peripheral base, see @ref i2c_block + * @param[in] buffer address of buffer start + * @param[in] len length of the buffer + */ +void i2c_set_rx_buffer(uint32_t i2c, uint8_t *buffer, uint8_t len) +{ + I2C_RXDPTR(i2c) = (uint32_t) buffer; + I2C_RXDMAXCNT(i2c) = len; + I2C_RXDLIST(i2c) = 0; +} + + +/** @} */ diff --git a/lib/nrf/common/i2c.c b/lib/nrf/common/i2c_common.c similarity index 57% rename from lib/nrf/common/i2c.c rename to lib/nrf/common/i2c_common.c index 5086bb23..8121774e 100644 --- a/lib/nrf/common/i2c.c +++ b/lib/nrf/common/i2c_common.c @@ -6,6 +6,8 @@ * LGPL License Terms @ref lgpl_license * @author @htmlonly © @endhtmlonly 2016 * Maxim Sloyko + * @author @htmlonly © @endhtmlonly 2021 - 2022 + * Eduard Drusa * */ @@ -35,16 +37,17 @@ /** @brief Enable I2C peripheral * - * @param[in] i2c uint32_t i2c peripheral base + * @param[in] i2c i2c peripheral base, see @ref i2c_block + * @param[in] mode i2c @ref i2c_mode */ -void i2c_enable(uint32_t i2c) +void i2c_enable(uint32_t i2c, uint32_t mode) { - I2C_ENABLE(i2c) = I2C_ENABLE_VALUE; + I2C_ENABLE(i2c) = mode; } /** @brief Disable I2C peripheral * - * @param[in] i2c uint32_t i2c peripheral base + * @param[in] i2c i2c peripheral base, see @ref i2c_block */ void i2c_disable(uint32_t i2c) { @@ -53,36 +56,37 @@ void i2c_disable(uint32_t i2c) /** @brief Start I2C transmission. * - * @param[in] i2c uint32_t i2c peripheral base. - * @param[in] data uint8_t the first byte to send. + * Starts STARTTX task, which generates start condition on I2C bus and + * transmits address previously configured by @ref i2c_set_address. + * + * @param[in] i2c i2c peripheral base, see @ref i2c_block. */ -void i2c_start_tx(uint32_t i2c, uint8_t data) +void i2c_start_tx(uint32_t i2c) { - PERIPH_TRIGGER_TASK(I2C_TASK_STARTTX(i2c)); - I2C_TXD(i2c) = data; + I2C_TASK_STARTTX(i2c) = 1; } /** @brief Start I2C reception. * - * @param[in] i2c uint32_t i2c peripheral base. + * @param[in] i2c i2c peripheral base, see @ref i2c_block. */ void i2c_start_rx(uint32_t i2c) { - PERIPH_TRIGGER_TASK(I2C_TASK_STARTRX(i2c)); + I2C_TASK_STARTRX(i2c) = 1; } /** @brief Signal stop on I2C line. * - * @param[in] i2c uint32_t i2c peripheral base. + * @param[in] i2c i2c peripheral base, see @ref i2c_block. */ void i2c_send_stop(uint32_t i2c) { - PERIPH_TRIGGER_TASK(I2C_TASK_STOP(i2c)); + I2C_TASK_STOP(i2c) = 1; } /** @brief Select Fast (400kHz) mode. * - * @param[in] i2c uint32_t i2c peripheral base. + * @param[in] i2c i2c peripheral base, see @ref i2c_block. */ void i2c_set_fast_mode(uint32_t i2c) { @@ -91,7 +95,7 @@ void i2c_set_fast_mode(uint32_t i2c) /** @brief Select Standard (100kHz) mode. * - * @param[in] i2c uint32_t i2c peripheral base. + * @param[in] i2c i2c peripheral base, see @ref i2c_block. */ void i2c_set_standard_mode(uint32_t i2c) { @@ -103,8 +107,8 @@ void i2c_set_standard_mode(uint32_t i2c) * In addition to Standard (100kHz) and Fast (400kHz) modes * this peripheral also supports 250kHz mode. * - * @param[in] i2c uint32_t i2c peripheral base. - * @param[in] freq uint32_t frequency constant. See defines for details + * @param[in] i2c i2c peripheral base, see @ref i2c_block + * @param[in] freq frequency constant. See @ref i2c_freq_const for details * and note that this is not actually a frequency in Hz or kHz. */ void i2c_set_frequency(uint32_t i2c, uint32_t freq) @@ -114,8 +118,11 @@ void i2c_set_frequency(uint32_t i2c, uint32_t freq) /** @brief Write Data to TXD register to be sent. * - * @param[in] i2c uint32_t i2c peripheral base. - * @param[in] data uint8_t byte to send next. + * Writes one byte into transmission buffer. This API is only + * available if @ref I2C_MODE_LEGACY is activated. + * + * @param[in] i2c i2c peripheral base, see @ref i2c_block + * @param[in] data byte to send next. */ void i2c_send_data(uint32_t i2c, uint8_t data) { @@ -124,8 +131,11 @@ void i2c_send_data(uint32_t i2c, uint8_t data) /** @brief Read Data from RXD register. * - * @param[in] i2c uint32_t i2c peripheral base. - * @returns uint8_t data from RXD register. + * Reads one byte from reception buffer. This API is only + * available if @ref I2C_MODE_LEGACY is activated. + * + * @param[in] i2c i2c peripheral base, see @ref i2c_block + * @returns data from RXD register. */ uint8_t i2c_get_data(uint32_t i2c) { @@ -133,15 +143,17 @@ uint8_t i2c_get_data(uint32_t i2c) } /** @brief Select GPIO pins to be used by this peripheral. + * + * Configures GPIO pins assigned to SCL and SDA signals. These pins are only occupied + * by I2C peripheral whenever it is enabled using @ref i2c_enable. It is possible to + * ignore any given signal and not map it to pin by using special value of + * @ref GPIO_UNCONNECTED instead of @ref gpio_pin_id values. * * This needs to be configured when no transaction is in progress. * - * @param[in] i2c i2c peripheral base. - * @param[in] scl_pin SCL pin. Use GPIO defines in @ref gpio_pin_id or GPIO_UNCONNECTED - * if signal shall not be connected to any pin. - * @param[in] sda_pin SDA pin. Use GPIO defines in @ref gpio_pin_id or GPIO_UNCONNECTED - * if signal shall not be connected to any pin. - + * @param[in] i2c i2c peripheral base, see @ref i2c_block + * @param[in] scl_pin GPIO pin used for SCL signal + * @param[in] sda_pin GPIO pin used for SDA signal */ void i2c_select_pins(uint32_t i2c, uint32_t scl_pin, uint32_t sda_pin) { @@ -160,8 +172,8 @@ void i2c_select_pins(uint32_t i2c, uint32_t scl_pin, uint32_t sda_pin) /** @brief Set 7bit I2C address of the device you wish to communicate with. * - * @param[in] i2c uint32_t i2c peripheral base. - * @param[in] addr uint8_t device address (7bit). + * @param[in] i2c i2c peripheral base, see @ref i2c_block + * @param[in] addr device address (7bit). */ void i2c_set_address(uint32_t i2c, uint8_t addr) { @@ -173,12 +185,22 @@ void i2c_set_address(uint32_t i2c, uint8_t addr) * This function is unusual, but required to implement * i2c exchange with this peripheral. * - * @param[in] i2c uint32_t i2c peripheral base. + * @param[in] i2c i2c peripheral base, see @ref i2c_block */ void i2c_resume(uint32_t i2c) { PERIPH_TRIGGER_TASK(I2C_TASK_RESUME(i2c)); } - +/** Configure event -> task shortcuts + * + * Sets new shortcut configuration bitmask for I2C peripheral. + * + * @param[in] i2c i2c peripheral base, see @ref i2c_block + * @param[in] shorts @ref i2c_shorts activated + */ +void i2c_set_shorts(uint32_t i2c, uint32_t shorts) +{ + I2C_SHORTS(i2c) = shorts; +} /**@}*/