From 090fcf928fcbd36fa6e4e90fd6c52967ad5d420d Mon Sep 17 00:00:00 2001 From: Lasse Dalegaard Date: Sun, 12 Dec 2021 12:13:13 +0100 Subject: [PATCH 1/2] rp2040: implement I2C This implements I2C for the rp2040 target. All output groupings of both I2C blocks are available for use. Signed-off-by: Lasse Dalegaard --- src/rp2040/Kconfig | 1 + src/rp2040/Makefile | 1 + src/rp2040/gpio.h | 10 +++ src/rp2040/i2c.c | 215 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 227 insertions(+) create mode 100644 src/rp2040/i2c.c diff --git a/src/rp2040/Kconfig b/src/rp2040/Kconfig index b509bc04b844..c2a45e553269 100644 --- a/src/rp2040/Kconfig +++ b/src/rp2040/Kconfig @@ -8,6 +8,7 @@ config RP2040_SELECT select HAVE_GPIO select HAVE_GPIO_ADC select HAVE_GPIO_SPI + select HAVE_GPIO_I2C select HAVE_GPIO_BITBANGING select HAVE_STRICT_TIMING select HAVE_CHIPID diff --git a/src/rp2040/Makefile b/src/rp2040/Makefile index 2915b994b972..fbeccb971050 100644 --- a/src/rp2040/Makefile +++ b/src/rp2040/Makefile @@ -21,6 +21,7 @@ src-$(CONFIG_USBSERIAL) += rp2040/chipid.c src-$(CONFIG_SERIAL) += rp2040/serial.c generic/serial_irq.c src-$(CONFIG_HAVE_GPIO_HARD_PWM) += rp2040/hard_pwm.c src-$(CONFIG_HAVE_GPIO_SPI) += rp2040/spi.c +src-$(CONFIG_HAVE_GPIO_I2C) += rp2040/i2c.c # rp2040 stage2 building $(OUT)stage2.o: lib/rp2040/boot_stage2/boot2_w25q080.S diff --git a/src/rp2040/gpio.h b/src/rp2040/gpio.h index 90680c6e4be0..ae6083780ce9 100644 --- a/src/rp2040/gpio.h +++ b/src/rp2040/gpio.h @@ -44,4 +44,14 @@ void spi_prepare(struct spi_config config); void spi_transfer(struct spi_config config, uint8_t receive_data , uint8_t len, uint8_t *data); +struct i2c_config { + void *i2c; + uint8_t addr; +}; + +struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr); +void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write); +void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg + , uint8_t read_len, uint8_t *read); + #endif // gpio.h diff --git a/src/rp2040/i2c.c b/src/rp2040/i2c.c new file mode 100644 index 000000000000..954de0e579ee --- /dev/null +++ b/src/rp2040/i2c.c @@ -0,0 +1,215 @@ +// I2C functions for rp2040 +// +// Copyright (C) 2022 Lasse Dalegaard +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "board/misc.h" // timer_is_before +#include "gpio.h" // i2c_setup, i2c_read, i2c_write +#include "command.h" // shutdown +#include "sched.h" // sched_shutdown +#include "internal.h" // pclock, gpio_peripheral +#include "hardware/regs/resets.h" // RESETS_RESET_I2C*_BITS +#include "hardware/structs/i2c.h" + +struct i2c_info { + i2c_hw_t *i2c; + uint8_t sda_pin, scl_pin, pclk; +}; + +DECL_ENUMERATION("i2c_bus", "i2c0a", 0); +DECL_ENUMERATION("i2c_bus", "i2c0b", 1); +DECL_ENUMERATION("i2c_bus", "i2c0c", 2); +DECL_ENUMERATION("i2c_bus", "i2c0d", 3); +DECL_ENUMERATION("i2c_bus", "i2c0e", 4); +DECL_ENUMERATION("i2c_bus", "i2c0f", 5); +DECL_ENUMERATION("i2c_bus", "i2c0g", 6); +DECL_ENUMERATION("i2c_bus", "i2c0h", 7); +DECL_ENUMERATION("i2c_bus", "i2c1a", 8); +DECL_ENUMERATION("i2c_bus", "i2c1b", 9); +DECL_ENUMERATION("i2c_bus", "i2c1c", 10); +DECL_ENUMERATION("i2c_bus", "i2c1d", 11); +DECL_ENUMERATION("i2c_bus", "i2c1e", 12); +DECL_ENUMERATION("i2c_bus", "i2c1f", 13); +DECL_ENUMERATION("i2c_bus", "i2c1g", 14); +DECL_CONSTANT_STR("BUS_PINS_i2c0a", "gpio0,gpio1"); +DECL_CONSTANT_STR("BUS_PINS_i2c0b", "gpio4,gpio5"); +DECL_CONSTANT_STR("BUS_PINS_i2c0c", "gpio8,gpio9"); +DECL_CONSTANT_STR("BUS_PINS_i2c0d", "gpio12,gpio13"); +DECL_CONSTANT_STR("BUS_PINS_i2c0e", "gpio16,gpio17"); +DECL_CONSTANT_STR("BUS_PINS_i2c0f", "gpio20,gpio21"); +DECL_CONSTANT_STR("BUS_PINS_i2c0g", "gpio24,gpio25"); +DECL_CONSTANT_STR("BUS_PINS_i2c0h", "gpio28,gpio29"); +DECL_CONSTANT_STR("BUS_PINS_i2c1a", "gpio2,gpio3"); +DECL_CONSTANT_STR("BUS_PINS_i2c1b", "gpio6,gpio7"); +DECL_CONSTANT_STR("BUS_PINS_i2c1c", "gpio10,gpio11"); +DECL_CONSTANT_STR("BUS_PINS_i2c1d", "gpio14,gpio15"); +DECL_CONSTANT_STR("BUS_PINS_i2c1e", "gpio18,gpio19"); +DECL_CONSTANT_STR("BUS_PINS_i2c1f", "gpio22,gpio23"); +DECL_CONSTANT_STR("BUS_PINS_i2c1g", "gpio26,gpio27"); + +static const struct i2c_info i2c_bus[] = { + { i2c0_hw, 0, 1, RESETS_RESET_I2C0_BITS }, + { i2c0_hw, 4, 5, RESETS_RESET_I2C0_BITS }, + { i2c0_hw, 8, 9, RESETS_RESET_I2C0_BITS }, + { i2c0_hw, 12, 13, RESETS_RESET_I2C0_BITS }, + { i2c0_hw, 16, 17, RESETS_RESET_I2C0_BITS }, + { i2c0_hw, 20, 21, RESETS_RESET_I2C0_BITS }, + { i2c0_hw, 24, 25, RESETS_RESET_I2C0_BITS }, + { i2c0_hw, 28, 29, RESETS_RESET_I2C0_BITS }, + + { i2c1_hw, 2, 3, RESETS_RESET_I2C1_BITS }, + { i2c1_hw, 6, 7, RESETS_RESET_I2C1_BITS }, + { i2c1_hw, 10, 11, RESETS_RESET_I2C1_BITS }, + { i2c1_hw, 14, 15, RESETS_RESET_I2C1_BITS }, + { i2c1_hw, 18, 19, RESETS_RESET_I2C1_BITS }, + { i2c1_hw, 22, 23, RESETS_RESET_I2C1_BITS }, + { i2c1_hw, 26, 27, RESETS_RESET_I2C1_BITS }, +}; + +struct i2c_config +i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr) +{ + if (bus > ARRAY_SIZE(i2c_bus)) + shutdown("Invalid i2c bus"); + + const struct i2c_info *info = &i2c_bus[bus]; + + gpio_peripheral(info->sda_pin, 3, 0); + gpio_peripheral(info->scl_pin, 3, 0); + + if (!is_enabled_pclock(info->pclk)) { + enable_pclock(info->pclk); + + i2c_hw_t *i2c = info->i2c; + + i2c->enable = 0; + + // We set up the bus in 400 kHz mode, but then set timings afterwards + // to match either 100k or 400k mode. This simplifies the setup. + + i2c->con = I2C_IC_CON_SPEED_VALUE_FAST << I2C_IC_CON_SPEED_LSB + | I2C_IC_CON_MASTER_MODE_BITS + | I2C_IC_CON_IC_SLAVE_DISABLE_BITS + | I2C_IC_CON_IC_RESTART_EN_BITS; + + i2c->tx_tl = 0; + i2c->rx_tl = 0; + + uint32_t pclk = get_pclock_frequency(info->pclk); + + // See `i2c_set_baudrate` in the Pico SDK `hardware_i2c/i2c.c` file + // for details on the calculations here. + if (rate > 1000000) + rate = 1000000; // Clamp the rate to 1Mbps + uint32_t period = (pclk + rate / 2) / rate; + uint32_t lcnt = period * 3 / 5; + uint32_t hcnt = period - lcnt; + uint32_t sda_tx_hold_count = ((pclk * 3) / 10000000) + 1; + + i2c->fs_scl_hcnt = hcnt; + i2c->fs_scl_lcnt = lcnt; + i2c->fs_spklen = lcnt < 16 ? 1 : lcnt / 16; + hw_write_masked(&i2c->sda_hold, + sda_tx_hold_count << I2C_IC_SDA_HOLD_IC_SDA_TX_HOLD_LSB, + I2C_IC_SDA_HOLD_IC_SDA_TX_HOLD_BITS); + } + + return (struct i2c_config){ .i2c=info->i2c, .addr=addr }; +} + +static void +i2c_start(i2c_hw_t *i2c, uint8_t addr) +{ + i2c->enable = 0; + i2c->tar = addr; + i2c->enable = 1; +} + +static void +i2c_stop(i2c_hw_t *i2c) +{ + i2c->enable = 0; +} + +static void +i2c_do_write(i2c_hw_t *i2c, uint8_t addr, uint8_t write_len, uint8_t *write + , uint8_t send_stop, uint32_t timeout) +{ + for (int i = 0; i < write_len; i++) { + int first = i == 0; + int last = send_stop && (i == write_len - 1); + + // Wait until there's a spot in the TX FIFO + while (i2c->txflr == 16) { + if (!timer_is_before(timer_read_time(), timeout)) + shutdown("i2c timeout"); + } + + i2c->data_cmd = first << I2C_IC_DATA_CMD_RESTART_LSB + | last << I2C_IC_DATA_CMD_STOP_LSB + | write[i]; + } + + if (!send_stop) + return; + + // Drain the transmit buffer + while (i2c->txflr != 0) { + if (!timer_is_before(timer_read_time(), timeout)) + shutdown("i2c timeout"); + } +} + +static void +i2c_do_read(i2c_hw_t *i2c, uint8_t addr, uint8_t read_len, uint8_t *read + , uint32_t timeout) +{ + int have_read = 0; + int to_send = read_len; + while (have_read < read_len) { + if (!timer_is_before(timer_read_time(), timeout)) + shutdown("i2c timeout"); + + if (to_send > 0 && i2c->txflr < 16) { + int first = to_send == read_len; + int last = to_send == 1; + + // Put a read command in the TX FIFO + i2c->data_cmd = first << I2C_IC_DATA_CMD_RESTART_LSB + | last << I2C_IC_DATA_CMD_STOP_LSB + | I2C_IC_DATA_CMD_CMD_BITS; + to_send--; + } + + if (have_read < read_len && i2c->rxflr > 0) { + *read++ = i2c->data_cmd & 0xFF; + have_read++; + } + } +} + +void +i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write) +{ + i2c_hw_t *i2c = (i2c_hw_t*)config.i2c; + uint32_t timeout = timer_read_time() + timer_from_us(5000); + + i2c_start(i2c, config.addr); + i2c_do_write(i2c, config.addr, write_len, write, 1, timeout); + i2c_stop(i2c); +} + +void +i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg + , uint8_t read_len, uint8_t *read) +{ + i2c_hw_t *i2c = (i2c_hw_t*)config.i2c; + uint32_t timeout = timer_read_time() + timer_from_us(5000); + + i2c_start(i2c, config.addr); + if (reg_len != 0) + i2c_do_write(i2c, config.addr, reg_len, reg, 0, timeout); + i2c_do_read(i2c, config.addr, read_len, read, timeout); + i2c_stop(i2c); +} From babb067b60e5a0b29fa8657faf01a709483b33b1 Mon Sep 17 00:00:00 2001 From: Tim Abraham Date: Wed, 12 Jan 2022 12:18:08 -0500 Subject: [PATCH 2/2] neopixel: Update neopixel.py to add BRG color order (#5110) Add BRG color option. Document BRG color option. Signed-off-by: Timothy Abraham --- docs/Config_Reference.md | 2 +- klippy/extras/neopixel.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index d344310a4c80..4c78b550ad19 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -2560,7 +2560,7 @@ pin: # Neopixel is connected to the pin). #color_order: GRB # Set the pixel order required by the LED hardware. Options are GRB, -# RGB, GRBW, or RGBW. The default is GRB. +# RGB, BRG, GRBW, or RGBW. The default is GRB. #initial_RED: 0.0 #initial_GREEN: 0.0 #initial_BLUE: 0.0 diff --git a/klippy/extras/neopixel.py b/klippy/extras/neopixel.py index 481f88253026..2c79b3278bfe 100644 --- a/klippy/extras/neopixel.py +++ b/klippy/extras/neopixel.py @@ -24,7 +24,7 @@ def __init__(self, config): self.oid = self.mcu.create_oid() self.pin = pin_params['pin'] self.mcu.register_config_callback(self.build_config) - formats = {v: v for v in ["RGB", "GRB", "RGBW", "GRBW"]} + formats = {v: v for v in ["RGB", "GRB", "BRG", "RGBW", "GRBW"]} self.color_order = config.getchoice("color_order", formats, "GRB") elem_size = len(self.color_order) self.chain_count = config.getint('chain_count', 1, minval=1, @@ -67,6 +67,8 @@ def update_color_data(self, red, green, blue, white, index=None): color_data = [green, red, blue] elif self.color_order == "RGB": color_data = [red, green, blue] + elif self.color_order == "BRG": + color_data = [blue, red, green] elif self.color_order == "GRBW": color_data = [green, red, blue, white] else: