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