1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/nrf52/periph/i2c.c

215 lines
5.9 KiB
C
Raw Normal View History

2017-09-01 15:33:10 +02:00
/*
* Copyright (C) 2017 HAW Hamburg
* 2018 Freie Universität Berlin
2018-06-05 21:52:18 +02:00
* 2018 Mesotic SAS
2017-09-01 15:33:10 +02:00
*
* 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.
2017-09-01 15:33:10 +02:00
*/
/**
* @ingroup cpu_nrf52
* @{
*
* @file
* @brief Low-level I2C (TWI) peripheral driver implementation
2017-09-01 15:33:10 +02:00
*
* @author Dimitri Nahm <dimitri.nahm@haw-hamburg.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
2018-06-05 21:52:18 +02:00
* @author Dylan Laduranty <dylan.laduranty@mesotic.com>
2017-09-01 15:33:10 +02:00
*
* @}
*/
#include <string.h>
2018-06-05 21:52:18 +02:00
#include <errno.h>
2017-09-01 15:33:10 +02:00
#include "cpu.h"
#include "mutex.h"
#include "assert.h"
#include "periph/i2c.h"
#include "periph/gpio.h"
2017-09-01 15:33:10 +02:00
#define ENABLE_DEBUG (0)
#include "debug.h"
/**
* @brief If any of the 8 lower bits are set, the speed value is invalid
2017-09-01 15:33:10 +02:00
*/
#define INVALID_SPEED_MASK (0xff)
2017-09-01 15:33:10 +02:00
/**
2018-06-05 21:52:18 +02:00
* @brief Initialized dev locks (we have a maximum of two devices...)
*/
2018-06-05 21:52:18 +02:00
static mutex_t locks[I2C_NUMOF];
2017-09-01 15:33:10 +02:00
2018-06-05 21:52:18 +02:00
static inline NRF_TWIM_Type *bus(i2c_t dev)
2017-09-01 15:33:10 +02:00
{
2018-06-05 21:52:18 +02:00
(void) dev;
return i2c_config[0].dev;
2017-09-01 15:33:10 +02:00
}
2018-06-05 21:52:18 +02:00
static int finish(i2c_t dev)
2017-09-01 15:33:10 +02:00
{
DEBUG("[i2c] waiting for STOPPED or ERROR event\n");
2018-06-05 21:52:18 +02:00
while ((!(bus(dev)->EVENTS_STOPPED)) && (!(bus(dev)->EVENTS_ERROR))) {
nrf52_sleep();
2017-09-01 15:33:10 +02:00
}
2018-06-05 21:52:18 +02:00
if ((bus(dev)->EVENTS_STOPPED)) {
bus(dev)->EVENTS_STOPPED = 0;
DEBUG("[i2c] finish: stop event occurred\n");
2017-09-01 15:33:10 +02:00
}
2018-06-05 21:52:18 +02:00
if (bus(dev)->EVENTS_ERROR) {
bus(dev)->EVENTS_ERROR = 0;
if (bus(dev)->ERRORSRC & TWIM_ERRORSRC_ANACK_Msk) {
bus(dev)->ERRORSRC = TWIM_ERRORSRC_ANACK_Msk;
DEBUG("[i2c] check_error: NACK on address byte\n");
2018-06-05 21:52:18 +02:00
return -ENXIO;
}
2018-06-05 21:52:18 +02:00
if (bus(dev)->ERRORSRC & TWIM_ERRORSRC_DNACK_Msk) {
bus(dev)->ERRORSRC = TWIM_ERRORSRC_DNACK_Msk;
DEBUG("[i2c] check_error: NACK on data byte\n");
2018-06-05 21:52:18 +02:00
return -EIO;
}
2017-09-01 15:33:10 +02:00
}
2018-06-05 21:52:18 +02:00
return 0;
2017-09-01 15:33:10 +02:00
}
2018-06-05 21:52:18 +02:00
void i2c_init(i2c_t dev)
2017-09-01 15:33:10 +02:00
{
2018-06-05 21:52:18 +02:00
assert(dev < I2C_NUMOF);
2017-09-01 15:33:10 +02:00
2018-06-05 21:52:18 +02:00
/* Initialize mutex */
mutex_init(&locks[dev]);
/* disable device during initialization, will be enabled when acquire is
* called */
2018-06-05 21:52:18 +02:00
bus(dev)->ENABLE = TWIM_ENABLE_ENABLE_Disabled;
/* configure pins */
2018-06-05 21:52:18 +02:00
gpio_init(i2c_config[dev].scl, GPIO_IN_PU);
gpio_init(i2c_config[dev].sda, GPIO_IN_PU);
bus(dev)->PSEL.SCL = i2c_config[dev].scl;
bus(dev)->PSEL.SDA = i2c_config[dev].sda;
/* configure dev clock speed */
bus(dev)->FREQUENCY = i2c_config[dev].speed;
2017-09-01 15:33:10 +02:00
/* re-enable the device. We expect that the device was being acquired before
* the i2c_init_master() function is called, so it should be enabled when
* exiting this function. */
2018-06-05 21:52:18 +02:00
bus(dev)->ENABLE = TWIM_ENABLE_ENABLE_Enabled;
2017-09-01 15:33:10 +02:00
}
2018-06-05 21:52:18 +02:00
int i2c_acquire(i2c_t dev)
2017-09-01 15:33:10 +02:00
{
2018-06-05 21:52:18 +02:00
assert(dev < I2C_NUMOF);
2018-06-05 21:52:18 +02:00
mutex_lock(&locks[dev]);
bus(dev)->ENABLE = TWIM_ENABLE_ENABLE_Enabled;
2018-06-05 21:52:18 +02:00
DEBUG("[i2c] acquired dev %i\n", (int)dev);
2017-09-01 15:33:10 +02:00
return 0;
}
2018-06-05 21:52:18 +02:00
int i2c_release(i2c_t dev)
2017-09-01 15:33:10 +02:00
{
2018-06-05 21:52:18 +02:00
assert(dev < I2C_NUMOF);
2018-06-05 21:52:18 +02:00
bus(dev)->ENABLE = TWIM_ENABLE_ENABLE_Disabled;
mutex_unlock(&locks[dev]);
2018-06-05 21:52:18 +02:00
DEBUG("[i2c] released dev %i\n", (int)dev);
2017-09-01 15:33:10 +02:00
return 0;
}
2018-06-05 21:52:18 +02:00
int i2c_write_regs(i2c_t dev, uint16_t addr, uint16_t reg,
const void *data, size_t len, uint8_t flags)
2017-09-01 15:33:10 +02:00
{
2018-06-05 21:52:18 +02:00
assert((dev < I2C_NUMOF) && data && (len > 0) && (len < 255));
if (flags & (I2C_NOSTART | I2C_REG16 | I2C_ADDR10)) {
return -EOPNOTSUPP;
}
/* the nrf52's TWI device does not support to do two consecutive transfers
* without a repeated start condition in between. So we have to put all data
* to be transfered into a temporary buffer
*
* CAUTION: this might become critical when transferring large blocks of
* data as the temporary buffer is allocated on the stack... */
uint8_t buf_tmp[len + 1];
buf_tmp[0] = reg;
memcpy(&buf_tmp[1], data, len);
return i2c_write_bytes(dev, addr, buf_tmp, (len + 1), flags);
2017-09-01 15:33:10 +02:00
}
2018-06-05 21:52:18 +02:00
int i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len,
uint8_t flags)
2017-09-01 15:33:10 +02:00
{
2018-06-05 21:52:18 +02:00
assert((dev < I2C_NUMOF) && data && (len > 0) && (len < 256));
2017-09-01 15:33:10 +02:00
2018-06-05 21:52:18 +02:00
if (flags & (I2C_NOSTART | I2C_REG16 | I2C_ADDR10)) {
return -EOPNOTSUPP;
}
DEBUG("[i2c] read_bytes: %i bytes from addr 0x%02x\n", (int)len, (int)addr);
2017-09-01 15:33:10 +02:00
2018-06-05 21:52:18 +02:00
bus(dev)->ADDRESS = addr;
bus(dev)->RXD.PTR = (uint32_t)data;
bus(dev)->RXD.MAXCNT = (uint8_t)len;
2017-09-01 15:33:10 +02:00
2018-06-05 21:52:18 +02:00
if (!(flags & I2C_NOSTOP)) {
bus(dev)->SHORTS = TWIM_SHORTS_LASTRX_STOP_Msk;
}
/* Start transmission */
bus(dev)->TASKS_STARTRX = 1;
2017-09-01 15:33:10 +02:00
2018-06-05 21:52:18 +02:00
return finish(dev);
2017-09-01 15:33:10 +02:00
}
2018-06-05 21:52:18 +02:00
int i2c_read_regs(i2c_t dev, uint16_t addr, uint16_t reg,
void *data, size_t len, uint8_t flags)
2017-09-01 15:33:10 +02:00
{
2018-06-05 21:52:18 +02:00
assert((dev < I2C_NUMOF) && data && (len > 0) && (len < 256));
2018-06-05 21:52:18 +02:00
if (flags & (I2C_NOSTART | I2C_REG16 | I2C_ADDR10)) {
return -EOPNOTSUPP;
}
DEBUG("[i2c] read_regs: %i byte(s) from reg 0x%02x at addr 0x%02x\n",
(int)len, (int)reg, (int)addr);
2018-06-05 21:52:18 +02:00
bus(dev)->ADDRESS = addr;
bus(dev)->TXD.PTR = (uint32_t)&reg;
bus(dev)->TXD.MAXCNT = 1;
bus(dev)->RXD.PTR = (uint32_t)data;
bus(dev)->RXD.MAXCNT = (uint8_t)len;
bus(dev)->SHORTS = (TWIM_SHORTS_LASTTX_STARTRX_Msk);
if (!(flags & I2C_NOSTOP)) {
bus(dev)->SHORTS |= TWIM_SHORTS_LASTRX_STOP_Msk;
}
bus(dev)->TASKS_STARTTX = 1;
2017-09-01 15:33:10 +02:00
2018-06-05 21:52:18 +02:00
return finish(dev);
2017-09-01 15:33:10 +02:00
}
2018-06-05 21:52:18 +02:00
int i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_t len,
uint8_t flags)
2017-09-01 15:33:10 +02:00
{
2018-06-05 21:52:18 +02:00
assert((dev < I2C_NUMOF) && data && (len > 0) && (len < 256));
2018-06-05 21:52:18 +02:00
if (flags & (I2C_NOSTART | I2C_REG16 | I2C_ADDR10)) {
return -EOPNOTSUPP;
}
DEBUG("[i2c] write_bytes: %i byte(s) to addr 0x%02x\n", (int)len, (int)addr);
2018-06-05 21:52:18 +02:00
bus(dev)->ADDRESS = addr;
bus(dev)->TXD.PTR = (uint32_t)data;
bus(dev)->TXD.MAXCNT = (uint8_t)len;
if (!(flags & I2C_NOSTOP)) {
bus(dev)->SHORTS = TWIM_SHORTS_LASTTX_STOP_Msk;
}
bus(dev)->TASKS_STARTTX = 1;
2018-06-05 21:52:18 +02:00
return finish(dev);
2017-09-01 15:33:10 +02:00
}