cpu/atxmega/periph/i2c: Add i2c driver
Add initial ATxmega i2c master driver. Signed-off-by: Gerson Fernando Budke <nandojve@gmail.com>
This commit is contained in:
parent
fed2c8e87e
commit
1dec526d0a
@ -6,6 +6,7 @@ include $(RIOTCPU)/avr8_common/Makefile.features
|
|||||||
FEATURES_PROVIDED += cpu_core_atxmega
|
FEATURES_PROVIDED += cpu_core_atxmega
|
||||||
FEATURES_PROVIDED += periph_cpuid
|
FEATURES_PROVIDED += periph_cpuid
|
||||||
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
||||||
|
FEATURES_PROVIDED += periph_i2c
|
||||||
FEATURES_PROVIDED += periph_nvm
|
FEATURES_PROVIDED += periph_nvm
|
||||||
FEATURES_PROVIDED += periph_pm
|
FEATURES_PROVIDED += periph_pm
|
||||||
FEATURES_PROVIDED += periph_timer periph_timer_periodic
|
FEATURES_PROVIDED += periph_timer periph_timer_periodic
|
||||||
|
|||||||
@ -264,6 +264,43 @@ typedef struct {
|
|||||||
cpu_int_lvl_t int_lvl[TIMER_CH_MAX_NUMOF]; /**< Interrupt channels level */
|
cpu_int_lvl_t int_lvl[TIMER_CH_MAX_NUMOF]; /**< Interrupt channels level */
|
||||||
} timer_conf_t;
|
} timer_conf_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Override I2C clock speed values
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define HAVE_I2C_SPEED_T
|
||||||
|
typedef enum {
|
||||||
|
I2C_SPEED_LOW = 10000ul, /**< low speed mode: ~10 kbit/s */
|
||||||
|
I2C_SPEED_NORMAL = 100000ul, /**< normal mode: ~100 kbit/s */
|
||||||
|
I2C_SPEED_FAST = 400000ul, /**< fast mode: ~400 kbit/s */
|
||||||
|
I2C_SPEED_FAST_PLUS = 1000000ul, /**< fast plus mode: ~1000 kbit/s */
|
||||||
|
/* High speed is not supported without external hardware hacks */
|
||||||
|
I2C_SPEED_HIGH = 3400000ul, /**< high speed mode: ~3400 kbit/s */
|
||||||
|
} i2c_speed_t;
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Use shared I2C functions
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define PERIPH_I2C_NEED_READ_REG
|
||||||
|
#define PERIPH_I2C_NEED_READ_REGS
|
||||||
|
#define PERIPH_I2C_NEED_WRITE_REG
|
||||||
|
#define PERIPH_I2C_NEED_WRITE_REGS
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief I2C configuration structure
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
TWI_t *dev; /**< Pointer to hardware module registers */
|
||||||
|
pwr_reduction_t pwr; /**< Power Management */
|
||||||
|
gpio_t sda_pin; /**< SDA GPIO pin */
|
||||||
|
gpio_t scl_pin; /**< SCL GPIO pin */
|
||||||
|
i2c_speed_t speed; /**< Configured bus speed, actual speed may be lower but never higher */
|
||||||
|
cpu_int_lvl_t int_lvl; /**< Serial Interrupt Level */
|
||||||
|
} i2c_conf_t;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
300
cpu/atxmega/periph/i2c.c
Normal file
300
cpu/atxmega/periph/i2c.c
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Gerson Fernando Budke <nandojve@gmail.com>
|
||||||
|
*
|
||||||
|
* 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_atxmega
|
||||||
|
* @ingroup cpu_atxmega_periph
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Low-level I2C driver implementation
|
||||||
|
*
|
||||||
|
* @author Gerson Fernando Budke <nandojve@gmail.com>
|
||||||
|
*
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "cpu_pm.h"
|
||||||
|
#include "periph/i2c.h"
|
||||||
|
#include "periph/gpio.h"
|
||||||
|
#include "periph/pm.h"
|
||||||
|
|
||||||
|
#define ENABLE_DEBUG 0
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Device Context allocation
|
||||||
|
*/
|
||||||
|
static struct {
|
||||||
|
mutex_t locks;
|
||||||
|
mutex_t xfer;
|
||||||
|
uint8_t *buffer;
|
||||||
|
size_t pos;
|
||||||
|
size_t len;
|
||||||
|
uint8_t flags;
|
||||||
|
int8_t status;
|
||||||
|
} i2c_ctx[I2C_NUMOF];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the pointer to the base register of the given I2C device
|
||||||
|
*
|
||||||
|
* @param[in] dev I2C device identifier
|
||||||
|
*
|
||||||
|
* @return base register address
|
||||||
|
*/
|
||||||
|
static inline TWI_t* dev(i2c_t dev)
|
||||||
|
{
|
||||||
|
return ((TWI_t*) (i2c_config[dev].dev));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t _i2c_calc_baud(i2c_t i2c)
|
||||||
|
{
|
||||||
|
uint16_t ftwi = (((CLOCK_CORECLOCK / (2*i2c_config[i2c].speed))-5)+1);
|
||||||
|
|
||||||
|
if (ftwi > 255) {
|
||||||
|
DEBUG("[i2c] init: dev speed is lower than minimal speed supported.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ftwi < 1) {
|
||||||
|
DEBUG("[i2c] init: dev speed is high than maximum speed supported.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ftwi & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_init(i2c_t i2c)
|
||||||
|
{
|
||||||
|
uint8_t baudrate;
|
||||||
|
|
||||||
|
if (i2c >= I2C_NUMOF) {
|
||||||
|
DEBUG("[i2c] init: dev is invalid.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
baudrate = _i2c_calc_baud(i2c);
|
||||||
|
if (baudrate == 0) {
|
||||||
|
DEBUG("[i2c] init: baudrate is invalid.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_init(&i2c_ctx[i2c].locks);
|
||||||
|
mutex_init(&i2c_ctx[i2c].xfer);
|
||||||
|
mutex_lock(&i2c_ctx[i2c].xfer);
|
||||||
|
i2c_init_pins(i2c);
|
||||||
|
|
||||||
|
pm_periph_enable(i2c_config[i2c].pwr);
|
||||||
|
dev(i2c)->MASTER.BAUD = baudrate;
|
||||||
|
pm_periph_disable(i2c_config[i2c].pwr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_init_pins(i2c_t i2c)
|
||||||
|
{
|
||||||
|
gpio_init(i2c_config[i2c].sda_pin, GPIO_OPC_WRD_AND_PULL);
|
||||||
|
gpio_init(i2c_config[i2c].scl_pin, GPIO_OPC_WRD_AND_PULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i2c_acquire(i2c_t i2c)
|
||||||
|
{
|
||||||
|
DEBUG("acquire\n");
|
||||||
|
pm_block(3);
|
||||||
|
mutex_lock(&i2c_ctx[i2c].locks);
|
||||||
|
pm_periph_enable(i2c_config[i2c].pwr);
|
||||||
|
|
||||||
|
dev(i2c)->MASTER.CTRLA = (i2c_config[i2c].int_lvl << TWI_MASTER_INTLVL_gp)
|
||||||
|
| TWI_MASTER_RIEN_bm
|
||||||
|
| TWI_MASTER_WIEN_bm
|
||||||
|
| TWI_MASTER_ENABLE_bm;
|
||||||
|
dev(i2c)->MASTER.STATUS = TWI_MASTER_BUSSTATE_IDLE_gc;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_release(i2c_t i2c)
|
||||||
|
{
|
||||||
|
dev(i2c)->MASTER.CTRLA = 0;
|
||||||
|
pm_periph_disable(i2c_config[i2c].pwr);
|
||||||
|
mutex_unlock(&i2c_ctx[i2c].locks);
|
||||||
|
pm_unblock(3);
|
||||||
|
DEBUG("release\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _i2c_transaction(i2c_t i2c, uint16_t addr, const void *data,
|
||||||
|
size_t len, uint8_t flags, bool is_read)
|
||||||
|
{
|
||||||
|
if (flags & I2C_ADDR10) {
|
||||||
|
DEBUG("[i2c] xfer: no 10 bit address support.\n");
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
if (data == NULL || len == 0) {
|
||||||
|
DEBUG("[i2c] xfer: invalid parameter.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_ctx[i2c].buffer = (uint8_t *) data;
|
||||||
|
i2c_ctx[i2c].pos = 0;
|
||||||
|
i2c_ctx[i2c].len = len;
|
||||||
|
i2c_ctx[i2c].flags = flags;
|
||||||
|
|
||||||
|
dev(i2c)->MASTER.ADDR = (addr << 1) | (is_read ? 0x01 : 0x00);
|
||||||
|
|
||||||
|
mutex_lock(&i2c_ctx[i2c].xfer);
|
||||||
|
|
||||||
|
DEBUG("[i2c] xfer: status = %d\n", i2c_ctx[i2c].status);
|
||||||
|
|
||||||
|
return i2c_ctx[i2c].status;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i2c_write_bytes(i2c_t i2c, uint16_t addr, const void *data, size_t len,
|
||||||
|
uint8_t flags)
|
||||||
|
{
|
||||||
|
return _i2c_transaction(i2c, addr, data, len, flags, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i2c_read_bytes(i2c_t i2c, uint16_t addr, void *data, size_t len,
|
||||||
|
uint8_t flags)
|
||||||
|
{
|
||||||
|
return _i2c_transaction(i2c, addr, data, len, flags, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @brief TWI master write interrupt handler.
|
||||||
|
*
|
||||||
|
* Handles TWI transactions (master write) and responses to (N)ACK.
|
||||||
|
*/
|
||||||
|
static inline void _i2c_write_handler(int i2c)
|
||||||
|
{
|
||||||
|
if (i2c_ctx[i2c].pos < i2c_ctx[i2c].len) {
|
||||||
|
const uint8_t* const data = i2c_ctx[i2c].buffer;
|
||||||
|
dev(i2c)->MASTER.DATA = data[i2c_ctx[i2c].pos++];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Send STOP condition to complete the transaction. */
|
||||||
|
if (!(i2c_ctx[i2c].flags & I2C_NOSTOP)) {
|
||||||
|
dev(i2c)->MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_ctx[i2c].status = 0;
|
||||||
|
mutex_unlock(&i2c_ctx[i2c].xfer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @brief TWI master read interrupt handler.
|
||||||
|
*
|
||||||
|
* This is the master read interrupt handler that takes care of reading bytes
|
||||||
|
* from the TWI slave device.
|
||||||
|
*/
|
||||||
|
static inline void _i2c_read_handler(int i2c)
|
||||||
|
{
|
||||||
|
if (i2c_ctx[i2c].pos < i2c_ctx[i2c].len) {
|
||||||
|
uint8_t* const data = i2c_ctx[i2c].buffer;
|
||||||
|
data[i2c_ctx[i2c].pos++] = dev(i2c)->MASTER.DATA;
|
||||||
|
|
||||||
|
/* If there is more to read, issue ACK and start a byte read.
|
||||||
|
* Otherwise, issue NACK and STOP to complete the transaction.
|
||||||
|
*/
|
||||||
|
if (i2c_ctx[i2c].pos < i2c_ctx[i2c].len) {
|
||||||
|
dev(i2c)->MASTER.CTRLC = TWI_MASTER_CMD_RECVTRANS_gc;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uint8_t control_c = TWI_MASTER_ACKACT_bm;
|
||||||
|
|
||||||
|
if (!(i2c_ctx[i2c].flags & I2C_NOSTOP)) {
|
||||||
|
control_c |= TWI_MASTER_CMD_STOP_gc;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev(i2c)->MASTER.CTRLC = control_c;
|
||||||
|
|
||||||
|
i2c_ctx[i2c].status = 0;
|
||||||
|
mutex_unlock(&i2c_ctx[i2c].xfer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Issue STOP and buffer overflow condition. */
|
||||||
|
dev(i2c)->MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
|
||||||
|
|
||||||
|
i2c_ctx[i2c].status = -ENOMEM;
|
||||||
|
mutex_unlock(&i2c_ctx[i2c].xfer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @brief Common TWI master interrupt service routine.
|
||||||
|
*
|
||||||
|
* Check current status and calls the appropriate handler.
|
||||||
|
*/
|
||||||
|
static inline void isr_handler(int i2c)
|
||||||
|
{
|
||||||
|
avr8_enter_isr();
|
||||||
|
|
||||||
|
int8_t const m_status = dev(i2c)->MASTER.STATUS;
|
||||||
|
|
||||||
|
if (m_status & TWI_MASTER_ARBLOST_bm) {
|
||||||
|
dev(i2c)->MASTER.STATUS = m_status | TWI_MASTER_ARBLOST_bm;
|
||||||
|
dev(i2c)->MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
|
||||||
|
i2c_ctx[i2c].status = -EBUSY;
|
||||||
|
mutex_unlock(&i2c_ctx[i2c].xfer);
|
||||||
|
}
|
||||||
|
else if ((m_status & TWI_MASTER_BUSERR_bm)
|
||||||
|
|| (m_status & TWI_MASTER_RXACK_bm)) {
|
||||||
|
|
||||||
|
dev(i2c)->MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
|
||||||
|
i2c_ctx[i2c].status = ((m_status & TWI_MASTER_BUSERR_bm) > 0)
|
||||||
|
? -EIO
|
||||||
|
: -ENXIO;
|
||||||
|
mutex_unlock(&i2c_ctx[i2c].xfer);
|
||||||
|
}
|
||||||
|
else if (m_status & TWI_MASTER_WIF_bm) {
|
||||||
|
_i2c_write_handler(i2c);
|
||||||
|
}
|
||||||
|
else if (m_status & TWI_MASTER_RIF_bm) {
|
||||||
|
_i2c_read_handler(i2c);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
i2c_ctx[i2c].status = -EPROTO;
|
||||||
|
mutex_unlock(&i2c_ctx[i2c].xfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
avr8_exit_isr();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef I2C_0_ISR
|
||||||
|
ISR(I2C_0_ISR, ISR_BLOCK)
|
||||||
|
{
|
||||||
|
isr_handler(0);
|
||||||
|
}
|
||||||
|
#endif /* I2C_0_ISR */
|
||||||
|
|
||||||
|
#ifdef I2C_1_ISR
|
||||||
|
ISR(I2C_1_ISR, ISR_BLOCK)
|
||||||
|
{
|
||||||
|
isr_handler(1);
|
||||||
|
}
|
||||||
|
#endif /* I2C_1_ISR */
|
||||||
|
|
||||||
|
#ifdef I2C_2_ISR
|
||||||
|
ISR(I2C_2_ISR, ISR_BLOCK)
|
||||||
|
{
|
||||||
|
isr_handler(2);
|
||||||
|
}
|
||||||
|
#endif /* I2C_2_ISR */
|
||||||
|
|
||||||
|
#ifdef I2C_3_ISR
|
||||||
|
ISR(I2C_3_ISR, ISR_BLOCK)
|
||||||
|
{
|
||||||
|
isr_handler(3);
|
||||||
|
}
|
||||||
|
#endif /* I2C_3_ISR */
|
||||||
Loading…
x
Reference in New Issue
Block a user