Skip to content

Commit 599ec57

Browse files
author
Matthew West
committed
Fix PWM in Linux 4.1+
1 parent a2a88fb commit 599ec57

File tree

5 files changed

+275
-20
lines changed

5 files changed

+275
-20
lines changed

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
packages = find_packages(),
4242
py_modules = ['Adafruit_I2C'],
4343
ext_modules = [Extension('Adafruit_BBIO.GPIO', ['source/py_gpio.c', 'source/event_gpio.c', 'source/constants.c', 'source/common.c'], extra_compile_args=['-Wno-format-security'], define_macros=kernel41),
44-
Extension('Adafruit_BBIO.PWM', ['source/py_pwm.c', 'source/c_pwm.c', 'source/constants.c', 'source/common.c'], extra_compile_args=['-Wno-format-security'], define_macros=kernel41),
44+
Extension('Adafruit_BBIO.PWM', ['source/py_pwm.c', 'source/c_pwm.c', 'source/c_pinmux.c', 'source/constants.c', 'source/common.c'], extra_compile_args=['-Wno-format-security'], define_macros=kernel41),
4545
Extension('Adafruit_BBIO.ADC', ['source/py_adc.c', 'source/c_adc.c', 'source/constants.c', 'source/common.c'], extra_compile_args=['-Wno-format-security'], define_macros=kernel41),
4646
Extension('Adafruit_BBIO.SPI', ['source/spimodule.c', 'source/constants.c', 'source/common.c'], extra_compile_args=['-Wno-format-security'], define_macros=kernel41),
4747
Extension('Adafruit_BBIO.UART', ['source/py_uart.c', 'source/c_uart.c', 'source/constants.c', 'source/common.c'], extra_compile_args=['-Wno-format-security'], define_macros=kernel41)] )

source/c_pinmux.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include <stdio.h>
2+
#include <string.h>
3+
4+
#include "c_pinmux.h"
5+
#include "common.h"
6+
7+
8+
// 0 = success
9+
// non-zero = error
10+
int set_pin_mode(const char *key, const char *mode)
11+
{
12+
// char ocp_dir[30] "/sys/devices/platform/ocp"
13+
char path[60]; // "/sys/devices/platform/ocp/ocp:P#_##_pinmux/state"
14+
char pinmux_dir[20]; // "ocp:P#_##_pinmux"
15+
FILE *f = NULL;
16+
17+
build_path("/sys/devices", "ocp", ocp_dir, sizeof(ocp_dir));
18+
19+
snprintf(pinmux_dir, sizeof(pinmux_dir), "ocp:%s_pinmux", key);
20+
snprintf(path, sizeof(path), "%s/%s/state", sizeof(path));
21+
22+
f = fopen(path, "w");
23+
if (NULL == f) {
24+
return -1;
25+
}
26+
27+
fprintf(f, "%s", mode);
28+
fclose(f);
29+
30+
return 0;
31+
}

source/c_pinmux.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
Copyright (c) 2016 Adafruit
3+
Author: Matthew West
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of
6+
this software and associated documentation files (the "Software"), to deal in
7+
the Software without restriction, including without limitation the rights to
8+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9+
of the Software, and to permit persons to whom the Software is furnished to do
10+
so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+
*/
23+
24+
#ifndef C_PINMUX_H
25+
#define C_PINMUX_H
26+
27+
int set_pin_mode(const char *key, const char *mode);
28+
29+
#endif

source/c_pwm.c

Lines changed: 127 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,19 @@ SOFTWARE.
2323

2424
#include <stdio.h>
2525
#include <stdlib.h>
26+
#include <sys/stat.h>
2627
#include <sys/types.h>
2728
#include <string.h>
2829
#include <fcntl.h>
2930
#include <unistd.h>
3031
#include "c_pwm.h"
3132
#include "common.h"
3233

33-
#define KEYLEN 7
34+
#ifdef BBBVERSION41
35+
#include "c_pinmux.h"
36+
#endif
3437

35-
#define PERIOD 0
36-
#define DUTY 1
38+
#define KEYLEN 7
3739

3840
int pwm_initialized = 0;
3941

@@ -85,13 +87,20 @@ void export_pwm(struct pwm_exp *new_pwm)
8587

8688
int initialize_pwm(void)
8789
{
90+
#ifdef BBBVERSION41 // don't load overlay in 4.1+
91+
if (!pwm_initialized) {
92+
#else
8893
if (!pwm_initialized && load_device_tree("am33xx_pwm")) {
89-
build_path("/sys/devices", "ocp", ocp_dir, sizeof(ocp_dir));
94+
#endif
95+
if (!build_path("/sys/devices", "ocp", ocp_dir, sizeof(ocp_dir)))
96+
{
97+
return -1;
98+
}
9099
pwm_initialized = 1;
91100
return 1;
92101
}
93102

94-
return 0;
103+
return 0;
95104
}
96105

97106
int pwm_set_frequency(const char *key, float freq) {
@@ -162,37 +171,128 @@ int pwm_set_duty_cycle(const char *key, float duty) {
162171

163172
int pwm_start(const char *key, float duty, float freq, int polarity)
164173
{
174+
#ifdef BBBVERSION41
175+
char pwm_dev_path[45]; // "/sys/devices/platform/ocp/48300000.epwmss"
176+
char pwm_addr_path[60]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm"
177+
char pwm_chip_path[75]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0"
178+
char pwm_export_path[80]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0/export"
179+
char pwm_path[80]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0/pwm1"
180+
char duty_path[90]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0/pwm1/duty_cycle"
181+
char period_path[90];
182+
char polarity_path[90];
183+
char enable_path[90];
184+
185+
int period_fd, duty_fd, polarity_fd;
186+
struct pwm_exp *new_pwm, *pwm;
187+
struct stat s;
188+
int err;
189+
FILE *f = NULL;
190+
pwm_t *p;
191+
192+
if(!pwm_initialized) {
193+
if (initialize_pwm() < 0) {
194+
return -1;
195+
}
196+
}
197+
198+
// Make sure that one of the universal capes is loaded
199+
if (!( device_tree_loaded("cape-univ-audio") // from cdsteinkuehler/beaglebone-universal-io
200+
|| device_tree_loaded("cape-univ-emmc") // ""
201+
|| device_tree_loaded("cape-univ-hdmi") // ""
202+
|| device_tree_loaded("cape-universal") // ""
203+
|| device_tree_loaded("cape-universala") // ""
204+
|| device_tree_loaded("cape-universaln") // ""
205+
|| device_tree_loaded("univ-all") // from latest BeagleBone Debian 8 images
206+
|| device_tree_loaded("univ-bbgw") // ""
207+
|| device_tree_loaded("univ-emmc") // ""
208+
|| device_tree_loaded("univ-hdmi") // ""
209+
|| device_tree_loaded("univ-nhdmi"))) // ""
210+
{
211+
return -1;
212+
}
213+
// Do pinmuxing
214+
set_pin_mode(key, "pwm");
215+
216+
// Get info for pwm
217+
if (!get_pwm_by_key(key, &p)) {
218+
return -1;
219+
}
220+
221+
build_path(ocp_dir, p->chip, pwm_dev_path, sizeof(pwm_dev_path));
222+
build_path(pwm_dev_path, p->addr, pwm_addr_path, sizeof(pwm_addr_path));
223+
build_path(pwm_addr_path, "pwm/pwmchip", pwm_chip_path, sizeof(pwm_chip_path));
224+
225+
snprintf(pwm_path, sizeof(pwm_path), "%s/pwm%d", pwm_chip_path, p->index);
226+
227+
// Export PWM if hasn't already been
228+
err = stat(pwm_path, &s);
229+
if (-1 == err) {
230+
if (ENOENT == errno) { // directory does not exist
231+
snprintf(pwm_export_path, sizeof(pwm_export_path), "%s/export", pwm_chip_path);
232+
f = fopen(pwm_export_path, "w");
233+
if (f == NULL) { // Can't open the export file
234+
return -1;
235+
}
236+
fprintf(f, "%d", p->index);
237+
fclose(f);
238+
} else {
239+
perror("stat");
240+
return -1;
241+
} else {
242+
if (S_ISDIR(s.st_mode)) {
243+
/* It is a directory. Already exported */
244+
} else {
245+
/* It's a file. Shouldn't ever happen */
246+
return -1;
247+
}
248+
}
249+
}
250+
251+
err = stat(pwm_path, &s);
252+
if (-1 == err) {
253+
if (ENOENT == errno) {
254+
// Directory still doesn't exist, exit with error
255+
return -1;
256+
}
257+
}
258+
259+
snprintf(duty_path, sizeof(duty_path), "%s/duty_cycle", pwm_path);
260+
#else
165261
char fragment[18];
166-
char pwm_test_fragment[20];
167-
char pwm_test_path[45];
262+
char pwm_fragment[20];
263+
char pwm_path[45];
264+
char duty_path[56]
168265
char period_path[50];
169-
char duty_path[50];
170266
char polarity_path[55];
171267
int period_fd, duty_fd, polarity_fd;
172-
struct pwm_exp *new_pwm, *pwm;
268+
struct pwm_exp *new_pwm;
173269

174270
if(!pwm_initialized) {
175-
initialize_pwm();
271+
if (initialize_pwm() < 0) {
272+
return -1;
273+
}
176274
}
177275

276+
// load tree
178277
snprintf(fragment, sizeof(fragment), "bone_pwm_%s", key);
179-
180-
181278
if (!load_device_tree(fragment)) {
182279
//error enabling pin for pwm
183280
return -1;
184281
}
185282

186283
//creates the fragment in order to build the pwm_test_filename, such as "pwm_test_P9_13"
187-
snprintf(pwm_test_fragment, sizeof(pwm_test_fragment), "pwm_test_%s", key);
284+
snprintf(pwm_fragment, sizeof(pwm_fragment), "pwm_test_%s", key);
285+
286+
//finds and builds the pwm_path, as it can be variable...
287+
build_path(ocp_dir, pwm_fragment, pwm_path, sizeof(pwm_path));
188288

189-
//finds and builds the pwm_test_path, as it can be variable...
190-
build_path(ocp_dir, pwm_test_fragment, pwm_test_path, sizeof(pwm_test_path));
289+
//create the path for duty
290+
snprintf(duty_path, sizeof(duty_path), "%s/duty", pwm_path);
291+
#endif
191292

192-
//create the path for the period and duty
193-
snprintf(period_path, sizeof(period_path), "%s/period", pwm_test_path);
194-
snprintf(duty_path, sizeof(duty_path), "%s/duty", pwm_test_path);
195-
snprintf(polarity_path, sizeof(polarity_path), "%s/polarity", pwm_test_path);
293+
// create the path for period and polarity
294+
snprintf(period_path, sizeof(period_path), "%s/period", pwm_path);
295+
snprintf(polarity_path, sizeof(polarity_path), "%s/polarity", pwm_path);
196296

197297
//add period and duty fd to pwm list
198298
if ((period_fd = open(period_path, O_RDWR)) < 0)
@@ -231,6 +331,14 @@ int pwm_start(const char *key, float duty, float freq, int polarity)
231331
pwm_set_polarity(key, polarity);
232332
pwm_set_duty_cycle(key, duty);
233333

334+
#ifdef BBBVERSION41 // Enable the PWM
335+
f = fopen(enable_path);
336+
if (f == NULL)
337+
return -1;
338+
fprintf(f, "1");
339+
fclose(f);
340+
#endif
341+
234342
return 1;
235343
}
236344

source/common.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ SOFTWARE.
3333
#include "Python.h"
3434
#include <dirent.h>
3535
#include <time.h>
36+
#include <string.h>
3637
#include <glob.h>
3738
#include "common.h"
3839

@@ -165,6 +166,40 @@ uart_t uart_table[] = {
165166
{ NULL, NULL, 0 }
166167
};
167168

169+
// Modeled after "pwm": submap in bone.js from bonescript
170+
// https://github.com/jadonk/bonescript/blob/master/src/bone.js#L680
171+
typedef struct pwm_t {
172+
const char *module;
173+
const int sysfs;
174+
const int index;
175+
const int muxmode;
176+
const char *path;
177+
const char *name;
178+
const char *chip;
179+
const char *addr;
180+
const char *key; // Pin name eg P9_21
181+
} pwm_t;
182+
183+
// Copied from https://github.com/jadonk/bonescript/blob/master/src/bone.js
184+
185+
pwm_t pwm_table[] = {
186+
{ "ehrpwm2", 6, 1, 4, "ehrpwm.2:1", "EHRPWM2B", "48304000", "48304200", "P8_13"},
187+
{ "ehrpwm2", 5, 0, 4, "ehrpwm.2:0", "EHRPWM2A", "48304000", "48304200", "P8_19"},
188+
{ "ehrpwm1", 4, 1, 2, "ehrpwm.1:1", "EHRPWM1B", "48302000", "48302200", "P8_34"},
189+
{ "ehrpwm1", 3, 0, 2, "ehrpwm.1:0", "EHRPWM1A", "48302000", "48302200", "P8_36"},
190+
{ "ehrpwm2", 5, 0, 3, "ehrpwm.2:0", "EHRPWM2A", "48304000", "48304200", "P8_45"},
191+
{ "ehrpwm2", 6, 1, 3, "ehrpwm.2:1", "EHRPWM2B", "48304000", "48304200", "P8_46"},
192+
{ "ehrpwm1", 3, 0, 6, "ehrpwm.1:0", "EHRPWM1A", "48302000", "48302200", "P9_14"},
193+
{ "ehrpwm1", 4, 1, 6, "ehrpwm.1:1", "EHRPWM1B", "48302000", "48302200", "P9_16"},
194+
{ "ehrpwm0", 1, 1, 3, "ehrpwm.0:1", "EHRPWM0B", "48300000", "48300200", "P9_21"},
195+
{ "ehrpwm0", 0, 0, 3, "ehrpwm.0:0", "EHRPWM0A", "48300000", "48300200", "P9_22"},
196+
{ "ecap2", 7, 2, 4, "ecap.2", "ECAPPWM2", "", "", "P9_28"},
197+
{ "ehrpwm0", 1, 1, 1, "ehrpwm.0:1", "EHRPWM0B", "48300000", "48300200", "P9_29"},
198+
{ "ehrpwm0", 0, 0, 1, "ehrpwm.0:0", "EHRPWM0A", "48300000", "48300200", "P9_31"},
199+
{ "ecap0", 2, 0, 0, "ecap.0", "ECAPPWM0", "", "", "P9_42"},
200+
{ NULL, 0, 0, 0, NULL, NULL, NULL, NULL, NULL }
201+
}
202+
168203
int lookup_gpio_by_key(const char *key)
169204
{
170205
pins_t *p;
@@ -276,6 +311,20 @@ int get_gpio_number(const char *key, unsigned int *gpio)
276311
return 0;
277312
}
278313

314+
int get_pwm_by_key(const char *key, pwm_t **pwm)
315+
{
316+
pwm_t *p;
317+
// Loop through the table
318+
for (p = pwm_table; p->key != NULL; ++p) {
319+
if (strcmp(p->key, key) == 0) {
320+
// Return the pwm_t struct
321+
*pwm = p;
322+
return 1;
323+
}
324+
}
325+
return 0;
326+
}
327+
279328
int get_pwm_key(const char *input, char *key)
280329
{
281330
if (!copy_pwm_key_by_key(input, key)) {
@@ -399,6 +448,44 @@ int load_device_tree(const char *name)
399448
return 1;
400449
}
401450

451+
// Find whether a device tree is loaded.
452+
// Returns 1 if so, <0 if error, and 0 if not.
453+
int device_tree_loaded(const char *name)
454+
{
455+
FILE *file = NULL;
456+
#ifdef BBBVERSION41
457+
char slots[41];
458+
snprintf(ctrl_dir, sizeof(ctrl_dir), "/sys/devices/platform/bone_capemgr");
459+
#else
460+
char slots[40];
461+
build_path("/sys/devices", "bone_capemgr", ctrl_dir, sizeof(ctrl_dir));
462+
#endif
463+
char line[256];
464+
char *slot_line;
465+
466+
snprintf(slots, sizeof(slots), "%s/slots", ctrl_dir);
467+
468+
469+
file = fopen(slots, "r+");
470+
if (!file) {
471+
PyErr_SetFromErrnoWithFilename(PyExc_IOError, slots);
472+
return -1;
473+
}
474+
475+
while (fgets(line, sizeof(line), file)) {
476+
//the device is loaded, close file and return true
477+
if (strstr(line, name)) {
478+
fclose(file);
479+
return 1;
480+
}
481+
}
482+
483+
//not loaded, close file
484+
fclose(file);
485+
486+
return 0;
487+
}
488+
402489
int unload_device_tree(const char *name)
403490
{
404491
FILE *file = NULL;

0 commit comments

Comments
 (0)