test_pwm.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. /*
  2. * c-periphery
  3. * https://github.com/vsergeev/c-periphery
  4. * License: MIT
  5. */
  6. #include "test.h"
  7. #include <stdlib.h>
  8. #include <stdbool.h>
  9. #include <string.h>
  10. #include <unistd.h>
  11. #include "../src/pwm.h"
  12. unsigned int chip;
  13. unsigned int channel;
  14. void test_arguments(void) {
  15. ptest();
  16. /* No real argument validation needed in the PWM wrapper */
  17. }
  18. static double fabs(double x) {
  19. return (x < 0) ? -x : x;
  20. }
  21. void test_open_config_close(void) {
  22. pwm_t *pwm;
  23. uint64_t period_ns;
  24. uint64_t duty_cycle_ns;
  25. double period;
  26. double frequency;
  27. double duty_cycle;
  28. pwm_polarity_t polarity;
  29. bool enabled;
  30. ptest();
  31. /* Allocate PWM */
  32. pwm = pwm_new();
  33. passert(pwm != NULL);
  34. /* Open non-existent PWM chip */
  35. passert(pwm_open(pwm, 9999, channel) == PWM_ERROR_OPEN);
  36. /* Open non-existent PWM channel */
  37. passert(pwm_open(pwm, chip, 9999) == PWM_ERROR_OPEN);
  38. /* Open legitimate PWM chip/channel */
  39. passert(pwm_open(pwm, chip, channel) == 0);
  40. /* Check properties */
  41. passert(pwm_chip(pwm) == chip);
  42. passert(pwm_channel(pwm) == channel);
  43. /* Initialize period and duty cycle */
  44. passert(pwm_set_period(pwm, 5e-3) == 0);
  45. passert(pwm_set_duty_cycle(pwm, 0) == 0);
  46. /* Set period, check period, check period_ns, check frequency */
  47. passert(pwm_set_period(pwm, 1e-3) == 0);
  48. passert(pwm_get_period(pwm, &period) == 0);
  49. passert(fabs(period - 1e-3) < 1e-4);
  50. passert(pwm_get_period_ns(pwm, &period_ns) == 0);
  51. passert(fabs(period_ns - 1000000) < 1e5);
  52. passert(pwm_get_frequency(pwm, &frequency) == 0);
  53. passert(fabs(frequency - 1000) < 100);
  54. passert(pwm_set_period(pwm, 5e-4) == 0);
  55. passert(pwm_get_period(pwm, &period) == 0);
  56. passert(fabs(period - 5e-4) < 1e-5);
  57. passert(pwm_get_period_ns(pwm, &period_ns) == 0);
  58. passert(fabs(period_ns - 500000) < 1e4);
  59. passert(pwm_get_frequency(pwm, &frequency) == 0);
  60. passert(fabs(frequency - 2000) < 100);
  61. /* Set frequency, check frequency, check period, check period_ns */
  62. passert(pwm_set_frequency(pwm, 1000) == 0);
  63. passert(pwm_get_frequency(pwm, &frequency) == 0);
  64. passert(fabs(frequency - 1000) < 100);
  65. passert(pwm_get_period(pwm, &period) == 0);
  66. passert(fabs(period - 1e-3) < 1e-4);
  67. passert(pwm_get_period_ns(pwm, &period_ns) == 0);
  68. passert(fabs(period_ns - 1000000) < 1e5);
  69. passert(pwm_set_frequency(pwm, 2000) == 0);
  70. passert(pwm_get_frequency(pwm, &frequency) == 0);
  71. passert(fabs(frequency - 2000) < 100);
  72. passert(pwm_get_period(pwm, &period) == 0);
  73. passert(fabs(period - 5e-4) < 1e-5);
  74. passert(pwm_get_period_ns(pwm, &period_ns) == 0);
  75. passert(fabs(period_ns - 500000) < 1e4);
  76. /* Set period_ns, check period_ns, check period, check frequency */
  77. passert(pwm_set_period_ns(pwm, 1000000) == 0);
  78. passert(pwm_get_period_ns(pwm, &period_ns) == 0);
  79. passert(fabs(period_ns - 1000000) < 1e5);
  80. passert(pwm_get_period(pwm, &period) == 0);
  81. passert(fabs(period - 1e-3) < 1e-4);
  82. passert(pwm_get_frequency(pwm, &frequency) == 0);
  83. passert(fabs(frequency - 1000) < 100);
  84. passert(pwm_set_period_ns(pwm, 500000) == 0);
  85. passert(pwm_get_period_ns(pwm, &period_ns) == 0);
  86. passert(fabs(period_ns - 500000) < 1e4);
  87. passert(pwm_get_period(pwm, &period) == 0);
  88. passert(fabs(period - 5e-4) < 1e-5);
  89. passert(pwm_get_frequency(pwm, &frequency) == 0);
  90. passert(fabs(frequency - 2000) < 100);
  91. passert(pwm_set_period_ns(pwm, 1000000) == 0);
  92. /* Set duty cycle, check duty cycle, check duty_cycle_ns */
  93. passert(pwm_set_duty_cycle(pwm, 0.25) == 0);
  94. passert(pwm_get_duty_cycle(pwm, &duty_cycle) == 0);
  95. passert(fabs(duty_cycle - 0.25) < 1e-3);
  96. passert(pwm_get_duty_cycle_ns(pwm, &duty_cycle_ns) == 0);
  97. passert(fabs(duty_cycle_ns - 250000) < 1e4);
  98. passert(pwm_set_duty_cycle(pwm, 0.50) == 0);
  99. passert(pwm_get_duty_cycle(pwm, &duty_cycle) == 0);
  100. passert(fabs(duty_cycle - 0.50) < 1e-3);
  101. passert(pwm_get_duty_cycle_ns(pwm, &duty_cycle_ns) == 0);
  102. passert(fabs(duty_cycle_ns - 500000) < 1e4);
  103. passert(pwm_set_duty_cycle(pwm, 0.75) == 0);
  104. passert(pwm_get_duty_cycle(pwm, &duty_cycle) == 0);
  105. passert(fabs(duty_cycle - 0.75) < 1e-3);
  106. passert(pwm_get_duty_cycle_ns(pwm, &duty_cycle_ns) == 0);
  107. passert(fabs(duty_cycle_ns - 750000) < 1e4);
  108. /* Set duty_cycle_ns, check duty_cycle_ns, check duty_cycle */
  109. passert(pwm_set_duty_cycle_ns(pwm, 250000) == 0);
  110. passert(pwm_get_duty_cycle_ns(pwm, &duty_cycle_ns) == 0);
  111. passert(fabs(duty_cycle_ns - 250000) < 1e4);
  112. passert(pwm_get_duty_cycle(pwm, &duty_cycle) == 0);
  113. passert(fabs(duty_cycle - 0.25) < 1e-3);
  114. passert(pwm_set_duty_cycle_ns(pwm, 500000) == 0);
  115. passert(pwm_get_duty_cycle_ns(pwm, &duty_cycle_ns) == 0);
  116. passert(fabs(duty_cycle_ns - 500000) < 1e4);
  117. passert(pwm_get_duty_cycle(pwm, &duty_cycle) == 0);
  118. passert(fabs(duty_cycle - 0.50) < 1e-3);
  119. passert(pwm_set_duty_cycle_ns(pwm, 750000) == 0);
  120. passert(pwm_get_duty_cycle_ns(pwm, &duty_cycle_ns) == 0);
  121. passert(fabs(duty_cycle_ns - 750000) < 1e4);
  122. passert(pwm_get_duty_cycle(pwm, &duty_cycle) == 0);
  123. passert(fabs(duty_cycle - 0.75) < 1e-3);
  124. /* Set polarity, check polarity */
  125. passert(pwm_set_polarity(pwm, PWM_POLARITY_NORMAL) == 0);
  126. passert(pwm_get_polarity(pwm, &polarity) == 0);
  127. passert(polarity == PWM_POLARITY_NORMAL);
  128. passert(pwm_set_polarity(pwm, PWM_POLARITY_INVERSED) == 0);
  129. passert(pwm_get_polarity(pwm, &polarity) == 0);
  130. passert(polarity == PWM_POLARITY_INVERSED);
  131. /* Set enabled, check enabled */
  132. passert(pwm_set_enabled(pwm, true) == 0);
  133. passert(pwm_get_enabled(pwm, &enabled) == 0);
  134. passert(enabled == true);
  135. passert(pwm_set_enabled(pwm, false) == 0);
  136. passert(pwm_get_enabled(pwm, &enabled) == 0);
  137. passert(enabled == false);
  138. /* Use pwm_enable()/pwm_disable(), check enabled */
  139. passert(pwm_enable(pwm) == 0);
  140. passert(pwm_get_enabled(pwm, &enabled) == 0);
  141. passert(enabled == true);
  142. passert(pwm_disable(pwm) == 0);
  143. passert(pwm_get_enabled(pwm, &enabled) == 0);
  144. passert(enabled == false);
  145. /* Set invalid polarity */
  146. passert(pwm_set_polarity(pwm, 123) == PWM_ERROR_ARG);
  147. passert(pwm_close(pwm) == 0);
  148. /* Free PWM */
  149. pwm_free(pwm);
  150. }
  151. void test_loopback(void) {
  152. ptest();
  153. }
  154. bool getc_yes(void) {
  155. char buf[4];
  156. fgets(buf, sizeof(buf), stdin);
  157. return (buf[0] == 'y' || buf[0] == 'Y');
  158. }
  159. void test_interactive(void) {
  160. char str[256];
  161. pwm_t *pwm;
  162. ptest();
  163. /* Allocate PWM */
  164. pwm = pwm_new();
  165. passert(pwm != NULL);
  166. passert(pwm_open(pwm, chip, channel) == 0);
  167. printf("Starting interactive test. Get out your oscilloscope, buddy!\n");
  168. printf("Press enter to continue...\n");
  169. getc(stdin);
  170. /* Set initial parameters and enable PWM */
  171. passert(pwm_set_duty_cycle(pwm, 0.0) == 0);
  172. passert(pwm_set_frequency(pwm, 1e3) == 0);
  173. passert(pwm_set_polarity(pwm, PWM_POLARITY_NORMAL) == 0);
  174. passert(pwm_enable(pwm) == 0);
  175. /* Check tostring */
  176. passert(pwm_tostring(pwm, str, sizeof(str)) > 0);
  177. printf("PWM description: %s\n", str);
  178. printf("PWM description looks OK? y/n\n");
  179. passert(getc_yes());
  180. /* Set 1 kHz frequency, 0.25 duty cycle */
  181. passert(pwm_set_frequency(pwm, 1e3) == 0);
  182. passert(pwm_set_duty_cycle(pwm, 0.25) == 0);
  183. printf("Frequency is 1 kHz, duty cycle is 25%%? y/n\n");
  184. passert(getc_yes());
  185. /* Set 1 kHz frequency, 0.50 duty cycle */
  186. passert(pwm_set_frequency(pwm, 1e3) == 0);
  187. passert(pwm_set_duty_cycle(pwm, 0.50) == 0);
  188. printf("Frequency is 1 kHz, duty cycle is 50%%? y/n\n");
  189. passert(getc_yes());
  190. /* Set 2 kHz frequency, 0.25 duty cycle */
  191. passert(pwm_set_frequency(pwm, 2e3) == 0);
  192. passert(pwm_set_duty_cycle(pwm, 0.25) == 0);
  193. printf("Frequency is 2 kHz, duty cycle is 25%%? y/n\n");
  194. passert(getc_yes());
  195. /* Set 2 kHz frequency, 0.50 duty cycle */
  196. passert(pwm_set_frequency(pwm, 2e3) == 0);
  197. passert(pwm_set_duty_cycle(pwm, 0.50) == 0);
  198. printf("Frequency is 2 kHz, duty cycle is 50%%? y/n\n");
  199. passert(getc_yes());
  200. passert(pwm_set_duty_cycle(pwm, 0.0) == 0);
  201. passert(pwm_disable(pwm) == 0);
  202. passert(pwm_close(pwm) == 0);
  203. /* Free PWM */
  204. pwm_free(pwm);
  205. }
  206. int main(int argc, char *argv[]) {
  207. if (argc < 2) {
  208. fprintf(stderr, "Usage: %s <PWM chip> <PWM channel>\n\n", argv[0]);
  209. fprintf(stderr, "[1/4] Arguments test: No requirements.\n");
  210. fprintf(stderr, "[2/4] Open/close test: PWM channel should be real.\n");
  211. fprintf(stderr, "[3/4] Loopback test: No test.\n");
  212. fprintf(stderr, "[4/4] Interactive test: PWM channel should be observed with an oscilloscope or logic analyzer.\n\n");
  213. fprintf(stderr, "Hint: for Raspberry Pi 3, enable PWM0 and PWM1 with:\n");
  214. fprintf(stderr, " $ echo \"dtoverlay=pwm-2chan,pin=18,func=2,pin2=13,func2=4\" | sudo tee -a /boot/config.txt\n");
  215. fprintf(stderr, " $ sudo reboot\n");
  216. fprintf(stderr, "Monitor GPIO 18 (header pin 12), and run this test with:\n");
  217. fprintf(stderr, " %s 0 0\n", argv[0]);
  218. fprintf(stderr, "or, monitor GPIO 13 (header pin 33), and run this test with:\n");
  219. fprintf(stderr, " %s 0 1\n\n", argv[0]);
  220. exit(1);
  221. }
  222. chip = strtoul(argv[1], NULL, 10);
  223. channel = strtoul(argv[2], NULL, 10);
  224. test_arguments();
  225. printf(" " STR_OK " Arguments test passed.\n\n");
  226. test_open_config_close();
  227. printf(" " STR_OK " Open/close test passed.\n\n");
  228. test_loopback();
  229. printf(" " STR_OK " Loopback test passed.\n\n");
  230. test_interactive();
  231. printf(" " STR_OK " Interactive test passed.\n\n");
  232. printf("All tests passed!\n");
  233. return 0;
  234. }