mcp4451: Add initial support for programming the mcp4451 on lpc176x

Add support for programming smoothieboard current.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2018-05-29 20:59:17 -04:00
parent cc6b416660
commit d725160706
5 changed files with 178 additions and 1 deletions

View File

@ -424,6 +424,35 @@
# default is to not scale the 'channel_x' parameters.
# Statically configured MCP4451 digipot connected via I2C bus (one may
# define any number of sections with an "mcp4451" prefix).
#[mcp4451 my_digipot]
#mcu: mcu
# The name of the micro-controller that the MCP4451 chip is
# connected to. The default is "mcu".
#i2c_address:
# The i2c address that the chip is using on the i2c bus. This
# parameter must be provided.
#wiper_0:
#wiper_1:
#wiper_2:
#wiper_3:
# The value to statically set the given MCP4451 "wiper" to. This is
# typically set to a number between 0.0 and 1.0 with 1.0 being the
# highest resistance and 0.0 being the lowest resistance. However,
# the range may be changed with the 'scale' parameter (see
# below). If a wiper is not specified then it is left unconfigured.
#scale:
# This parameter can be used to alter how the 'wiper_x' parameters
# are interpreted. If provided, then the 'wiper_x' parameters should
# be between 0.0 and 'scale'. This may be useful when the MCP4451 is
# used to set stepper voltage references. The 'scale' can be set to
# the equivalent stepper amperage if the MCP4451 were at its highest
# resistance, and then the 'wiper_x' parameters can be specified
# using the desired amperage value for the stepper. The default is
# to not scale the 'wiper_x' parameters.
# Configure a TMC2130 stepper motor driver via SPI bus. To use this
# feature, define a config section with a "tmc2130" prefix followed by
# the name of the corresponding stepper config section (for example,

View File

@ -85,3 +85,19 @@ max_z_accel: 100
[static_digital_output leds]
pins: P1.18, P1.19, P1.20, P1.21, P4.28
[mcp4451 stepper_digipot1]
i2c_address: 88
# Scale the config so that values can be specified in amps.
scale: 2.25
# wiper 0 is X (aka alpha), 1 is Y, 2 is Z, 3 is E0
wiper_0: 1.0
wiper_1: 1.0
wiper_2: 1.0
wiper_3: 1.0
[mcp4451 stepper_digipot2]
i2c_address: 90
scale: 2.25
# wiper 0 is E1
wiper_0: 1.0

32
klippy/extras/mcp4451.py Normal file
View File

@ -0,0 +1,32 @@
# MCP4451 digipot code
#
# Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import mcu
WiperRegisters = [0x00, 0x01, 0x06, 0x07]
class mcp4451:
def __init__(self, config):
printer = config.get_printer()
self.mcu = mcu.get_printer_mcu(printer, config.get('mcu', 'mcu'))
self.i2c_addr = config.getint('i2c_address')
scale = config.getfloat('scale', 1., above=0.)
wipers = [None]*4
for i in range(len(wipers)):
val = config.getfloat('wiper_%d' % (i,), None,
minval=0., maxval=scale)
if val is not None:
wipers[i] = int(val * 255. / scale + .5)
self.add_config_cmd(0x04, 0xff)
self.add_config_cmd(0x0a, 0xff)
for reg, val in zip(WiperRegisters, wipers):
if val is not None:
self.add_config_cmd(reg, val)
def add_config_cmd(self, reg, val):
self.mcu.add_config_cmd("i2c_send data=%02x%02x%02x" % (
self.i2c_addr, (reg << 4) | ((val >> 8) & 0x03), val), is_init=True)
def load_config_prefix(config):
return mcp4451(config)

View File

@ -13,7 +13,7 @@ CFLAGS_klipper.elf += -T $(OUT)LPC1768.ld
CFLAGS_klipper.elf += --specs=nano.specs --specs=nosys.specs
# Add source files
src-y += lpc176x/main.c lpc176x/timer.c lpc176x/gpio.c
src-y += lpc176x/main.c lpc176x/timer.c lpc176x/gpio.c lpc176x/i2c.c
src-y += generic/crc16_ccitt.c generic/alloc.c
src-y += generic/armcm_irq.c generic/timer_irq.c
src-y += ../lib/lpc176x/device/system_LPC17xx.c

100
src/lpc176x/i2c.c Normal file
View File

@ -0,0 +1,100 @@
// I2C functions on lpc176x
//
// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "LPC17xx.h" // LPC_I2C1
#include "board/misc.h" // timer_is_before
#include "command.h" // DECL_COMMAND
#include "internal.h" // gpio_peripheral
#include "sched.h" // sched_shutdown
// i2c connection status flags
enum {
IF_START = 1<<5, IF_STOP = 1<<4, IF_IRQ = 1<<3, IF_ACK = 1<<2, IF_ENA = 1<<6
};
static void
i2c_init(void)
{
static int have_run_init;
if (have_run_init)
return;
have_run_init = 1;
// Init i2c bus 1 pins
gpio_peripheral(0, 0, 3, 0);
gpio_peripheral(0, 1, 3, 0);
// Set 100Khz frequency
uint32_t PCLK = SystemCoreClock / 4, pulse = PCLK / (100000 * 2);
LPC_I2C1->I2SCLL = pulse;
LPC_I2C1->I2SCLH = pulse;
// Enable interface
LPC_I2C1->I2CONCLR = IF_START | IF_IRQ | IF_ACK | IF_ENA;
LPC_I2C1->I2CONSET = IF_ENA;
}
static void
i2c_wait(uint32_t bit, uint32_t timeout)
{
for (;;) {
uint32_t flags = LPC_I2C1->I2CONSET;
if (flags & bit)
break;
if (!timer_is_before(timer_read_time(), timeout))
shutdown("i2c timeout");
}
}
static void
i2c_start(uint32_t timeout)
{
LPC_I2C1->I2CONCLR = IF_ACK | IF_IRQ | IF_START;
LPC_I2C1->I2CONSET = IF_ACK | IF_START;
i2c_wait(IF_IRQ, timeout);
uint32_t status = LPC_I2C1->I2STAT;
if (status != 0x10 && status != 0x08)
shutdown("Failed to send i2c start");
LPC_I2C1->I2CONCLR = IF_START;
}
static uint32_t
i2c_send_byte(uint8_t b, uint32_t timeout)
{
LPC_I2C1->I2DAT = b;
LPC_I2C1->I2CONCLR = IF_IRQ;
i2c_wait(IF_IRQ, timeout);
return LPC_I2C1->I2STAT;
}
static void
i2c_stop(uint32_t timeout)
{
LPC_I2C1->I2CONSET = IF_STOP;
LPC_I2C1->I2CONCLR = IF_IRQ;
i2c_wait(IF_STOP, timeout);
}
static void
i2c_send(uint8_t *data, int data_len)
{
i2c_init();
uint32_t timeout = timer_read_time() + timer_from_us(5000);
i2c_start(timeout);
while (data_len--)
i2c_send_byte(*data++, timeout);
i2c_stop(timeout);
}
// This provides just enough functionality to program an MCP4451 chip
void
command_i2c_send(uint32_t *args)
{
uint8_t data_len = args[0], *data = (void*)(size_t)args[1];
i2c_send(data, data_len);
}
DECL_COMMAND(command_i2c_send, "i2c_send data=%*s");