Skip to content

Commit

Permalink
extras/replicape: add support for Linux hardware PWM
Browse files Browse the repository at this point in the history
The servo pins (P9_14/P9_16) are muxed to the SOCs hardware PWM unit
driven by a 13MHz GP timer. They have to be driven by the linux host
mcu for example by the sysfs user space interface. The easiest way to
ensure the hardware pwm commands are used is to make the servo pins
replicape specific. Fixes #1105.

Signed-off-by: Janne Grunau <janne-3d@jannau.net>
  • Loading branch information
Janne Grunau committed Mar 25, 2019
1 parent 9aadc3c commit 63fed25
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 7 deletions.
8 changes: 6 additions & 2 deletions config/generic-replicape.cfg
Expand Up @@ -40,10 +40,14 @@ host_mcu: host
# False.
#servo0_enable: False
# This parameter controls whether end_stop_X_2 is used for endstops
# (via P9_11) or for servo_0 (via P9_14). The default is False.
# (via P9_11) or for servo_0 (P9_14). The servo_0 pin is driven by
# by hardware PWM from Linux. The pin has to be specified as
# replicape:P9_14. The default is False.
#servo1_enable: False
# This parameter controls whether end_stop_Y_2 is used for endstops
# (via P9_28) or for servo_1 (via P9_16). The default is False.
# (via P9_28) or for servo_1 (P9_16). The servo_1 pin is driven by
# by hardware PWM from Linux. The pin has to be specified as
# replicape:P9_16. The default is False.
stepper_x_microstep_mode: spread16
# This parameter controls the CFG1 and CFG2 pins of the given
# stepper motor driver. Available options are: disable, 1, 2,
Expand Down
10 changes: 9 additions & 1 deletion klippy/extras/replicape.py
Expand Up @@ -108,6 +108,13 @@ def set_digital(self, print_time, value):
else:
self.pwm.set_pwm(print_time, 0.)

class linux_pwm(mcu.MCU_pwm):
def __init__(self, replicape, channel, pin_type, pin_params):
mcu.MCU_pwm.__init__(self, replicape.host_mcu, pin_params)

def setup_cycle_time(self, cycle_time, hardware_pww=False):
mcu.MCU_pwm.setup_cycle_time(self, cycle_time, True);

ReplicapeStepConfig = {
'disable': None,
'1': (1<<7)|(1<<5), '2': (1<<7)|(1<<5)|(1<<6), 'spread2': (1<<5),
Expand All @@ -134,7 +141,8 @@ def __init__(self, config):
"power_e": (pca9685_pwm, 5), "power_h": (pca9685_pwm, 3),
"power_hotbed": (pca9685_pwm, 4),
"power_fan0": (pca9685_pwm, 7), "power_fan1": (pca9685_pwm, 8),
"power_fan2": (pca9685_pwm, 9), "power_fan3": (pca9685_pwm, 10) }
"power_fan2": (pca9685_pwm, 9), "power_fan3": (pca9685_pwm, 10),
"P9_14": (linux_pwm, 0), "P9_16": (linux_pwm, 1) }
# Setup stepper config
self.last_stepper_time = 0.
self.stepper_dacs = {}
Expand Down
1 change: 1 addition & 0 deletions src/linux/Kconfig
Expand Up @@ -8,6 +8,7 @@ config LINUX_SELECT
default y
select HAVE_GPIO_ADC
select HAVE_GPIO_SPI
select HAVE_GPIO_HARD_PWM

config BOARD_DIRECTORY
string
Expand Down
2 changes: 1 addition & 1 deletion src/linux/Makefile
Expand Up @@ -3,7 +3,7 @@
dirs-y += src/linux src/generic

src-y += linux/main.c linux/timer.c linux/console.c linux/watchdog.c
src-y += linux/pca9685.c linux/spidev.c linux/analog.c
src-y += linux/pca9685.c linux/spidev.c linux/analog.c linux/hard_pwm.c
src-y += generic/crc16_ccitt.c generic/alloc.c

CFLAGS_klipper.elf += -lutil
Expand Down
7 changes: 7 additions & 0 deletions src/linux/gpio.h
Expand Up @@ -28,4 +28,11 @@ 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 gpio_pwm {
int fd;
uint32_t period;
};
struct gpio_pwm gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val);
void gpio_pwm_write(struct gpio_pwm g, uint8_t val);

#endif // gpio.h
91 changes: 91 additions & 0 deletions src/linux/hard_pwm.c
@@ -0,0 +1,91 @@
// HW PWM upport via Linux PWM sysfs interface
//
// Copyright (C) 2019 Janne Grunau <janne-3d@jannau.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "gpio.h" // struct gpio_pwm
#include "internal.h" // NSECS_PER_TICK
#include "command.h" // shutdown
#include "sched.h" // sched_shutdown

#define MAX_PWM 255
DECL_CONSTANT("PWM_MAX", MAX_PWM);

#define NUM_PWM_PINS 2
#define PWM_PIN_START 8

DECL_ENUMERATION("pin", "P9_14", PWM_PIN_START);
DECL_ENUMERATION("pin", "P9_16", PWM_PIN_START + 1);

#define PWM_PATH "/sys/class/pwm/pwmchip%u/pwm%u/%s"

struct gpio_pwm gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val)
{
char filename[256];
char scratch[16];
uint8_t chip_id = 0, pwm_id = pin - PWM_PIN_START;

struct gpio_pwm g = {};
g.period = cycle_time * NSECS_PER_TICK;

// configure period/cycle time. Always in nanoseconds
snprintf(filename, sizeof(filename), PWM_PATH, chip_id, pwm_id, "period");
int fd = open(filename, O_WRONLY|O_CLOEXEC);
if (fd == -1) {
report_errno("pwm period", fd);
goto fail;
}
snprintf(scratch, sizeof(scratch), "%u", cycle_time * NSECS_PER_TICK);
write(fd, scratch, strlen(scratch));
close(fd);

// write duty cycle
snprintf(filename, sizeof(filename), PWM_PATH, chip_id, pwm_id,
"duty_cycle");
fd = open(filename, O_WRONLY|O_CLOEXEC);
if (fd == -1) {
report_errno("pwm duty_cycle", fd);
goto fail;
}

g.fd = fd;
gpio_pwm_write(g, val);

// enable PWM
snprintf(filename, sizeof(filename), PWM_PATH, chip_id, pwm_id, "enable");
fd = open(filename, O_WRONLY|O_CLOEXEC);
if (fd == -1) {
close(g.fd);
report_errno("pwm enable", fd);
goto fail;
}
write(fd, "1", 2);
close(fd);

return g;

fail:
if (fd >= 0)
close(fd);
shutdown("Unable to config pwm device");
}


void gpio_pwm_write(struct gpio_pwm g, uint8_t val)
{
char scratch[16];
snprintf(scratch, sizeof(scratch), "%u", g.period * val / 255);
if (g.fd != -1) {
write(g.fd, scratch, strlen(scratch));
} else {
report_errno("pwm set duty_cycle", g.fd);
}
}
4 changes: 4 additions & 0 deletions src/linux/internal.h
Expand Up @@ -3,6 +3,10 @@
// Local definitions for micro-controllers running on linux

#include <time.h> // struct timespec
#include "autoconf.h" // CONFIG_CLOCK_FREQ

#define NSECS 1000000000
#define NSECS_PER_TICK (NSECS / CONFIG_CLOCK_FREQ)

// console.c
void report_errno(char *where, int rc);
Expand Down
3 changes: 0 additions & 3 deletions src/linux/timer.c
Expand Up @@ -21,9 +21,6 @@ static uint32_t last_read_time_counter;
static struct timespec last_read_time, next_wake_time;
static time_t start_sec;

#define NSECS 1000000000
#define NSECS_PER_TICK (NSECS / CONFIG_CLOCK_FREQ)

// Compare two 'struct timespec' times
static inline uint8_t
timespec_is_before(struct timespec ts1, struct timespec ts2)
Expand Down

0 comments on commit 63fed25

Please sign in to comment.