Skip to content

Commit 8e965a1

Browse files
author
Demo User
committed
add workaround to allow changing pwm frequency
1 parent c86cb7d commit 8e965a1

File tree

5 files changed

+109
-12
lines changed

5 files changed

+109
-12
lines changed

examples/src/rc_test_motors.c

+11-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ void print_usage()
3131
printf("\n");
3232
printf("-d {duty} define a duty cycle from -1.0 to 1.0\n");
3333
printf("-b enable motor brake function\n");
34+
printf("-F {freq} set a custom pwm frequency in HZ, otherwise default 25000 is used\n");
3435
printf("-f enable free spin function\n");
3536
printf("-s {duty} sweep motors back and forward at duty cycle\n");
3637
printf("-m {motor} specify a single motor from 1-4, otherwise all will be driven\n");
@@ -51,11 +52,12 @@ int main(int argc, char *argv[])
5152
double duty = 0.0;
5253
int ch = 0; // assume all motor unless set otherwise
5354
int c, in;
55+
int freq_hz = RC_MOTOR_DEFAULT_PWM_FREQ;
5456
m_mode_t m_mode = DISABLED;
5557

5658
// parse arguments
5759
opterr = 0;
58-
while ((c = getopt(argc, argv, "m:d:fbs:h")) != -1){
60+
while ((c = getopt(argc, argv, "m:d:F:fbs:h")) != -1){
5961
switch (c){
6062
case 'm': // motor channel option
6163
in = atoi(optarg);
@@ -78,6 +80,13 @@ int main(int argc, char *argv[])
7880
return -1;
7981
}
8082
break;
83+
case 'F': // pwm frequency option
84+
freq_hz = atoi(optarg);
85+
if(freq_hz<1){
86+
fprintf(stderr,"PWM frequency must be >=1\n");
87+
return -1;
88+
}
89+
break;
8190
case 'f':
8291
if(m_mode!=DISABLED) print_usage();
8392
m_mode = FREE;
@@ -119,7 +128,7 @@ int main(int argc, char *argv[])
119128
running =1;
120129

121130
// initialize hardware first
122-
if(rc_motor_init()) return -1;
131+
if(rc_motor_init_freq(freq_hz)) return -1;
123132

124133
// decide what to do
125134
switch(m_mode){

library/Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ prefix ?= /usr
4646
$(TARGET): $(OBJECTS)
4747
@mkdir -p $(LIBDIR)
4848
@$(LINKER) -o $(TARGET) $(OBJECTS) $(LDFLAGS)
49-
@ln -s $(FULLNAME) $(LIBDIR)/$(SONAME)
49+
@ln -sf $(FULLNAME) $(LIBDIR)/$(SONAME)
5050
@echo "Done making $(TARGET)"
5151

5252
# rule for math libs
@@ -84,7 +84,7 @@ install:
8484
@cp -r include/* $(DESTDIR)$(prefix)/include
8585
@$(INSTALLDIR) $(DESTDIR)$(prefix)/lib
8686
@$(INSTALL) $(TARGET) $(DESTDIR)$(prefix)/lib
87-
@ln -s $(FULLNAME) $(DESTDIR)$(prefix)/lib/$(SONAME)
87+
@ln -sf $(FULLNAME) $(DESTDIR)$(prefix)/lib/$(SONAME)
8888
@echo "Library Install Complete"
8989

9090
# cleanup local binaries

library/include/rc/motor.h

+25-1
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,44 @@
2525
extern "C" {
2626
#endif
2727

28+
#define RC_MOTOR_DEFAULT_PWM_FREQ 25000 // 25kHz
29+
2830

2931
/**
3032
* @brief Initializes all 4 motors and leaves them in a free-spin (0
3133
* throttle) state.
3234
*
3335
* Note, if the user is optionally using the rc_motor_standby
3436
* functionality they should be aware that rc_motor_init starts
35-
* standby mode in a disabled state so it is not necessary for the majority of users who are not interested in standby mode to
37+
* standby mode in a disabled state so it is not necessary for the
38+
* majority of users who are not interested in standby mode.
39+
*
40+
* This starts the motor drivers at RC_MOTOR_DEFAULT_PWM_FREQ. To
41+
* use another frequency initialize with rc_motor_init_freq instead.
3642
*
3743
* @return 0 on success, -1 on failure which is usually due to lack of user
3844
* permissions to access the gpio and pwm systems.
3945
*/
4046
int rc_motor_init();
4147

48+
/**
49+
* @brief Just like rc_motor_init but allows the user to set the pwm
50+
* frequency
51+
*
52+
* RC_MOTOR_DEFAULT_PWM_FREQ is a good frequency to start at.
53+
*
54+
* Note, if the user is optionally using the rc_motor_standby
55+
* functionality they should be aware that rc_motor_init starts
56+
* standby mode in a disabled state so it is not necessary for the
57+
* majority of users who are not interested in standby mode.
58+
*
59+
* @param[in] pwm_frequency_hz The pwm frequency in hz
60+
*
61+
* @return 0 on success, -1 on failure which is usually due to lack of user
62+
* permissions to access the gpio and pwm systems.
63+
*/
64+
int rc_motor_init_freq(int pwm_frequency_hz);
65+
4266
/**
4367
* @brief Puts all 4 motors into a free-spin (0 throttle) state, puts the
4468
* h-bridges into standby mode, and closes all file pointers to GPIO

library/src/io/pwm.c

+62-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <string.h>
1313
#include <errno.h>
1414
#include <rc/pwm.h>
15+
#include <rc/time.h>
1516

1617
#define MIN_HZ 1
1718
#define MAX_HZ 1000000000
@@ -90,6 +91,42 @@ int __export_channels(int ss)
9091
return 0;
9192
}
9293

94+
/**
95+
* @brief unexports A and B pwm channels
96+
*
97+
* @param[in] ss subsystem, to export
98+
*
99+
* @return 0 on succcess, -1 on failure
100+
*/
101+
int __unexport_channels(int ss)
102+
{
103+
int unexport_fd=0;
104+
char buf[MAXBUF];
105+
int len;
106+
107+
// open export file for that subsystem
108+
len = snprintf(buf, sizeof(buf), BASE_DIR "%d/unexport", ss*2);
109+
unexport_fd = open(buf, O_WRONLY);
110+
if(unlikely(unexport_fd<0)){
111+
perror("ERROR in rc_pwm_init, can't open pwm unexport file for writing");
112+
fprintf(stderr,"Probably kernel or BeagleBone image is too old\n");
113+
return -1;
114+
}
115+
len=write(unexport_fd, "0", 2);
116+
if(unlikely(len<0 && errno!=EBUSY && errno!=ENODEV)){
117+
perror("ERROR: in rc_pwm_init, failed to write 0 to unexport file");
118+
return -1;
119+
}
120+
len=write(unexport_fd, "1", 2);
121+
if(unlikely(len<0 && errno!=EBUSY && errno!=ENODEV)){
122+
perror("ERROR: in rc_pwm_init, failed to write 1 to unexport file");
123+
return -1;
124+
}
125+
close(unexport_fd);
126+
return 0;
127+
}
128+
129+
93130
int rc_pwm_init(int ss, int frequency)
94131
{
95132
int periodA_fd; // pointers to frequency file descriptor
@@ -111,9 +148,17 @@ int rc_pwm_init(int ss, int frequency)
111148
return -1;
112149
}
113150

114-
// export channels first
151+
// unexport then export channels first
152+
if(__unexport_channels(ss)==-1) return -1;
115153
if(__export_channels(ss)==-1) return -1;
116154

155+
// wait for udev to set correct permissions
156+
157+
//system("ls -la /sys/class/pwm/pwmchip4/pwm-4:0/");
158+
//system("udevadm trigger");
159+
//rc_usleep(1000000);
160+
//system("ls -la /sys/class/pwm/pwmchip4/pwm-4:0/");
161+
117162
#ifdef DEBUG
118163
printf("pwm ss:%d mode:%d\n",ss,mode);
119164
#endif
@@ -122,11 +167,18 @@ int rc_pwm_init(int ss, int frequency)
122167
if(mode==0) len = snprintf(buf, sizeof(buf), BASE_DIR "%d/pwm0/duty_cycle", ss*2); // mode 0
123168
else len = snprintf(buf, sizeof(buf), BASE_DIR "%d/pwm-%d:0/duty_cycle", ss*2, ss*2); // mode 1
124169
dutyA_fd[ss] = open(buf,O_WRONLY);
170+
125171
if(unlikely(dutyA_fd[ss]==-1)){
126-
perror("ERROR in rc_pwm_init, failed to open duty_cycle channel A FD");
127-
fprintf(stderr,"tried accessing: %s\n", buf);
128-
return -1;
172+
// first error is probably from udev being slow, wait a bit and try again
173+
rc_usleep(600000);
174+
dutyA_fd[ss] = open(buf,O_WRONLY);
175+
if(unlikely(dutyA_fd[ss]==-1)){
176+
perror("ERROR in rc_pwm_init, failed to open duty_cycle channel A FD");
177+
fprintf(stderr,"tried accessing: %s\n", buf);
178+
return -1;
179+
}
129180
}
181+
130182
if(mode==0) len = snprintf(buf, sizeof(buf), BASE_DIR "%d/pwm1/duty_cycle", ss*2); // mode 0
131183
else len = snprintf(buf, sizeof(buf), BASE_DIR "%d/pwm-%d:1/duty_cycle", ss*2, ss*2); // mode 1
132184
dutyB_fd[ss] = open(buf,O_WRONLY);
@@ -295,6 +347,12 @@ int rc_pwm_cleanup(int ss)
295347
// close fds
296348
close(enableA_fd);
297349
close(enableB_fd);
350+
close(dutyA_fd[ss]);
351+
close(dutyB_fd[ss]);
352+
353+
// unexport channels, not critical if this fails since everything else
354+
// has been closed
355+
__unexport_channels(ss);
298356

299357
init_flag[ss] = 0;
300358
return 0;

library/src/motor.c

+9-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
#define MOT_STBY 0,20 //gpio0.20 P9.41
4242

4343
#define CHANNELS 4
44-
#define PWM_FREQ 25000 // 25kHz
44+
4545

4646
// polarity of the motor connections
4747
static const double polarity[]={1.0,-1.0,-1.0,1.0};
@@ -59,6 +59,12 @@ static int pwmch[CHANNELS];
5959

6060

6161
int rc_motor_init()
62+
{
63+
return rc_motor_init_freq(RC_MOTOR_DEFAULT_PWM_FREQ);
64+
}
65+
66+
67+
int rc_motor_init_freq(int pwm_frequency_hz)
6268
{
6369
int i;
6470

@@ -108,11 +114,11 @@ int rc_motor_init()
108114
pwmch[3]='B';
109115

110116
// set up pwm channels
111-
if(unlikely(rc_pwm_init(1,PWM_FREQ))){
117+
if(unlikely(rc_pwm_init(1,pwm_frequency_hz))){
112118
fprintf(stderr,"ERROR in rc_motor_init, failed to initialize pwm subsystem 1\n");
113119
return -1;
114120
}
115-
if(unlikely(rc_pwm_init(2,PWM_FREQ))){
121+
if(unlikely(rc_pwm_init(2,pwm_frequency_hz))){
116122
fprintf(stderr,"ERROR in rc_motor_init, failed to initialize pwm subsystem 2\n");
117123
return -1;
118124
}

0 commit comments

Comments
 (0)