forked from modmaker/BeBoPr
/
pwm.c
156 lines (142 loc) · 4.39 KB
/
pwm.c
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include "pwm.h"
#include "debug.h"
struct pwm_channel_record {
channel_tag id;
const char* device_path;
unsigned int frequency;
int duty_fd;
};
static struct pwm_channel_record* pwm_channels;
static unsigned int num_pwm_channels;
static int pwm_index_lookup( channel_tag pwm_channel)
{
for (int ix = 0 ; ix < num_pwm_channels ; ++ix) {
if (pwm_channels[ ix].id == pwm_channel) {
return ix;
}
}
if (debug_flags & DEBUG_PWM) {
fprintf( stderr, "pwm_index_lookup failed for '%s'\n", tag_name( pwm_channel));
}
return -1;
}
/*
* Configuration settings are stored seperately (bebopr_rx.c) and
* a configuration call is used to communicate these with this
* code.
*/
static pwm_config_record* pwm_config_data = NULL;
static int pwm_config_items = 0;
int pwm_config( pwm_config_record* config_data, int nr_config_items)
{
if (debug_flags & DEBUG_PWM) {
printf( "pwm_config called with %d records'\n", nr_config_items);
}
pwm_config_data = config_data;
pwm_config_items = nr_config_items;
// this mustn't be called more than once, so keep the code simple
pwm_channels = calloc( nr_config_items, sizeof( struct pwm_channel_record));
for (int ch = 0 ; ch < nr_config_items ; ++ch) {
pwm_channels[ ch].duty_fd = -1;
}
num_pwm_channels = 0;
return 0;
}
static int pwm_write_int_to_file( const char* path, const char* fname, int value)
{
char s[ 100];
snprintf( s, sizeof( s), "%s/%s", path, fname);
int fd = open( s, O_WRONLY);
if (fd < 0) {
perror( "pwm_write_int_to_file: open failed");
fprintf( stderr, "pwm_write_int_to_file: open failed for file '%s'\n", fname);
return -1;
}
snprintf( s, sizeof( s), "%d", value);
int count = strlen( s);
int result = write( fd, s, count);
if (result < 0) {
// TODO: do we need strerror_r ?
fprintf( stderr, "pwm_write_int_to_file: write to '%s' failed: %s\n", fname, strerror( errno));
} else if (result == count) {
result = 0;
} else {
fprintf( stderr, "pwm_write_int_to_file: short write on file '%s'\n", fname);
result = -1;
}
close( fd);
return result;
}
void pwm_exit( void)
{
if (debug_flags & DEBUG_PWM) {
printf( "pwm_exit called, releasing PWM subsystem\n");
}
for (int ch = 0 ; ch < num_pwm_channels ; ++ch) {
if (pwm_channels[ ch].duty_fd != -1) {
struct pwm_channel_record* pd = &pwm_channels[ ch];
pwm_set_output( pd->id, 0);
pwm_write_int_to_file( pd->device_path, "run", 0);
pwm_write_int_to_file( pd->device_path, "request", 0);
close (pd->duty_fd);
}
}
}
int pwm_init( void)
{
if (debug_flags & DEBUG_PWM) {
printf( "pwm_init called'\n");
}
if (pwm_config_data) {
char s[ 100];
atexit( pwm_exit);
for (int ch = 0 ; ch < pwm_config_items ; ++ch) {
pwm_config_record* ps = &pwm_config_data[ ch];
struct pwm_channel_record* pd = &pwm_channels[ ch];
pd->id = ps->tag;
pd->device_path = ps->device_path;
pd->frequency = ps->frequency;
pd->duty_fd = -1;
++num_pwm_channels;
pwm_write_int_to_file( pd->device_path, "request", 1);
pwm_write_int_to_file( pd->device_path, "polarity", 0);
pwm_write_int_to_file( pd->device_path, "duty_percent", 0);
if (pd->frequency) {
pwm_write_int_to_file( pd->device_path, "period_freq", pd->frequency);
}
snprintf( s, sizeof( s), "%s/duty_percent", pwm_channels[ ch].device_path);
pwm_channels[ ch].duty_fd = open( s, O_WRONLY);
if (pwm_channels[ ch].duty_fd < 0) {
perror( "pwm_init: failed to open 'duty_percent' file");
}
pwm_set_output( pd->id, 0);
pwm_write_int_to_file( pd->device_path, "run", 1);
}
return 0;
}
fprintf( stderr, "pwm_init: no configuration data!\n");
return -1;
}
int pwm_set_output( channel_tag pwm_channel, unsigned int percentage)
{
int ix = pwm_index_lookup( pwm_channel);
if (ix >= 0 && percentage <= 100) {
char s[ 10];
snprintf( s, sizeof( s), "%d", percentage);
int count = strlen( s);
int result = write( pwm_channels[ ix].duty_fd, s, count);
if (result < 0) {
perror( "pwm_set_output: error writing to duty_fd file");
} else if (result == count) {
return 0;
}
}
return -1;
}