The yocto kernel support the PWM on stout. The PWM is implemented as simple sysfs interface
Supported channels: PWM0..2 = shared with other function (needs reprogramming of CPLD) PWM3 = pwmchip0 PWM4 = pwmchip1 /sys/class/pwm/ `-- pwmchipN/ for each PWM chip (N = 0 or 1) |-- export (w/o) ask the kernel to export a PWM channel |-- npwm (r/o) number of PWM channels in this PWM chip |-- pwmX/ for each exported PWM channel (X = 0 or 1) | |-- duty_cycle (r/w) duty cycle (in nanoseconds) | |-- enable (r/w) enable/disable PWM (0 is disabled, 1 is enabled) | |-- period (r/w) period (in nanoseconds) | `-- polarity (r/w) polarity of PWM (normal/inversed) `-- unexport (w/o) return a PWM channel to the kernel
Examples (command line):
cd /sys/class/pwm cd pwmchip0 echo 0 > export ' get PWM control from kernel ' cd pwm0 echo 20000000 > period ' set period to 20ms (50Hz = RC servo signal) ' echo 2000000 > duty_cycle ' set duty cycle to 2ms ' echo 1 > enable ' enable output ' echo 0 > enable ' disable output ' cd .. echo 0 > unexport ' release PWM to kernel '
Output on PWM0:
Example C-Program (part of RC-Test program:
/* * pwm.h * * Created on: Jan 4, 2016 * Author: rcar */ #ifndef PWM_H_ #define PWM_H_ #include <stdbool.h> struct pwmdata { uint32_t dutycyc; uint32_t period; bool enabled; }; void pwmtest(int pwmch, int pwmval); bool pwmcheckexport(int pwmch); void pwmexport(int pwmch, int pwmval); void pwmGetValue(int pwmch, struct pwmdata *pwm); void pwmSetValue(int pwmch, struct pwmdata pwm); void pwmstatus(void); #endif /* PWM_H_ */
#include <fcntl.h> #include <stdint.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <termios.h> #include <stdbool.h> #include <errno.h> #include <sys/stat.h> #include "pwm.h" /* PWM implemented as simple sysfs interface on stout * * supported channels: * pwmchip0 = PWM3 (STEER) * pwmchip1 = PWM4 (MOTOR) * * /sys/class/pwm/ * `-- pwmchipN/ for each PWM chip * |-- export (w/o) ask the kernel to export a PWM channel * |-- npwm (r/o) number of PWM channels in this PWM chip * |-- pwmX/ for each exported PWM channel * | |-- duty_cycle (r/w) duty cycle (in nanoseconds) * | |-- enable (r/w) enable/disable PWM (0 is disabled, 1 is enabled) * | |-- period (r/w) period (in nanoseconds) * | `-- polarity (r/w) polarity of PWM (normal/inversed) * `-- unexport (w/o) return a PWM channel to the kernel */ struct pwmdata pwm; //------------------------------------------------------------------------ /* sleep until a key is pressed and return value. echo = 0 disables key echo. */ int keypress(unsigned char echo) { struct termios savedState, newState; int c; if (-1 == tcgetattr(STDIN_FILENO, &savedState)) { return EOF; /* error on tcgetattr */ } newState = savedState; if ((echo = !echo)) /* yes i'm doing an assignment in an if clause */ { echo = ECHO; /* echo bit to disable echo */ } /* disable canonical input and disable echo. set minimal input to 1. */ newState.c_lflag &= ~(echo | ICANON); newState.c_cc[VMIN] = 1; if (-1 == tcsetattr(STDIN_FILENO, TCSANOW, &newState)) { return EOF; /* error on tcsetattr */ } c = getchar(); /* block (withot spinning) until we get a keypress */ /* restore the saved state */ if (-1 == tcsetattr(STDIN_FILENO, TCSANOW, &savedState)) { return EOF; /* error on tcsetattr */ } return c; } //------------------------------------------------------------------------ /* PWM activate * Ask kernel to export PWM channel */ void pwmExport(int pwmch) { char path[50]; // Buffer for path int fd; // File descriptor int res; snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip%d/export", pwmch); fd = open(path, O_WRONLY); if (fd < 0) { perror("not able to write on export\n"); exit(1); } res = write(fd, "0", 1); if (res < 0) { perror("Can't activate PWM (write)\n"); exit(1); } close(fd); } //------------------------------------------------------------------------ /* PWM deactivate * release PWM channel back to kernel */ void pwmUnExport(int pwmch) { char path[50]; // Buffer for path int fd; // File descriptor int res; // result snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip%d/unexport", pwmch); fd = open(path, O_WRONLY); if (fd < 0) { perror("not able to write on un-export\n"); exit(1); } res = write(fd, "0", 1); if (res < 0) { perror("not able to un-activate PWM (write)\n"); exit(1); } close(fd); } //------------------------------------------------------------------------ /* Set parameter of PWM (period and duty cycle) */ void pwmSetValue(int pwmch, struct pwmdata pwm) { char path[50]; // Buffer for path char value[10]; int fd; // File descriptor int res; // result if (pwmcheckexport(pwmch)) { snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip%d/pwm0/period", pwmch); fd = open(path, O_WRONLY); if (fd < 0) { perror("Can't access PWM channel (open)!\n"); exit(1); } snprintf(value, sizeof(value), "%d", pwm.period*1000); res = write(fd,value,sizeof(value)); if (res < 0) { perror("Can't access PWM channel (write)!\n"); exit(1); } snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip%d/pwm0/duty_cycle", pwmch); fd = open(path, O_WRONLY); if (fd < 0) { perror("Can't access PWM channel (open)!\n"); exit(1); } snprintf(value, sizeof(value), "%d", pwm.dutycyc*1000); res = write(fd,value,sizeof(value)); if (res < 0) { perror("Can't access PWM channel (write)!\n"); exit(1); } snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip%d/pwm0/enable", pwmch); fd = open(path, O_WRONLY); if (fd < 0) { perror("Can't access PWM channel (open)!\n"); exit(1); } if (pwm.enabled) snprintf(value, sizeof(value), "1"); else snprintf(value, sizeof(value), "0"); res = write(fd,value,sizeof(value)); if (res < 0) { perror("Can't access PWM channel (write)!\n"); exit(1); } close(fd); printf("PWM parameter set!\n"); } else printf("PWM channel to exported. Can't set values!\n"); } //------------------------------------------------------------------------ void pwmGetValue(int pwmch, struct pwmdata *pwm) { char path[50]; // Buffer for path char value[10]; int fd; // File descriptor int res; // result if (pwmcheckexport(pwmch)) { snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip%d/pwm0/period", pwmch); fd = open(path, O_RDONLY); res = read(fd,value,sizeof(value)); (*pwm).period = atol(value)/1000; close(fd); snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip%d/pwm0/duty_cycle", pwmch); fd = open(path, O_RDONLY); res = read(fd,value,sizeof(value)); (*pwm).dutycyc = atol(value)/1000; close(fd); snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip%d/pwm0/enable", pwmch); fd = open(path, O_RDONLY); res = read(fd,value,sizeof(value)); res = atoi(value); if (res == 1) (*pwm).enabled = true; else (*pwm).enabled = false; close(fd); } } //------------------------------------------------------------------------ /* Enable PWM * Ergebnis: -1 = Fehler, 0 = O.K. */ void pwmEnable(int pwmch, bool enable) { char path[50]; // Buffer for path int fd; // File descriptor int res; // result snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip%d/pwm0/enable", pwmch); fd = open(path, O_WRONLY); if (fd < 0) { perror("Can't access PWM channel (open)!\n"); exit(1); } if (enable) res = write(fd, "1", 1); else res = write(fd, "0", 1); if (res < 0) { perror("Can't access PWM channel (write)!\n"); exit(1); } close(fd); } //------------------------------------------------------------------------ // check whether PWM channel is exported or not from Kernel bool pwmcheckexport(int pwmch) { char path[50]; // Buffer for path struct stat s; snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip%d/pwm0", pwmch); int err = stat(path, &s); if(-1 == err) { if(ENOENT == errno) { return false; } else { perror("stat"); exit(1); } } else { if(S_ISDIR(s.st_mode)) { return true; } } return false; } //------------------------------------------------------------------------ // un-/export PWM channel from Kernel void pwmexport(int pwmch, int pwmval) { if (pwmval == 1) { if (pwmcheckexport(pwmch)) printf("PWM channel %d already exported\n",pwmch); else { printf("Export PWM channel %d\n", pwmch); pwmExport(pwmch); } } else { if (!pwmcheckexport(pwmch)) printf("PWM channel %d already unexported\n",pwmch); else { printf("Un-Export PWM channel %d\n", pwmch); pwmUnExport(pwmch); } } } //------------------------------------------------------------------------ // list status pf PWM channels void pwmstatus(void) { int i; printf("PWM Status\n"); printf("CH\tPERIOD[us]\tDUTY_CYCLE[us]\tENABLED\n"); // 0 20000 1500 NORMAL YES // 0 ### NOT EXPORTED ### // channel 0+1 for(i=0;i<2;i++) { if (!pwmcheckexport(i)) { printf("%d\t### NOT EXPORTED ###\n",i); } else { pwmGetValue(i, &pwm); printf("%d\t%d\t\t%d\t\t",i, pwm.period, pwm.dutycyc); if (pwm.enabled) printf("YES\n"); else printf("NO\n"); } } }