bleSDK_expansion_board/drivers/src/i2c.c

287 lines
7.3 KiB
C

/**
****************************************************************************************
*
* @file i2c.c
*
* @brief IIC(I2C) Driver
*
****************************************************************************************
*/
#include "i2c.h"
#include "rcc.h"
#include "iopad.h"
#include "reg_i2c.h"
/*
* DEFINES
****************************************************************************************
*/
// Must clear SI before setting STA and then STA must be manually cleared.
#define I2C_START() \
dowl( \
I2C->CONCLR.Word = I2C_CR_IFLG_BIT; \
I2C->CONSET.Word = I2C_CR_START_BIT; \
while ((I2C->CONSET.Word & I2C_CR_IFLG_BIT) == 0); \
I2C->CONCLR.Word = I2C_CR_START_BIT; \
)
// Must set STO before clearing SI.
#define I2C_STOP() \
dowl( \
I2C->CONSET.Word = I2C_CR_STOP_BIT; \
I2C->CONCLR.Word = I2C_CR_IFLG_BIT; \
while ((I2C->CONSET.Word & I2C_CR_STOP_BIT) != 0); \
)
// Stop clock-stretching and then read when it arrives.
#define I2C_GETC(_X_) \
dowl( \
I2C->CONCLR.Word = I2C_CR_IFLG_BIT; \
while ((I2C->CONSET.Word & I2C_CR_IFLG_BIT) == 0); \
(_X_) = I2C->DATA.Word; \
)
// First write new data and then stop clock-stretching.
#define I2C_PUTC(_X_) \
dowl( \
I2C->DATA.Word = (_X_); \
I2C->CONCLR.Word = I2C_CR_IFLG_BIT; \
while ((I2C->CONSET.Word & I2C_CR_IFLG_BIT) == 0); \
)
/*
* FUNCTION DEFINITIONS
****************************************************************************************
*/
void i2c_init(uint8_t io_scl, uint8_t io_sda, uint8_t sclk)
{
// i2c_clk_en rst_req or soft_reset
RCC_APBCLK_EN(APB_I2C_BIT);
//RCC_APBRST_REQ(APB_I2C_BIT);
I2C->SRST = 0x07;
// iocsc_i2c(io_scl, io_sda);
csc_output(io_scl, CSC_I2C_SCL);
csc_input(io_scl, CSC_I2C_SCL);
csc_output(io_sda, CSC_I2C_SDA);
csc_input(io_sda, CSC_I2C_SDA);
iom_ctrl(io_scl, IOM_SEL_CSC | IOM_DRV_LVL1 | IOM_PULLUP | IOM_INPUT | IOM_OPENDRAIN);
iom_ctrl(io_sda, IOM_SEL_CSC | IOM_DRV_LVL1 | IOM_PULLUP | IOM_INPUT | IOM_OPENDRAIN);
// SCL clock = PCLK/(2^clk_samp * (clk_scl+1) *10)
I2C->CLK.Word = sclk;
I2C_ENABLE();
}
void i2c_deinit(void)
{
I2C_DISABLE();
RCC_APBCLK_DIS(1 << (RCC_I2C_CLKEN_RUN_POS));
}
uint8_t i2c_start(uint8_t slv_addr)
{
I2C_START();
// A start condition has been transmitted
if ((I2C_STAT_GET() == I2C_MS_START_OK) || (I2C_STAT_GET() == I2C_MS_RESTART_OK))
{
I2C_PUTC(slv_addr);
}
return I2C_STAT_GET();
}
void i2c_stop(void)
{
I2C_STOP();
}
uint16_t i2c_send(uint16_t len, const uint8_t *data)
{
uint16_t cnt;
for (cnt = 0; cnt < len; cnt++)
{
I2C_PUTC(*data++);
if (I2C_STAT_GET() != I2C_MS_SEND_DAT_ACK)
{
if (I2C_STAT_GET() == I2C_MS_SEND_DAT_NAK)
{
cnt += 1; // last byte
}
break;
}
}
return cnt;
}
uint16_t i2c_recv(uint16_t len, uint8_t *buff)
{
uint16_t cnt;
// All bytes are ACK'd except for the last one which is NACK'd. If only
// 1 byte is being read, a single NACK will be sent. Thus, we only want
// to enable ACK if more than 1 byte is going to be read.
if (len > 1)
{
I2C_SET_ACK();
}
for (cnt = 0; cnt < len; cnt++)
{
// slave devices require NACK to be sent after reading last byte
if (cnt + 1 == len)
{
I2C_SET_NAK();
}
// read a byte from the I2C interface
I2C_GETC(*buff++);
if (I2C_STAT_GET() != I2C_MS_READ_DAT_ACK)
{
if (I2C_STAT_GET() == I2C_MS_READ_DAT_NAK)
{
cnt += 1; // count last byte
}
break;
}
}
return cnt;
}
uint16_t i2c_read(uint8_t addr_r, uint16_t len, uint8_t *buff)
{
uint16_t cnt = 0;
if (i2c_start(addr_r) == I2C_MS_ADDR_R_ACK)
{
cnt = i2c_recv(len, buff);
}
i2c_stop();
return cnt;
}
uint16_t i2c_write(uint8_t addr_w, uint16_t len, const uint8_t *data)
{
uint16_t cnt = 0;
if (i2c_start(addr_w) == I2C_MS_ADDR_W_ACK)
{
cnt = i2c_send(len, data);
}
i2c_stop();
return cnt;
}
uint16_t eeprom_write(uint8_t eep, uint16_t addr, uint16_t len, const uint8_t *data)
{
uint16_t cnt = 0;
if (i2c_start(eep) == I2C_MS_ADDR_W_ACK)
{
#if (OPTM_FAST)
do {
I2C_PUTC((addr >> 8) & 0xff); // address High byte
if (I2C_STAT_GET() != I2C_MS_SEND_DAT_ACK) break;
I2C_PUTC(addr & 0xff); // address High byte
if (I2C_STAT_GET() != I2C_MS_SEND_DAT_ACK) break;
for (; cnt < len; cnt++)
{
I2C_PUTC(*data++);
if (I2C_STAT_GET() != I2C_MS_SEND_DAT_ACK)
{
if (I2C_STAT_GET() == I2C_MS_SEND_DAT_NAK)
{
cnt += 1; // last byte
}
break;
}
}
} while (0);
#else
addr = (addr >> 8) | (addr << 8); // send address in MSB
if (i2c_send(2, (uint8_t *)&addr) == 2)
{
cnt = i2c_send(len, data);
}
#endif
}
i2c_stop();
return cnt;
}
uint16_t eeprom_read(uint8_t eep, uint16_t addr, uint16_t len, uint8_t *buff)
{
uint16_t cnt = 0;
if (i2c_start(eep) == I2C_MS_ADDR_W_ACK)
{
#if (OPTM_FAST)
do {
I2C_PUTC((addr >> 8) & 0xff); // address High byte
if (I2C_STAT_GET() != I2C_MS_SEND_DAT_ACK) break;
I2C_PUTC(addr & 0xff); // address High byte
if (I2C_STAT_GET() != I2C_MS_SEND_DAT_ACK) break;
if (i2c_start(eep | I2C_RD_OP) != I2C_MS_ADDR_R_ACK) break;
if (len > 1)
{
I2C_SET_ACK();
}
for (; cnt < len; cnt++)
{
// slave devices require NACK to be sent after reading last byte
if (cnt + 1 == len)
{
I2C_SET_NAK();
}
// read a byte from the I2C interface
I2C_GETC(buff[cnt]);
if (I2C_STAT_GET() != I2C_MS_READ_DAT_ACK)
{
if (I2C_STAT_GET() == I2C_MS_READ_DAT_NAK)
{
cnt += 1; // count last byte
}
break;
}
}
} while (0);
#else
addr = (addr >> 8) | (addr << 8); // send address in MSB
if (i2c_send(2, (uint8_t *)&addr) == 2)
{
if (i2c_start(eep | I2C_RD_OP) == I2C_MS_ADDR_R_ACK)
{
cnt = i2c_recv(len, buff);
}
}
#endif
}
i2c_stop();
return cnt;
}