pwm.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. /****************************************************************************
  2. * drivers/timers/pwm.c
  3. *
  4. * Licensed to the Apache Software Foundation (ASF) under one or more
  5. * contributor license agreements. See the NOTICE file distributed with
  6. * this work for additional information regarding copyright ownership. The
  7. * ASF licenses this file to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance with the
  9. * License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  15. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  16. * License for the specific language governing permissions and limitations
  17. * under the License.
  18. *
  19. ****************************************************************************/
  20. /****************************************************************************
  21. * Included Files
  22. ****************************************************************************/
  23. #include <nuttx/config.h>
  24. #include <sys/types.h>
  25. #include <inttypes.h>
  26. #include <stdint.h>
  27. #include <stdbool.h>
  28. #include <stdlib.h>
  29. #include <unistd.h>
  30. #include <string.h>
  31. #include <fcntl.h>
  32. #include <assert.h>
  33. #include <errno.h>
  34. #include <debug.h>
  35. #include <nuttx/arch.h>
  36. #include <nuttx/kmalloc.h>
  37. #include <nuttx/semaphore.h>
  38. #include <nuttx/fs/fs.h>
  39. #include <nuttx/timers/pwm.h>
  40. #include <nuttx/irq.h>
  41. #ifdef CONFIG_PWM
  42. /****************************************************************************
  43. * Private Type Definitions
  44. ****************************************************************************/
  45. /* This structure describes the state of the upper half driver */
  46. struct pwm_upperhalf_s
  47. {
  48. uint8_t crefs; /* The number of times the device has
  49. * been opened */
  50. volatile bool started; /* True: pulsed output is being
  51. * generated */
  52. #ifdef CONFIG_PWM_PULSECOUNT
  53. volatile bool waiting; /* True: Caller is waiting for the pulse
  54. * count to expire */
  55. #endif
  56. sem_t exclsem; /* Supports mutual exclusion */
  57. #ifdef CONFIG_PWM_PULSECOUNT
  58. sem_t waitsem; /* Used to wait for the pulse count to
  59. * expire */
  60. #endif
  61. struct pwm_info_s info; /* Pulsed output characteristics */
  62. FAR struct pwm_lowerhalf_s *dev; /* lower-half state */
  63. };
  64. /****************************************************************************
  65. * Private Function Prototypes
  66. ****************************************************************************/
  67. static void pwm_dump(FAR const char *msg,
  68. FAR const struct pwm_info_s *info,
  69. bool started);
  70. static int pwm_open(FAR struct file *filep);
  71. static int pwm_close(FAR struct file *filep);
  72. static ssize_t pwm_read(FAR struct file *filep, FAR char *buffer,
  73. size_t buflen);
  74. static ssize_t pwm_write(FAR struct file *filep, FAR const char *buffer,
  75. size_t buflen);
  76. static int pwm_start(FAR struct pwm_upperhalf_s *upper,
  77. unsigned int oflags);
  78. static int pwm_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
  79. /****************************************************************************
  80. * Private Data
  81. ****************************************************************************/
  82. static const struct file_operations g_pwmops =
  83. {
  84. pwm_open, /* open */
  85. pwm_close, /* close */
  86. pwm_read, /* read */
  87. pwm_write, /* write */
  88. NULL, /* seek */
  89. pwm_ioctl, /* ioctl */
  90. NULL /* poll */
  91. };
  92. /****************************************************************************
  93. * Private Functions
  94. ****************************************************************************/
  95. /****************************************************************************
  96. * Name: pwm_dump
  97. ****************************************************************************/
  98. static void pwm_dump(FAR const char *msg, FAR const struct pwm_info_s *info,
  99. bool started)
  100. {
  101. #ifdef CONFIG_PWM_MULTICHAN
  102. int i;
  103. #endif
  104. pwminfo("%s: frequency: %" PRId32 "\n", msg, info->frequency);
  105. #ifdef CONFIG_PWM_MULTICHAN
  106. for (i = 0; i < CONFIG_PWM_NCHANNELS; i++)
  107. {
  108. pwminfo(" channel: %d duty: %08" PRIx32 "\n",
  109. info->channels[i].channel, info->channels[i].duty);
  110. }
  111. #else
  112. pwminfo(" duty: %08" PRIx32 "\n", info->duty);
  113. #endif
  114. #ifdef CONFIG_PWM_PULSECOUNT
  115. pwminfo(" count: %" PRIx32 "\n", info->count);
  116. #endif
  117. pwminfo(" started: %d\n", started);
  118. }
  119. /****************************************************************************
  120. * Name: pwm_open
  121. *
  122. * Description:
  123. * This function is called whenever the PWM device is opened.
  124. *
  125. ****************************************************************************/
  126. static int pwm_open(FAR struct file *filep)
  127. {
  128. FAR struct inode *inode = filep->f_inode;
  129. FAR struct pwm_upperhalf_s *upper = inode->i_private;
  130. uint8_t tmp;
  131. int ret;
  132. pwminfo("crefs: %d\n", upper->crefs);
  133. /* Get exclusive access to the device structures */
  134. ret = nxsem_wait(&upper->exclsem);
  135. if (ret < 0)
  136. {
  137. goto errout;
  138. }
  139. /* Increment the count of references to the device. If this the first
  140. * time that the driver has been opened for this device, then initialize
  141. * the device.
  142. */
  143. tmp = upper->crefs + 1;
  144. if (tmp == 0)
  145. {
  146. /* More than 255 opens; uint8_t overflows to zero */
  147. ret = -EMFILE;
  148. goto errout_with_sem;
  149. }
  150. /* Check if this is the first time that the driver has been opened. */
  151. if (tmp == 1)
  152. {
  153. FAR struct pwm_lowerhalf_s *lower = upper->dev;
  154. /* Yes.. perform one time hardware initialization. */
  155. DEBUGASSERT(lower->ops->setup != NULL);
  156. pwminfo("calling setup\n");
  157. ret = lower->ops->setup(lower);
  158. if (ret < 0)
  159. {
  160. goto errout_with_sem;
  161. }
  162. }
  163. /* Save the new open count on success */
  164. upper->crefs = tmp;
  165. ret = OK;
  166. errout_with_sem:
  167. nxsem_post(&upper->exclsem);
  168. errout:
  169. return ret;
  170. }
  171. /****************************************************************************
  172. * Name: pwm_close
  173. *
  174. * Description:
  175. * This function is called when the PWM device is closed.
  176. *
  177. ****************************************************************************/
  178. static int pwm_close(FAR struct file *filep)
  179. {
  180. FAR struct inode *inode = filep->f_inode;
  181. FAR struct pwm_upperhalf_s *upper = inode->i_private;
  182. int ret;
  183. pwminfo("crefs: %d\n", upper->crefs);
  184. /* Get exclusive access to the device structures */
  185. ret = nxsem_wait(&upper->exclsem);
  186. if (ret < 0)
  187. {
  188. goto errout;
  189. }
  190. /* Decrement the references to the driver. If the reference count will
  191. * decrement to 0, then uninitialize the driver.
  192. */
  193. if (upper->crefs > 1)
  194. {
  195. upper->crefs--;
  196. }
  197. else
  198. {
  199. FAR struct pwm_lowerhalf_s *lower = upper->dev;
  200. /* There are no more references to the port */
  201. upper->crefs = 0;
  202. /* Disable the PWM device */
  203. DEBUGASSERT(lower->ops->shutdown != NULL);
  204. pwminfo("calling shutdown\n");
  205. lower->ops->shutdown(lower);
  206. }
  207. ret = OK;
  208. nxsem_post(&upper->exclsem);
  209. errout:
  210. return ret;
  211. }
  212. /****************************************************************************
  213. * Name: pwm_read
  214. *
  215. * Description:
  216. * A dummy read method. This is provided only to satisfy the VFS layer.
  217. *
  218. ****************************************************************************/
  219. static ssize_t pwm_read(FAR struct file *filep, FAR char *buffer,
  220. size_t buflen)
  221. {
  222. /* Return zero -- usually meaning end-of-file */
  223. return 0;
  224. }
  225. /****************************************************************************
  226. * Name: pwm_write
  227. *
  228. * Description:
  229. * A dummy write method. This is provided only to satisfy the VFS layer.
  230. *
  231. ****************************************************************************/
  232. static ssize_t pwm_write(FAR struct file *filep, FAR const char *buffer,
  233. size_t buflen)
  234. {
  235. return 0;
  236. }
  237. /****************************************************************************
  238. * Name: pwm_start
  239. *
  240. * Description:
  241. * Handle the PWMIOC_START ioctl command
  242. *
  243. ****************************************************************************/
  244. #ifdef CONFIG_PWM_PULSECOUNT
  245. static int pwm_start(FAR struct pwm_upperhalf_s *upper, unsigned int oflags)
  246. {
  247. FAR struct pwm_lowerhalf_s *lower;
  248. irqstate_t flags;
  249. int ret = OK;
  250. DEBUGASSERT(upper != NULL);
  251. lower = upper->dev;
  252. DEBUGASSERT(lower != NULL && lower->ops->start != NULL);
  253. /* Verify that the PWM is not already running */
  254. if (!upper->started)
  255. {
  256. /* Disable interrupts to avoid race conditions */
  257. flags = enter_critical_section();
  258. /* Indicate that if will be waiting for the pulse count to complete.
  259. * Note that we will only wait if a non-zero pulse count is specified
  260. * and if the PWM driver was opened in normal, blocking mode. Also
  261. * assume for now that the pulse train will be successfully started.
  262. *
  263. * We do these things before starting the PWM to avoid race conditions.
  264. */
  265. upper->waiting = (upper->info.count > 0) &&
  266. ((oflags & O_NONBLOCK) == 0);
  267. upper->started = true;
  268. /* Invoke the bottom half method to start the pulse train */
  269. ret = lower->ops->start(lower, &upper->info, upper);
  270. /* A return value of zero means that the pulse train was started
  271. * successfully.
  272. */
  273. if (ret == OK)
  274. {
  275. /* Should we wait for the pulse output to complete? Loop in
  276. * in case the wakeup form nxsem_wait() is a false alarm.
  277. */
  278. while (upper->waiting)
  279. {
  280. /* Wait until we are awakened by pwm_expired(). When
  281. * pwm_expired is called, it will post the waitsem and
  282. * clear the waiting flag.
  283. */
  284. ret = nxsem_wait_uninterruptible(&upper->waitsem);
  285. if (ret < 0)
  286. {
  287. upper->started = false;
  288. upper->waiting = false;
  289. }
  290. }
  291. }
  292. else
  293. {
  294. /* Looks like we won't be waiting after all */
  295. pwminfo("start failed: %d\n", ret);
  296. upper->started = false;
  297. upper->waiting = false;
  298. }
  299. leave_critical_section(flags);
  300. }
  301. return ret;
  302. }
  303. #else
  304. static int pwm_start(FAR struct pwm_upperhalf_s *upper, unsigned int oflags)
  305. {
  306. FAR struct pwm_lowerhalf_s *lower;
  307. int ret = OK;
  308. DEBUGASSERT(upper != NULL);
  309. lower = upper->dev;
  310. DEBUGASSERT(lower != NULL && lower->ops->start != NULL);
  311. /* Verify that the PWM is not already running */
  312. if (!upper->started)
  313. {
  314. /* Invoke the bottom half method to start the pulse train */
  315. ret = lower->ops->start(lower, &upper->info);
  316. /* A return value of zero means that the pulse train was started
  317. * successfully.
  318. */
  319. if (ret == OK)
  320. {
  321. /* Indicate that the pulse train has started */
  322. upper->started = true;
  323. }
  324. }
  325. return ret;
  326. }
  327. #endif
  328. /****************************************************************************
  329. * Name: pwm_ioctl
  330. *
  331. * Description:
  332. * The standard ioctl method. This is where ALL of the PWM work is done.
  333. *
  334. ****************************************************************************/
  335. static int pwm_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
  336. {
  337. FAR struct inode *inode = filep->f_inode;
  338. FAR struct pwm_upperhalf_s *upper = inode->i_private;
  339. FAR struct pwm_lowerhalf_s *lower = upper->dev;
  340. int ret;
  341. pwminfo("cmd: %d arg: %ld\n", cmd, arg);
  342. /* Get exclusive access to the device structures */
  343. ret = nxsem_wait(&upper->exclsem);
  344. if (ret < 0)
  345. {
  346. return ret;
  347. }
  348. /* Handle built-in ioctl commands */
  349. switch (cmd)
  350. {
  351. /* PWMIOC_SETCHARACTERISTICS - Set the characteristics of the next
  352. * pulsed output. This command will neither start nor stop the
  353. * pulsed output. It will either setup the configuration that will
  354. * be used when the output is started; or it will change the
  355. * characteristics of the pulsed output on the fly if the timer is
  356. * already started.
  357. *
  358. * ioctl argument: A read-only reference to struct pwm_info_s that
  359. * provides the characteristics of the pulsed output.
  360. */
  361. case PWMIOC_SETCHARACTERISTICS:
  362. {
  363. FAR const struct pwm_info_s *info =
  364. (FAR const struct pwm_info_s *)((uintptr_t)arg);
  365. DEBUGASSERT(info != NULL && lower->ops->start != NULL);
  366. pwm_dump("PWMIOC_SETCHARACTERISTICS", info, upper->started);
  367. /* Save the pulse train characteristics */
  368. memcpy(&upper->info, info, sizeof(struct pwm_info_s));
  369. /* If PWM is already running, then re-start it with the new
  370. * characteristics.
  371. */
  372. if (upper->started)
  373. {
  374. #ifdef CONFIG_PWM_PULSECOUNT
  375. ret = lower->ops->start(lower, &upper->info, upper);
  376. #else
  377. ret = lower->ops->start(lower, &upper->info);
  378. #endif
  379. }
  380. }
  381. break;
  382. /* PWMIOC_GETCHARACTERISTICS - Get the currently selected
  383. * characteristics of the pulsed output (independent of whether the
  384. * output is start or stopped).
  385. *
  386. * ioctl argument: A reference to struct pwm_info_s to receive the
  387. * characteristics of the pulsed output.
  388. */
  389. case PWMIOC_GETCHARACTERISTICS:
  390. {
  391. FAR struct pwm_info_s *info =
  392. (FAR struct pwm_info_s *)((uintptr_t)arg);
  393. DEBUGASSERT(info != NULL);
  394. memcpy(info, &upper->info, sizeof(struct pwm_info_s));
  395. pwm_dump("PWMIOC_GETCHARACTERISTICS", info, upper->started);
  396. }
  397. break;
  398. /* PWMIOC_START - Start the pulsed output. The
  399. * PWMIOC_SETCHARACTERISTICS command must have previously been sent.
  400. *
  401. * ioctl argument: None
  402. */
  403. case PWMIOC_START:
  404. {
  405. pwm_dump("PWMIOC_START", &upper->info, upper->started);
  406. DEBUGASSERT(lower->ops->start != NULL);
  407. /* Start the pulse train */
  408. ret = pwm_start(upper, filep->f_oflags);
  409. }
  410. break;
  411. /* PWMIOC_STOP - Stop the pulsed output.
  412. *
  413. * ioctl argument: None
  414. */
  415. case PWMIOC_STOP:
  416. {
  417. pwminfo("PWMIOC_STOP: started: %d\n", upper->started);
  418. DEBUGASSERT(lower->ops->stop != NULL);
  419. if (upper->started)
  420. {
  421. ret = lower->ops->stop(lower);
  422. upper->started = false;
  423. #ifdef CONFIG_PWM_PULSECOUNT
  424. if (upper->waiting)
  425. {
  426. upper->waiting = false;
  427. }
  428. #endif
  429. }
  430. }
  431. break;
  432. /* Any unrecognized IOCTL commands might be platform-specific ioctl
  433. * commands.
  434. */
  435. default:
  436. {
  437. pwminfo("Forwarding unrecognized cmd: %d arg: %ld\n", cmd, arg);
  438. DEBUGASSERT(lower->ops->ioctl != NULL);
  439. ret = lower->ops->ioctl(lower, cmd, arg);
  440. }
  441. break;
  442. }
  443. nxsem_post(&upper->exclsem);
  444. return ret;
  445. }
  446. /****************************************************************************
  447. * Public Functions
  448. ****************************************************************************/
  449. /****************************************************************************
  450. * Name: pwm_register
  451. *
  452. * Description:
  453. * This function binds an instance of a "lower half" timer driver with the
  454. * "upper half" PWM device and registers that device so that can be used
  455. * by application code.
  456. *
  457. * When this function is called, the "lower half" driver should be in the
  458. * reset state (as if the shutdown() method had already been called).
  459. *
  460. * Input Parameters:
  461. * path - The full path to the driver to be registered in the NuttX pseudo-
  462. * filesystem. The recommended convention is to name all PWM drivers
  463. * as "/dev/pwm0", "/dev/pwm1", etc. where the driver path differs only
  464. * in the "minor" number at the end of the device name.
  465. * dev - A pointer to an instance of lower half timer driver. This
  466. * instance is bound to the PWM driver and must persists as long as the
  467. * driver persists.
  468. *
  469. * Returned Value:
  470. * Zero on success; a negated errno value on failure.
  471. *
  472. ****************************************************************************/
  473. int pwm_register(FAR const char *path, FAR struct pwm_lowerhalf_s *dev)
  474. {
  475. FAR struct pwm_upperhalf_s *upper;
  476. /* Allocate the upper-half data structure */
  477. upper = (FAR struct pwm_upperhalf_s *)
  478. kmm_zalloc(sizeof(struct pwm_upperhalf_s));
  479. if (!upper)
  480. {
  481. pwmerr("Allocation failed\n");
  482. return -ENOMEM;
  483. }
  484. /* Initialize the PWM device structure (it was already zeroed by
  485. * kmm_zalloc()).
  486. */
  487. nxsem_init(&upper->exclsem, 0, 1);
  488. #ifdef CONFIG_PWM_PULSECOUNT
  489. nxsem_init(&upper->waitsem, 0, 0);
  490. /* The wait semaphore is used for signaling and, hence, should not have
  491. * priority inheritance enabled.
  492. */
  493. nxsem_set_protocol(&upper->waitsem, SEM_PRIO_NONE);
  494. #endif
  495. upper->dev = dev;
  496. /* Register the PWM device */
  497. pwminfo("Registering %s\n", path);
  498. return register_driver(path, &g_pwmops, 0666, upper);
  499. }
  500. /****************************************************************************
  501. * Name: pwm_expired
  502. *
  503. * Description:
  504. * If CONFIG_PWM_PULSECOUNT is defined and the pulse count was configured
  505. * to a non-zero value, then the "upper half" driver will wait for the
  506. * pulse count to expire. The sequence of expected events is as follows:
  507. *
  508. * 1. The upper half driver calls the start method, providing the lower
  509. * half driver with the pulse train characteristics. If a fixed
  510. * number of pulses is required, the 'count' value will be nonzero.
  511. * 2. The lower half driver's start() methoc must verify that it can
  512. * support the request pulse train (frequency, duty, AND pulse count).
  513. * If it cannot, it should return an error. If the pulse count is
  514. * non-zero, it should set up the hardware for that number of pulses
  515. * and return success. NOTE: That is CONFIG_PWM_PULSECOUNT is
  516. * defined, the start() method receives an additional parameter
  517. * that must be used in this callback.
  518. * 3. When the start() method returns success, the upper half driver
  519. * will "sleep" until the pwm_expired method is called.
  520. * 4. When the lower half detects that the pulse count has expired
  521. * (probably through an interrupt), it must call the pwm_expired
  522. * interface using the handle that was previously passed to the
  523. * start() method
  524. *
  525. * Input Parameters:
  526. * handle - This is the handle that was provided to the lower-half
  527. * start() method.
  528. *
  529. * Returned Value:
  530. * None
  531. *
  532. * Assumptions:
  533. * This function may be called from an interrupt handler.
  534. *
  535. ****************************************************************************/
  536. #ifdef CONFIG_PWM_PULSECOUNT
  537. void pwm_expired(FAR void *handle)
  538. {
  539. FAR struct pwm_upperhalf_s *upper = (FAR struct pwm_upperhalf_s *)handle;
  540. pwminfo("started: %d waiting: %d\n", upper->started, upper->waiting);
  541. /* Make sure that the PWM is started */
  542. if (upper->started)
  543. {
  544. /* Is there a thread waiting for the pulse train to complete? */
  545. if (upper->waiting)
  546. {
  547. /* Yes.. clear the waiting flag and awakened the waiting thread */
  548. upper->waiting = false;
  549. nxsem_post(&upper->waitsem);
  550. }
  551. /* The PWM is now stopped */
  552. upper->started = false;
  553. }
  554. }
  555. #endif
  556. #endif /* CONFIG_PWM */