229 lines
5.7 KiB
C
229 lines
5.7 KiB
C
/*
|
|
* Copyright (c) 2006-2020, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2020-04-27 hqfang first implementation.
|
|
*/
|
|
|
|
#include "drv_i2c.h"
|
|
|
|
#ifdef RT_USING_I2C
|
|
|
|
#if !defined(BSP_USING_I2C0) && !defined(BSP_USING_I2C1)
|
|
#error "Please define at least one BSP_USING_I2Cx"
|
|
/* this driver can be disabled at menuconfig -> Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable I2C */
|
|
#endif
|
|
|
|
static struct gd32_i2c_config i2c_config[] =
|
|
{
|
|
#ifdef BSP_USING_I2C0
|
|
{
|
|
"i2c0",
|
|
I2C0,
|
|
100000,
|
|
},
|
|
#endif
|
|
#ifdef BSP_USING_I2C1
|
|
{
|
|
"i2c1",
|
|
I2C1,
|
|
100000,
|
|
},
|
|
#endif
|
|
};
|
|
|
|
static struct gd32_i2c i2c_obj[sizeof(i2c_config) / sizeof(i2c_config[0])] = {0};
|
|
|
|
#define GD32_I2C_TIMEOUT 10
|
|
|
|
static int gd32_i2c_read(rt_uint32_t i2c_periph, rt_uint16_t slave_address, rt_uint8_t *p_buffer, rt_uint16_t cnt)
|
|
{
|
|
/* send slave address to I2C bus */
|
|
i2c_master_addressing(i2c_periph, slave_address << 1, I2C_RECEIVER);
|
|
|
|
/* wait until ADDSEND bit is set */
|
|
while (!i2c_flag_get(i2c_periph, I2C_FLAG_ADDSEND));
|
|
|
|
/* clear the ADDSEND bit */
|
|
i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND);
|
|
|
|
/* while there is data to be read */
|
|
while (cnt)
|
|
{
|
|
if (cnt == 1)
|
|
{
|
|
// Send NACK for last 1 byte receive
|
|
i2c_ack_config(i2c_periph, I2C_ACK_DISABLE);
|
|
}
|
|
/* wait until the RBNE bit is set */
|
|
while (i2c_flag_get(i2c_periph, I2C_FLAG_RBNE) == RESET);
|
|
|
|
/* read a byte from i2c */
|
|
*p_buffer = i2c_data_receive(i2c_periph);
|
|
|
|
/* point to the next location where the byte read will be saved */
|
|
p_buffer++;
|
|
|
|
/* decrement the read bytes counter */
|
|
cnt--;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gd32_i2c_write(rt_uint32_t i2c_periph, uint16_t slave_address, uint8_t *p_buffer, uint16_t cnt)
|
|
{
|
|
/* send slave address to I2C bus */
|
|
i2c_master_addressing(i2c_periph, slave_address << 1, I2C_TRANSMITTER);
|
|
|
|
/* wait until ADDSEND bit is set */
|
|
while (!i2c_flag_get(i2c_periph, I2C_FLAG_ADDSEND));
|
|
|
|
/* clear the ADDSEND bit */
|
|
i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND);
|
|
|
|
/* wait until the transmit data buffer is empty */
|
|
while (SET != i2c_flag_get(i2c_periph, I2C_FLAG_TBE));
|
|
|
|
/* while there is data to be read */
|
|
while (cnt)
|
|
{
|
|
i2c_data_transmit(i2c_periph, *p_buffer);
|
|
|
|
/* point to the next byte to be written */
|
|
p_buffer++;
|
|
|
|
/* decrement the write bytes counter */
|
|
cnt--;
|
|
|
|
/* wait until BTC bit is set */
|
|
while (!i2c_flag_get(i2c_periph, I2C_FLAG_BTC));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gd32_i2c_configure(struct gd32_i2c_config *i2c_cfg)
|
|
{
|
|
RT_ASSERT(i2c_cfg != RT_NULL);
|
|
|
|
/* configure i2c speed to 100Khz */
|
|
i2c_clock_config(i2c_cfg->i2c_periph, i2c_cfg->speed, I2C_DTCY_2);
|
|
/* enable I2C */
|
|
i2c_enable(i2c_cfg->i2c_periph);
|
|
/* enable acknowledge */
|
|
i2c_ack_config(i2c_cfg->i2c_periph, I2C_ACK_ENABLE);
|
|
}
|
|
|
|
|
|
static rt_ssize_t gd32_i2c_xfer(struct rt_i2c_bus_device *device, struct rt_i2c_msg msgs[], rt_uint32_t num)
|
|
{
|
|
struct rt_i2c_msg *msg;
|
|
rt_uint32_t i;
|
|
rt_err_t ret = RT_ERROR;
|
|
rt_uint16_t last_flags;
|
|
|
|
RT_ASSERT(device != RT_NULL);
|
|
|
|
struct gd32_i2c *i2c_obj = (struct gd32_i2c *)(device);
|
|
struct gd32_i2c_config *i2c_cfg = (struct gd32_i2c_config *)(i2c_obj->config);
|
|
RT_ASSERT(i2c_cfg != RT_NULL);
|
|
|
|
/* wait until I2C bus is idle */
|
|
while (i2c_flag_get(i2c_cfg->i2c_periph, I2C_FLAG_I2CBSY));
|
|
|
|
if (num)
|
|
{
|
|
if (msg[0].flags & RT_I2C_ADDR_10BIT)
|
|
{
|
|
i2c_mode_addr_config(i2c_cfg->i2c_periph, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_10BITS, 0x82);
|
|
}
|
|
else
|
|
{
|
|
i2c_mode_addr_config(i2c_cfg->i2c_periph, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x82);
|
|
}
|
|
}
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
msg = &msgs[i];
|
|
|
|
if (!(msg->flags & RT_I2C_NO_START))
|
|
{
|
|
/* send a start condition to I2C bus */
|
|
i2c_start_on_bus(i2c_cfg->i2c_periph);
|
|
/* wait until SBSEND bit is set */
|
|
while (!i2c_flag_get(i2c_cfg->i2c_periph, I2C_FLAG_SBSEND));
|
|
}
|
|
|
|
if (msg->flags & RT_I2C_RD)
|
|
{
|
|
gd32_i2c_read(i2c_cfg->i2c_periph, msg->addr, msg->buf, msg->len);
|
|
}
|
|
else
|
|
{
|
|
gd32_i2c_write(i2c_cfg->i2c_periph, msg->addr, msg->buf, msg->len);
|
|
}
|
|
}
|
|
|
|
if (num)
|
|
{
|
|
/* send a stop condition to I2C bus */
|
|
i2c_stop_on_bus(i2c_cfg->i2c_periph);
|
|
|
|
/* wait until the stop condition is finished */
|
|
while (I2C_CTL0(i2c_cfg->i2c_periph) & I2C_CTL0_STOP);
|
|
}
|
|
i2c_ack_config(i2c_cfg->i2c_periph, I2C_ACK_ENABLE);
|
|
|
|
ret = i;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct rt_i2c_bus_device_ops i2c_ops =
|
|
{
|
|
gd32_i2c_xfer,
|
|
RT_NULL,
|
|
RT_NULL
|
|
};
|
|
|
|
int rt_hw_i2c_init(void)
|
|
{
|
|
rt_size_t obj_num;
|
|
int index;
|
|
rt_err_t result = 0;
|
|
|
|
#ifdef BSP_USING_I2C0
|
|
rcu_periph_clock_enable(RCU_I2C0);
|
|
#endif
|
|
#ifdef BSP_USING_I2C1
|
|
rcu_periph_clock_enable(RCU_I2C1);
|
|
#endif
|
|
|
|
obj_num = sizeof(i2c_obj) / sizeof(struct gd32_i2c);
|
|
for (index = 0; index < obj_num; index++)
|
|
{
|
|
/* init i2c object */
|
|
i2c_obj[index].config = &i2c_config[index];
|
|
i2c_obj[index].bus.ops = &i2c_ops;
|
|
|
|
/* init i2c device */
|
|
gd32_i2c_configure(&i2c_config[index]);
|
|
|
|
/* register i2c device */
|
|
result = rt_i2c_bus_device_register(&i2c_obj[index].bus,
|
|
i2c_obj[index].config->name
|
|
);
|
|
RT_ASSERT(result == RT_EOK);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
INIT_DEVICE_EXPORT(rt_hw_i2c_init);
|
|
|
|
#endif
|
|
/* end of i2c driver */
|