/*
* This file is part of Betaflight.
*
* Betaflight is free software. You can redistribute this software
* and/or modify this software under the terms of the GNU General
* Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later
* version.
*
* Betaflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this software.
*
* If not, see .
*/
#include
#include
#include
#include "platform.h"
#if defined(USE_I2C) && !defined(SOFT_I2C)
#include "hardware/i2c.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "drivers/bus_i2c.h"
#include "drivers/bus_i2c_impl.h"
#include "drivers/io.h"
#include "drivers/io_impl.h"
#define I2C_TX_BUFFER_LENGTH 32
static volatile uint16_t i2cErrorCount = 0;
i2cDevice_t i2cDevice[I2CDEV_COUNT];
const i2cHardware_t i2cHardware[I2CDEV_COUNT] = {
#ifdef USE_I2C_DEVICE_0
{
.device = I2CDEV_0,
.reg = I2C0,
.sclPins = {
{ DEFIO_TAG_E(PA1) },
{ DEFIO_TAG_E(PA5) },
{ DEFIO_TAG_E(PA9) },
{ DEFIO_TAG_E(PA13) },
},
.sdaPins = {
{ DEFIO_TAG_E(PA0) },
{ DEFIO_TAG_E(PA4) },
{ DEFIO_TAG_E(PA8) },
{ DEFIO_TAG_E(PA12) },
},
},
#endif
#ifdef USE_I2C_DEVICE_1
{
.device = I2CDEV_1,
.reg = I2C1,
.sclPins = {
{ DEFIO_TAG_E(PA3) },
{ DEFIO_TAG_E(PA7) },
{ DEFIO_TAG_E(PA11) },
{ DEFIO_TAG_E(PA15) },
},
.sdaPins = {
{ DEFIO_TAG_E(PA2) },
{ DEFIO_TAG_E(PA6) },
{ DEFIO_TAG_E(PA10) },
{ DEFIO_TAG_E(PA14) },
}
},
#endif
};
static bool i2cHandleHardwareFailure(I2CDevice device)
{
UNUSED(device);
i2cErrorCount++;
return false;
}
uint16_t i2cGetErrorCounter(void)
{
return i2cErrorCount;
}
bool i2cWrite(I2CDevice device, uint8_t addr_, uint8_t reg_, uint8_t data)
{
return i2cWriteBuffer(device, addr_, reg_, 1, &data);
}
bool i2cWriteBuffer(I2CDevice device, uint8_t addr_, uint8_t reg_, uint8_t len_, uint8_t *data)
{
// TODO: Implement non-blocking write using DMA or similar mechanism
if (device == I2CINVALID || device >= I2CDEV_COUNT) {
return false;
}
i2c_inst_t *port = I2C_INST(&i2cHardware[device].reg);
if (!port) {
return false;
}
if (len_ > I2C_TX_BUFFER_LENGTH - 1) {
return false; // Buffer too long
}
uint8_t buf[I2C_TX_BUFFER_LENGTH] = { reg_, 0 };
memcpy(&buf[1], data, len_);
int status = i2c_write_timeout_us(port, addr_ << 1, buf, len_ + 1, true, I2C_TIMEOUT_US);
if (status < 0) {
return i2cHandleHardwareFailure(device);
}
return true;
}
bool i2cRead(I2CDevice device, uint8_t addr_, uint8_t reg_, uint8_t len, uint8_t* buf)
{
if (device == I2CINVALID || device >= I2CDEV_COUNT) {
return false;
}
i2c_inst_t *port = I2C_INST(&i2cHardware[device].reg);
if (!port) {
return false;
}
int status = i2c_write_timeout_us(port, addr_ << 1, ®_, 1, true, I2C_TIMEOUT_US);
if (status < 0) {
return i2cHandleHardwareFailure(device);
}
status = i2c_read_timeout_us(port, addr_ << 1, buf, len, true, I2C_TIMEOUT_US);
if (status < 0) {
return i2cHandleHardwareFailure(device);
}
return true;
}
bool i2cReadBuffer(I2CDevice device, uint8_t addr_, uint8_t reg_, uint8_t len, uint8_t* buf)
{
// TODO: Implement genuine non-blocking read using DMA or similar mechanism
return i2cRead(device, addr_, reg_, len, buf);
}
bool i2cBusy(I2CDevice device, bool *error)
{
if (device == I2CINVALID || device >= I2CDEV_COUNT) {
return false;
}
i2c_inst_t *port = I2C_INST(&i2cHardware[device].reg);
if (!port) {
return false;
}
if (error) {
*error = 0;
}
// Read the IC_STATUS register
uint32_t status_reg = port->hw->status;
// The bit for MST_ACTIVITY is (1 << 5).
return (status_reg & (1 << 5)) != 0;
}
void i2cInit(I2CDevice device)
{
if (device == I2CINVALID) {
return;
}
i2cDevice_t *pDev = &i2cDevice[device];
const i2cHardware_t *hardware = pDev->hardware;
const IO_t scl = pDev->scl;
const IO_t sda = pDev->sda;
if (!hardware || IOGetOwner(scl) || IOGetOwner(sda)) {
return;
}
i2c_init(I2C_INST(hardware->reg), pDev->clockSpeed);
// Set up GPIO pins for I2C
gpio_set_function(IO_Pin(sda), GPIO_FUNC_I2C);
gpio_set_function(IO_Pin(scl), GPIO_FUNC_I2C);
// Enable internal pull-up resistors
gpio_pull_up(IO_Pin(sda));
gpio_pull_up(IO_Pin(scl));
}
#endif