diff options
author | Janne Grunau <janne-3d@jannau.net> | 2019-03-24 10:25:56 +0100 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2019-04-05 19:06:15 -0400 |
commit | aab89e7f85d01fffb349b2046daebdc0deb1ce92 (patch) | |
tree | c875725a48bce1170d6039103671f7331cb1fda1 /src/linux/hard_pwm.c | |
parent | e6c3eeafd79cac08172377f447c1dd963b5cb530 (diff) | |
download | kutter-aab89e7f85d01fffb349b2046daebdc0deb1ce92.tar.gz kutter-aab89e7f85d01fffb349b2046daebdc0deb1ce92.tar.xz kutter-aab89e7f85d01fffb349b2046daebdc0deb1ce92.zip |
linux: add support for Linux hardware PWM
The replicape 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. This commits adds hardware PWM support using the linux
sysfs user space interface.
Signed-off-by: Janne Grunau <janne-3d@jannau.net>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'src/linux/hard_pwm.c')
-rw-r--r-- | src/linux/hard_pwm.c | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/src/linux/hard_pwm.c b/src/linux/hard_pwm.c new file mode 100644 index 00000000..25bfb2e7 --- /dev/null +++ b/src/linux/hard_pwm.c @@ -0,0 +1,97 @@ +// 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 (1 << 15) +DECL_CONSTANT("PWM_MAX", MAX_PWM); + +#define HARD_PWM_START (1<<16) +#define HARD_PWM(chip, pin) (((chip)<<8) + (pin) + HARD_PWM_START) +#define HARD_PWM_TO_CHIP(hard_pwm) (((hard_pwm) - HARD_PWM_START) >> 8) +#define HARD_PWM_TO_PIN(hard_pwm) (((hard_pwm) - HARD_PWM_START) & 0xff) + +DECL_ENUMERATION_RANGE("pin", "pwmchip0/pwm0", HARD_PWM(0, 0), 256); +DECL_ENUMERATION_RANGE("pin", "pwmchip1/pwm0", HARD_PWM(1, 0), 256); +DECL_ENUMERATION_RANGE("pin", "pwmchip2/pwm0", HARD_PWM(2, 0), 256); +DECL_ENUMERATION_RANGE("pin", "pwmchip3/pwm0", HARD_PWM(3, 0), 256); + +#define PWM_PATH "/sys/class/pwm/pwmchip%u/pwm%u/%s" + +struct gpio_pwm gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint16_t val) +{ + char filename[256]; + char scratch[16]; + uint8_t chip_id = HARD_PWM_TO_CHIP(pin); + uint8_t pwm_id = HARD_PWM_TO_PIN(pin); + + 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, uint16_t val) +{ + char scratch[16]; + uint32_t duty_cycle = g.period * (uint64_t)val / MAX_PWM; + snprintf(scratch, sizeof(scratch), "%u", duty_cycle); + if (g.fd != -1) { + write(g.fd, scratch, strlen(scratch)); + } else { + report_errno("pwm set duty_cycle", g.fd); + } +} |