1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/stm32f2/periph/i2c.c
Toon Stegen 71dce7584f stm32f2: i2c: update driver
Remove unneeded workaround from stm32f1
Make pullup configurable
Fix OAR1 register initialization
improve i2c read functions with repeatead start conditions
avoid to any loop to become infinite
improve i2c driver error handling
add missing coma in array initializers
2016-04-22 14:03:21 +02:00

731 lines
18 KiB
C

/*
* Copyright (C) 2014 FU Berlin
*
* 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.
*/
/**
* @addtogroup driver_periph
* @{
*
* @file
* @brief Low-level I2C driver implementation
*
* @note This implementation only implements the 7-bit addressing mode.
*
* @author Toon Stegen <toon.stegen@altran.com>
* @author Vincent Dupont <vincent@otakeys.com>
*
* @}
*/
#include <stdint.h>
#include "cpu.h"
#include "irq.h"
#include "mutex.h"
#include "periph_conf.h"
#include "periph/i2c.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
/* guard file in case no I2C device is defined */
#if I2C_NUMOF
#define I2C_MAX_LOOP_CNT 10000
/* static function definitions */
static int _read_bytes(I2C_TypeDef *i2c, uint8_t address, char *data, int length, char *err);
static void _i2c_init(I2C_TypeDef *i2c, int ccr);
static void _pin_config(GPIO_TypeDef *port_scl, GPIO_TypeDef *port_sda, int pin_scl, int pin_sda);
static int _start(I2C_TypeDef *dev, uint8_t address, uint8_t rw_flag, char *err);
static inline void _clear_addr(I2C_TypeDef *dev);
static inline int _write(I2C_TypeDef *dev, char *data, int length, char *err);
static inline int _stop(I2C_TypeDef *dev, char *err);
static inline int _wait_ready(I2C_TypeDef *dev);
/**
* @brief Array holding one pre-initialized mutex for each I2C device
*/
static mutex_t locks[] = {
#if I2C_0_EN
[I2C_0] = MUTEX_INIT,
#endif
#if I2C_1_EN
[I2C_1] = MUTEX_INIT,
#endif
#if I2C_2_EN
[I2C_2] = MUTEX_INIT,
#endif
#if I2C_3_EN
[I2C_3] = MUTEX_INIT
#endif
};
static char err_flag[] = {
#if I2C_0_EN
[I2C_0] = 0x00,
#endif
#if I2C_1_EN
[I2C_1] = 0x00,
#endif
#if I2C_2_EN
[I2C_2] = 0x00,
#endif
#if I2C_3_EN
[I2C_3] = 0x00
#endif
};
int i2c_init_master(i2c_t dev, i2c_speed_t speed)
{
I2C_TypeDef *i2c;
GPIO_TypeDef *port_scl;
GPIO_TypeDef *port_sda;
int pin_scl = 0, pin_sda = 0;
int ccr;
/* read speed configuration */
switch (speed) {
case I2C_SPEED_NORMAL:
ccr = I2C_APBCLK / 200000;
break;
case I2C_SPEED_FAST:
ccr = I2C_APBCLK / 800000;
break;
default:
return -2;
}
/* read static device configuration */
switch (dev) {
#if I2C_0_EN
case I2C_0:
i2c = I2C_0_DEV;
port_scl = I2C_0_SCL_PORT;
pin_scl = I2C_0_SCL_PIN;
port_sda = I2C_0_SDA_PORT;
pin_sda = I2C_0_SDA_PIN;
I2C_0_CLKEN();
I2C_0_SCL_CLKEN();
I2C_0_SDA_CLKEN();
NVIC_SetPriority(I2C_0_ERR_IRQ, I2C_IRQ_PRIO);
NVIC_EnableIRQ(I2C_0_ERR_IRQ);
break;
#endif
default:
return -1;
}
/* configure pins */
_pin_config(port_scl, port_sda, pin_scl, pin_sda);
/* configure device */
_i2c_init(i2c, ccr);
return 0;
}
static void _i2c_init(I2C_TypeDef *i2c, int ccr)
{
/* disable device and set ACK bit */
i2c->CR1 = I2C_CR1_ACK;
/* configure I2C clock */
i2c->CR2 = (I2C_APBCLK / 1000000) | I2C_CR2_ITERREN;
i2c->CCR = ccr;
i2c->TRISE = (I2C_APBCLK / 1000000) + 1;
/* configure device */
i2c->OAR1 |= (1 << 14); /* datasheet: bit 14 should be kept 1 */
i2c->OAR1 &= ~I2C_OAR1_ADDMODE; /* make sure we are in 7-bit address mode */
/* enable device */
i2c->CR1 |= I2C_CR1_PE;
}
static void _pin_config(GPIO_TypeDef *port_scl, GPIO_TypeDef *port_sda, int pin_scl, int pin_sda)
{
/* Set GPIOs to AF mode */
port_scl->MODER &= ~(3 << (2 * pin_scl));
port_scl->MODER |= (2 << (2 * pin_scl));
port_sda->MODER &= ~(3 << (2 * pin_sda));
port_sda->MODER |= (2 << (2 * pin_sda));
/* Set speed high*/
port_scl->OSPEEDR |= (3 << (2 * pin_scl));
port_sda->OSPEEDR |= (3 << (2 * pin_sda));
/* Set to push-pull configuration open drain*/
port_scl->OTYPER |= (1 << pin_scl);
port_sda->OTYPER |= (1 << pin_sda);
/* Enable pull-up resistors */
port_scl->PUPDR &= ~(3 << (2 * pin_scl));
port_sda->PUPDR &= ~(3 << (2 * pin_sda));
if (I2C_0_SCL_PULLUP) {
port_scl->PUPDR |= (1 << (2 * pin_scl));
}
if (I2C_0_SDA_PULLUP) {
port_sda->PUPDR |= (1 << (2 * pin_sda));
}
/* Configure GPIOs to for the I2C alternate function */
if (pin_scl < 8) {
port_scl->AFR[0] &= ~(0xf << (4 * pin_scl));
port_scl->AFR[0] |= (I2C_0_SCL_AF << (4 * pin_scl));
}
else {
port_scl->AFR[1] &= ~(0xf << (4 * (pin_scl - 8)));
port_scl->AFR[1] |= (I2C_0_SCL_AF << (4 * (pin_scl - 8)));
}
if (pin_sda < 8) {
port_sda->AFR[0] &= ~(0xf << (4 * pin_sda));
port_sda->AFR[0] |= (I2C_0_SDA_AF << (4 * pin_sda));
}
else {
port_sda->AFR[1] &= ~(0xf << (4 * (pin_sda - 8)));
port_sda->AFR[1] |= (I2C_0_SDA_AF << (4 * (pin_sda - 8)));
}
}
int i2c_acquire(i2c_t dev)
{
if (dev >= I2C_NUMOF) {
return -1;
}
mutex_lock(&locks[dev]);
return 0;
}
int i2c_release(i2c_t dev)
{
if (dev >= I2C_NUMOF) {
return -1;
}
mutex_unlock(&locks[dev]);
return 0;
}
int i2c_read_byte(i2c_t dev, uint8_t address, char *data)
{
return i2c_read_bytes(dev, address, data, 1);
}
int i2c_read_bytes(i2c_t dev, uint8_t address, char *data, int length)
{
I2C_TypeDef *i2c;
switch (dev) {
#if I2C_0_EN
case I2C_0:
i2c = I2C_0_DEV;
break;
#endif
default:
return -1;
}
int res = _wait_ready(i2c);
if (res != 0) {
return res;
}
return _read_bytes(i2c, address, data, length, &err_flag[dev]);
}
static inline int _wait_ready(I2C_TypeDef *dev)
{
/* wait for device to be ready */
DEBUG("Wait for device to be ready\n");
int cnt = 0;
while ((dev->SR2 & I2C_SR2_BUSY) && cnt++ < I2C_MAX_LOOP_CNT) {}
if (cnt == I2C_MAX_LOOP_CNT) {
return -3;
}
return 0;
}
static int _read_bytes(I2C_TypeDef *i2c, uint8_t address, char *data, int length, char *err)
{
unsigned int state;
int i = 0;
int cnt = 0;
int res;
switch (length) {
case 1:
DEBUG("Send Slave address and wait for ADDR == 1\n");
res = _start(i2c, address, I2C_FLAG_READ, err);
if (res != 0) {
return res;
}
if (*err) {
return -3;
}
DEBUG("Set ACK = 0\n");
i2c->CR1 &= ~(I2C_CR1_ACK);
DEBUG("Clear ADDR and set STOP = 1\n");
state = irq_disable();
_clear_addr(i2c);
i2c->CR1 |= (I2C_CR1_STOP);
irq_restore(state);
DEBUG("Wait for RXNE == 1\n");
cnt = 0;
*err = 0;
while (!(i2c->SR1 & I2C_SR1_RXNE) && cnt++ < I2C_MAX_LOOP_CNT && !(*err)) {}
if (cnt == I2C_MAX_LOOP_CNT || *err) {
return -3;
}
DEBUG("Read received data\n");
*data = (char)i2c->DR;
/* wait until STOP is cleared by hardware */
cnt = 0;
*err = 0;
while ((i2c->CR1 & I2C_CR1_STOP) && cnt++ < I2C_MAX_LOOP_CNT && !(*err)) {}
if (cnt == I2C_MAX_LOOP_CNT) {
return -3;
}
/* reset ACK to be able to receive new data */
i2c->CR1 |= (I2C_CR1_ACK);
break;
case 2:
DEBUG("Send Slave address and wait for ADDR == 1\n");
res = _start(i2c, address, I2C_FLAG_READ, err);
if (res != 0) {
return res;
}
if (*err) {
return -3;
}
DEBUG("Set POS bit\n");
i2c->CR1 |= (I2C_CR1_POS | I2C_CR1_ACK);
DEBUG("Crit block: Clear ADDR bit and clear ACK flag\n");
state = irq_disable();
_clear_addr(i2c);
i2c->CR1 &= ~(I2C_CR1_ACK);
irq_restore(state);
DEBUG("Wait for transfer to be completed\n");
cnt = 0;
*err = 0;
while (!(i2c->SR1 & I2C_SR1_BTF) && cnt++ < I2C_MAX_LOOP_CNT && !(*err)) {}
if (cnt == I2C_MAX_LOOP_CNT || *err) {
return -3;
}
DEBUG("Crit block: set STOP and read first byte\n");
state = irq_disable();
i2c->CR1 |= (I2C_CR1_STOP);
data[0] = (char)i2c->DR;
irq_restore(state);
DEBUG("read second byte\n");
data[1] = (char)i2c->DR;
DEBUG("wait for STOP bit to be cleared again\n");
cnt = 0;
*err = 0;
while ((i2c->CR1 & I2C_CR1_STOP) && cnt++ < I2C_MAX_LOOP_CNT && !(*err)) {}
if (cnt == I2C_MAX_LOOP_CNT || *err) {
return -3;
}
DEBUG("reset POS = 0 and ACK = 1\n");
i2c->CR1 &= ~(I2C_CR1_POS);
i2c->CR1 |= (I2C_CR1_ACK);
break;
default:
DEBUG("Send Slave address and wait for ADDR == 1\n");
res = _start(i2c, address, I2C_FLAG_READ, err);
if (res != 0) {
return res;
}
_clear_addr(i2c);
while (i < (length - 3)) {
DEBUG("Wait until byte was received\n");
cnt = 0;
*err = 0;
while (!(i2c->SR1 & I2C_SR1_RXNE) && cnt++ < I2C_MAX_LOOP_CNT && !(*err)) {}
if (cnt == I2C_MAX_LOOP_CNT || *err) {
return -3;
}
DEBUG("Copy byte from DR\n");
data[i++] = (char)i2c->DR;
}
DEBUG("Reading the last 3 bytes, waiting for BTF flag\n");
cnt = 0;
*err = 0;
while (!(i2c->SR1 & I2C_SR1_BTF) && cnt++ < I2C_MAX_LOOP_CNT && !(*err));
if (cnt == I2C_MAX_LOOP_CNT || *err) {
return -3;
}
DEBUG("Disable ACK\n");
i2c->CR1 &= ~(I2C_CR1_ACK);
DEBUG("Crit block: set STOP and read N-2 byte\n");
state = irq_disable();
data[i++] = (char)i2c->DR;
i2c->CR1 |= (I2C_CR1_STOP);
irq_restore(state);
DEBUG("Read N-1 byte\n");
data[i++] = (char)i2c->DR;
cnt = 0;
*err = 0;
while (!(i2c->SR1 & I2C_SR1_RXNE) && cnt++ < I2C_MAX_LOOP_CNT && !(*err)) {}
if (cnt == I2C_MAX_LOOP_CNT || *err) {
return -3;
}
DEBUG("Read last byte\n");
data[i++] = (char)i2c->DR;
DEBUG("wait for STOP bit to be cleared again\n");
cnt = 0;
*err = 0;
while ((i2c->CR1 & I2C_CR1_STOP) && cnt++ < I2C_MAX_LOOP_CNT && !(*err)) {}
if (cnt == I2C_MAX_LOOP_CNT || *err) {
return -3;
}
DEBUG("reset POS = 0 and ACK = 1\n");
i2c->CR1 &= ~(I2C_CR1_POS);
i2c->CR1 |= (I2C_CR1_ACK);
}
return length;
}
int i2c_read_reg(i2c_t dev, uint8_t address, uint8_t reg, char *data)
{
return i2c_read_regs(dev, address, reg, data, 1);
}
int i2c_read_regs(i2c_t dev, uint8_t address, uint8_t reg, char *data, int length)
{
I2C_TypeDef *i2c;
int res;
switch (dev) {
#if I2C_0_EN
case I2C_0:
i2c = I2C_0_DEV;
break;
#endif
default:
return -1;
}
/* send start condition and slave address */
DEBUG("Send slave address and clear ADDR flag\n");
res = _wait_ready(i2c);
if (res != 0) {
return res;
}
res = _start(i2c, address, I2C_FLAG_WRITE, &err_flag[dev]);
if (res != 0) {
return res;
}
if (err_flag[dev]) {
return -3;
}
_clear_addr(i2c);
DEBUG("Write reg into DR\n");
i2c->DR = reg;
DEBUG("Now start a read transaction\n");
return _read_bytes(i2c, address, data, length, &err_flag[dev]);
}
int i2c_write_byte(i2c_t dev, uint8_t address, char data)
{
return i2c_write_bytes(dev, address, &data, 1);
}
int i2c_write_bytes(i2c_t dev, uint8_t address, char *data, int length)
{
I2C_TypeDef *i2c;
int res;
switch (dev) {
#if I2C_0_EN
case I2C_0:
i2c = I2C_0_DEV;
break;
#endif
default:
return -1;
}
/* start transmission and send slave address */
DEBUG("sending start sequence\n");
res = _wait_ready(i2c);
if (res != 0) {
return res;
}
res = _start(i2c, address, I2C_FLAG_WRITE, &err_flag[dev]);
if (res != 0) {
return res;
}
if (err_flag[dev]) {
return -3;
}
_clear_addr(i2c);
/* send out data bytes */
res = _write(i2c, data, length, &err_flag[dev]);
if (res != 0) {
return res;
}
if (err_flag[dev]) {
return -3;
}
/* end transmission */
DEBUG("Ending transmission\n");
res = _stop(i2c, &err_flag[dev]);
if (res != 0) {
return res;
}
if (err_flag[dev]) {
return -3;
}
DEBUG("STOP condition was send out\n");
return length;
}
int i2c_write_reg(i2c_t dev, uint8_t address, uint8_t reg, char data)
{
return i2c_write_regs(dev, address, reg, &data, 1);
}
int i2c_write_regs(i2c_t dev, uint8_t address, uint8_t reg, char *data, int length)
{
I2C_TypeDef *i2c;
int res;
switch (dev) {
#if I2C_0_EN
case I2C_0:
i2c = I2C_0_DEV;
break;
#endif
default:
return -1;
}
/* start transmission and send slave address */
res = _wait_ready(i2c);
if (res != 0) {
return res;
}
res = _start(i2c, address, I2C_FLAG_WRITE, &err_flag[dev]);
if (res != 0) {
return res;
}
if (err_flag[dev]) {
return -3;
}
_clear_addr(i2c);
/* send register address and wait for complete transfer to be finished*/
res = _write(i2c, (char *)(&reg), 1, &err_flag[dev]);
if (res != 0) {
return res;
}
if (err_flag[dev]) {
return -3;
}
/* write data to register */
res = _write(i2c, data, length, &err_flag[dev]);
if (res != 0) {
return res;
}
if (err_flag[dev]) {
return -3;
}
/* finish transfer */
res = _stop(i2c, &err_flag[dev]);
if (res != 0) {
return res;
}
if (err_flag[dev]) {
return -3;
}
/* return number of bytes send */
return length;
}
void i2c_poweron(i2c_t dev)
{
switch (dev) {
#if I2C_0_EN
case I2C_0:
I2C_0_CLKEN();
break;
#endif
}
}
void i2c_poweroff(i2c_t dev)
{
int cnt = 0;
switch (dev) {
#if I2C_0_EN
case I2C_0:
while ((I2C_0_DEV->SR2 & I2C_SR2_BUSY) && cnt++ < I2C_MAX_LOOP_CNT) {}
I2C_0_CLKDIS();
break;
#endif
}
}
static int _start(I2C_TypeDef *dev, uint8_t address, uint8_t rw_flag, char *err)
{
int cnt = 0;
*err = 0;
/* generate start condition */
DEBUG("Generate start condition\n");
dev->CR1 |= I2C_CR1_START;
DEBUG("Wait for SB flag to be set\n");
while (!(dev->SR1 & I2C_SR1_SB) && cnt++ < I2C_MAX_LOOP_CNT && !(*err));
if (cnt == I2C_MAX_LOOP_CNT || *err) {
return -3;
}
/* send address and read/write flag */
DEBUG("Send address\n");
dev->DR = (address << 1) | rw_flag;
/* clear ADDR flag by reading first SR1 and then SR2 */
DEBUG("Wait for ADDR flag to be set\n");
cnt = 0;
*err = 0;
while (!(dev->SR1 & I2C_SR1_ADDR) && cnt++ < I2C_MAX_LOOP_CNT && !(*err)) {}
if (cnt == I2C_MAX_LOOP_CNT) {
return -3;
}
return 0;
}
static inline void _clear_addr(I2C_TypeDef *dev)
{
dev->SR1;
dev->SR2;
DEBUG("Cleared address\n");
}
static inline int _write(I2C_TypeDef *dev, char *data, int length, char *err)
{
DEBUG("Looping through bytes\n");
for (int i = 0; i < length; i++) {
/* write data to data register */
dev->DR = (uint8_t)data[i];
DEBUG("Written %i byte to data reg, now waiting for DR to be empty again\n", i);
/* wait for transfer to finish */
int cnt = 0;
*err = 0;
while (!(dev->SR1 & I2C_SR1_TXE) && cnt++ < I2C_MAX_LOOP_CNT && !(*err)) {}
if (cnt == I2C_MAX_LOOP_CNT || *err) {
return -3;
}
DEBUG("DR is now empty again\n");
}
return 0;
}
static inline int _stop(I2C_TypeDef *dev, char *err)
{
/* make sure last byte was send */
DEBUG("Wait if last byte hasn't been sent\n");
int cnt = 0;
*err = 0;
while (!(dev->SR1 & I2C_SR1_BTF) && cnt++ < I2C_MAX_LOOP_CNT && !(*err)) {}
if (cnt == I2C_MAX_LOOP_CNT) {
return -3;
}
/* send STOP condition */
dev->CR1 |= I2C_CR1_STOP;
return 0;
}
static inline void i2c_irq_handler(i2c_t i2c_dev, I2C_TypeDef *dev)
{
unsigned volatile state = dev->SR1;
/* record and clear errors */
err_flag[i2c_dev] = (state >> 8);
dev->SR1 &= 0x00ff;
DEBUG("\n\n### I2C %d ERROR OCCURED ###\n", i2c_dev);
DEBUG("status: %08x\n", state);
if (state & I2C_SR1_OVR) {
DEBUG("OVR\n");
}
if (state & I2C_SR1_AF) {
DEBUG("AF\n");
}
if (state & I2C_SR1_ARLO) {
DEBUG("ARLO\n");
}
if (state & I2C_SR1_BERR) {
DEBUG("BERR\n");
}
if (state & I2C_SR1_PECERR) {
DEBUG("PECERR\n");
}
if (state & I2C_SR1_TIMEOUT) {
DEBUG("TIMEOUT\n");
}
if (state & I2C_SR1_SMBALERT) {
DEBUG("SMBALERT\n");
}
}
#if I2C_0_EN
void I2C_0_ERR_ISR(void)
{
i2c_irq_handler(I2C_0, I2C_0_DEV);
}
#endif
#endif /* I2C_NUMOF */