softPwm.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /*
  2. * softPwm.c:
  3. * Provide many channels of software driven PWM.
  4. * Copyright (c) 2012-2017 Gordon Henderson
  5. ***********************************************************************
  6. * This file is part of wiringPi:
  7. * https://projects.drogon.net/raspberry-pi/wiringpi/
  8. *
  9. * wiringPi is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Lesser General Public License as
  11. * published by the Free Software Foundation, either version 3 of the
  12. * License, or (at your option) any later version.
  13. *
  14. * wiringPi is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with wiringPi.
  21. * If not, see <http://www.gnu.org/licenses/>.
  22. ***********************************************************************
  23. */
  24. #include <stdio.h>
  25. #include <malloc.h>
  26. #include <pthread.h>
  27. #include "wiringPi.h"
  28. #include "softPwm.h"
  29. // MAX_PINS:
  30. // This is more than the number of Pi pins because we can actually softPwm.
  31. // Once upon a time I let pins on gpio expanders be softPwm'd, but it's really
  32. // really not a good thing.
  33. #define MAX_PINS 64
  34. // The PWM Frequency is derived from the "pulse time" below. Essentially,
  35. // the frequency is a function of the range and this pulse time.
  36. // The total period will be range * pulse time in µS, so a pulse time
  37. // of 100 and a range of 100 gives a period of 100 * 100 = 10,000 µS
  38. // which is a frequency of 100Hz.
  39. //
  40. // It's possible to get a higher frequency by lowering the pulse time,
  41. // however CPU uage will skyrocket as wiringPi uses a hard-loop to time
  42. // periods under 100µS - this is because the Linux timer calls are just
  43. // not accurate at all, and have an overhead.
  44. //
  45. // Another way to increase the frequency is to reduce the range - however
  46. // that reduces the overall output accuracy...
  47. #define PULSE_TIME 100
  48. static volatile int marks [MAX_PINS] ;
  49. static volatile int range [MAX_PINS] ;
  50. static volatile pthread_t threads [MAX_PINS] ;
  51. static volatile int newPin = -1 ;
  52. /*
  53. * softPwmThread:
  54. * Thread to do the actual PWM output
  55. *********************************************************************************
  56. */
  57. static void *softPwmThread (void *arg)
  58. {
  59. int pin, mark, space ;
  60. struct sched_param param ;
  61. param.sched_priority = sched_get_priority_max (SCHED_RR) ;
  62. pthread_setschedparam (pthread_self (), SCHED_RR, &param) ;
  63. pin = *((int *)arg) ;
  64. free (arg) ;
  65. pin = newPin ;
  66. newPin = -1 ;
  67. piHiPri (90) ;
  68. for (;;)
  69. {
  70. mark = marks [pin] ;
  71. space = range [pin] - mark ;
  72. if (mark != 0)
  73. digitalWrite (pin, HIGH) ;
  74. delayMicroseconds (mark * 100) ;
  75. if (space != 0)
  76. digitalWrite (pin, LOW) ;
  77. delayMicroseconds (space * 100) ;
  78. }
  79. return NULL ;
  80. }
  81. /*
  82. * softPwmWrite:
  83. * Write a PWM value to the given pin
  84. *********************************************************************************
  85. */
  86. void softPwmWrite (int pin, int value)
  87. {
  88. if (pin < MAX_PINS)
  89. {
  90. /**/ if (value < 0)
  91. value = 0 ;
  92. else if (value > range [pin])
  93. value = range [pin] ;
  94. marks [pin] = value ;
  95. }
  96. }
  97. /*
  98. * softPwmCreate:
  99. * Create a new softPWM thread.
  100. *********************************************************************************
  101. */
  102. int softPwmCreate (int pin, int initialValue, int pwmRange)
  103. {
  104. int res ;
  105. pthread_t myThread ;
  106. int *passPin ;
  107. if (pin >= MAX_PINS)
  108. return -1 ;
  109. if (range [pin] != 0) // Already running on this pin
  110. return -1 ;
  111. if (pwmRange <= 0)
  112. return -1 ;
  113. passPin = malloc (sizeof (*passPin)) ;
  114. if (passPin == NULL)
  115. return -1 ;
  116. digitalWrite (pin, LOW) ;
  117. pinMode (pin, OUTPUT) ;
  118. marks [pin] = initialValue ;
  119. range [pin] = pwmRange ;
  120. *passPin = pin ;
  121. newPin = pin ;
  122. res = pthread_create (&myThread, NULL, softPwmThread, (void *)passPin) ;
  123. if (res != 0)
  124. return res ;
  125. while (newPin != -1)
  126. delay (1) ;
  127. threads [pin] = myThread ;
  128. return res ;
  129. }
  130. /*
  131. * softPwmStop:
  132. * Stop an existing softPWM thread
  133. *********************************************************************************
  134. */
  135. void softPwmStop (int pin)
  136. {
  137. if (pin < MAX_PINS)
  138. {
  139. if (range [pin] != 0)
  140. {
  141. pthread_cancel (threads [pin]) ;
  142. pthread_join (threads [pin], NULL) ;
  143. range [pin] = 0 ;
  144. digitalWrite (pin, LOW) ;
  145. }
  146. }
  147. }