ESP32PWM.cpp 8.8 KB

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