Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 78 additions & 12 deletions klippy/extras/bus.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import mcu
import logging

def resolve_bus_name(mcu, param, bus):
# Find enumerations for the given bus
Expand Down Expand Up @@ -155,12 +156,14 @@ def MCU_SPI_from_config(config, mode, pin_option="cs_pin",

# Helper code for working with devices connected to an MCU via an I2C bus
class MCU_I2C:
def __init__(self, mcu, bus, addr, speed, sw_pins=None):
def __init__(self, mcu, bus, addr, speed, sw_pins=None,
async_write_only=False):
self.mcu = mcu
self.bus = bus
self.i2c_address = addr
self.oid = self.mcu.create_oid()
self.speed = speed
self.async_write_only = async_write_only
self.config_fmt_ticks = None
mcu.add_config_cmd("config_i2c oid=%d" % (self.oid,))
# Generate I2C bus config message
Expand All @@ -180,10 +183,13 @@ def __init__(self, mcu, bus, addr, speed, sw_pins=None):
self.cmd_queue = self.mcu.alloc_command_queue()
self.mcu.register_config_callback(self.build_config)
self.i2c_write_cmd = self.i2c_read_cmd = None
self.i2c_transfer_cmd = self.i2c_write_only_cmd = None
printer = self.mcu.get_printer()
printer.register_event_handler("klippy:connect", self._handle_connect)
# backward support i2c_write inside the init section
self._to_write = []
# Host side I2C error handling
self._configured = False
def _handle_connect(self):
for data in self._to_write:
self.i2c_write(data)
Expand All @@ -195,6 +201,13 @@ def get_i2c_address(self):
return self.i2c_address
def get_command_queue(self):
return self.cmd_queue
def _async_write_status(self, params):
status = params["i2c_bus_status"]
if status == "SUCCESS":
return
err_msg = "MCU '%s' I2C request to addr %i reports error %s" % (
self.mcu.get_name(), self.i2c_address, status)
logging.error(err_msg)
def build_config(self):
if '%' in self.config_fmt:
bus = resolve_bus_name(self.mcu, "i2c_bus", self.bus)
Expand All @@ -206,25 +219,78 @@ def build_config(self):
pulse_ticks = self.mcu.seconds_to_clock(1./self.speed/2)
self.config_fmt = self.config_fmt_ticks % (pulse_ticks,)
self.mcu.add_config_cmd(self.config_fmt)
self.i2c_write_cmd = self.mcu.lookup_command(
"i2c_write oid=%c data=%*s", cq=self.cmd_queue)
self.i2c_read_cmd = self.mcu.lookup_query_command(
"i2c_read oid=%c reg=%*s read_len=%u",
"i2c_read_response oid=%c response=%*s", oid=self.oid,
cq=self.cmd_queue)
if self.mcu.try_lookup_command("i2c_read oid=%c reg=%*s read_len=%u"):
self.i2c_write_cmd = self.mcu.lookup_command(
"i2c_write oid=%c data=%*s", cq=self.cmd_queue)
self.i2c_write_only_cmd = self.i2c_write_cmd
self.i2c_read_cmd = self.mcu.lookup_query_command(
"i2c_read oid=%c reg=%*s read_len=%u",
"i2c_read_response oid=%c response=%*s", oid=self.oid,
cq=self.cmd_queue)
else:
self.i2c_transfer_cmd = self.mcu.lookup_query_command(
"i2c_transfer oid=%c write=%*s read_len=%u",
"i2c_response oid=%c i2c_bus_status=%c response=%*s",
oid=self.oid, cq=self.cmd_queue)
self.i2c_write_only_cmd = self.mcu.lookup_command(
"i2c_transfer oid=%c write=%*s read_len=%u",
cq=self.cmd_queue)
if self.mcu.is_fileoutput():
self.i2c_transfer_cmd = self.mcu.lookup_command(
"i2c_transfer oid=%c write=%*s read_len=%u",
cq=self.cmd_queue)
if self.async_write_only:
self.mcu.register_response(self._async_write_status,
"i2c_response", self.oid)
self._configured = True
def i2c_write_noack(self, data, minclock=0, reqclock=0):
if self.async_write_only:
self.i2c_write_only_cmd.send([self.oid, data, 0],
minclock=minclock, reqclock=reqclock)
return
self.i2c_write_cmd.send([self.oid, data],
minclock=minclock, reqclock=reqclock)
def i2c_write(self, data, minclock=0, reqclock=0):
if self.i2c_write_cmd is None:
def i2c_write(self, data, minclock=0, reqclock=0, retry=True):
if not self._configured:
self._to_write.append(data)
return
if self.async_write_only:
self.i2c_write_only_cmd.send([self.oid, data, 0],
minclock=minclock, reqclock=reqclock)
return
if self.i2c_transfer_cmd is not None:
self.i2c_transfer(data, minclock=minclock, reqclock=reqclock,
retry=retry)
return
self.i2c_write_cmd.send_wait_ack([self.oid, data],
minclock=minclock, reqclock=reqclock)
def i2c_read(self, write, read_len, retry=True):
return self.i2c_read_cmd.send([self.oid, write, read_len], retry)
if self.async_write_only:
raise mcu.error("I2C dev is write only")
if self.i2c_transfer_cmd is not None:
return self.i2c_transfer(write, read_len, retry=retry)
return self.i2c_read_cmd.send([self.oid, write, read_len],
retry=retry)
def i2c_transfer(self, write, read_len=0, minclock=0, reqclock=0,
retry=True):
if self.async_write_only:
raise mcu.error("I2C dev is write only")
cmd = self.i2c_transfer_cmd
if self.mcu.is_fileoutput():
cmd.send([self.oid, write, read_len],
minclock=minclock, reqclock=reqclock)
return
param = cmd.send([self.oid, write, read_len],
minclock=minclock, reqclock=reqclock, retry=retry)
status = param["i2c_bus_status"]
if status == "SUCCESS":
return param
err_msg = "MCU '%s' I2C request to addr %i reports error %s" % (
self.mcu.get_name(), self.i2c_address, status)
self.mcu.get_printer().invoke_shutdown(err_msg)

def MCU_I2C_from_config(config, default_addr=None, default_speed=100000):
def MCU_I2C_from_config(config, default_addr=None, default_speed=100000,
async_write_only=False):
# Load bus parameters
printer = config.get_printer()
i2c_mcu = mcu.get_printer_mcu(printer, config.get('i2c_mcu', 'mcu'))
Expand All @@ -250,7 +316,7 @@ def MCU_I2C_from_config(config, default_addr=None, default_speed=100000):
bus = config.get('i2c_bus', None)
sw_pins = None
# Create MCU_I2C object
return MCU_I2C(i2c_mcu, bus, addr, speed, sw_pins)
return MCU_I2C(i2c_mcu, bus, addr, speed, sw_pins, async_write_only)


######################################################################
Expand Down
3 changes: 2 additions & 1 deletion klippy/extras/display/uc1701.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ def send(self, cmds, is_data=False):
class I2C:
def __init__(self, config, default_addr):
self.i2c = bus.MCU_I2C_from_config(config, default_addr=default_addr,
default_speed=400000)
default_speed=400000,
async_write_only=True)
def send(self, cmds, is_data=False):
if is_data:
hdr = 0x40
Expand Down
3 changes: 2 additions & 1 deletion klippy/extras/sx1509.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ class SX1509(object):
def __init__(self, config):
self._printer = config.get_printer()
self._name = config.get_name().split()[1]
self._i2c = bus.MCU_I2C_from_config(config, default_speed=400000)
self._i2c = bus.MCU_I2C_from_config(config, default_speed=400000,
async_write_only=True)
self._ppins = self._printer.lookup_object("pins")
self._ppins.register_chip("sx1509_" + self._name, self)
self._mcu = self._i2c.get_mcu()
Expand Down
39 changes: 19 additions & 20 deletions src/i2ccmds.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ enum {
IF_SOFTWARE = 1, IF_HARDWARE = 2
};

DECL_ENUMERATION("i2c_bus_status", "SUCCESS", I2C_BUS_SUCCESS);
DECL_ENUMERATION("i2c_bus_status", "NACK", I2C_BUS_NACK);
DECL_ENUMERATION("i2c_bus_status", "START_NACK", I2C_BUS_START_NACK);
DECL_ENUMERATION("i2c_bus_status", "START_READ_NACK", I2C_BUS_START_READ_NACK);
DECL_ENUMERATION("i2c_bus_status", "BUS_TIMEOUT", I2C_BUS_TIMEOUT);

void
command_config_i2c(uint32_t *args)
{
Expand Down Expand Up @@ -72,17 +78,6 @@ int i2c_dev_write(struct i2cdev_s *i2c, uint8_t write_len, uint8_t *data)
return i2c_write(i2c->i2c_hw, write_len, data);
}

void command_i2c_write(uint32_t *args)
{
uint8_t oid = args[0];
struct i2cdev_s *i2c = oid_lookup(oid, command_config_i2c);
uint8_t data_len = args[1];
uint8_t *data = command_decode_ptr(args[2]);
int ret = i2c_dev_write(i2c, data_len, data);
i2c_shutdown_on_err(ret);
}
DECL_COMMAND(command_i2c_write, "i2c_write oid=%c data=%*s");

int i2c_dev_read(struct i2cdev_s *i2c, uint8_t reg_len, uint8_t *reg
, uint8_t read_len, uint8_t *read)
{
Expand All @@ -93,16 +88,20 @@ int i2c_dev_read(struct i2cdev_s *i2c, uint8_t reg_len, uint8_t *reg
return i2c_read(i2c->i2c_hw, reg_len, reg, read_len, read);
}

void command_i2c_read(uint32_t *args)
void command_i2c_transfer(uint32_t *args)
{
uint8_t oid = args[0];
struct i2cdev_s *i2c = oid_lookup(oid, command_config_i2c);
uint8_t reg_len = args[1];
uint8_t *reg = command_decode_ptr(args[2]);
uint8_t data_len = args[3];
uint8_t data[data_len];
int ret = i2c_dev_read(i2c, reg_len, reg, data_len, data);
i2c_shutdown_on_err(ret);
sendf("i2c_read_response oid=%c response=%*s", oid, data_len, data);
uint8_t wlen = args[1];
uint8_t *wdata = command_decode_ptr(args[2]);
uint8_t rlen = args[3];
uint8_t rdata[rlen];
uint8_t nack;
if (rlen > 0)
nack = i2c_dev_read(i2c, wlen, wdata, rlen, rdata);
else
nack = i2c_dev_write(i2c, wlen, wdata);
sendf("i2c_response oid=%c i2c_bus_status=%c response=%*s",
oid, nack, rlen, rdata);
}
DECL_COMMAND(command_i2c_read, "i2c_read oid=%c reg=%*s read_len=%u");
DECL_COMMAND(command_i2c_transfer, "i2c_transfer oid=%c write=%*s read_len=%u");
Loading