-
Notifications
You must be signed in to change notification settings - Fork 29
/
pwm-output.js
137 lines (112 loc) · 4.31 KB
/
pwm-output.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
'use strict';
var fs = require('fs'),
glob = require('glob'),
pinmux = require('./pinmux');
var OCP_PATH = '/sys/devices/platform/ocp/',
FS_OPTIONS = {encoding: 'utf8'};
function pwmChipPattern(pinData) {
return OCP_PATH +
pinData.custom.pwm.module.subSystem.addr.toString(16) +
'.epwmss/' +
pinData.custom.pwm.module.addr.toString(16) +
'.' +
pinData.custom.pwm.module.suffix +
'/pwm/pwmchip*';
}
function waitForAccessPermission(paths) {
// This is a bit of a hack but it works and it's not as bad as it initially
// looks. When a pwm is exported with kernel v4.11+ udev will set the
// permissions on the files used to control pwm to allow these files to be
// accessed without root privileges. It takes udev a while to set the
// permissions and we need to wait for it to complete its work to avoid
// EACCES errors. To determine when udev has finished its work we
// continuously try to access the appropriate files until they are
// successfully accessed. We stop after 10000 tries. Under normal conditions
// approximately 200 tries are needed to successfully access the first path
// in the paths array. The remaining paths can typically be accessed
// successfully on the first try. On kernels prior to v4.11 the code will
// actually attempt to access the file 10000 times if Node.js was started
// without root privileges. The 10000 attemps take approximately 2 seconds
// to complete and in the end an EACCES is thrown.
paths.forEach(function (path) {
var tries = 0,
fd;
while (true) {
try {
tries += 1;
fd = fs.openSync(path, 'r+');
fs.closeSync(fd);
break;
} catch (e) {
if (tries === 10000) {
throw e;
}
}
}
});
}
function PwmOutput(pinData, period) {
var matches,
pwmChipPath,
pwmChannelPattern,
pwmChannelPath,
channel;
if (!(this instanceof PwmOutput)) {
return new PwmOutput(pinData, period);
}
// The paths for PWM files vary from kernel to kernel.
// On kernel v4.4 and v4.9 they will look something like this:
// /sys/devices/platform/ocp/48302000.epwmss/48302200.pwm/pwm/pwmchip*/pwm0/duty_cycle
// On kernel v4.11+ they will look something like this:
// /sys/devices/platform/ocp/48302000.epwmss/48302200.pwm/pwm/pwmchip*/pwm-*:0/duty_cycle
matches = glob.sync(pwmChipPattern(pinData));
if (matches.length !== 1) {
throw new Error(
'Can\'t find unique directory macthing "' + pwmChipPattern(pinData) + '"'
);
}
pwmChipPath = matches[0];
channel = pinData.custom.pwm.channel;
pwmChannelPattern = pwmChipPath + '/pwm*' + channel;
matches = glob.sync(pwmChannelPattern);
if (matches.length === 0) {
// The pwm channel hasn't been exported yet, so export it.
fs.writeFileSync(pwmChipPath + '/export', channel, FS_OPTIONS);
matches = glob.sync(pwmChannelPattern);
}
if (matches.length !== 1) {
throw new Error(
'Can\'t find unique directory macthing "' + pwmChannelPattern + '"'
);
}
pwmChannelPath = matches[0];
// On kernel v4.11+ we wait for udev to set the permissions on the files
// used to control pwm enabling to be accessed without root privileges.
waitForAccessPermission([
pwmChannelPath + '/period',
pwmChannelPath + '/enable',
pwmChannelPath + '/duty_cycle'
]);
this.period = period;
fs.writeFileSync(pwmChannelPath + '/period', period, FS_OPTIONS);
this.enableFd = fs.openSync(pwmChannelPath + '/enable', 'r+');
fs.writeSync(this.enableFd, 1, FS_OPTIONS);
this.dutyCycleFd = fs.openSync(pwmChannelPath + '/duty_cycle', 'r+');
pinmux.state(pinData.custom.id, 'pwm');
}
PwmOutput.prototype.pwmWrite = function (dutyCycle) {
// There are glitches if an attempt is made to set the duty cycle to a value
// less that 10. This is very visible when pulsing an LED. Setting the duty
// cycle to a value less than 10 should make the LED very very dim, however,
// it results in the LED becoming very bright for a fraction of a second.
// Disabling PWM while setting the duty cycle appears to work around the
// issue.
if (dutyCycle < 10) {
fs.writeSync(this.enableFd, 0, FS_OPTIONS);
}
fs.writeSync(this.dutyCycleFd, dutyCycle, FS_OPTIONS);
if (dutyCycle < 10) {
fs.writeSync(this.enableFd, 1, FS_OPTIONS);
}
};
module.exports = PwmOutput;