CreatBotKlipper/src/stm32/stm32f0_i2c.c
Timofey Titovets a4aa2a9002 i2c: handle errors at i2ccmds
Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
2024-10-26 22:06:30 -04:00

237 lines
9.3 KiB
C

// I2C functions on stm32f0
//
// Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "board/misc.h" // timer_is_before
#include "command.h" // shutdown
#include "gpio.h" // i2c_setup
#include "internal.h" // GPIO
#include "sched.h" // sched_shutdown
#include "i2ccmds.h" // I2C_BUS_SUCCESS
struct i2c_info {
I2C_TypeDef *i2c;
uint8_t scl_pin, sda_pin, function;
};
#if CONFIG_MACH_STM32F0
DECL_ENUMERATION("i2c_bus", "i2c1_PB6_PB7", 0);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PB6_PB7", "PB6,PB7");
DECL_ENUMERATION("i2c_bus", "i2c1_PF1_PF0", 1);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PF1_PF0", "PF1,PF0");
DECL_ENUMERATION("i2c_bus", "i2c1_PB8_PB9", 2);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PB8_PB9", "PB8,PB9");
DECL_ENUMERATION("i2c_bus", "i2c1_PB8_PB7", 3);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PB8_PB7", "PB8,PB7");
// Deprecated "i2c1a" style mappings
DECL_ENUMERATION("i2c_bus", "i2c1", 0);
DECL_CONSTANT_STR("BUS_PINS_i2c1", "PB6,PB7");
DECL_ENUMERATION("i2c_bus", "i2c1a", 1);
DECL_CONSTANT_STR("BUS_PINS_i2c1a", "PF1,PF0");
#elif CONFIG_MACH_STM32F7
DECL_ENUMERATION("i2c_bus", "i2c1_PB6_PB7", 0);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PB6_PB7", "PB6,PB7");
#elif CONFIG_MACH_STM32G0
DECL_ENUMERATION("i2c_bus", "i2c1_PB6_PB7", 0);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PB6_PB7", "PB6,PB7");
DECL_ENUMERATION("i2c_bus", "i2c1_PB8_PB9", 1);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PB8_PB9", "PB8,PB9");
DECL_ENUMERATION("i2c_bus", "i2c1_PA9_PA10", 2);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PA9_PA10", "PA9,PA10");
DECL_ENUMERATION("i2c_bus", "i2c2_PB10_PB11", 3);
DECL_CONSTANT_STR("BUS_PINS_i2c2_PB10_PB11", "PB10,PB11");
DECL_ENUMERATION("i2c_bus", "i2c2_PB13_PB14", 4);
DECL_CONSTANT_STR("BUS_PINS_i2c2_PB13_PB14", "PB13,PB14");
#ifdef I2C3
DECL_ENUMERATION("i2c_bus", "i2c3_PB3_PB4", 5);
DECL_CONSTANT_STR("BUS_PINS_i2c3_PB3_PB4", "PB3,PB4");
DECL_ENUMERATION("i2c_bus", "i2c3_PC0_PC1", 6);
DECL_CONSTANT_STR("BUS_PINS_i2c3_PC0_PC1", "PC0,PC1");
#endif
#elif CONFIG_MACH_STM32L4
DECL_ENUMERATION("i2c_bus", "i2c1_PB6_PB7", 0);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PB6_PB7", "PB6,PB7");
DECL_ENUMERATION("i2c_bus", "i2c1_PB8_PB9", 1);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PB8_PB9", "PB8,PB9");
DECL_ENUMERATION("i2c_bus", "i2c1_PA9_PA10", 2);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PA9_PA10", "PA9,PA10");
DECL_ENUMERATION("i2c_bus", "i2c2_PB10_PB11", 3);
DECL_CONSTANT_STR("BUS_PINS_i2c2_PB10_PB11", "PB10,PB11");
DECL_ENUMERATION("i2c_bus", "i2c2_PB13_PB14", 4);
DECL_CONSTANT_STR("BUS_PINS_i2c2_PB13_PB14", "PB13,PB14");
DECL_ENUMERATION("i2c_bus", "i2c3_PA7_PB4", 5);
DECL_CONSTANT_STR("BUS_PINS_i2c3_PA7_PB4", "PA7,PB4");
#elif CONFIG_MACH_STM32G4
DECL_ENUMERATION("i2c_bus", "i2c1_PA13_PA14", 0);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PA13_PA14", "PA13,PA14");
DECL_ENUMERATION("i2c_bus", "i2c1_PA15_PA14", 1);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PA15_PA14", "PA15,PA14");
DECL_ENUMERATION("i2c_bus", "i2c1_PB8_PB7", 2);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PB8_PB7", "PB8,PB7");
DECL_ENUMERATION("i2c_bus", "i2c1_PB8_PB9", 3);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PB8_PB9", "PB8,PB9");
DECL_ENUMERATION("i2c_bus", "i2c2_PA9_PA8", 4);
DECL_CONSTANT_STR("BUS_PINS_i2c2_PA9_PA8", "PA9,PA8");
DECL_ENUMERATION("i2c_bus", "i2c2_PC4_PF0", 5);
DECL_CONSTANT_STR("BUS_PINS_i2c2_PC4_PF0", "PC4,PF0");
DECL_ENUMERATION("i2c_bus", "i2c3_PC8_PC9", 6);
DECL_CONSTANT_STR("BUS_PINS_i2c3_PC8_PC9", "PC8,PC9");
DECL_ENUMERATION("i2c_bus", "i2c3_PC8_PC11", 7);
DECL_CONSTANT_STR("BUS_PINS_i2c3_PC8_PC11", "PC8,PC11");
#elif CONFIG_MACH_STM32H7
DECL_ENUMERATION("i2c_bus", "i2c1_PB6_PB7", 0);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PB6_PB7", "PB6,PB7");
DECL_ENUMERATION("i2c_bus", "i2c1_PB8_PB9", 1);
DECL_CONSTANT_STR("BUS_PINS_i2c1_PB8_PB9", "PB8,PB9");
DECL_ENUMERATION("i2c_bus", "i2c2_PB10_PB11", 2);
DECL_CONSTANT_STR("BUS_PINS_i2c2_PB10_PB11", "PB10,PB11");
DECL_ENUMERATION("i2c_bus", "i2c3_PA8_PC9", 3);
DECL_CONSTANT_STR("BUS_PINS_i2c3_PA8_PC9", "PA8,PC9");
#endif
static const struct i2c_info i2c_bus[] = {
#if CONFIG_MACH_STM32F0
{ I2C1, GPIO('B', 6), GPIO('B', 7), GPIO_FUNCTION(1) },
{ I2C1, GPIO('F', 1), GPIO('F', 0), GPIO_FUNCTION(1) },
{ I2C1, GPIO('B', 8), GPIO('B', 9), GPIO_FUNCTION(1) },
{ I2C1, GPIO('B', 8), GPIO('B', 7), GPIO_FUNCTION(1) },
#elif CONFIG_MACH_STM32F7
{ I2C1, GPIO('B', 6), GPIO('B', 7), GPIO_FUNCTION(1) },
#elif CONFIG_MACH_STM32G0
{ I2C1, GPIO('B', 6), GPIO('B', 7), GPIO_FUNCTION(6) },
{ I2C1, GPIO('B', 8), GPIO('B', 9), GPIO_FUNCTION(6) },
{ I2C1, GPIO('A', 9), GPIO('A', 10), GPIO_FUNCTION(6) },
{ I2C2, GPIO('B', 10), GPIO('B', 11), GPIO_FUNCTION(6) },
{ I2C2, GPIO('B', 13), GPIO('B', 14), GPIO_FUNCTION(6) },
#ifdef I2C3
{ I2C3, GPIO('B', 3), GPIO('B', 4), GPIO_FUNCTION(6) },
{ I2C3, GPIO('C', 0), GPIO('C', 1), GPIO_FUNCTION(6) },
#endif
#elif CONFIG_MACH_STM32L4
{ I2C1, GPIO('B', 6), GPIO('B', 7), GPIO_FUNCTION(4) },
{ I2C1, GPIO('B', 8), GPIO('B', 9), GPIO_FUNCTION(4) },
{ I2C1, GPIO('A', 9), GPIO('A', 10), GPIO_FUNCTION(4) },
{ I2C2, GPIO('B', 10), GPIO('B', 11), GPIO_FUNCTION(4) },
{ I2C2, GPIO('B', 13), GPIO('B', 14), GPIO_FUNCTION(4) },
{ I2C3, GPIO('A', 7), GPIO('B', 4), GPIO_FUNCTION(4) },
#elif CONFIG_MACH_STM32G4
{ I2C1, GPIO('A', 13), GPIO('A', 14), GPIO_FUNCTION(4) },
{ I2C1, GPIO('A', 15), GPIO('A', 14), GPIO_FUNCTION(4) },
{ I2C1, GPIO('B', 8), GPIO('B', 7), GPIO_FUNCTION(4) },
{ I2C1, GPIO('B', 8), GPIO('B', 9), GPIO_FUNCTION(4) },
{ I2C2, GPIO('A', 9), GPIO('A', 8), GPIO_FUNCTION(4) },
{ I2C2, GPIO('C', 4), GPIO('F', 0), GPIO_FUNCTION(4) },
{ I2C3, GPIO('C', 8), GPIO('C', 9), GPIO_FUNCTION(8) },
{ I2C3, GPIO('C', 8), GPIO('C', 11), GPIO_FUNCTION(8) },
#elif CONFIG_MACH_STM32H7
{ I2C1, GPIO('B', 6), GPIO('B', 7), GPIO_FUNCTION(4) },
{ I2C1, GPIO('B', 8), GPIO('B', 9), GPIO_FUNCTION(4) },
{ I2C2, GPIO('B', 10), GPIO('B', 11), GPIO_FUNCTION(4) },
{ I2C3, GPIO('A', 8), GPIO('C', 9), GPIO_FUNCTION(4) },
#endif
};
struct i2c_config
i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr)
{
// Lookup requested i2c bus
if (bus >= ARRAY_SIZE(i2c_bus))
shutdown("Unsupported i2c bus");
const struct i2c_info *ii = &i2c_bus[bus];
I2C_TypeDef *i2c = ii->i2c;
if (!is_enabled_pclock((uint32_t)i2c)) {
// Enable i2c clock and gpio
enable_pclock((uint32_t)i2c);
gpio_peripheral(ii->scl_pin, ii->function | GPIO_OPEN_DRAIN, 1);
gpio_peripheral(ii->sda_pin, ii->function | GPIO_OPEN_DRAIN, 1);
// Set 100Khz frequency and enable
uint32_t nom_i2c_clock = 8000000; // 8mhz internal clock = 125ns ticks
uint32_t scll = 40; // 40 * 125ns = 5us
uint32_t sclh = 32; // 32 * 125ns = 4us
uint32_t sdadel = 4; // 4 * 125ns = 500ns
uint32_t scldel = 10; // 10 * 125ns = 1250ns
// Clamp the rate to 400Khz
if (rate >= 400000) {
scll = 10; // 10 * 125ns = 1250ns
sclh = 4; // 4 * 125 = 500ns
sdadel = 3; // 3 * 125 = 375ns
scldel = 4; // 4 * 125 = 500ns
}
uint32_t pclk = get_pclock_frequency((uint32_t)i2c);
uint32_t presc = DIV_ROUND_UP(pclk, nom_i2c_clock);
i2c->TIMINGR = (((presc - 1) << I2C_TIMINGR_PRESC_Pos)
| ((scll - 1) << I2C_TIMINGR_SCLL_Pos)
| ((sclh - 1) << I2C_TIMINGR_SCLH_Pos)
| (sdadel << I2C_TIMINGR_SDADEL_Pos)
| ((scldel - 1) << I2C_TIMINGR_SCLDEL_Pos));
i2c->CR1 = I2C_CR1_PE;
}
return (struct i2c_config){ .i2c=i2c, .addr=addr<<1 };
}
static uint32_t
i2c_wait(I2C_TypeDef *i2c, uint32_t set, uint32_t timeout)
{
for (;;) {
uint32_t isr = i2c->ISR;
if (isr & set)
return isr;
if (isr & I2C_ISR_NACKF)
shutdown("I2C NACK error encountered");
if (!timer_is_before(timer_read_time(), timeout))
shutdown("i2c timeout");
}
}
int
i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write)
{
I2C_TypeDef *i2c = config.i2c;
uint32_t timeout = timer_read_time() + timer_from_us(5000);
// Send start and address
i2c->CR2 = (I2C_CR2_START | config.addr | (write_len << I2C_CR2_NBYTES_Pos)
| I2C_CR2_AUTOEND);
while (write_len--) {
i2c_wait(i2c, I2C_ISR_TXIS, timeout);
i2c->TXDR = *write++;
}
i2c_wait(i2c, I2C_ISR_TXE, timeout);
return I2C_BUS_SUCCESS;
}
int
i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg
, uint8_t read_len, uint8_t *read)
{
I2C_TypeDef *i2c = config.i2c;
uint32_t timeout = timer_read_time() + timer_from_us(5000);
// Send start, address, reg
i2c->CR2 = (I2C_CR2_START | config.addr |
(reg_len << I2C_CR2_NBYTES_Pos));
while (reg_len--) {
i2c_wait(i2c, I2C_ISR_TXIS, timeout);
i2c->TXDR = *reg++;
}
i2c_wait(i2c, I2C_ISR_TC, timeout);
// send restart, read data
i2c->CR2 = (I2C_CR2_START | I2C_CR2_RD_WRN | config.addr |
(read_len << I2C_CR2_NBYTES_Pos) | I2C_CR2_AUTOEND);
while (read_len--) {
i2c_wait(i2c, I2C_ISR_RXNE, timeout);
*read++ = i2c->RXDR;
}
i2c_wait(i2c, I2C_ISR_STOPF, timeout);
return I2C_BUS_SUCCESS;
}