Skip to content

Commit 942b57a

Browse files
authored
Added ESP32Servo
1 parent 5840f3e commit 942b57a

File tree

8 files changed

+897
-0
lines changed

8 files changed

+897
-0
lines changed

cores/esp32/ESP32PWM.cpp

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
/*
2+
* ESP32PWM.cpp
3+
*
4+
* Created on: Sep 22, 2018
5+
* Author: hephaestus
6+
*/
7+
8+
#include <ESP32PWM.h>
9+
#include "esp32-hal-ledc.h"
10+
11+
// initialize the class variable ServoCount
12+
int ESP32PWM::PWMCount = -1; // the total number of attached servos
13+
ESP32PWM * ESP32PWM::ChannelUsed[NUM_PWM]; // used to track whether a channel is in service
14+
long ESP32PWM::timerFreqSet[4] = { -1, -1, -1, -1 };
15+
int ESP32PWM::timerCount[4] = { 0, 0, 0, 0 };
16+
// The ChannelUsed array elements are 0 if never used, 1 if in use, and -1 if used and disposed
17+
// (i.e., available for reuse)
18+
19+
ESP32PWM::ESP32PWM() {
20+
resolutionBits = 8;
21+
pwmChannel = -1;
22+
pin = -1;
23+
myFreq = -1;
24+
if (PWMCount == -1) {
25+
for (int i = 0; i < NUM_PWM; i++)
26+
ChannelUsed[i] = NULL; // load invalid data into the storage array of pin mapping
27+
PWMCount = PWM_BASE_INDEX; // 0th channel does not work with the PWM system
28+
}
29+
}
30+
31+
ESP32PWM::~ESP32PWM() {
32+
// TODO Auto-generated destructor stub
33+
}
34+
35+
double ESP32PWM::_ledcSetupTimerFreq(uint8_t chan, double freq,
36+
uint8_t bit_num) {
37+
return ledcSetup(chan, freq, bit_num);
38+
39+
}
40+
41+
int ESP32PWM::timerAndIndexToChannel(int timerNum, int index) {
42+
int localIndex = 0;
43+
for (int j = 0; j < NUM_PWM; j++) {
44+
if (((j / 2) % 4) == timerNum) {
45+
if (localIndex == index) {
46+
return j;
47+
}
48+
localIndex++;
49+
}
50+
}
51+
return -1;
52+
}
53+
int ESP32PWM::allocatenext(double freq) {
54+
long freqlocal = (long) freq;
55+
if (pwmChannel < 0) {
56+
for (int i = 0; i < 4; i++) {
57+
bool freqAllocated = ((timerFreqSet[i] == freqlocal)
58+
|| (timerFreqSet[i] == -1));
59+
if (freqAllocated && timerCount[i] < 4) {
60+
if (timerFreqSet[i] == -1) {
61+
//Serial.println("Starting timer "+String(i)+" at freq "+String(freq));
62+
timerFreqSet[i] = freqlocal;
63+
}
64+
//Serial.println("Free channel timer "+String(i)+" at freq "+String(freq)+" remaining "+String(4-timerCount[i]));
65+
66+
timerNum = i;
67+
int myTimerNumber = timerAndIndexToChannel(timerNum,
68+
timerCount[timerNum]);
69+
if (myTimerNumber >= 0) {
70+
pwmChannel = myTimerNumber;
71+
Serial.println(
72+
"PWM on ledc channel #" + String(pwmChannel)
73+
+ " using 'timer " + String(timerNum)
74+
+ "' to freq " + String(freq) + "Hz");
75+
ChannelUsed[pwmChannel] = this;
76+
timerCount[timerNum]++;
77+
PWMCount++;
78+
myFreq = freq;
79+
return pwmChannel;
80+
}
81+
} else {
82+
// if(timerFreqSet[i]>0)
83+
// Serial.println("Timer freq mismatch target="+String(freq)+" on timer "+String(i)+" was "+String(timerFreqSet[i]));
84+
// else
85+
// Serial.println("Timer out of channels target="+String(freq)+" on timer "+String(i)+" was "+String(timerCount[i]));
86+
}
87+
}
88+
} else {
89+
return pwmChannel;
90+
}
91+
Serial.println(
92+
"ERROR All PWM timers allocated! Can't accomodate " + String(freq)
93+
+ "Hz\r\nHalting...");
94+
while (1)
95+
;
96+
}
97+
void ESP32PWM::deallocate() {
98+
if (pwmChannel < 0)
99+
return;
100+
Serial.println("PWM deallocating LEDc #" + String(pwmChannel));
101+
timerCount[getTimer()]--;
102+
if (timerCount[getTimer()] == 0) {
103+
timerFreqSet[getTimer()] = -1; // last pwn closed out
104+
}
105+
timerNum = -1;
106+
attachedState = false;
107+
ChannelUsed[pwmChannel] = NULL;
108+
pwmChannel = -1;
109+
PWMCount--;
110+
111+
}
112+
113+
int ESP32PWM::getChannel() {
114+
if (pwmChannel < 0) {
115+
Serial.println("FAIL! must setup() before using get channel!");
116+
}
117+
return pwmChannel;
118+
}
119+
120+
double ESP32PWM::setup(double freq, uint8_t resolution_bits) {
121+
checkFrequencyForSideEffects(freq);
122+
123+
resolutionBits = resolution_bits;
124+
if (attached()) {
125+
ledcDetachPin(pin);
126+
double val = ledcSetup(getChannel(), freq, resolution_bits);
127+
attachPin(pin);
128+
return val;
129+
}
130+
return ledcSetup(getChannel(), freq, resolution_bits);
131+
}
132+
float ESP32PWM::getDutyScaled() {
133+
return mapf((float) myDuty, 0, (float) ((1 << resolutionBits) - 1), 0.0,
134+
1.0);
135+
}
136+
void ESP32PWM::writeScaled(float duty) {
137+
write(mapf(duty, 0.0, 1.0, 0, (float) ((1 << resolutionBits) - 1)));
138+
}
139+
void ESP32PWM::write(uint32_t duty) {
140+
myDuty = duty;
141+
ledcWrite(getChannel(), duty);
142+
}
143+
void ESP32PWM::adjustFrequencyLocal(double freq, float dutyScaled) {
144+
timerFreqSet[getTimer()] = (long) freq;
145+
myFreq = freq;
146+
if (attached()) {
147+
ledcDetachPin(pin);
148+
// Remove the PWM during frequency adjust
149+
_ledcSetupTimerFreq(getChannel(), freq, resolutionBits);
150+
writeScaled(dutyScaled);
151+
ledcAttachPin(pin, getChannel()); // re-attach the pin after frequency adjust
152+
} else {
153+
_ledcSetupTimerFreq(getChannel(), freq, resolutionBits);
154+
writeScaled(dutyScaled);
155+
}
156+
}
157+
void ESP32PWM::adjustFrequency(double freq, float dutyScaled) {
158+
if(dutyScaled<0)
159+
dutyScaled=getDutyScaled();
160+
writeScaled(dutyScaled);
161+
for (int i = 0; i < timerCount[getTimer()]; i++) {
162+
int pwm = timerAndIndexToChannel(getTimer(), i);
163+
if (ChannelUsed[pwm] != NULL) {
164+
if (ChannelUsed[pwm]->myFreq != freq) {
165+
ChannelUsed[pwm]->adjustFrequencyLocal(freq,
166+
ChannelUsed[pwm]->getDutyScaled());
167+
}
168+
}
169+
}
170+
}
171+
double ESP32PWM::writeTone(double freq) {
172+
for (int i = 0; i < timerCount[getTimer()]; i++) {
173+
int pwm = timerAndIndexToChannel(getTimer(), i);
174+
if (ChannelUsed[pwm] != NULL) {
175+
if (ChannelUsed[pwm]->myFreq != freq) {
176+
ChannelUsed[pwm]->adjustFrequencyLocal(freq,
177+
ChannelUsed[pwm]->getDutyScaled());
178+
}
179+
}
180+
}
181+
182+
return 0;
183+
}
184+
double ESP32PWM::writeNote(note_t note, uint8_t octave) {
185+
const uint16_t noteFrequencyBase[12] = {
186+
// C C# D Eb E F F# G G# A Bb B
187+
4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459,
188+
7902 };
189+
190+
if (octave > 8 || note >= NOTE_MAX) {
191+
return 0;
192+
}
193+
double noteFreq = (double) noteFrequencyBase[note]
194+
/ (double) (1 << (8 - octave));
195+
return writeTone(noteFreq);
196+
}
197+
uint32_t ESP32PWM::read() {
198+
return ledcRead(getChannel());
199+
}
200+
double ESP32PWM::readFreq() {
201+
return myFreq;
202+
}
203+
void ESP32PWM::attach(int p) {
204+
pin = p;
205+
attachedState = true;
206+
}
207+
void ESP32PWM::attachPin(uint8_t pin) {
208+
209+
if (hasPwm(pin)) {
210+
attach(pin);
211+
ledcAttachPin(pin, getChannel());
212+
} else {
213+
Serial.println(
214+
"ERROR PWM channel unavailible on pin requested! " + String(pin)
215+
+ "\r\nPWM availible on: 2,4,5,12-19,21-23,25-27,32-33");
216+
return;
217+
}
218+
//Serial.print(" on pin "+String(pin));
219+
}
220+
void ESP32PWM::attachPin(uint8_t pin, double freq, uint8_t resolution_bits) {
221+
222+
if (hasPwm(pin))
223+
setup(freq, resolution_bits);
224+
attachPin(pin);
225+
}
226+
void ESP32PWM::detachPin(int pin) {
227+
ledcDetachPin(pin);
228+
deallocate();
229+
}
230+
/* Side effects of frequency changes happen because of shared timers
231+
*
232+
* LEDC Chan to Group/Channel/Timer Mapping
233+
** ledc: 0 => Group: 0, Channel: 0, Timer: 0
234+
** ledc: 1 => Group: 0, Channel: 1, Timer: 0
235+
** ledc: 2 => Group: 0, Channel: 2, Timer: 1
236+
** ledc: 3 => Group: 0, Channel: 3, Timer: 1
237+
** ledc: 4 => Group: 0, Channel: 4, Timer: 2
238+
** ledc: 5 => Group: 0, Channel: 5, Timer: 2
239+
** ledc: 6 => Group: 0, Channel: 6, Timer: 3
240+
** ledc: 7 => Group: 0, Channel: 7, Timer: 3
241+
** ledc: 8 => Group: 1, Channel: 0, Timer: 0
242+
** ledc: 9 => Group: 1, Channel: 1, Timer: 0
243+
** ledc: 10 => Group: 1, Channel: 2, Timer: 1
244+
** ledc: 11 => Group: 1, Channel: 3, Timer: 1
245+
** ledc: 12 => Group: 1, Channel: 4, Timer: 2
246+
** ledc: 13 => Group: 1, Channel: 5, Timer: 2
247+
** ledc: 14 => Group: 1, Channel: 6, Timer: 3
248+
** ledc: 15 => Group: 1, Channel: 7, Timer: 3
249+
*/
250+
251+
bool ESP32PWM::checkFrequencyForSideEffects(double freq) {
252+
253+
allocatenext(freq);
254+
for (int i = 0; i < timerCount[getTimer()]; i++) {
255+
int pwm = timerAndIndexToChannel(getTimer(), i);
256+
257+
if (pwm == pwmChannel)
258+
continue;
259+
if (ChannelUsed[pwm] != NULL)
260+
if (ChannelUsed[pwm]->getTimer() == getTimer()) {
261+
double diff = abs(ChannelUsed[pwm]->myFreq - freq);
262+
if (abs(diff) > 0.1) {
263+
Serial.println(
264+
"\tWARNING PWM channel " + String(pwmChannel)
265+
+ " shares a timer with channel "
266+
+ String(pwm) + "\n"
267+
"\tchanging the frequency to "
268+
+ String(freq)
269+
+ " Hz will ALSO change channel "
270+
+ String(pwm)
271+
+ " \n\tfrom its previous frequency of "
272+
+ String(ChannelUsed[pwm]->myFreq) + " Hz\n"
273+
" ");
274+
ChannelUsed[pwm]->myFreq = freq;
275+
}
276+
}
277+
}
278+
return true;
279+
}
280+
281+
ESP32PWM* pwmFactory(int pin) {
282+
for (int i = 0; i < NUM_PWM; i++)
283+
if (ESP32PWM::ChannelUsed[i] != NULL) {
284+
if (ESP32PWM::ChannelUsed[i]->getPin() == pin)
285+
return ESP32PWM::ChannelUsed[i];
286+
}
287+
return NULL;
288+
}

0 commit comments

Comments
 (0)