Skip to content

Commit

Permalink
[I2C] Introduce computational I2C waveform configuration for HAL base…
Browse files Browse the repository at this point in the history
…d MCUs

Tested with
Nucleo-F722ZE I2C1 on PB8,PB9
Nucleo-H743ZI I2C1 on PB8,PB9 and I2C4 on PF14,PF15
  • Loading branch information
ezshinoda committed Mar 10, 2020
1 parent 0bb5c02 commit ec2aac3
Show file tree
Hide file tree
Showing 9 changed files with 384 additions and 236 deletions.
1 change: 1 addition & 0 deletions make/mcu/STM32F7.mk
Expand Up @@ -171,6 +171,7 @@ MCU_COMMON_SRC = \
drivers/adc_stm32f7xx.c \
drivers/audio_stm32f7xx.c \
drivers/bus_i2c_hal.c \
drivers/bus_i2c_hal_init.c \
drivers/dma_stm32f7xx.c \
drivers/light_ws2811strip_hal.c \
drivers/transponder_ir_io_hal.c \
Expand Down
1 change: 1 addition & 0 deletions make/mcu/STM32H7.mk
Expand Up @@ -242,6 +242,7 @@ MCU_COMMON_SRC = \
drivers/light_ws2811strip_hal.c \
drivers/adc_stm32h7xx.c \
drivers/bus_i2c_hal.c \
drivers/bus_i2c_hal_init.c \
drivers/pwm_output_dshot_hal.c \
drivers/pwm_output_dshot_shared.c \
drivers/persistent.c \
Expand Down
3 changes: 3 additions & 0 deletions make/source.mk
Expand Up @@ -359,6 +359,9 @@ SPEED_OPTIMISED_SRC := $(SPEED_OPTIMISED_SRC) \
drivers/pwm_output_dshot.c \
drivers/pwm_output_dshot_shared.c \
drivers/pwm_output_dshot_hal.c

SIZE_OPTIMISED_SRC := $(SIZE_OPTIMISED_SRC) \
drivers/bus_i2c_hal_init.c
endif #!F3
endif #!F1

Expand Down
241 changes: 5 additions & 236 deletions src/main/drivers/bus_i2c_hal.c
Expand Up @@ -35,122 +35,6 @@
#include "drivers/bus_i2c.h"
#include "drivers/bus_i2c_impl.h"

#define CLOCKSPEED 800000 // i2c clockspeed 400kHz default (conform specs), 800kHz and 1200kHz (Betaflight default)

// Number of bits in I2C protocol phase
#define LEN_ADDR 7
#define LEN_RW 1
#define LEN_ACK 1

// Clock period in us during unstick transfer
#define UNSTICK_CLK_US 10

// Allow 500us for clock strech to complete during unstick
#define UNSTICK_CLK_STRETCH (500/UNSTICK_CLK_US)

static void i2cUnstick(IO_t scl, IO_t sda);

#define IOCFG_I2C_PU IO_CONFIG(GPIO_MODE_AF_OD, GPIO_SPEED_FREQ_VERY_HIGH, GPIO_PULLUP)
#define IOCFG_I2C IO_CONFIG(GPIO_MODE_AF_OD, GPIO_SPEED_FREQ_VERY_HIGH, GPIO_NOPULL)

#define GPIO_AF4_I2C GPIO_AF4_I2C1

const i2cHardware_t i2cHardware[I2CDEV_COUNT] = {
#if defined(STM32F7)
#ifdef USE_I2C_DEVICE_1
{
.device = I2CDEV_1,
.reg = I2C1,
.sclPins = { I2CPINDEF(PB6), I2CPINDEF(PB8) },
.sdaPins = { I2CPINDEF(PB7), I2CPINDEF(PB9) },
.rcc = RCC_APB1(I2C1),
.ev_irq = I2C1_EV_IRQn,
.er_irq = I2C1_ER_IRQn,
},
#endif
#ifdef USE_I2C_DEVICE_2
{
.device = I2CDEV_2,
.reg = I2C2,
.sclPins = { I2CPINDEF(PB10), I2CPINDEF(PF1) },
.sdaPins = { I2CPINDEF(PB11), I2CPINDEF(PF0) },
.rcc = RCC_APB1(I2C2),
.ev_irq = I2C2_EV_IRQn,
.er_irq = I2C2_ER_IRQn,
},
#endif
#ifdef USE_I2C_DEVICE_3
{
.device = I2CDEV_3,
.reg = I2C3,
.sclPins = { I2CPINDEF(PA8) },
.sdaPins = { I2CPINDEF(PC9) },
.rcc = RCC_APB1(I2C3),
.ev_irq = I2C3_EV_IRQn,
.er_irq = I2C3_ER_IRQn,
},
#endif
#ifdef USE_I2C_DEVICE_4
{
.device = I2CDEV_4,
.reg = I2C4,
.sclPins = { I2CPINDEF(PD12), I2CPINDEF(PF14) },
.sdaPins = { I2CPINDEF(PD13), I2CPINDEF(PF15) },
.rcc = RCC_APB1(I2C4),
.ev_irq = I2C4_EV_IRQn,
.er_irq = I2C4_ER_IRQn,
},
#endif
#elif defined(STM32H7)
#ifdef USE_I2C_DEVICE_1
{
.device = I2CDEV_1,
.reg = I2C1,
.sclPins = { I2CPINDEF(PB6, GPIO_AF4_I2C1), I2CPINDEF(PB8, GPIO_AF4_I2C1) },
.sdaPins = { I2CPINDEF(PB7, GPIO_AF4_I2C1), I2CPINDEF(PB9, GPIO_AF4_I2C1) },
.rcc = RCC_APB1L(I2C1),
.ev_irq = I2C1_EV_IRQn,
.er_irq = I2C1_ER_IRQn,
},
#endif
#ifdef USE_I2C_DEVICE_2
{
.device = I2CDEV_2,
.reg = I2C2,
.sclPins = { I2CPINDEF(PB10, GPIO_AF4_I2C2), I2CPINDEF(PF1, GPIO_AF4_I2C2) },
.sdaPins = { I2CPINDEF(PB11, GPIO_AF4_I2C2), I2CPINDEF(PF0, GPIO_AF4_I2C2) },
.rcc = RCC_APB1L(I2C2),
.ev_irq = I2C2_EV_IRQn,
.er_irq = I2C2_ER_IRQn,
},
#endif
#ifdef USE_I2C_DEVICE_3
{
.device = I2CDEV_3,
.reg = I2C3,
.sclPins = { I2CPINDEF(PA8, GPIO_AF4_I2C3) },
.sdaPins = { I2CPINDEF(PC9, GPIO_AF4_I2C3) },
.rcc = RCC_APB1L(I2C3),
.ev_irq = I2C3_EV_IRQn,
.er_irq = I2C3_ER_IRQn,
},
#endif
#ifdef USE_I2C_DEVICE_4
{
.device = I2CDEV_4,
.reg = I2C4,
.sclPins = { I2CPINDEF(PD12, GPIO_AF4_I2C4), I2CPINDEF(PF14, GPIO_AF4_I2C4), I2CPINDEF(PB6, GPIO_AF6_I2C4), I2CPINDEF(PB8, GPIO_AF6_I2C4) },
.sdaPins = { I2CPINDEF(PD13, GPIO_AF4_I2C4), I2CPINDEF(PF15, GPIO_AF4_I2C4), I2CPINDEF(PB7, GPIO_AF6_I2C4), I2CPINDEF(PB9, GPIO_AF6_I2C4) },
.rcc = RCC_APB4(I2C4),
.ev_irq = I2C4_EV_IRQn,
.er_irq = I2C4_ER_IRQn,
},
#endif
#endif
};

i2cDevice_t i2cDevice[I2CDEV_COUNT];

#ifdef USE_I2C_DEVICE_1
void I2C1_ER_IRQHandler(void)
{
Expand Down Expand Up @@ -210,6 +94,11 @@ static bool i2cHandleHardwareFailure(I2CDevice device)
return false;
}

uint16_t i2cGetErrorCounter(void)
{
return i2cErrorCount;
}

// Blocking write
bool i2cWrite(I2CDevice device, uint8_t addr_, uint8_t reg_, uint8_t data)
{
Expand Down Expand Up @@ -341,124 +230,4 @@ bool i2cBusy(I2CDevice device, bool *error)
return true;
}

void i2cInit(I2CDevice device)
{
if (device == I2CINVALID) {
return;
}

i2cDevice_t *pDev = &i2cDevice[device];

const i2cHardware_t *hardware = pDev->hardware;

if (!hardware) {
return;
}

IO_t scl = pDev->scl;
IO_t sda = pDev->sda;

RCC_ClockCmd(hardware->rcc, ENABLE);

IOInit(scl, OWNER_I2C_SCL, RESOURCE_INDEX(device));
IOInit(sda, OWNER_I2C_SDA, RESOURCE_INDEX(device));

// Enable RCC
RCC_ClockCmd(hardware->rcc, ENABLE);

i2cUnstick(scl, sda);

// Init pins
#if defined(STM32F7)
IOConfigGPIOAF(scl, pDev->pullUp ? IOCFG_I2C_PU : IOCFG_I2C, GPIO_AF4_I2C);
IOConfigGPIOAF(sda, pDev->pullUp ? IOCFG_I2C_PU : IOCFG_I2C, GPIO_AF4_I2C);
#elif defined(STM32H7)
IOConfigGPIOAF(scl, pDev->pullUp ? IOCFG_I2C_PU : IOCFG_I2C, pDev->sclAF);
IOConfigGPIOAF(sda, pDev->pullUp ? IOCFG_I2C_PU : IOCFG_I2C, pDev->sdaAF);
#else
IOConfigGPIO(scl, IOCFG_AF_OD);
IOConfigGPIO(sda, IOCFG_AF_OD);
#endif

// Init I2C peripheral

I2C_HandleTypeDef *pHandle = &pDev->handle;

memset(pHandle, 0, sizeof(*pHandle));

pHandle->Instance = pDev->hardware->reg;

/// TODO: HAL check if I2C timing is correct

if (pDev->overClock) {
// 800khz Maximum speed tested on various boards without issues
pHandle->Init.Timing = 0x00500D1D;
} else {
pHandle->Init.Timing = 0x00500C6F;
}

pHandle->Init.OwnAddress1 = 0x0;
pHandle->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
pHandle->Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
pHandle->Init.OwnAddress2 = 0x0;
pHandle->Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
pHandle->Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;

HAL_I2C_Init(pHandle);

// Enable the Analog I2C Filter
HAL_I2CEx_ConfigAnalogFilter(pHandle, I2C_ANALOGFILTER_ENABLE);

// Setup interrupt handlers
HAL_NVIC_SetPriority(hardware->er_irq, NVIC_PRIORITY_BASE(NVIC_PRIO_I2C_ER), NVIC_PRIORITY_SUB(NVIC_PRIO_I2C_ER));
HAL_NVIC_EnableIRQ(hardware->er_irq);
HAL_NVIC_SetPriority(hardware->ev_irq, NVIC_PRIORITY_BASE(NVIC_PRIO_I2C_EV), NVIC_PRIORITY_SUB(NVIC_PRIO_I2C_EV));
HAL_NVIC_EnableIRQ(hardware->ev_irq);
}

uint16_t i2cGetErrorCounter(void)
{
return i2cErrorCount;
}

static void i2cUnstick(IO_t scl, IO_t sda)
{
int i;

IOHi(scl);
IOHi(sda);

IOConfigGPIO(scl, IOCFG_OUT_OD);
IOConfigGPIO(sda, IOCFG_OUT_OD);

// Clock out, with SDA high:
// 7 data bits
// 1 READ bit
// 1 cycle for the ACK
for (i = 0; i < (LEN_ADDR + LEN_RW + LEN_ACK); i++) {
// Wait for any clock stretching to finish
int timeout = UNSTICK_CLK_STRETCH;
while (!IORead(scl) && timeout) {
delayMicroseconds(UNSTICK_CLK_US);
timeout--;
}

// Pull low
IOLo(scl); // Set bus low
delayMicroseconds(UNSTICK_CLK_US/2);
IOHi(scl); // Set bus high
delayMicroseconds(UNSTICK_CLK_US/2);
}

// Generate a stop condition in case there was none
IOLo(scl);
delayMicroseconds(UNSTICK_CLK_US/2);
IOLo(sda);
delayMicroseconds(UNSTICK_CLK_US/2);

IOHi(scl); // Set bus scl high
delayMicroseconds(UNSTICK_CLK_US/2);
IOHi(sda); // Set bus sda high
}

#endif

0 comments on commit ec2aac3

Please sign in to comment.