softPwm.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. /*
  2. * softPwm.c:
  3. * Provide 2 channels of software driven PWM.
  4. * Copyright (c) 2012-2014 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. // pins that are on GPIO expanders. It's not that efficient and more than 1 or
  32. // 2 pins on e.g. (SPI) mcp23s17 won't really be that effective, however...
  33. #define MAX_PINS 1024
  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. // 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. pin &= (MAX_PINS - 1) ;
  89. /**/ if (value < 0)
  90. value = 0 ;
  91. else if (value > range [pin])
  92. value = range [pin] ;
  93. marks [pin] = value ;
  94. }
  95. /*
  96. * softPwmCreate:
  97. * Create a new softPWM thread.
  98. *********************************************************************************
  99. */
  100. int softPwmCreate (int pin, int initialValue, int pwmRange)
  101. {
  102. int res ;
  103. pthread_t myThread ;
  104. int *passPin ;
  105. if (range [pin] != 0) // Already running on this pin
  106. return -1 ;
  107. if (pwmRange <= 0)
  108. return -1 ;
  109. passPin = malloc (sizeof (*passPin)) ;
  110. if (passPin == NULL)
  111. return -1 ;
  112. pinMode (pin, OUTPUT) ;
  113. digitalWrite (pin, LOW) ;
  114. marks [pin] = initialValue ;
  115. range [pin] = pwmRange ;
  116. *passPin = pin ;
  117. newPin = pin ;
  118. res = pthread_create (&myThread, NULL, softPwmThread, (void *)passPin) ;
  119. while (newPin != -1)
  120. delay (1) ;
  121. threads [pin] = myThread ;
  122. return res ;
  123. }
  124. /*
  125. * softPwmStop:
  126. * Stop an existing softPWM thread
  127. *********************************************************************************
  128. */
  129. void softPwmStop (int pin)
  130. {
  131. if (range [pin] != 0)
  132. {
  133. pthread_cancel (threads [pin]) ;
  134. pthread_join (threads [pin], NULL) ;
  135. range [pin] = 0 ;
  136. digitalWrite (pin, LOW) ;
  137. }
  138. }