Files
lishanpi/drv/i2c/i2c_driver.cpp
T
hm fc10ef2e10 Initial commit: LSPi (LiangShanPi) project
GD32F470ZGT6 based project with full peripheral support:
- SDRAM (W9825G6KH) via EXMC
- LCD NT35510 (480x800) via EXMC NOR/PSRAM
- Flash (W25Q64) via SPI
- UART (USART0) debug console
- LED indicators
- CMSIS-DAP debug interface
- CMake + ARM GCC toolchain build system
2026-04-26 16:24:42 +08:00

264 lines
6.6 KiB
C++

#include "i2c_driver.h"
#include "gd32f4xx.h"
#include <cstddef>
#define I2C_TIMEOUT 50000
struct i2c_hw_config_t {
uint32_t i2c_periph;
rcu_periph_enum rcu_clock;
uint32_t gpio_port;
uint32_t scl_pin;
uint32_t sda_pin;
uint32_t af;
rcu_periph_enum gpio_rcu;
};
static constexpr i2c_hw_config_t i2c_hw_map[] = {
{I2C0, RCU_I2C0, GPIOB, GPIO_PIN_8, GPIO_PIN_9, GPIO_AF_4, RCU_GPIOB},
{I2C1, RCU_I2C1, GPIOB, GPIO_PIN_10, GPIO_PIN_11, GPIO_AF_4, RCU_GPIOB},
{I2C2, RCU_I2C2, GPIOC, GPIO_PIN_4, GPIO_PIN_5, GPIO_AF_4, RCU_GPIOC},
};
static RetCode wait_flag(uint32_t i2c_periph, i2c_flag_enum flag)
{
uint32_t timeout = I2C_TIMEOUT;
while (RESET == i2c_flag_get(i2c_periph, flag)) {
if (--timeout == 0) {
return RET_ERROR;
}
}
return RET_OK;
}
extern "C" {
static I2cBus i2c_0(I2cPort::_0);
static I2cBus i2c_1(I2cPort::_1);
static I2cBus i2c_2(I2cPort::_2);
static I2cBus *i2c_instances[] = { &i2c_0, &i2c_1, &i2c_2 };
static uint8_t i2c_inited[3] = {0};
int i2c_init(uint8_t port, uint32_t speed_hz)
{
if (port >= 3) return -1;
I2cConfig cfg;
cfg.speed_hz = speed_hz;
RetCode ret = i2c_instances[port]->init(cfg);
i2c_inited[port] = (ret == RET_OK) ? 1 : 0;
return i2c_inited[port] ? 0 : -1;
}
int i2c_master_write(uint8_t port, uint16_t dev_addr, const uint8_t *data, uint32_t len)
{
if (port >= 3 || !i2c_inited[port]) return -1;
I2cDevice dev(dev_addr);
return i2c_instances[port]->master_write(dev, data, len) == RET_OK ? 0 : -1;
}
int i2c_master_read(uint8_t port, uint16_t dev_addr, uint8_t *data, uint32_t len)
{
if (port >= 3 || !i2c_inited[port]) return -1;
I2cDevice dev(dev_addr);
return i2c_instances[port]->master_read(dev, data, len) == RET_OK ? 0 : -1;
}
int i2c_master_write_read(uint8_t port, uint16_t dev_addr, const uint8_t *wdata, uint32_t wlen, uint8_t *rdata, uint32_t rlen)
{
if (port >= 3 || !i2c_inited[port]) return -1;
I2cDevice dev(dev_addr);
return i2c_instances[port]->master_write_read(dev, wdata, wlen, rdata, rlen) == RET_OK ? 0 : -1;
}
} /* extern "C" */
I2cDevice::I2cDevice(uint16_t dev_addr)
: dev_addr_(dev_addr)
{
}
RetCode I2cDevice::init()
{
return RET_OK;
}
I2cBus::I2cBus(I2cPort port)
: port_(port)
{
}
I2cBus::~I2cBus()
{
if (initialized_) {
deinit();
}
}
RetCode I2cBus::init(const I2cConfig &config)
{
uint8_t port_idx = static_cast<uint8_t>(port_);
if (port_idx >= sizeof(i2c_hw_map) / sizeof(i2c_hw_map[0])) {
return RET_INVALID_PARAM;
}
const i2c_hw_config_t &hw = i2c_hw_map[port_idx];
if (hw.i2c_periph == 0) {
return RET_NOT_SUPPORTED;
}
config_ = config;
rcu_periph_clock_enable(hw.rcu_clock);
rcu_periph_clock_enable(hw.gpio_rcu);
gpio_af_set(hw.gpio_port, hw.af, hw.scl_pin | hw.sda_pin);
gpio_mode_set(hw.gpio_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, hw.scl_pin | hw.sda_pin);
gpio_output_options_set(hw.gpio_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, hw.scl_pin | hw.sda_pin);
i2c_deinit(hw.i2c_periph);
uint32_t speed = config.speed_hz;
if (speed == 0) {
speed = 100000;
}
i2c_clock_config(hw.i2c_periph, speed, I2C_DTCY_2);
i2c_mode_addr_config(hw.i2c_periph, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x00);
i2c_enable(hw.i2c_periph);
i2c_ack_config(hw.i2c_periph, I2C_ACK_ENABLE);
initialized_ = true;
return RET_OK;
}
RetCode I2cBus::deinit()
{
uint8_t port_idx = static_cast<uint8_t>(port_);
if (port_idx >= sizeof(i2c_hw_map) / sizeof(i2c_hw_map[0])) {
return RET_INVALID_PARAM;
}
const i2c_hw_config_t &hw = i2c_hw_map[port_idx];
i2c_deinit(hw.i2c_periph);
rcu_periph_clock_disable(hw.rcu_clock);
initialized_ = false;
return RET_OK;
}
static RetCode i2c_master_start(uint32_t i2c_periph, uint16_t addr, uint32_t direction)
{
uint32_t timeout = I2C_TIMEOUT;
while (i2c_flag_get(i2c_periph, I2C_FLAG_I2CBSY)) {
if (--timeout == 0) {
return RET_TIMEOUT;
}
}
i2c_start_on_bus(i2c_periph);
if (wait_flag(i2c_periph, I2C_FLAG_SBSEND) != RET_OK) {
return RET_TIMEOUT;
}
i2c_master_addressing(i2c_periph, addr, direction);
if (wait_flag(i2c_periph, I2C_FLAG_ADDSEND) != RET_OK) {
return RET_TIMEOUT;
}
i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND);
return RET_OK;
}
RetCode I2cBus::master_write(I2cDevice &dev, const uint8_t *data, uint32_t len)
{
if (data == nullptr || len == 0) {
return RET_INVALID_PARAM;
}
if (!initialized_) return RET_NOT_INITIALIZED;
uint8_t port_idx = static_cast<uint8_t>(port_);
const i2c_hw_config_t &hw = i2c_hw_map[port_idx];
uint32_t i2c = hw.i2c_periph;
RetCode ret = i2c_master_start(i2c, dev.dev_addr_, I2C_TRANSMITTER);
if (ret != RET_OK) {
i2c_stop_on_bus(i2c);
return ret;
}
for (uint32_t i = 0; i < len; i++) {
if (wait_flag(i2c, I2C_FLAG_TBE) != RET_OK) {
i2c_stop_on_bus(i2c);
return RET_TIMEOUT;
}
i2c_data_transmit(i2c, data[i]);
}
if (wait_flag(i2c, I2C_FLAG_TBE) != RET_OK) {
i2c_stop_on_bus(i2c);
return RET_TIMEOUT;
}
i2c_stop_on_bus(i2c);
return RET_OK;
}
RetCode I2cBus::master_read(I2cDevice &dev, uint8_t *data, uint32_t len)
{
if (data == nullptr || len == 0) {
return RET_INVALID_PARAM;
}
if (!initialized_) return RET_NOT_INITIALIZED;
uint8_t port_idx = static_cast<uint8_t>(port_);
const i2c_hw_config_t &hw = i2c_hw_map[port_idx];
uint32_t i2c = hw.i2c_periph;
if (len == 1) {
i2c_ack_config(i2c, I2C_ACK_DISABLE);
} else {
i2c_ack_config(i2c, I2C_ACK_ENABLE);
}
RetCode ret = i2c_master_start(i2c, dev.dev_addr_, I2C_RECEIVER);
if (ret != RET_OK) {
i2c_stop_on_bus(i2c);
return ret;
}
for (uint32_t i = 0; i < len; i++) {
if (i == len - 1) {
i2c_ack_config(i2c, I2C_ACK_DISABLE);
i2c_stop_on_bus(i2c);
}
if (wait_flag(i2c, I2C_FLAG_RBNE) != RET_OK) {
i2c_stop_on_bus(i2c);
return RET_TIMEOUT;
}
data[i] = i2c_data_receive(i2c);
}
return RET_OK;
}
RetCode I2cBus::master_write_read(I2cDevice &dev, const uint8_t *wdata, uint32_t wlen, uint8_t *rdata, uint32_t rlen)
{
if (wlen > 0) {
RetCode ret = master_write(dev, wdata, wlen);
if (ret != RET_OK) {
return ret;
}
}
if (rlen > 0) {
RetCode ret = master_read(dev, rdata, rlen);
if (ret != RET_OK) {
return ret;
}
}
return RET_OK;
}